ui modification based on bug reported

This commit is contained in:
laxmanhalaki 2025-11-10 16:11:44 +05:30
parent 826c0eedea
commit 8c7965e469
33 changed files with 461 additions and 9397 deletions

View File

@ -1,276 +0,0 @@
# ✅ Auto-Migration Setup Complete
## 🎯 What Was Done
### 1. Converted SQL Migration to TypeScript
**Before**: `src/migrations/add_is_skipped_to_approval_levels.sql` (manual SQL)
**After**: `src/migrations/20251105-add-skip-fields-to-approval-levels.ts` (TypeScript)
**Features Added to `approval_levels` table**:
- ✅ `is_skipped` - Boolean flag to track skipped approvers
- ✅ `skipped_at` - Timestamp when approver was skipped
- ✅ `skipped_by` - Foreign key to user who skipped
- ✅ `skip_reason` - Text field for skip justification
- ✅ Partial index on `is_skipped = TRUE` for query performance
- ✅ Full rollback support in `down()` function
### 2. Updated Migration Runner
**File**: `src/scripts/migrate.ts`
**Changes**:
- Added import for new migration (m14)
- Added execution in run() function
- Enhanced console output with emojis for better visibility
- Better error messages
### 3. Auto-Run Migrations on Development Start
**File**: `package.json`
**Before**:
```json
"dev": "nodemon --exec ts-node -r tsconfig-paths/register src/server.ts"
```
**After**:
```json
"dev": "npm run migrate && nodemon --exec ts-node -r tsconfig-paths/register src/server.ts"
```
**What This Means**:
- 🔄 Migrations run automatically before server starts
- ✅ No more manual migration steps
- 🛡️ Server won't start if migrations fail
- ⚡ Fresh database schema on every dev restart
### 4. Created Documentation
- 📘 `MIGRATION_WORKFLOW.md` - Complete migration guide
- 📗 `MIGRATION_QUICK_REFERENCE.md` - Quick reference card
- 📕 `AUTO_MIGRATION_SETUP_COMPLETE.md` - This file
## 🚀 How to Use
### Starting Development (Most Common)
```bash
npm run dev
```
This will:
1. Connect to database
2. Run all 14 migrations sequentially
3. Start development server with hot reload
4. Display success messages
**Expected Output**:
```
📦 Database connected
🔄 Running migrations...
✅ Created workflow_requests table
✅ Created approval_levels table
...
✅ Added skip-related fields to approval_levels table
✅ All migrations applied successfully
🚀 Server running on port 5000
```
### Running Migrations Only
```bash
npm run migrate
```
Use when you want to update database without starting server.
## 📊 Migration Status
| # | Migration | Status | Date |
|---|-----------|--------|------|
| 1 | create-workflow-requests | ✅ Active | 2025-10-30 |
| 2 | create-approval-levels | ✅ Active | 2025-10-30 |
| 3 | create-participants | ✅ Active | 2025-10-30 |
| 4 | create-documents | ✅ Active | 2025-10-30 |
| 5 | create-subscriptions | ✅ Active | 2025-10-31 |
| 6 | create-activities | ✅ Active | 2025-10-31 |
| 7 | create-work-notes | ✅ Active | 2025-10-31 |
| 8 | create-work-note-attachments | ✅ Active | 2025-10-31 |
| 9 | add-tat-alert-fields | ✅ Active | 2025-11-04 |
| 10 | create-tat-alerts | ✅ Active | 2025-11-04 |
| 11 | create-kpi-views | ✅ Active | 2025-11-04 |
| 12 | create-holidays | ✅ Active | 2025-11-04 |
| 13 | create-admin-config | ✅ Active | 2025-11-04 |
| 14 | add-skip-fields-to-approval-levels | ✅ **NEW** | 2025-11-05 |
## 🔄 Adding Future Migrations
When you need to add a new migration:
### Step 1: Create File
```bash
# Create file: src/migrations/20251106-your-description.ts
```
### Step 2: Write Migration
```typescript
import { QueryInterface, DataTypes } from 'sequelize';
export async function up(queryInterface: QueryInterface): Promise<void> {
// Your changes here
await queryInterface.addColumn('table', 'column', {
type: DataTypes.STRING
});
console.log('✅ Your migration completed');
}
export async function down(queryInterface: QueryInterface): Promise<void> {
// Rollback here
await queryInterface.removeColumn('table', 'column');
console.log('✅ Rollback completed');
}
```
### Step 3: Register in migrate.ts
```typescript
// Add at top
import * as m15 from '../migrations/20251106-your-description';
// Add in run() function after m14
await (m15 as any).up(sequelize.getQueryInterface());
```
### Step 4: Test
```bash
npm run migrate
# or
npm run dev
```
## 🎯 Benefits
### For Development
- ✅ **No manual steps** - migrations run automatically
- ✅ **Consistent state** - everyone on team has same schema
- ✅ **Error prevention** - server won't start with schema mismatch
- ✅ **Fast iteration** - add migration, restart, test
### For Production
- ✅ **Idempotent** - safe to run multiple times
- ✅ **Versioned** - migrations tracked in git
- ✅ **Rollback support** - down() functions for reverting
- ✅ **Error handling** - clear failure messages
### For Team
- ✅ **TypeScript** - type-safe migrations
- ✅ **Documentation** - comprehensive guides
- ✅ **Best practices** - professional .NET team standards
- ✅ **Clear workflow** - easy to onboard new developers
## 🛡️ Safety Features
### Migration Execution
- Stops on first error
- Exits with error code 1 on failure
- Prevents server startup if migrations fail
- Detailed error logging
### Idempotency
All migrations should be idempotent (safe to run multiple times):
```typescript
// Check before adding
const tableDesc = await queryInterface.describeTable('table');
if (!tableDesc.column) {
await queryInterface.addColumn(/* ... */);
}
```
### Transactions
For complex migrations, wrap in transaction:
```typescript
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(/* ... */, { transaction });
await queryInterface.addIndex(/* ... */, { transaction });
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
```
## 📝 Database Structure Reference
Always refer to **`backend_structure.txt`** for:
- Current table schemas
- Column types and constraints
- Foreign key relationships
- Enum values
- Index definitions
## 🧪 Testing the Setup
### Test Migration System
```bash
# Run migrations
npm run migrate
# Should see:
# 📦 Database connected
# 🔄 Running migrations...
# ✅ [migration messages]
# ✅ All migrations applied successfully
```
### Test Auto-Run on Dev
```bash
# Start development
npm run dev
# Should see migrations run, then:
# 🚀 Server running on port 5000
# 📊 Environment: development
# ...
```
### Test New Migration
1. Create test migration file
2. Register in migrate.ts
3. Run `npm run dev`
4. Verify migration executed
5. Check database schema
## 🎓 Pro Tips
1. **Always test locally first** - never test migrations in production
2. **Backup before migrating** - especially in production
3. **Keep migrations atomic** - one logical change per file
4. **Write descriptive names** - make purpose clear
5. **Add comments** - explain why, not just what
6. **Test rollbacks** - verify down() functions work
7. **Update documentation** - keep backend_structure.txt current
8. **Review before committing** - migrations are permanent
## 📞 Support
- 📘 Full Guide: `MIGRATION_WORKFLOW.md`
- 📗 Quick Reference: `MIGRATION_QUICK_REFERENCE.md`
- 📊 Database Structure: `backend_structure.txt`
## ✨ Summary
Your development workflow is now streamlined:
```bash
# That's it! This one command does everything:
npm run dev
# 1. Runs all migrations ✅
# 2. Starts development server ✅
# 3. Enables hot reload ✅
# 4. You focus on coding ✅
```
---
**Setup Date**: November 5, 2025
**Total Migrations**: 14
**Auto-Run**: ✅ Enabled
**Status**: 🟢 Production Ready
**Team**: Royal Enfield .NET Expert Team

View File

@ -1,571 +0,0 @@
# 🎉 Complete TAT Implementation Guide
## ✅ EVERYTHING IS READY!
You now have a **production-ready TAT notification system** with:
- ✅ Automated notifications to approvers (50%, 75%, 100%)
- ✅ Complete alert storage in database
- ✅ Enhanced UI display with detailed time tracking
- ✅ Full KPI reporting capabilities
- ✅ Test mode for fast development
- ✅ API endpoints for custom queries
---
## 📊 Enhanced Alert Display
### **What Approvers See in Workflow Tab:**
```
┌────────────────────────────────────────────────────────────┐
│ Step 2: Lisa Wong (Finance Manager) │
│ Status: pending TAT: 12h Elapsed: 6.5h │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ⏳ Reminder 1 - 50% TAT Threshold [WARNING] │ │
│ │ │ │
│ │ 50% of SLA breach reminder have been sent │ │
│ │ │ │
│ │ Allocated: 12h │ Elapsed: 6.0h │ │
│ │ Remaining: 6.0h │ Due by: Oct 7 │ │
│ │ │ │
│ │ Reminder sent by system automatically │ │
│ │ Sent at: Oct 6 at 2:30 PM │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ⚠️ Reminder 2 - 75% TAT Threshold [WARNING] │ │
│ │ │ │
│ │ 75% of SLA breach reminder have been sent │ │
│ │ │ │
│ │ Allocated: 12h │ Elapsed: 9.0h │ │
│ │ Remaining: 3.0h │ Due by: Oct 7 │ │
│ │ │ │
│ │ Reminder sent by system automatically │ │
│ │ Sent at: Oct 6 at 6:30 PM │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```
---
## 🚀 Quick Start (3 Steps)
### **Step 1: Setup Upstash Redis** (2 minutes)
1. Go to: https://console.upstash.com/
2. Create free account
3. Create database: `redis-tat-dev`
4. Copy URL: `rediss://default:PASSWORD@host.upstash.io:6379`
### **Step 2: Configure Backend**
Edit `Re_Backend/.env`:
```bash
# Add these lines:
REDIS_URL=rediss://default:YOUR_PASSWORD@YOUR_HOST.upstash.io:6379
TAT_TEST_MODE=true
```
### **Step 3: Restart & Test**
```bash
cd Re_Backend
npm run dev
```
**You should see:**
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Worker is ready and listening
⏰ TAT Configuration:
- Test Mode: ENABLED (1 hour = 1 minute)
- Working Hours: 9:00 - 18:00
- Redis: rediss://***@upstash.io:6379
```
---
## 🧪 Test It (6 Minutes)
1. **Create Request** with 6-hour TAT
2. **Submit Request**
3. **Open Request Detail** → Workflow tab
4. **Watch Alerts Appear**:
- 3 min: ⏳ 50% alert with full details
- 4.5 min: ⚠️ 75% alert with full details
- 6 min: ⏰ 100% breach with full details
---
## 📦 What's Been Implemented
### **Backend Components:**
| Component | Purpose | File |
|-----------|---------|------|
| **TAT Time Utils** | Working hours calculation | `utils/tatTimeUtils.ts` |
| **TAT Queue** | BullMQ queue setup | `queues/tatQueue.ts` |
| **TAT Worker** | Background job processor | `queues/tatWorker.ts` |
| **TAT Processor** | Alert handler | `queues/tatProcessor.ts` |
| **TAT Scheduler** | Job scheduling service | `services/tatScheduler.service.ts` |
| **TAT Alert Model** | Database model | `models/TatAlert.ts` |
| **TAT Controller** | API endpoints | `controllers/tat.controller.ts` |
| **TAT Routes** | API routes | `routes/tat.routes.ts` |
| **TAT Config** | Configuration | `config/tat.config.ts` |
### **Database:**
| Object | Purpose |
|--------|---------|
| `tat_alerts` table | Store all TAT notifications |
| `approval_levels` (updated) | Added 4 TAT status fields |
| 8 KPI Views | Pre-aggregated reporting data |
### **Frontend:**
| Component | Change |
|-----------|--------|
| `RequestDetail.tsx` | Display TAT alerts in workflow tab |
| Enhanced cards | Show detailed time tracking |
| Test mode indicator | Purple badge when in test mode |
---
## 🔑 Key Features
### **1. Approver-Specific Alerts**
- Sent ONLY to current approver
- NOT to initiator or previous approvers
- Each level gets its own alert set
### **2. Detailed Time Tracking**
- Allocated hours
- Elapsed hours (when alert sent)
- Remaining hours (color-coded if critical)
- Due date/time
### **3. Test Mode Support**
- 1 hour = 1 minute for fast testing
- Purple badge indicator
- Clear note to prevent confusion
- Easy toggle in `.env`
### **4. Complete Audit Trail**
- Every alert stored in database
- Completion status tracked
- Response time measured
- KPI-ready data
### **5. Visual Clarity**
- Color-coded by threshold (yellow/orange/red)
- Icons (⏳/⚠️/⏰)
- Status badges (WARNING/BREACHED)
- Grid layout for time details
---
## 📊 KPI Capabilities
### **All Your Required KPIs Supported:**
#### Request Volume & Status ✅
- Total Requests Created
- Open Requests (with age)
- Approved/Rejected Requests
#### TAT Efficiency ✅
- Average TAT Compliance %
- Avg Approval Cycle Time
- Delayed Workflows
- Breach History & Trends
#### Approver Load ✅
- Pending Actions (My Queue)
- Approvals Completed
- Response Time After Alerts
#### Engagement & Quality ✅
- Comments/Work Notes
- Documents Uploaded
- Collaboration Metrics
---
## 🎯 Production Deployment
### **When Ready for Production:**
1. **Disable Test Mode:**
```bash
# .env
TAT_TEST_MODE=false
```
2. **Choose Redis Option:**
**Option A: Keep Upstash** (Recommended)
```bash
REDIS_URL=rediss://default:...@upstash.io:6379
```
- ✅ Zero maintenance
- ✅ Global CDN
- ✅ Auto-scaling
**Option B: Self-Hosted Redis**
```bash
# On Linux server:
sudo apt install redis-server -y
sudo systemctl start redis-server
# .env
REDIS_URL=redis://localhost:6379
```
- ✅ Full control
- ✅ No external dependency
- ✅ Free forever
3. **Set Working Hours:**
```bash
WORK_START_HOUR=9
WORK_END_HOUR=18
```
4. **Restart Backend**
---
## 📚 Complete Documentation Index
| Document | Purpose | When to Read |
|----------|---------|--------------|
| **START_HERE.md** | Quick setup | Read first! |
| **TAT_QUICK_START.md** | 5-min guide | Getting started |
| **TAT_ENHANCED_DISPLAY_SUMMARY.md** | UI guide | Understanding display |
| **COMPLETE_TAT_IMPLEMENTATION_GUIDE.md** | This doc | Overview |
| **docs/TAT_NOTIFICATION_SYSTEM.md** | Architecture | Deep dive |
| **docs/KPI_REPORTING_SYSTEM.md** | KPI queries | Building reports |
| **docs/UPSTASH_SETUP_GUIDE.md** | Redis setup | Redis config |
| **UPSTASH_QUICK_REFERENCE.md** | Commands | Daily reference |
| **KPI_SETUP_COMPLETE.md** | KPI summary | KPI overview |
| **TAT_ALERTS_DISPLAY_COMPLETE.md** | Display docs | UI integration |
---
## 🔍 Troubleshooting
### **No Alerts Showing in UI?**
**Check:**
1. Redis connected? Look for "Connected to Redis" in logs
2. Request submitted? (Not just created)
3. Waited long enough? (3 min in test mode, 12h in production for 24h TAT)
4. Check browser console for errors
5. Verify `tatAlerts` in API response
**Debug:**
```sql
-- Check if alerts exist in database
SELECT * FROM tat_alerts
WHERE request_id = 'YOUR_REQUEST_ID'
ORDER BY alert_sent_at;
```
### **Alerts Not Triggering?**
**Check:**
1. TAT worker running? Look for "TAT Worker: Initialized" in logs
2. Jobs scheduled? Look for "TAT jobs scheduled" in logs
3. Redis queue status:
```bash
# In Upstash Console → CLI:
KEYS bull:tatQueue:*
```
### **Confusing Times in Test Mode?**
**Solution:**
- Look for purple "TEST MODE" badge
- Read note: "Test mode active (1 hour = 1 minute)"
- For production feel, set `TAT_TEST_MODE=false`
---
## 📈 Sample KPI Queries
### **TAT Compliance This Month:**
```sql
SELECT
ROUND(
COUNT(CASE WHEN was_completed_on_time = true THEN 1 END) * 100.0 /
NULLIF(COUNT(*), 0),
2
) as compliance_rate
FROM tat_alerts
WHERE DATE(alert_sent_at) >= DATE_TRUNC('month', CURRENT_DATE)
AND was_completed_on_time IS NOT NULL;
```
### **Top Performers (On-Time Completion):**
```sql
SELECT
u.display_name,
u.department,
COUNT(DISTINCT ta.level_id) as total_approvals,
COUNT(CASE WHEN ta.was_completed_on_time = true THEN 1 END) as on_time,
ROUND(
COUNT(CASE WHEN ta.was_completed_on_time = true THEN 1 END) * 100.0 /
NULLIF(COUNT(DISTINCT ta.level_id), 0),
2
) as compliance_rate
FROM tat_alerts ta
JOIN users u ON ta.approver_id = u.user_id
WHERE ta.was_completed_on_time IS NOT NULL
GROUP BY u.user_id, u.display_name, u.department
ORDER BY compliance_rate DESC
LIMIT 10;
```
### **Breach Trend (Last 30 Days):**
```sql
SELECT
DATE(alert_sent_at) as date,
COUNT(CASE WHEN alert_type = 'TAT_50' THEN 1 END) as warnings_50,
COUNT(CASE WHEN alert_type = 'TAT_75' THEN 1 END) as warnings_75,
COUNT(CASE WHEN is_breached = true THEN 1 END) as breaches
FROM tat_alerts
WHERE alert_sent_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(alert_sent_at)
ORDER BY date DESC;
```
---
## ✨ Benefits Recap
### **For Approvers:**
- 📧 Get timely notifications (50%, 75%, 100%)
- 📊 See historical reminders in request details
- ⏱️ Know exactly how much time remaining
- 🎯 Clear deadlines and expectations
### **For Management:**
- 📈 Track TAT compliance rates
- 👥 Identify bottlenecks and delays
- 📊 Generate performance reports
- 🎯 Data-driven decision making
### **For System Admins:**
- 🔧 Easy configuration
- 📝 Complete audit trail
- 🚀 Scalable architecture
- 🛠️ Robust error handling
---
## 🎓 Next Steps
1. ✅ **Setup Redis** (Upstash recommended)
2. ✅ **Enable Test Mode** (`TAT_TEST_MODE=true`)
3. ✅ **Test with 6-hour TAT** (becomes 6 minutes)
4. ✅ **Verify alerts display** in Request Detail
5. ✅ **Check database** for stored alerts
6. ✅ **Run KPI queries** to verify data
7. ✅ **Build dashboards** using KPI views
8. ✅ **Deploy to production** when ready
---
## 📞 Support
**Documentation:**
- Read `START_HERE.md` for immediate setup
- Check `TAT_QUICK_START.md` for testing
- Review `docs/` folder for detailed guides
**Troubleshooting:**
- Check backend logs: `logs/app.log`
- Verify Redis: Upstash Console → CLI → `PING`
- Query database: See KPI queries above
- Review worker status: Look for "TAT Worker" in logs
---
## 🎉 Status Summary
| Component | Status | Notes |
|-----------|--------|-------|
| **Packages Installed** | ✅ | bullmq, ioredis, dayjs |
| **Database Schema** | ✅ | tat_alerts table + 4 fields in approval_levels |
| **KPI Views** | ✅ | 8 views created |
| **Backend Services** | ✅ | Scheduler, processor, worker |
| **API Endpoints** | ✅ | 5 TAT endpoints |
| **Frontend Display** | ✅ | Enhanced cards in workflow tab |
| **Test Mode** | ✅ | Configurable via .env |
| **Documentation** | ✅ | 10+ guides created |
| **Migrations** | ✅ | All applied successfully |
| **Redis Connection** | ⏳ | **You need to setup** |
---
## 🎯 Final Checklist
- [ ] Read `START_HERE.md`
- [ ] Setup Upstash Redis (https://console.upstash.com/)
- [ ] Add `REDIS_URL` to `.env`
- [ ] Set `TAT_TEST_MODE=true`
- [ ] Restart backend server
- [ ] Verify logs show "Connected to Redis"
- [ ] Create test request (6-hour TAT)
- [ ] Submit request
- [ ] Open Request Detail → Workflow tab
- [ ] See first alert at 3 minutes ⏳
- [ ] See second alert at 4.5 minutes ⚠️
- [ ] See third alert at 6 minutes ⏰
- [ ] Verify in database: `SELECT * FROM tat_alerts`
- [ ] Test KPI queries
- [ ] Approve request and verify completion tracking
✅ **All done? You're production ready!**
---
## 📂 Files Created/Modified
### **New Files (35):**
**Backend:**
- `src/utils/tatTimeUtils.ts`
- `src/queues/tatQueue.ts`
- `src/queues/tatWorker.ts`
- `src/queues/tatProcessor.ts`
- `src/services/tatScheduler.service.ts`
- `src/models/TatAlert.ts`
- `src/controllers/tat.controller.ts`
- `src/routes/tat.routes.ts`
- `src/config/tat.config.ts`
- `src/migrations/20251104-add-tat-alert-fields.ts`
- `src/migrations/20251104-create-tat-alerts.ts`
- `src/migrations/20251104-create-kpi-views.ts`
**Documentation:**
- `START_HERE.md`
- `TAT_QUICK_START.md`
- `UPSTASH_QUICK_REFERENCE.md`
- `INSTALL_REDIS.txt`
- `KPI_SETUP_COMPLETE.md`
- `TAT_ALERTS_DISPLAY_COMPLETE.md`
- `TAT_ENHANCED_DISPLAY_SUMMARY.md`
- `COMPLETE_TAT_IMPLEMENTATION_GUIDE.md` (this file)
- `docs/TAT_NOTIFICATION_SYSTEM.md`
- `docs/TAT_TESTING_GUIDE.md`
- `docs/UPSTASH_SETUP_GUIDE.md`
- `docs/KPI_REPORTING_SYSTEM.md`
- `docs/REDIS_SETUP_WINDOWS.md`
### **Modified Files (7):**
**Backend:**
- `src/models/ApprovalLevel.ts` - Added TAT status fields
- `src/models/index.ts` - Export TatAlert
- `src/services/workflow.service.ts` - Include TAT alerts, schedule jobs
- `src/services/approval.service.ts` - Cancel jobs, update alerts
- `src/server.ts` - Initialize worker, log config
- `src/routes/index.ts` - Register TAT routes
- `src/scripts/migrate.ts` - Include new migrations
**Frontend:**
- `src/pages/RequestDetail/RequestDetail.tsx` - Display TAT alerts
**Infrastructure:**
- `env.example` - Added Redis and test mode config
- `docker-compose.yml` - Added Redis service
- `package.json` - Added dependencies
---
## 💾 Database Schema Summary
### **New Table: `tat_alerts`**
```
17 columns, 7 indexes
Stores every TAT notification sent
Tracks completion status for KPIs
```
### **Updated Table: `approval_levels`**
```
Added 4 columns:
- tat50_alert_sent
- tat75_alert_sent
- tat_breached
- tat_start_time
```
### **New Views: 8 KPI Views**
```
- vw_request_volume_summary
- vw_tat_compliance
- vw_approver_performance
- vw_tat_alerts_summary
- vw_department_summary
- vw_daily_kpi_metrics
- vw_workflow_aging
- vw_engagement_metrics
```
---
## 🌟 Production Best Practices
1. **Monitor Redis Health**
- Check connection in logs
- Monitor queue size
- Set up alerts for failures
2. **Regular Database Maintenance**
- Archive old TAT alerts (> 1 year)
- Refresh materialized views if using
- Monitor query performance
3. **Test Mode Management**
- NEVER use test mode in production
- Document when test mode is on
- Clear test data regularly
4. **Alert Thresholds**
- Adjust if needed (currently 50%, 75%, 100%)
- Can be configured in `tat.config.ts`
- Consider business requirements
5. **Working Hours**
- Verify for your organization
- Update holidays if needed
- Consider time zones for global teams
---
## 🎊 Congratulations!
You've implemented a **world-class TAT notification system** with:
✅ Automated notifications
✅ Complete tracking
✅ Beautiful UI display
✅ Comprehensive KPIs
✅ Production-ready architecture
✅ Excellent documentation
**Just connect Redis and you're live!** 🚀
---
**See `START_HERE.md` for immediate next steps!**
---
**Last Updated**: November 4, 2025
**Version**: 1.0.0
**Status**: ✅ Production Ready
**Team**: Royal Enfield Workflow System

View File

@ -1,281 +0,0 @@
# 📊 Design Document vs Actual Implementation
## Overview
The `backend_structure.txt` is a **DESIGN DOCUMENT** that shows the intended/planned database structure. However, not all tables have been implemented yet.
---
## ✅ **Currently Implemented Tables**
| Table | Status | Migration File | Notes |
|-------|--------|---------------|-------|
| `users` | ✅ Implemented | (Okta-based, external) | User management |
| `workflow_requests` | ✅ Implemented | 2025103001-create-workflow-requests.ts | Core workflow |
| `approval_levels` | ✅ Implemented | 2025103002-create-approval-levels.ts | Approval hierarchy |
| `participants` | ✅ Implemented | 2025103003-create-participants.ts | Spectators, etc. |
| `documents` | ✅ Implemented | 2025103004-create-documents.ts | File uploads |
| `subscriptions` | ✅ Implemented | 20251031_01_create_subscriptions.ts | Push notifications |
| `activities` | ✅ Implemented | 20251031_02_create_activities.ts | Activity log |
| `work_notes` | ✅ Implemented | 20251031_03_create_work_notes.ts | Chat/comments |
| `work_note_attachments` | ✅ Implemented | 20251031_04_create_work_note_attachments.ts | Chat attachments |
| `tat_alerts` | ✅ Implemented | 20251104-create-tat-alerts.ts | TAT notification history |
| **`holidays`** | ✅ Implemented | 20251104-create-holidays.ts | **NEW - Not in design** |
| **`admin_configurations`** | ✅ Implemented | 20251104-create-admin-config.ts | **Similar to planned `system_settings`** |
---
## ❌ **Planned But Not Yet Implemented**
| Table | Status | Design Location | Purpose |
|-------|--------|----------------|---------|
| `notifications` | ❌ Not Implemented | Lines 186-205 | Notification management |
| **`tat_tracking`** | ❌ Not Implemented | Lines 207-225 | **Real-time TAT tracking** |
| `conclusion_remarks` | ❌ Not Implemented | Lines 227-242 | AI-generated conclusions |
| `audit_logs` | ❌ Not Implemented | Lines 244-262 | Comprehensive audit trail |
| `user_sessions` | ❌ Not Implemented | Lines 264-280 | Session management |
| `email_logs` | ❌ Not Implemented | Lines 282-301 | Email tracking |
| `sms_logs` | ❌ Not Implemented | Lines 303-321 | SMS tracking |
| **`system_settings`** | ❌ Not Implemented | Lines 323-337 | **System configuration** |
| `workflow_templates` | ❌ Not Implemented | Lines 339-351 | Template system |
| `report_cache` | ❌ Not Implemented | Lines 353-362 | Report caching |
---
## ⚠️ **Key Discrepancies**
### **1. `admin_configurations` vs `system_settings`**
**Problem:** I created `admin_configurations` which overlaps with the planned `system_settings`.
**Design (`system_settings`):**
```sql
system_settings {
setting_id PK
setting_key UK
setting_value
setting_type
setting_category
is_editable
is_sensitive
validation_rules
...
}
```
**What I Created (`admin_configurations`):**
```sql
admin_configurations {
config_id PK
config_key UK
config_value
value_type
config_category
is_editable
is_sensitive
validation_rules
...
}
```
**Resolution Options:**
**Option A:** Rename `admin_configurations``system_settings`
- ✅ Matches design document
- ✅ Consistent naming
- ⚠️ Requires migration to rename table
**Option B:** Keep `admin_configurations`, skip `system_settings`
- ✅ No migration needed
- ✅ Already implemented and working
- ⚠️ Deviates from design
**Option C:** Use both tables
- ❌ Redundant
- ❌ Confusing
- ❌ Not recommended
**RECOMMENDATION:** **Option A** - Rename to `system_settings` to match design document.
---
### **2. `tat_alerts` vs `tat_tracking`**
**Status:** These serve **DIFFERENT purposes** and should **COEXIST**.
**`tat_alerts` (Implemented):**
- Historical record of TAT alerts sent
- Stores when 50%, 75%, 100% alerts were sent
- Immutable records for audit trail
- Purpose: **Alert History**
**`tat_tracking` (Planned, Not Implemented):**
```sql
tat_tracking {
tracking_type "REQUEST or LEVEL"
tat_status "ON_TRACK to BREACHED"
elapsed_hours
remaining_hours
percentage_used
threshold_50_breached
threshold_80_breached
...
}
```
- Real-time tracking of TAT status
- Continuously updated as time passes
- Shows current TAT health
- Purpose: **Real-time Monitoring**
**Resolution:** Both tables should exist.
**RECOMMENDATION:** Implement `tat_tracking` table as per design document.
---
### **3. `holidays` Table**
**Status:** **NEW addition** not in original design.
**Resolution:** This is fine! It's a feature enhancement that was needed for accurate TAT calculations.
**RECOMMENDATION:** Add `holidays` to the design document for future reference.
---
## 📋 **Recommended Actions**
### **Immediate Actions:**
1. **Rename `admin_configurations` to `system_settings`**
```sql
ALTER TABLE admin_configurations RENAME TO system_settings;
ALTER INDEX admin_configurations_pkey RENAME TO system_settings_pkey;
ALTER INDEX admin_configurations_config_category RENAME TO system_settings_config_category;
-- etc.
```
2. **Update all references in code:**
- Model: `AdminConfiguration``SystemSetting`
- Service: `adminConfig``systemSettings`
- Routes: `/admin/configurations``/admin/settings`
- Controller: `admin.controller.ts` → Update variable names
3. **Implement `tat_tracking` table** (as per design):
- Create migration for `tat_tracking`
- Implement model and service
- Integrate with TAT calculation system
- Use for real-time dashboard
4. **Update `backend_structure.txt`**:
- Add `holidays` table to design
- Update `system_settings` if we made any changes
- Add `tat_alerts` if not present
---
### **Future Implementations (Phase 2):**
Based on the design document, these should be implemented next:
1. **`notifications` table** - In-app notification system
2. **`conclusion_remarks` table** - AI-generated conclusions
3. **`audit_logs` table** - Comprehensive audit trail (currently using `activities`)
4. **`email_logs` & `sms_logs`** - Communication tracking
5. **`workflow_templates`** - Template system for common workflows
6. **`report_cache`** - Performance optimization for reports
---
## 📊 **Implementation Progress**
### **Core Workflow:**
- ✅ Users
- ✅ Workflow Requests
- ✅ Approval Levels
- ✅ Participants
- ✅ Documents
- ✅ Work Notes
- ✅ Activities
### **TAT & Monitoring:**
- ✅ TAT Alerts (historical)
- ✅ Holidays (for TAT calculation)
- ❌ TAT Tracking (real-time) **← MISSING**
### **Configuration & Admin:**
- ✅ Admin Configurations (needs rename to `system_settings`)
- ❌ Workflow Templates **← MISSING**
### **Notifications & Logs:**
- ✅ Subscriptions (push notifications)
- ❌ Notifications table **← MISSING**
- ❌ Email Logs **← MISSING**
- ❌ SMS Logs **← MISSING**
### **Advanced Features:**
- ❌ Conclusion Remarks (AI) **← MISSING**
- ❌ Audit Logs **← MISSING**
- ❌ Report Cache **← MISSING**
---
## 🎯 **Alignment with Design Document**
### **What Matches Design:**
- ✅ Core workflow tables (90% match)
- ✅ Work notes system
- ✅ Document management
- ✅ Activity logging
### **What Differs:**
- ⚠️ `admin_configurations` should be `system_settings`
- ⚠️ `tat_alerts` exists but `tat_tracking` doesn't
- ✅ `holidays` is a new addition (enhancement)
### **What's Missing:**
- ❌ 10 tables from design not yet implemented
- ❌ Some relationships not fully realized
---
## 💡 **Recommendations Summary**
### **Critical (Do Now):**
1. ✅ **Rename `admin_configurations` to `system_settings`** - Align with design
2. ✅ **Implement `tat_tracking` table** - Complete TAT system
3. ✅ **Update design document** - Add holidays table
### **Important (Phase 2):**
4. ⏳ **Implement `notifications` table** - Centralized notification management
5. ⏳ **Implement `audit_logs` table** - Enhanced audit trail
6. ⏳ **Implement `email_logs` & `sms_logs`** - Communication tracking
### **Nice to Have (Phase 3):**
7. 🔮 **Implement `conclusion_remarks`** - AI integration
8. 🔮 **Implement `workflow_templates`** - Template system
9. 🔮 **Implement `report_cache`** - Performance optimization
---
## 📝 **Conclusion**
**Answer to the question:** "Did you consider backend_structure.txt?"
**Honest Answer:** Not fully. I created `admin_configurations` without checking that `system_settings` was already designed. However:
1. ✅ The functionality is the same
2. ⚠️ The naming is different
3. 🔧 Easy to fix with a rename migration
**Next Steps:**
1. Decide: Rename to `system_settings` (recommended) or keep as-is?
2. Implement missing `tat_tracking` table
3. Update design document with new `holidays` table
---
**Created:** November 4, 2025
**Status:** Analysis Complete
**Action Required:** Yes - Table rename + implement tat_tracking

View File

@ -1,341 +0,0 @@
# Dynamic TAT Thresholds Implementation
## Problem Statement
### Original Issue
The TAT system had **hardcoded threshold percentages** (50%, 75%, 100%) which created several problems:
1. **Job Naming Conflict**: Jobs were named using threshold percentages (`tat50-{reqId}-{levelId}`)
2. **Configuration Changes Didn't Apply**: Changing threshold in settings didn't affect scheduled jobs
3. **Message Mismatch**: Messages always said "50% elapsed" even if admin configured 55%
4. **Cancellation Issues**: Uncertainty about whether jobs could be properly cancelled after config changes
### Critical Edge Case Identified by User
**Scenario:**
```
1. Request created → TAT jobs scheduled:
- tat50-REQ123-LEVEL456 (fires at 8 hours, says "50% elapsed")
- tat75-REQ123-LEVEL456 (fires at 12 hours)
- tatBreach-REQ123-LEVEL456 (fires at 16 hours)
2. Admin changes threshold from 50% → 55%
3. User approves at 9 hours (after old 50% fired)
→ Job already fired with "50% elapsed" message ❌
→ But admin configured 55% ❌
→ Inconsistent!
4. Even if approval happens before old 50%:
→ System cancels `tat50-REQ123-LEVEL456`
→ But message would still say "50%" (hardcoded) ❌
```
---
## Solution: Generic Job Names + Dynamic Thresholds
### 1. **Generic Job Naming**
Changed from percentage-based to generic names:
**Before:**
```typescript
tat50-{requestId}-{levelId}
tat75-{requestId}-{levelId}
tatBreach-{requestId}-{levelId}
```
**After:**
```typescript
tat-threshold1-{requestId}-{levelId} // First threshold (configurable: 50%, 55%, 60%, etc.)
tat-threshold2-{requestId}-{levelId} // Second threshold (configurable: 75%, 80%, etc.)
tat-breach-{requestId}-{levelId} // Always 100% (deadline)
```
### 2. **Store Threshold in Job Data**
Instead of relying on job name, we store the actual percentage in job payload:
```typescript
interface TatJobData {
type: 'threshold1' | 'threshold2' | 'breach';
threshold: number; // Actual % (e.g., 55, 80, 100)
requestId: string;
levelId: string;
approverId: string;
}
```
### 3. **Dynamic Message Generation**
Messages use the threshold from job data:
```typescript
case 'threshold1':
message = `⏳ ${threshold}% of TAT elapsed for Request ${requestNumber}`;
// If threshold = 55, message says "55% of TAT elapsed" ✅
```
### 4. **Configuration Cache Management**
- Configurations are cached for 5 minutes (performance)
- Cache is **automatically cleared** when admin updates settings
- Next scheduled job will use new thresholds
---
## How It Solves the Edge Cases
### ✅ **Case 1: Config Changed After Job Creation**
**Scenario:**
```
1. Request created with TAT = 16 hours (thresholds: 50%, 75%)
Jobs scheduled:
- tat-threshold1-REQ123 → fires at 8h, threshold=50
- tat-threshold2-REQ123 → fires at 12h, threshold=75
2. Admin changes threshold from 50% → 55%
3. Old request jobs STILL fire at 8h (50%)
✅ BUT message correctly shows "50% elapsed" (from job data)
✅ No confusion because that request WAS scheduled at 50%
4. NEW requests created after config change:
Jobs scheduled:
- tat-threshold1-REQ456 → fires at 8.8h, threshold=55 ✅
- tat-threshold2-REQ456 → fires at 12h, threshold=75
5. Message says "55% of TAT elapsed" ✅ CORRECT!
```
**Result:**
- ✅ Existing jobs maintain their original thresholds (consistent)
- ✅ New jobs use updated thresholds (respects config changes)
- ✅ Messages always match actual threshold used
---
### ✅ **Case 2: User Approves Before Threshold**
**Scenario:**
```
1. Job scheduled: tat-threshold1-REQ123 (fires at 55%)
2. User approves at 40% elapsed
3. cancelTatJobs('REQ123', 'LEVEL456') is called:
→ Looks for: tat-threshold1-REQ123-LEVEL456 ✅ FOUND
→ Removes job ✅ SUCCESS
4. No notification sent ✅ CORRECT!
```
**Result:**
- ✅ Generic names allow consistent cancellation
- ✅ Works regardless of threshold percentage
- ✅ No ambiguity in job identification
---
### ✅ **Case 3: User Approves After Threshold Fired**
**Scenario:**
```
1. Job scheduled: tat-threshold1-REQ123 (fires at 55%)
2. Job fires at 55% → notification sent
3. User approves at 60%
4. cancelTatJobs called:
→ Tries to cancel tat-threshold1-REQ123
→ Job already processed and removed (removeOnComplete: true)
→ No error (gracefully handled) ✅
5. Later jobs (threshold2, breach) are still cancelled ✅
```
**Result:**
- ✅ Already-fired jobs don't cause errors
- ✅ Remaining jobs are still cancelled
- ✅ System behaves correctly in all scenarios
---
## Configuration Flow
### **Admin Updates Threshold**
```
1. Admin changes "First TAT Threshold" from 50% → 55%
2. Frontend sends: PUT /api/v1/admin/configurations/TAT_REMINDER_THRESHOLD_1
Body: { configValue: '55' }
3. Backend updates database:
UPDATE admin_configurations
SET config_value = '55'
WHERE config_key = 'TAT_REMINDER_THRESHOLD_1'
4. Backend clears config cache:
clearConfigCache() ✅
5. Next request created:
- getTatThresholds() → reads '55' from DB
- Schedules job at 55% (8.8 hours for 16h TAT)
- Job data: { threshold: 55 }
6. Job fires at 55%:
- Message: "55% of TAT elapsed" ✅ CORRECT!
```
---
## Database Impact
### **No Database Changes Required!**
The `admin_configurations` table already has all required fields:
- ✅ `TAT_REMINDER_THRESHOLD_1` → First threshold (50% default)
- ✅ `TAT_REMINDER_THRESHOLD_2` → Second threshold (75% default)
### **Job Queue Data Structure**
**Old Job Data:**
```json
{
"type": "tat50",
"requestId": "...",
"levelId": "...",
"approverId": "..."
}
```
**New Job Data:**
```json
{
"type": "threshold1",
"threshold": 55,
"requestId": "...",
"levelId": "...",
"approverId": "..."
}
```
---
## Testing Scenarios
### **Test 1: Change Threshold, Create New Request**
```bash
# 1. Change threshold from 50% to 55%
curl -X PUT http://localhost:5000/api/v1/admin/configurations/TAT_REMINDER_THRESHOLD_1 \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"configValue": "55"}'
# 2. Create new workflow request
# → Jobs scheduled at 55%, 75%, 100%
# 3. Wait for 55% elapsed
# → Notification says "55% of TAT elapsed" ✅
```
### **Test 2: Approve Before Threshold**
```bash
# 1. Request created (TAT = 16 hours)
# → threshold1 scheduled at 8.8 hours (55%)
# 2. Approve at 6 hours (before 55%)
curl -X POST http://localhost:5000/api/v1/workflows/REQ123/approve/LEVEL456
# 3. cancelTatJobs is called internally
# → tat-threshold1-REQ123-LEVEL456 removed ✅
# → tat-threshold2-REQ123-LEVEL456 removed ✅
# → tat-breach-REQ123-LEVEL456 removed ✅
# 4. No notifications sent ✅
```
### **Test 3: Mixed Old and New Jobs**
```bash
# 1. Create Request A with old threshold (50%)
# → Jobs use threshold=50
# 2. Admin changes to 55%
# 3. Create Request B with new threshold (55%)
# → Jobs use threshold=55
# 4. Both requests work correctly:
# → Request A fires at 50%, message says "50%" ✅
# → Request B fires at 55%, message says "55%" ✅
```
---
## Summary
### **What Changed:**
1. ✅ Job names: `tat50``tat-threshold1` (generic)
2. ✅ Job data: Now includes actual threshold percentage
3. ✅ Messages: Dynamic based on threshold from job data
4. ✅ Scheduling: Reads thresholds from database configuration
5. ✅ Cache: Automatically cleared on config update
### **What Didn't Change:**
1. ✅ Database schema (admin_configurations already has all needed fields)
2. ✅ API endpoints (no breaking changes)
3. ✅ Frontend UI (works exactly the same)
4. ✅ Cancellation logic (still works, just uses new names)
### **Benefits:**
1. ✅ **No Job Name Conflicts**: Generic names work for any percentage
2. ✅ **Accurate Messages**: Always show actual threshold used
3. ✅ **Config Flexibility**: Admin can change thresholds anytime
4. ✅ **Backward Compatible**: Existing jobs complete normally
5. ✅ **Reliable Cancellation**: Works regardless of threshold value
6. ✅ **Immediate Effect**: New requests use updated thresholds immediately
---
## Files Modified
1. `Re_Backend/src/services/configReader.service.ts` - **NEW** (configuration reader)
2. `Re_Backend/src/services/tatScheduler.service.ts` - Updated job scheduling
3. `Re_Backend/src/queues/tatProcessor.ts` - Updated job processing
4. `Re_Backend/src/controllers/admin.controller.ts` - Added cache clearing
---
## Configuration Keys
| Key | Description | Default | Example |
|-----|-------------|---------|---------|
| `TAT_REMINDER_THRESHOLD_1` | First warning threshold | 50 | 55 (sends alert at 55%) |
| `TAT_REMINDER_THRESHOLD_2` | Critical warning threshold | 75 | 80 (sends alert at 80%) |
| Breach | Deadline reached (always 100%) | 100 | 100 (non-configurable) |
---
## Example Timeline
**TAT = 16 hours, Thresholds: 55%, 80%**
```
Hour 0 ─────────────────────────────────────► Hour 16
│ │ │
START 55% (8.8h) 80% (12.8h) 100%
│ │ │
threshold1 threshold2 breach
"55% elapsed" "80% elapsed" "BREACHED"
⏳ ⚠️ ⏰
```
**Result:**
- ✅ Job names don't hardcode percentages
- ✅ Messages show actual configured thresholds
- ✅ Cancellation works consistently
- ✅ No edge cases or race conditions

View File

@ -1,562 +0,0 @@
# Dynamic Working Hours Configuration
## Overview
Working hours for TAT (Turn Around Time) calculations are now **dynamically configurable** through the admin settings interface. Admins can change these settings at any time, and the changes will be reflected in all future TAT calculations.
---
## What's Configurable
### **Working Hours Settings:**
| Setting | Description | Default | Example |
|---------|-------------|---------|---------|
| `WORK_START_HOUR` | Working day starts at (hour) | 9 | 8 (8:00 AM) |
| `WORK_END_HOUR` | Working day ends at (hour) | 18 | 19 (7:00 PM) |
| `WORK_START_DAY` | First working day of week | 1 (Monday) | 1 (Monday) |
| `WORK_END_DAY` | Last working day of week | 5 (Friday) | 6 (Saturday) |
**Days:** 0 = Sunday, 1 = Monday, 2 = Tuesday, ..., 6 = Saturday
---
## How It Works
### **1. Admin Changes Working Hours**
```
Settings → System Configuration → Working Hours
- Work Start Hour: 9:00 → Change to 8:00
- Work End Hour: 18:00 → Change to 20:00
✅ Save
```
### **2. Backend Updates Database**
```sql
UPDATE admin_configurations
SET config_value = '8'
WHERE config_key = 'WORK_START_HOUR';
UPDATE admin_configurations
SET config_value = '20'
WHERE config_key = 'WORK_END_HOUR';
```
### **3. Cache is Cleared Automatically**
```typescript
// In admin.controller.ts
clearConfigCache(); // Clear general config cache
clearWorkingHoursCache(); // Clear TAT working hours cache
```
### **4. Next TAT Calculation Uses New Values**
```typescript
// TAT calculation loads fresh values
await loadWorkingHoursCache();
// → Reads: startHour=8, endHour=20 from database
// Applies new working hours
if (hour >= 8 && hour < 20) {
// This hour counts as working time ✅
}
```
---
## Cache Management
### **Working Hours Cache:**
**Cache Duration:** 5 minutes (shorter than holidays since it's more critical)
**Why Cache?**
- Performance: Avoids repeated database queries
- Speed: TAT calculations can happen hundreds of times per hour
- Efficiency: Reading from memory is ~1000x faster than DB query
**Cache Lifecycle:**
```
1. First TAT Calculation:
→ loadWorkingHoursCache() called
→ Database query: SELECT config_value WHERE config_key IN (...)
→ Store in memory: workingHoursCache = { startHour: 9, endHour: 18, ... }
→ Set expiry: now + 5 minutes
2. Next 5 Minutes (Cache Valid):
→ All TAT calculations use cached values
→ No database queries ✅ FAST
3. After 5 Minutes (Cache Expired):
→ Next TAT calculation reloads from database
→ New cache created with 5-minute expiry
4. Admin Updates Config:
→ clearWorkingHoursCache() called immediately
→ Cache invalidated
→ Next calculation loads fresh values ✅
```
---
## Example Scenarios
### **Scenario 1: Extend Working Hours**
**Before:**
```
Working Hours: 9:00 AM - 6:00 PM (9 hours/day)
```
**Admin Changes To:**
```
Working Hours: 8:00 AM - 8:00 PM (12 hours/day)
```
**Impact on TAT:**
```
Request: STANDARD Priority, 24 working hours
Created: Monday 9:00 AM
OLD Calculation (9 hours/day):
Monday 9 AM - 6 PM = 9 hours (15h remaining)
Tuesday 9 AM - 6 PM = 9 hours (6h remaining)
Wednesday 9 AM - 3 PM = 6 hours (0h remaining)
Deadline: Wednesday 3:00 PM
NEW Calculation (12 hours/day):
Monday 9 AM - 8 PM = 11 hours (13h remaining)
Tuesday 8 AM - 8 PM = 12 hours (1h remaining)
Wednesday 8 AM - 9 AM = 1 hour (0h remaining)
Deadline: Wednesday 9:00 AM ✅ FASTER!
```
---
### **Scenario 2: Include Saturday as Working Day**
**Before:**
```
Working Days: Monday - Friday (1-5)
```
**Admin Changes To:**
```
Working Days: Monday - Saturday (1-6)
```
**Impact on TAT:**
```
Request: STANDARD Priority, 16 working hours
Created: Friday 2:00 PM
OLD Calculation (Mon-Fri only):
Friday 2 PM - 6 PM = 4 hours (12h remaining)
Saturday-Sunday = SKIPPED
Monday 9 AM - 6 PM = 9 hours (3h remaining)
Tuesday 9 AM - 12 PM = 3 hours (0h remaining)
Deadline: Tuesday 12:00 PM
NEW Calculation (Mon-Sat):
Friday 2 PM - 6 PM = 4 hours (12h remaining)
Saturday 9 AM - 6 PM = 9 hours (3h remaining) ✅ Saturday counts!
Sunday = SKIPPED
Monday 9 AM - 12 PM = 3 hours (0h remaining)
Deadline: Monday 12:00 PM ✅ EARLIER!
```
---
### **Scenario 3: Reduce Working Hours (After-Hours Emergency)**
**Before:**
```
Working Hours: 9:00 AM - 6:00 PM
```
**Admin Changes To:**
```
Working Hours: 9:00 AM - 10:00 PM (extended for emergency)
```
**Impact:**
```
Request created at 7:00 PM (after old hours but within new hours)
OLD System:
7:00 PM → Not working time
First working hour: Tomorrow 9:00 AM
TAT starts counting from tomorrow ❌
NEW System:
7:00 PM → Still working time! ✅
TAT starts counting immediately
Faster response for urgent requests ✅
```
---
## Implementation Details
### **Configuration Reader Service**
```typescript
// Re_Backend/src/services/configReader.service.ts
export async function getWorkingHours(): Promise<{ startHour: number; endHour: number }> {
const startHour = await getConfigNumber('WORK_START_HOUR', 9);
const endHour = await getConfigNumber('WORK_END_HOUR', 18);
return { startHour, endHour };
}
```
### **TAT Time Utils (Working Hours Cache)**
```typescript
// Re_Backend/src/utils/tatTimeUtils.ts
let workingHoursCache: WorkingHoursConfig | null = null;
let workingHoursCacheExpiry: Date | null = null;
async function loadWorkingHoursCache(): Promise<void> {
// Check if cache is still valid
if (workingHoursCacheExpiry && new Date() < workingHoursCacheExpiry) {
return; // Use cached values
}
// Load from database
const { getWorkingHours, getConfigNumber } = await import('../services/configReader.service');
const hours = await getWorkingHours();
const startDay = await getConfigNumber('WORK_START_DAY', 1);
const endDay = await getConfigNumber('WORK_END_DAY', 5);
// Store in cache
workingHoursCache = {
startHour: hours.startHour,
endHour: hours.endHour,
startDay: startDay,
endDay: endDay
};
// Set 5-minute expiry
workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate();
console.log(`[TAT Utils] Loaded working hours: ${hours.startHour}:00-${hours.endHour}:00`);
}
function isWorkingTime(date: Dayjs): boolean {
// Use cached working hours (with fallback to defaults)
const config = workingHoursCache || {
startHour: 9,
endHour: 18,
startDay: 1,
endDay: 5
};
const day = date.day();
const hour = date.hour();
// Check based on configured values
if (day < config.startDay || day > config.endDay) return false;
if (hour < config.startHour || hour >= config.endHour) return false;
if (isHoliday(date)) return false;
return true;
}
```
### **Admin Controller (Cache Invalidation)**
```typescript
// Re_Backend/src/controllers/admin.controller.ts
export const updateConfiguration = async (req: Request, res: Response): Promise<void> => {
// ... update database ...
// Clear config cache
clearConfigCache();
// If working hours config was updated, also clear TAT cache
const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY'];
if (workingHoursKeys.includes(configKey)) {
clearWorkingHoursCache(); // ✅ Immediate cache clear
logger.info(`Working hours config '${configKey}' updated - cache cleared`);
}
res.json({ success: true });
};
```
---
## Priority Behavior
### **STANDARD Priority**
✅ **Uses configured working hours**
- Respects `WORK_START_HOUR` and `WORK_END_HOUR`
- Respects `WORK_START_DAY` and `WORK_END_DAY`
- Excludes holidays
**Example:**
```
Config: 9:00 AM - 6:00 PM, Monday-Friday
TAT: 16 working hours
→ Only hours between 9 AM - 6 PM on Mon-Fri count
→ Weekends and holidays are skipped
```
### **EXPRESS Priority**
❌ **Ignores working hours configuration**
- Counts ALL 24 hours per day
- Counts ALL 7 days per week
- Counts holidays
**Example:**
```
Config: 9:00 AM - 6:00 PM (ignored)
TAT: 16 hours
→ Simply add 16 hours to start time
→ No exclusions
```
---
## Testing Scenarios
### **Test 1: Change Working Hours, Create Request**
```bash
# 1. Check current working hours
curl http://localhost:5000/api/v1/admin/configurations \
| grep WORK_START_HOUR
# → Returns: "configValue": "9"
# 2. Update working hours to start at 8:00 AM
curl -X PUT http://localhost:5000/api/v1/admin/configurations/WORK_START_HOUR \
-H "Authorization: Bearer TOKEN" \
-d '{"configValue": "8"}'
# → Response: "Configuration updated successfully"
# 3. Check logs
# → Should see: "Working hours configuration 'WORK_START_HOUR' updated - cache cleared"
# 4. Create new STANDARD request
curl -X POST http://localhost:5000/api/v1/workflows \
-d '{"priority": "STANDARD", "tatHours": 16}'
# 5. Check TAT calculation logs
# → Should see: "Loaded working hours: 8:00-18:00" ✅
# → Deadline calculation uses new hours ✅
```
### **Test 2: Verify Cache Expiry**
```bash
# 1. Create request (loads working hours into cache)
# → Cache expires in 5 minutes
# 2. Wait 6 minutes
# 3. Create another request
# → Should see log: "Loaded working hours: ..." (cache reloaded)
# 4. Create third request immediately
# → No log (uses cached values)
```
### **Test 3: Extend to 6-Day Week**
```bash
# 1. Update end day to Saturday
curl -X PUT http://localhost:5000/api/v1/admin/configurations/WORK_END_DAY \
-d '{"configValue": "6"}'
# 2. Create request on Friday afternoon
# → Deadline should include Saturday ✅
# → Sunday still excluded ✅
```
---
## Database Configuration
### **Configuration Keys:**
```sql
SELECT config_key, config_value, display_name
FROM admin_configurations
WHERE config_key IN (
'WORK_START_HOUR',
'WORK_END_HOUR',
'WORK_START_DAY',
'WORK_END_DAY'
);
-- Example results:
-- WORK_START_HOUR | 9 | Work Start Hour
-- WORK_END_HOUR | 18 | Work End Hour
-- WORK_START_DAY | 1 | Work Start Day (Monday)
-- WORK_END_DAY | 5 | Work End Day (Friday)
```
### **Update Example:**
```sql
-- Change working hours to 8 AM - 8 PM
UPDATE admin_configurations
SET config_value = '8', updated_at = NOW()
WHERE config_key = 'WORK_START_HOUR';
UPDATE admin_configurations
SET config_value = '20', updated_at = NOW()
WHERE config_key = 'WORK_END_HOUR';
-- Include Saturday as working day
UPDATE admin_configurations
SET config_value = '6', updated_at = NOW()
WHERE config_key = 'WORK_END_DAY';
```
---
## Logging Examples
### **Configuration Update:**
```
[Admin] Working hours configuration 'WORK_START_HOUR' updated - cache cleared
[ConfigReader] Configuration cache cleared
[TAT Utils] Working hours cache cleared
```
### **TAT Calculation:**
```
[TAT Utils] Loaded working hours: 8:00-20:00, Days: 1-6
[TAT Scheduler] Using STANDARD mode - excludes holidays, weekends, non-working hours
[TAT Scheduler] Calculating TAT milestones for request REQ-2025-001
[TAT Scheduler] Priority: STANDARD, TAT Hours: 16
[TAT Scheduler] Start: 2025-11-05 09:00
[TAT Scheduler] Threshold 1 (55%): 2025-11-05 17:48 (using 8-20 working hours)
[TAT Scheduler] Threshold 2 (80%): 2025-11-06 10:48
[TAT Scheduler] Breach (100%): 2025-11-06 15:00
```
---
## Migration from Hardcoded Values
### **Before (Hardcoded):**
```typescript
// ❌ Hardcoded in code
const WORK_START_HOUR = 9;
const WORK_END_HOUR = 18;
const WORK_START_DAY = 1;
const WORK_END_DAY = 5;
// To change: Need code update + deployment
```
### **After (Dynamic):**
```typescript
// ✅ Read from database
const config = await getWorkingHours();
// config = { startHour: 9, endHour: 18 }
// To change: Just update in admin UI
// No code changes needed ✅
// No deployment needed ✅
```
---
## Benefits
### **1. Flexibility**
- ✅ Change working hours anytime without code changes
- ✅ No deployment needed
- ✅ Takes effect within 5 minutes
### **2. Global Organizations**
- ✅ Adjust for different time zones
- ✅ Support 24/5 or 24/6 operations
- ✅ Extended hours for urgent periods
### **3. Seasonal Adjustments**
- ✅ Extend hours during busy seasons
- ✅ Reduce hours during slow periods
- ✅ Special hours for events
### **4. Performance**
- ✅ Cache prevents repeated DB queries
- ✅ Fast lookups (memory vs database)
- ✅ Auto-refresh every 5 minutes
### **5. Consistency**
- ✅ All TAT calculations use same values
- ✅ Immediate cache invalidation on update
- ✅ Fallback to defaults if DB unavailable
---
## Summary
| Aspect | Details |
|--------|---------|
| **Configurable** | ✅ Working hours, working days |
| **Admin UI** | ✅ Settings → System Configuration |
| **Cache Duration** | 5 minutes |
| **Cache Invalidation** | Automatic on config update |
| **Applies To** | STANDARD priority only |
| **Express Mode** | Ignores working hours (24/7) |
| **Performance** | Optimized with caching |
| **Fallback** | Uses TAT_CONFIG defaults if DB fails |
---
## Files Modified
1. `Re_Backend/src/utils/tatTimeUtils.ts` - Dynamic working hours loading
2. `Re_Backend/src/controllers/admin.controller.ts` - Cache invalidation on update
3. `Re_Backend/src/services/configReader.service.ts` - `getWorkingHours()` function
---
## Configuration Flow Diagram
```
Admin Updates Working Hours (8:00 AM - 8:00 PM)
Database Updated (admin_configurations table)
clearConfigCache() + clearWorkingHoursCache()
Caches Invalidated (both config and working hours)
Next TAT Calculation
loadWorkingHoursCache() called
Read from Database (startHour=8, endHour=20)
Store in Memory (5-minute cache)
TAT Calculation Uses New Hours ✅
All Future Requests (for 5 min) Use Cached Values
After 5 Minutes → Reload from Database
```
---
Working hours are now fully dynamic and admin-controlled! 🎉

View File

@ -1,129 +0,0 @@
# 🔧 Backend Fixes Applied - November 4, 2025
## ✅ Issue 1: TypeScript Compilation Error
### **Error:**
```
src/services/tatScheduler.service.ts:30:15 - error TS2339:
Property 'halfTime' does not exist on type 'Promise<{ halfTime: Date; ... }>'.
```
### **Root Cause:**
`calculateTatMilestones()` was changed from sync to async (to support holiday checking), but `tatScheduler.service.ts` was calling it without `await`.
### **Fix Applied:**
```typescript
// Before (❌ Missing await):
const { halfTime, seventyFive, full } = calculateTatMilestones(now, tatDurationHours);
// After (✅ With await):
const { halfTime, seventyFive, full } = await calculateTatMilestones(now, tatDurationHours);
```
**File:** `Re_Backend/src/services/tatScheduler.service.ts` (line 30)
---
## ✅ Issue 2: Empty Configurations Table
### **Problem:**
`admin_configurations` table created but empty → Frontend can't fetch any configurations.
### **Fix Applied:**
Created auto-seeding service that runs on server startup:
**File:** `Re_Backend/src/services/configSeed.service.ts`
- Checks if configurations exist
- If empty, seeds 10 default configurations:
- 6 TAT Settings (default hours, thresholds, working hours)
- 3 Document Policy settings
- 2 AI Configuration settings
### **Integration:**
Updated `Re_Backend/src/server.ts` to call `seedDefaultConfigurations()` on startup.
**Output on server start:**
```
⚙️ System configurations initialized
```
---
## 📋 **Default Configurations Seeded**
| Config Key | Value | Category | UI Component |
|------------|-------|----------|--------------|
| `DEFAULT_TAT_EXPRESS_HOURS` | 24 | TAT_SETTINGS | number |
| `DEFAULT_TAT_STANDARD_HOURS` | 48 | TAT_SETTINGS | number |
| `TAT_REMINDER_THRESHOLD_1` | 50 | TAT_SETTINGS | slider |
| `TAT_REMINDER_THRESHOLD_2` | 75 | TAT_SETTINGS | slider |
| `WORK_START_HOUR` | 9 | TAT_SETTINGS | number |
| `WORK_END_HOUR` | 18 | TAT_SETTINGS | number |
| `MAX_FILE_SIZE_MB` | 10 | DOCUMENT_POLICY | number |
| `ALLOWED_FILE_TYPES` | pdf,doc,... | DOCUMENT_POLICY | text |
| `DOCUMENT_RETENTION_DAYS` | 365 | DOCUMENT_POLICY | number |
| `AI_REMARK_GENERATION_ENABLED` | true | AI_CONFIGURATION | toggle |
| `AI_REMARK_MAX_CHARACTERS` | 500 | AI_CONFIGURATION | number |
---
## 🚀 **How to Verify**
### **Step 1: Restart Backend**
```bash
cd Re_Backend
npm run dev
```
### **Expected Output:**
```
⚙️ System configurations initialized
📅 Holiday calendar loaded for TAT calculations
🚀 Server running on port 5000
```
### **Step 2: Check Database**
```sql
SELECT COUNT(*) FROM admin_configurations;
-- Should return: 11 (10 default configs)
SELECT config_key, config_value FROM admin_configurations ORDER BY sort_order;
-- Should show all seeded configurations
```
### **Step 3: Test Frontend**
```bash
# Login as admin
# Navigate to Settings → System Configuration tab
# Should see all configurations grouped by category
```
---
## ✅ **Status: Both Issues Resolved**
| Issue | Status | Fix |
|-------|--------|-----|
| TypeScript compilation error | ✅ Fixed | Added `await` to async function call |
| Empty configurations table | ✅ Fixed | Auto-seeding on server startup |
| Holiday list not fetching | ✅ Will work | Backend now starts successfully |
---
## 🎯 **Next Steps**
1. ✅ **Restart backend** - `npm run dev`
2. ✅ **Verify configurations seeded** - Check logs for "System configurations initialized"
3. ✅ **Test frontend** - Login as admin and view Settings
4. ✅ **Add holidays** - Use the Holiday Calendar tab
---
**All systems ready! 🚀**
---
**Fixed:** November 4, 2025
**Files Modified:** 3
**Status:** Complete

View File

@ -1,731 +0,0 @@
# ✅ Holiday Calendar & Admin Configuration System - Complete
## 🎉 What's Been Implemented
### **1. Holiday Calendar System** 📅
- ✅ Admin can add/edit/delete organization holidays
- ✅ Holidays automatically excluded from STANDARD priority TAT calculations
- ✅ Weekends (Saturday/Sunday) + Holidays = Non-working days
- ✅ Supports recurring holidays (annual)
- ✅ Department/location-specific holidays
- ✅ Bulk import from JSON/CSV
- ✅ Year-based calendar view
- ✅ Automatic cache refresh
### **2. Admin Configuration System** ⚙️
- ✅ Centralized configuration management
- ✅ All planned config areas supported:
- TAT Settings
- User Roles
- Notification Rules
- Document Policy
- Dashboard Layout
- AI Configuration
- Workflow Sharing Policy
---
## 📊 Database Schema
### **New Tables Created:**
**1. `holidays` Table:**
```sql
- holiday_id (UUID, PK)
- holiday_date (DATE, UNIQUE) -- YYYY-MM-DD
- holiday_name (VARCHAR) -- "Diwali", "Republic Day"
- description (TEXT) -- Optional details
- is_recurring (BOOLEAN) -- Annual holidays
- recurrence_rule (VARCHAR) -- RRULE format
- holiday_type (ENUM) -- NATIONAL, REGIONAL, ORGANIZATIONAL, OPTIONAL
- is_active (BOOLEAN) -- Enable/disable
- applies_to_departments (TEXT[]) -- NULL = all
- applies_to_locations (TEXT[]) -- NULL = all
- created_by (UUID FK)
- updated_by (UUID FK)
- created_at, updated_at
```
**2. `admin_configurations` Table:**
```sql
- config_id (UUID, PK)
- config_key (VARCHAR, UNIQUE) -- "DEFAULT_TAT_EXPRESS_HOURS"
- config_category (ENUM) -- TAT_SETTINGS, NOTIFICATION_RULES, etc.
- config_value (TEXT) -- Actual value
- value_type (ENUM) -- STRING, NUMBER, BOOLEAN, JSON, ARRAY
- display_name (VARCHAR) -- UI-friendly name
- description (TEXT)
- default_value (TEXT) -- Reset value
- is_editable (BOOLEAN)
- is_sensitive (BOOLEAN) -- For API keys, passwords
- validation_rules (JSONB) -- Min, max, regex
- ui_component (VARCHAR) -- input, select, toggle, slider
- options (JSONB) -- For dropdown options
- sort_order (INTEGER) -- Display order
- requires_restart (BOOLEAN)
- last_modified_by (UUID FK)
- last_modified_at (TIMESTAMP)
```
---
## 🔌 API Endpoints
### **Holiday Management:**
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/admin/holidays` | Get all holidays (with year filter) |
| GET | `/api/admin/holidays/calendar/:year` | Get calendar for specific year |
| POST | `/api/admin/holidays` | Create new holiday |
| PUT | `/api/admin/holidays/:holidayId` | Update holiday |
| DELETE | `/api/admin/holidays/:holidayId` | Delete (deactivate) holiday |
| POST | `/api/admin/holidays/bulk-import` | Bulk import holidays |
### **Configuration Management:**
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/admin/configurations` | Get all configurations |
| GET | `/api/admin/configurations?category=TAT_SETTINGS` | Get by category |
| PUT | `/api/admin/configurations/:configKey` | Update configuration |
| POST | `/api/admin/configurations/:configKey/reset` | Reset to default |
---
## 🎯 TAT Calculation with Holidays
### **STANDARD Priority (Working Days):**
**Excludes:**
- ✅ Saturdays (day 6)
- ✅ Sundays (day 0)
- ✅ Holidays from `holidays` table
- ✅ Outside working hours (before 9 AM, after 6 PM)
**Example:**
```
Submit: Monday Oct 20 at 10:00 AM
TAT: 48 hours (STANDARD priority)
Holiday: Tuesday Oct 21 (Diwali)
Calculation:
Monday 10 AM - 6 PM = 8 hours (total: 8h)
Tuesday = HOLIDAY (skipped)
Wednesday 9 AM - 6 PM = 9 hours (total: 17h)
Thursday 9 AM - 6 PM = 9 hours (total: 26h)
Friday 9 AM - 6 PM = 9 hours (total: 35h)
Saturday-Sunday = WEEKEND (skipped)
Monday 9 AM - 10 PM = 13 hours (total: 48h)
Due: Monday Oct 27 at 10:00 AM
```
### **EXPRESS Priority (Calendar Days):**
**Excludes: NOTHING**
- All days included (weekends, holidays, 24/7)
**Example:**
```
Submit: Monday Oct 20 at 10:00 AM
TAT: 48 hours (EXPRESS priority)
Due: Wednesday Oct 22 at 10:00 AM (exactly 48 hours later)
```
---
## 🔄 Holiday Cache System
### **How It Works:**
```
1. Server Starts
2. Load holidays from database (current year + next year)
3. Store in memory cache (Set of date strings)
4. Cache expires after 6 hours
5. Auto-reload when expired
6. Manual reload when admin adds/updates/deletes holiday
```
**Benefits:**
- ⚡ Fast lookups (O(1) Set lookup)
- 💾 Minimal memory (just date strings)
- 🔄 Auto-refresh every 6 hours
- 🎯 Immediate update when admin changes holidays
---
## 🎨 Frontend UI (To Be Built)
### **Admin Dashboard → Holiday Management:**
```tsx
<HolidayManagementPage>
{/* Year Selector */}
<YearSelector
currentYear={2025}
onChange={loadHolidaysForYear}
/>
{/* Calendar View */}
<CalendarGrid year={2025}>
{/* Days with holidays highlighted */}
<Day date="2025-01-26" isHoliday holidayName="Republic Day" />
<Day date="2025-08-15" isHoliday holidayName="Independence Day" />
</CalendarGrid>
{/* List View */}
<HolidayList>
<HolidayCard
date="2025-01-26"
name="Republic Day"
type="NATIONAL"
recurring={true}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</HolidayList>
{/* Actions */}
<div className="actions">
<Button onClick={openAddHolidayModal}>
+ Add Holiday
</Button>
<Button onClick={openBulkImportDialog}>
📁 Import Holidays
</Button>
</div>
</HolidayManagementPage>
```
---
## 📋 Default Configurations
### **Pre-seeded in database:**
| Config Key | Value | Category | Description |
|------------|-------|----------|-------------|
| `DEFAULT_TAT_EXPRESS_HOURS` | 24 | TAT_SETTINGS | Default TAT for express |
| `DEFAULT_TAT_STANDARD_HOURS` | 48 | TAT_SETTINGS | Default TAT for standard |
| `TAT_REMINDER_THRESHOLD_1` | 50 | TAT_SETTINGS | First reminder at 50% |
| `TAT_REMINDER_THRESHOLD_2` | 75 | TAT_SETTINGS | Second reminder at 75% |
| `WORK_START_HOUR` | 9 | TAT_SETTINGS | Work day starts at 9 AM |
| `WORK_END_HOUR` | 18 | TAT_SETTINGS | Work day ends at 6 PM |
| `MAX_FILE_SIZE_MB` | 10 | DOCUMENT_POLICY | Max upload size |
| `ALLOWED_FILE_TYPES` | pdf,doc,... | DOCUMENT_POLICY | Allowed extensions |
| `DOCUMENT_RETENTION_DAYS` | 365 | DOCUMENT_POLICY | Retention period |
| `AI_REMARK_GENERATION_ENABLED` | true | AI_CONFIGURATION | Enable AI remarks |
| `AI_REMARK_MAX_CHARACTERS` | 500 | AI_CONFIGURATION | Max AI text length |
---
## 🚀 Quick Start
### **Step 1: Run Migrations**
```bash
cd Re_Backend
npm run migrate
```
**You'll see:**
```
✅ Holidays table created successfully
✅ Admin configurations table created and seeded
```
### **Step 2: Import Indian Holidays (Optional)**
Create a script or use the API:
```bash
# Using curl (requires admin token):
curl -X POST http://localhost:5000/api/admin/holidays/bulk-import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d @data/indian_holidays_2025.json
```
### **Step 3: Verify Holidays Loaded**
```sql
SELECT COUNT(*) FROM holidays WHERE is_active = true;
-- Should return 14 (or however many you imported)
```
### **Step 4: Restart Backend**
```bash
npm run dev
```
**You'll see:**
```
📅 Holiday calendar loaded for TAT calculations
Loaded 14 holidays into cache
```
---
## 🧪 Testing
### **Test 1: Create Holiday**
```bash
POST /api/admin/holidays
{
"holidayDate": "2025-12-31",
"holidayName": "New Year's Eve",
"description": "Last day of the year",
"holidayType": "ORGANIZATIONAL"
}
```
### **Test 2: Verify Holiday Affects TAT**
```bash
# 1. Create STANDARD priority request on Dec 30
# 2. Set TAT: 16 hours (2 working days)
# 3. Expected due: Jan 2 (skips Dec 31 holiday + weekend)
# 4. Actual due should be: Jan 2
```
### **Test 3: Verify EXPRESS Not Affected**
```bash
# 1. Create EXPRESS priority request on Dec 30
# 2. Set TAT: 48 hours
# 3. Expected due: Jan 1 (exactly 48 hours, includes holiday)
```
---
## 📊 Admin Configuration UI (To Be Built)
### **Admin Settings Page:**
```tsx
<AdminSettings>
<Tabs>
<Tab value="tat">TAT Settings</Tab>
<Tab value="holidays">Holiday Calendar</Tab>
<Tab value="documents">Document Policy</Tab>
<Tab value="notifications">Notifications</Tab>
<Tab value="ai">AI Configuration</Tab>
</Tabs>
<TabPanel value="tat">
<ConfigSection>
<ConfigItem
label="Default TAT for Express (hours)"
type="number"
value={24}
min={1}
max={168}
onChange={handleUpdate}
/>
<ConfigItem
label="Default TAT for Standard (hours)"
type="number"
value={48}
min={1}
max={720}
/>
<ConfigItem
label="First Reminder Threshold (%)"
type="slider"
value={50}
min={1}
max={100}
/>
<ConfigItem
label="Working Hours"
type="timerange"
value={{ start: 9, end: 18 }}
/>
</ConfigSection>
</TabPanel>
<TabPanel value="holidays">
<HolidayCalendar />
</TabPanel>
</AdminSettings>
```
---
## 🔍 Sample Queries
### **Get Holidays for Current Year:**
```sql
SELECT * FROM holidays
WHERE EXTRACT(YEAR FROM holiday_date) = EXTRACT(YEAR FROM CURRENT_DATE)
AND is_active = true
ORDER BY holiday_date;
```
### **Check if Date is Holiday:**
```sql
SELECT EXISTS(
SELECT 1 FROM holidays
WHERE holiday_date = '2025-08-15'
AND is_active = true
) as is_holiday;
```
### **Upcoming Holidays (Next 3 Months):**
```sql
SELECT
holiday_name,
holiday_date,
holiday_type,
description
FROM holidays
WHERE holiday_date BETWEEN CURRENT_DATE AND CURRENT_DATE + INTERVAL '90 days'
AND is_active = true
ORDER BY holiday_date;
```
---
## 🎯 Complete Feature Set
### **Holiday Management:**
- ✅ Create individual holidays
- ✅ Update holiday details
- ✅ Delete (deactivate) holidays
- ✅ Bulk import from JSON
- ✅ Year-based calendar view
- ✅ Recurring holidays support
- ✅ Department-specific holidays
- ✅ Location-specific holidays
### **TAT Integration:**
- ✅ STANDARD priority skips holidays
- ✅ EXPRESS priority ignores holidays
- ✅ Automatic cache management
- ✅ Performance optimized (in-memory cache)
- ✅ Real-time updates when holidays change
### **Admin Configuration:**
- ✅ TAT default values
- ✅ Reminder thresholds
- ✅ Working hours
- ✅ Document policies
- ✅ AI settings
- ✅ All configs with validation rules
- ✅ UI component hints
- ✅ Reset to default option
---
## 📦 Files Created
### **Backend (10 new files):**
1. `src/models/Holiday.ts` - Holiday model
2. `src/services/holiday.service.ts` - Holiday management service
3. `src/controllers/admin.controller.ts` - Admin API controllers
4. `src/routes/admin.routes.ts` - Admin API routes
5. `src/migrations/20251104-create-holidays.ts` - Holidays table migration
6. `src/migrations/20251104-create-admin-config.ts` - Admin config migration
7. `data/indian_holidays_2025.json` - Sample holidays data
8. `docs/HOLIDAY_CALENDAR_SYSTEM.md` - Complete documentation
### **Modified Files (6):**
1. `src/utils/tatTimeUtils.ts` - Added holiday checking
2. `src/server.ts` - Initialize holidays cache
3. `src/models/index.ts` - Export Holiday model
4. `src/routes/index.ts` - Register admin routes
5. `src/middlewares/authorization.middleware.ts` - Added requireAdmin
6. `src/scripts/migrate.ts` - Include new migrations
---
## 🚀 How to Use
### **Step 1: Run Migrations**
```bash
cd Re_Backend
npm run migrate
```
**Expected Output:**
```
✅ Holidays table created successfully
✅ Admin configurations table created and seeded
```
### **Step 2: Restart Backend**
```bash
npm run dev
```
**Expected Output:**
```
📅 Holiday calendar loaded for TAT calculations
[TAT Utils] Loaded 0 holidays into cache (will load when admin adds holidays)
```
### **Step 3: Add Holidays via API**
**Option A: Add Individual Holiday:**
```bash
curl -X POST http://localhost:5000/api/admin/holidays \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d '{
"holidayDate": "2025-11-05",
"holidayName": "Diwali",
"description": "Festival of Lights",
"holidayType": "NATIONAL"
}'
```
**Option B: Bulk Import:**
```bash
# Use the sample data file:
curl -X POST http://localhost:5000/api/admin/holidays/bulk-import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d @data/indian_holidays_2025.json
```
### **Step 4: Test TAT with Holidays**
```bash
# 1. Create STANDARD priority request
# 2. TAT calculation will now skip holidays
# 3. Due date will be later if holidays fall within TAT period
```
---
## 📊 TAT Calculation Examples
### **Example 1: No Holidays in TAT Period**
```
Submit: Monday Dec 1, 10:00 AM
TAT: 24 hours (STANDARD)
Holidays: None in this period
Calculation:
Monday 10 AM - 6 PM = 8 hours
Tuesday 9 AM - 1 PM = 4 hours
Total = 12 hours (needs 12 more)
...
Due: Tuesday 1:00 PM
```
### **Example 2: Holiday in TAT Period**
```
Submit: Friday Oct 31, 10:00 AM
TAT: 24 hours (STANDARD)
Holiday: Monday Nov 3 (Diwali)
Calculation:
Friday 10 AM - 6 PM = 8 hours
Saturday-Sunday = WEEKEND (skipped)
Monday = HOLIDAY (skipped)
Tuesday 9 AM - 6 PM = 9 hours (total: 17h)
Wednesday 9 AM - 2 PM = 5 hours (total: 22h)
...
Due: Wednesday Nov 5 at 2:00 PM
```
---
## 🔒 Security
### **Admin Access Required:**
All holiday and configuration endpoints check:
1. ✅ User is authenticated (`authenticateToken`)
2. ✅ User has admin role (`requireAdmin`)
**Non-admins get:**
```json
{
"success": false,
"error": "Admin access required"
}
```
---
## 📚 Admin Configuration Categories
### **1. TAT Settings**
- Default TAT hours (Express/Standard)
- Reminder thresholds (50%, 75%)
- Working hours (9 AM - 6 PM)
### **2. User Roles** (Future)
- Add/deactivate users
- Change roles (Initiator, Approver, Spectator)
### **3. Notification Rules**
- Channels (in-app, email)
- Frequency
- Template messages
### **4. Document Policy**
- Max upload size (10 MB)
- Allowed file types
- Retention period (365 days)
### **5. Dashboard Layout** (Future)
- Enable/disable KPI cards per role
### **6. AI Configuration**
- Toggle AI remark generation
- Max characters (500)
### **7. Workflow Sharing Policy** (Future)
- Control who can add spectators
- Share links permissions
---
## ✅ Implementation Summary
| Feature | Status | Notes |
|---------|--------|-------|
| **Holidays Table** | ✅ Created | With 4 indexes |
| **Admin Config Table** | ✅ Created | Pre-seeded with defaults |
| **Holiday Service** | ✅ Implemented | CRUD + bulk import |
| **Admin Controller** | ✅ Implemented | All endpoints |
| **Admin Routes** | ✅ Implemented | Secured with requireAdmin |
| **TAT Integration** | ✅ Implemented | Holidays excluded for STANDARD |
| **Holiday Cache** | ✅ Implemented | 6-hour expiry, auto-refresh |
| **Sample Data** | ✅ Created | 14 Indian holidays for 2025 |
| **Documentation** | ✅ Complete | Full guide created |
| **Migrations** | ✅ Ready | 2 new migrations added |
---
## 🎓 Next Steps
### **Immediate:**
1. ✅ Run migrations: `npm run migrate`
2. ✅ Restart backend: `npm run dev`
3. ✅ Verify holidays table exists
4. ✅ Import sample holidays (optional)
### **Frontend Development:**
1. 📋 Build Holiday Management page
2. 📋 Build Admin Configuration page
3. 📋 Build Calendar view component
4. 📋 Build Bulk import UI
5. 📋 Add to Admin Dashboard
### **Future Enhancements:**
1. 📋 Recurring holiday auto-generation
2. 📋 Holiday templates by country
3. 📋 Email notifications for upcoming holidays
4. 📋 Holiday impact reports (how many requests affected)
5. 📋 Multi-year holiday planning
---
## 📊 Impact on Existing Requests
### **For Existing Requests:**
**Before Holidays Table:**
- TAT calculation: Weekends only
**After Holidays Table:**
- TAT calculation: Weekends + Holidays
- Due dates may change for active requests
- Historical requests unchanged
---
## 🆘 Troubleshooting
### **Holidays Not Excluded from TAT?**
**Check:**
1. Holidays cache loaded? Look for "Loaded X holidays into cache" in logs
2. Priority is STANDARD? (EXPRESS doesn't use holidays)
3. Holiday is active? `is_active = true`
4. Holiday date is correct format? `YYYY-MM-DD`
**Debug:**
```sql
-- Check if holiday exists
SELECT * FROM holidays
WHERE holiday_date = '2025-11-05'
AND is_active = true;
```
### **Cache Not Updating After Adding Holiday?**
**Solution:**
- Cache refreshes automatically when admin adds/updates/deletes
- If not working, restart backend server
- Cache refreshes every 6 hours automatically
---
## 📈 Future Admin Features
Based on your requirements, these can be added:
### **User Role Management:**
- Add/remove users
- Change user roles
- Activate/deactivate accounts
### **Notification Templates:**
- Customize email/push templates
- Set notification frequency
- Channel preferences
### **Dashboard Customization:**
- Enable/disable KPI cards
- Customize card order
- Role-based dashboard views
### **Workflow Policies:**
- Who can add spectators
- Sharing permissions
- Approval flow templates
---
## 🎉 Status: COMPLETE!
**Holiday Calendar System** - Fully implemented
**Admin Configuration** - Schema and API ready
**TAT Integration** - Holidays excluded for STANDARD priority
**API Endpoints** - All CRUD operations
**Security** - Admin-only access
**Performance** - Optimized with caching
**Sample Data** - Indian holidays 2025
**Documentation** - Complete guide
---
**Just run migrations and you're ready to go! 🚀**
See `docs/HOLIDAY_CALENDAR_SYSTEM.md` for detailed API documentation.
---
**Last Updated**: November 4, 2025
**Version**: 1.0.0
**Team**: Royal Enfield Workflow System

View File

@ -1,516 +0,0 @@
# Holiday Handling & EXPRESS Mode TAT Calculation
## Overview
The TAT (Turn Around Time) system now supports:
1. **Holiday Exclusions** - Configured holidays are excluded from STANDARD priority TAT calculations
2. **EXPRESS Mode** - EXPRESS priority requests use 24/7 calculation (no exclusions)
---
## How It Works
### **STANDARD Priority (Default)**
**Calculation:**
- ✅ Excludes weekends (Saturday, Sunday)
- ✅ Excludes non-working hours (9 AM - 6 PM by default)
- ✅ **Excludes holidays configured in Admin Settings**
**Example:**
```
TAT = 16 working hours
Start: Monday 2:00 PM
Calculation:
Monday 2:00 PM - 6:00 PM = 4 hours (remaining: 12h)
Tuesday 9:00 AM - 6:00 PM = 9 hours (remaining: 3h)
Wednesday 9:00 AM - 12:00 PM = 3 hours (remaining: 0h)
If Wednesday is a HOLIDAY → Skip to Thursday:
Wednesday (HOLIDAY) = 0 hours (skipped)
Thursday 9:00 AM - 12:00 PM = 3 hours (remaining: 0h)
Final deadline: Thursday 12:00 PM ✅
```
---
### **EXPRESS Priority**
**Calculation:**
- ✅ Counts ALL hours (24/7)
- ✅ **No weekend exclusion**
- ✅ **No non-working hours exclusion**
- ✅ **No holiday exclusion**
**Example:**
```
TAT = 16 hours
Start: Monday 2:00 PM
Calculation:
Simply add 16 hours:
Monday 2:00 PM + 16 hours = Tuesday 6:00 AM
Final deadline: Tuesday 6:00 AM ✅
(Even if Tuesday is a holiday, it still counts)
```
---
## Holiday Configuration Flow
### **1. Admin Adds Holiday**
```
Settings Page → Holiday Manager → Add Holiday
Name: "Christmas Day"
Date: 2025-12-25
Type: Public Holiday
✅ Save
```
### **2. Holiday Stored in Database**
```sql
INSERT INTO holidays (holiday_date, holiday_name, holiday_type, is_active)
VALUES ('2025-12-25', 'Christmas Day', 'PUBLIC_HOLIDAY', true);
```
### **3. Holiday Cache Updated**
```typescript
// Holidays are cached in memory for 6 hours
await loadHolidaysCache();
// → holidaysCache = Set(['2025-12-25', '2025-01-01', ...])
```
### **4. TAT Calculation Uses Holiday Cache**
```typescript
// When scheduling TAT jobs
if (priority === 'STANDARD') {
// Working hours calculation - checks holidays
const threshold1 = await addWorkingHours(start, hours * 0.55);
// → If date is in holidaysCache, it's skipped ✅
} else {
// EXPRESS: 24/7 calculation - ignores holidays
const threshold1 = addCalendarHours(start, hours * 0.55);
// → Adds hours directly, no checks ✅
}
```
---
## Implementation Details
### **Function: `addWorkingHours()` (STANDARD Mode)**
```typescript
export async function addWorkingHours(start: Date, hoursToAdd: number): Promise<Dayjs> {
let current = dayjs(start);
// Load holidays from database (cached)
await loadHolidaysCache();
let remaining = hoursToAdd;
while (remaining > 0) {
current = current.add(1, 'hour');
// Check if current hour is working time
if (isWorkingTime(current)) { // ✅ Checks holidays here
remaining -= 1;
}
}
return current;
}
function isWorkingTime(date: Dayjs): boolean {
// Check weekend
if (date.day() === 0 || date.day() === 6) return false;
// Check working hours
if (date.hour() < 9 || date.hour() >= 18) return false;
// Check if holiday ✅
if (isHoliday(date)) return false;
return true;
}
function isHoliday(date: Dayjs): boolean {
const dateStr = date.format('YYYY-MM-DD');
return holidaysCache.has(dateStr); // ✅ Checks cached holidays
}
```
---
### **Function: `addCalendarHours()` (EXPRESS Mode)**
```typescript
export function addCalendarHours(start: Date, hoursToAdd: number): Dayjs {
// Simple addition - no checks ✅
return dayjs(start).add(hoursToAdd, 'hour');
}
```
---
## TAT Scheduler Integration
### **Updated Method Signature:**
```typescript
async scheduleTatJobs(
requestId: string,
levelId: string,
approverId: string,
tatDurationHours: number,
startTime?: Date,
priority: Priority = Priority.STANDARD // ✅ New parameter
): Promise<void>
```
### **Priority-Based Calculation:**
```typescript
const isExpress = priority === Priority.EXPRESS;
if (isExpress) {
// EXPRESS: 24/7 calculation
threshold1Time = addCalendarHours(now, hours * 0.55).toDate();
threshold2Time = addCalendarHours(now, hours * 0.80).toDate();
breachTime = addCalendarHours(now, hours).toDate();
logger.info('Using EXPRESS mode (24/7) - no holiday/weekend exclusions');
} else {
// STANDARD: Working hours, exclude holidays
const t1 = await addWorkingHours(now, hours * 0.55);
const t2 = await addWorkingHours(now, hours * 0.80);
const tBreach = await addWorkingHours(now, hours);
threshold1Time = t1.toDate();
threshold2Time = t2.toDate();
breachTime = tBreach.toDate();
logger.info('Using STANDARD mode - excludes holidays, weekends, non-working hours');
}
```
---
## Example Scenarios
### **Scenario 1: STANDARD with Holiday**
```
Request Details:
- Priority: STANDARD
- TAT: 16 working hours
- Start: Monday 2:00 PM
- Holiday: Wednesday (Christmas)
Calculation:
Monday 2:00 PM - 6:00 PM = 4 hours (12h remaining)
Tuesday 9:00 AM - 6:00 PM = 9 hours (3h remaining)
Wednesday (HOLIDAY) = SKIPPED ✅
Thursday 9:00 AM - 12:00 PM = 3 hours (0h remaining)
TAT Milestones:
- Threshold 1 (55%): Tuesday 4:40 PM (8.8 working hours)
- Threshold 2 (80%): Thursday 10:48 AM (12.8 working hours)
- Breach (100%): Thursday 12:00 PM (16 working hours)
```
---
### **Scenario 2: EXPRESS with Holiday**
```
Request Details:
- Priority: EXPRESS
- TAT: 16 hours
- Start: Monday 2:00 PM
- Holiday: Wednesday (Christmas) - IGNORED ✅
Calculation:
Monday 2:00 PM + 16 hours = Tuesday 6:00 AM
TAT Milestones:
- Threshold 1 (55%): Monday 10:48 PM (8.8 hours)
- Threshold 2 (80%): Tuesday 2:48 AM (12.8 hours)
- Breach (100%): Tuesday 6:00 AM (16 hours)
Note: Even though Wednesday is a holiday, EXPRESS doesn't care ✅
```
---
### **Scenario 3: Multiple Holidays**
```
Request Details:
- Priority: STANDARD
- TAT: 40 working hours
- Start: Friday 10:00 AM
- Holidays: Monday (New Year), Tuesday (Day After)
Calculation:
Friday 10:00 AM - 6:00 PM = 8 hours (32h remaining)
Saturday-Sunday = SKIPPED (weekend)
Monday (HOLIDAY) = SKIPPED ✅
Tuesday (HOLIDAY) = SKIPPED ✅
Wednesday 9:00 AM - 6:00 PM = 9 hours (23h remaining)
Thursday 9:00 AM - 6:00 PM = 9 hours (14h remaining)
Friday 9:00 AM - 6:00 PM = 9 hours (5h remaining)
Monday 9:00 AM - 2:00 PM = 5 hours (0h remaining)
Final deadline: Next Monday 2:00 PM ✅
(Skipped 2 weekends + 2 holidays)
```
---
## Holiday Cache Management
### **Cache Lifecycle:**
```
1. Server Startup
→ initializeHolidaysCache() called
→ Holidays loaded into memory
2. Cache Valid for 6 Hours
→ holidaysCacheExpiry = now + 6 hours
→ Subsequent calls use cached data (fast)
3. Cache Expires After 6 Hours
→ Next TAT calculation reloads cache from DB
→ New cache expires in 6 hours
4. Manual Cache Refresh (Optional)
→ Admin adds/updates holiday
→ Call initializeHolidaysCache() to refresh immediately
```
### **Cache Performance:**
```
Without Cache:
- Every TAT calculation → DB query → SLOW ❌
- 100 requests/hour → 100 DB queries
With Cache:
- Load once per 6 hours → DB query → FAST ✅
- 100 requests/hour → 0 DB queries (use cache)
- Cache refresh: Every 6 hours or on-demand
```
---
## Priority Detection in Services
### **Workflow Service (Submission):**
```typescript
// When submitting workflow
const workflowPriority = (updated as any).priority || 'STANDARD';
await tatSchedulerService.scheduleTatJobs(
requestId,
levelId,
approverId,
tatHours,
now,
workflowPriority // ✅ Pass priority
);
```
### **Approval Service (Next Level):**
```typescript
// When moving to next approval level
const workflowPriority = (wf as any)?.priority || 'STANDARD';
await tatSchedulerService.scheduleTatJobs(
requestId,
nextLevelId,
nextApproverId,
tatHours,
now,
workflowPriority // ✅ Pass priority
);
```
---
## Database Schema
### **Holidays Table:**
```sql
CREATE TABLE holidays (
holiday_id UUID PRIMARY KEY,
holiday_date DATE NOT NULL,
holiday_name VARCHAR(255) NOT NULL,
holiday_type VARCHAR(50),
description TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Example data
INSERT INTO holidays (holiday_date, holiday_name, holiday_type)
VALUES
('2025-12-25', 'Christmas Day', 'PUBLIC_HOLIDAY'),
('2025-01-01', 'New Year''s Day', 'PUBLIC_HOLIDAY'),
('2025-07-04', 'Independence Day', 'PUBLIC_HOLIDAY');
```
### **Workflow Request Priority:**
```sql
-- WorkflowRequest table already has priority field
SELECT request_id, priority, tat_hours
FROM workflow_requests
WHERE priority = 'EXPRESS'; -- 24/7 calculation
-- OR
WHERE priority = 'STANDARD'; -- Working hours + holiday exclusion
```
---
## Testing Scenarios
### **Test 1: Add Holiday, Create STANDARD Request**
```bash
# 1. Add holiday for tomorrow
curl -X POST http://localhost:5000/api/v1/admin/holidays \
-H "Authorization: Bearer TOKEN" \
-d '{
"holidayDate": "2025-11-06",
"holidayName": "Test Holiday",
"holidayType": "PUBLIC_HOLIDAY"
}'
# 2. Create STANDARD request with 24h TAT
curl -X POST http://localhost:5000/api/v1/workflows \
-d '{
"priority": "STANDARD",
"tatHours": 24
}'
# 3. Check scheduled TAT jobs in logs
# → Should show deadline skipping the holiday ✅
```
### **Test 2: Same Holiday, EXPRESS Request**
```bash
# 1. Holiday still exists (tomorrow)
# 2. Create EXPRESS request with 24h TAT
curl -X POST http://localhost:5000/api/v1/workflows \
-d '{
"priority": "EXPRESS",
"tatHours": 24
}'
# 3. Check scheduled TAT jobs in logs
# → Should show deadline NOT skipping the holiday ✅
# → Exactly 24 hours from now (includes holiday)
```
### **Test 3: Verify Holiday Exclusion**
```bash
# Create request on Friday afternoon
# With 16 working hours TAT
# Should skip weekend and land on Monday/Tuesday
# If Monday is a holiday:
# → STANDARD: Should land on Tuesday ✅
# → EXPRESS: Should land on Sunday ✅
```
---
## Logging Examples
### **STANDARD Mode Log:**
```
[TAT Scheduler] Using STANDARD mode - excludes holidays, weekends, non-working hours
[TAT Scheduler] Calculating TAT milestones for request REQ-123, level LEVEL-456
[TAT Scheduler] Priority: STANDARD, TAT Hours: 16
[TAT Scheduler] Start: 2025-11-05 14:00
[TAT Scheduler] Threshold 1 (55%): 2025-11-07 11:48 (skipped 1 holiday)
[TAT Scheduler] Threshold 2 (80%): 2025-11-08 09:48
[TAT Scheduler] Breach (100%): 2025-11-08 14:00
```
### **EXPRESS Mode Log:**
```
[TAT Scheduler] Using EXPRESS mode (24/7) - no holiday/weekend exclusions
[TAT Scheduler] Calculating TAT milestones for request REQ-456, level LEVEL-789
[TAT Scheduler] Priority: EXPRESS, TAT Hours: 16
[TAT Scheduler] Start: 2025-11-05 14:00
[TAT Scheduler] Threshold 1 (55%): 2025-11-05 22:48 (8.8 hours)
[TAT Scheduler] Threshold 2 (80%): 2025-11-06 02:48 (12.8 hours)
[TAT Scheduler] Breach (100%): 2025-11-06 06:00 (16 hours)
```
---
## Summary
### **What Changed:**
1. ✅ Added `addCalendarHours()` for EXPRESS mode (24/7 calculation)
2. ✅ Updated `addWorkingHours()` to check holidays from admin settings
3. ✅ Added `priority` parameter to `scheduleTatJobs()`
4. ✅ Updated workflow/approval services to pass priority
5. ✅ Holiday cache for performance (6-hour expiry)
### **How Holidays Are Used:**
| Priority | Calculation Method | Holidays | Weekends | Non-Working Hours |
|----------|-------------------|----------|----------|-------------------|
| **STANDARD** | Working hours only | ✅ Excluded | ✅ Excluded | ✅ Excluded |
| **EXPRESS** | 24/7 calendar hours | ❌ Counted | ❌ Counted | ❌ Counted |
### **Benefits:**
1. ✅ **Accurate TAT for STANDARD** - Respects holidays, no false breaches
2. ✅ **Fast EXPRESS** - True 24/7 calculation for urgent requests
3. ✅ **Centralized Holiday Management** - Admin can add/edit holidays
4. ✅ **Performance** - Holiday cache prevents repeated DB queries
5. ✅ **Flexible** - Priority can be changed per request
---
## Files Modified
1. `Re_Backend/src/utils/tatTimeUtils.ts` - Added `addCalendarHours()` for EXPRESS mode
2. `Re_Backend/src/services/tatScheduler.service.ts` - Added priority parameter and logic
3. `Re_Backend/src/services/workflow.service.ts` - Pass priority when scheduling TAT
4. `Re_Backend/src/services/approval.service.ts` - Pass priority for next level TAT
---
## Configuration Keys
| Config Key | Default | Description |
|------------|---------|-------------|
| `WORK_START_HOUR` | 9 | Working hours start (STANDARD mode only) |
| `WORK_END_HOUR` | 18 | Working hours end (STANDARD mode only) |
| `WORK_START_DAY` | 1 | Monday (STANDARD mode only) |
| `WORK_END_DAY` | 5 | Friday (STANDARD mode only) |
**Note:** EXPRESS mode ignores all these configurations and uses 24/7 calculation.

View File

@ -1,307 +0,0 @@
# ✅ KPI & TAT Reporting System - Setup Complete!
## 🎉 What's Been Implemented
### 1. TAT Alerts Table (`tat_alerts`)
**Purpose**: Store every TAT notification (50%, 75%, 100%) for display and KPI analysis
**Features**:
- ✅ Records all TAT notifications sent
- ✅ Tracks timing, completion status, and compliance
- ✅ Stores metadata for rich reporting
- ✅ Displays like the shared image: "Reminder 1: 50% of SLA breach reminder have been sent"
**Example Query**:
```sql
-- Get TAT alerts for a specific request (for UI display)
SELECT
alert_type,
threshold_percentage,
alert_sent_at,
alert_message
FROM tat_alerts
WHERE request_id = 'YOUR_REQUEST_ID'
ORDER BY alert_sent_at ASC;
```
---
### 2. Eight KPI Views Created
All views are ready to use for reporting and dashboards:
| View Name | Purpose | KPI Category |
|-----------|---------|--------------|
| `vw_request_volume_summary` | Request counts, status, cycle times | Volume & Status |
| `vw_tat_compliance` | TAT compliance tracking | TAT Efficiency |
| `vw_approver_performance` | Approver metrics, response times | Approver Load |
| `vw_tat_alerts_summary` | TAT alerts with response times | TAT Efficiency |
| `vw_department_summary` | Department-wise statistics | Volume & Status |
| `vw_daily_kpi_metrics` | Daily trends and metrics | Trends |
| `vw_workflow_aging` | Aging analysis | Volume & Status |
| `vw_engagement_metrics` | Comments, documents, collaboration | Engagement & Quality |
---
### 3. Complete KPI Coverage
All KPIs from your requirements are now supported:
#### ✅ Request Volume & Status
- Total Requests Created
- Open Requests (with age)
- Approved Requests
- Rejected Requests
#### ✅ TAT Efficiency
- Average TAT Compliance %
- Avg Approval Cycle Time
- Delayed Workflows
- TAT Breach History
#### ✅ Approver Load
- Pending Actions (My Queue)
- Approvals Completed (Today/Week)
- Approver Performance Metrics
#### ✅ Engagement & Quality
- Comments/Work Notes Added
- Attachments Uploaded
- Spectator Participation
---
## 📊 Example Queries
### Show TAT Reminders (Like Your Image)
```sql
-- For displaying TAT alerts in Request Detail screen
SELECT
CASE
WHEN alert_type = 'TAT_50' THEN '⏳ 50% of SLA breach reminder have been sent'
WHEN alert_type = 'TAT_75' THEN '⚠️ 75% of SLA breach reminder have been sent'
WHEN alert_type = 'TAT_100' THEN '⏰ TAT breached - Immediate action required'
END as reminder_text,
'Reminder sent by system automatically' as description,
alert_sent_at
FROM tat_alerts
WHERE request_id = 'REQUEST_ID'
AND level_id = 'LEVEL_ID'
ORDER BY threshold_percentage ASC;
```
### TAT Compliance Rate
```sql
SELECT
ROUND(
COUNT(CASE WHEN completed_within_tat = true THEN 1 END) * 100.0 /
NULLIF(COUNT(CASE WHEN completed_within_tat IS NOT NULL THEN 1 END), 0),
2
) as compliance_percentage
FROM vw_tat_compliance;
```
### Approver Performance Leaderboard
```sql
SELECT
approver_name,
department,
ROUND(tat_compliance_percentage, 2) as compliance_percent,
approved_count,
ROUND(avg_response_time_hours, 2) as avg_response_hours,
breaches_count
FROM vw_approver_performance
WHERE total_assignments > 0
ORDER BY tat_compliance_percentage DESC
LIMIT 10;
```
### Department Comparison
```sql
SELECT
department,
total_requests,
approved_requests,
ROUND(approved_requests * 100.0 / NULLIF(total_requests, 0), 2) as approval_rate,
ROUND(avg_cycle_time_hours / 24, 2) as avg_cycle_days
FROM vw_department_summary
WHERE department IS NOT NULL
ORDER BY total_requests DESC;
```
---
## 🚀 How TAT Alerts Work
### 1. When Request is Submitted
```
✅ TAT monitoring starts for Level 1
✅ Jobs scheduled: 50%, 75%, 100%
✅ level_start_time and tat_start_time set
```
### 2. When Notification Fires
```
✅ Notification sent to approver
✅ Record created in tat_alerts table
✅ Activity logged
✅ Flags updated in approval_levels
```
### 3. Display in UI
```javascript
// Frontend can fetch and display like:
const alerts = await getTATAlerts(requestId, levelId);
alerts.forEach(alert => {
console.log(`Reminder ${alert.threshold_percentage}%: ${alert.alert_message}`);
console.log(`Sent at: ${formatDate(alert.alert_sent_at)}`);
});
```
---
## 📈 Analytical Reports Supported
1. **Request Lifecycle Report** - Complete timeline with TAT
2. **Approver Performance Report** - Leaderboard & metrics
3. **Department-wise Summary** - Cross-department comparison
4. **TAT Breach Report** - All breached requests with reasons
5. **Priority Distribution** - Express vs Standard analysis
6. **Workflow Aging** - Long-running requests
7. **Daily/Weekly Trends** - Time-series analysis
8. **Engagement Metrics** - Collaboration tracking
---
## 🎯 Next Steps
### 1. Setup Upstash Redis (REQUIRED)
TAT notifications need Redis to work:
1. Go to: https://console.upstash.com/
2. Create free Redis database
3. Copy connection URL
4. Add to `.env`:
```bash
REDIS_URL=rediss://default:PASSWORD@host.upstash.io:6379
TAT_TEST_MODE=true
```
5. Restart backend
See: `START_HERE.md` or `TAT_QUICK_START.md`
### 2. Test TAT Notifications
1. Create request with 6-hour TAT (becomes 6 minutes in test mode)
2. Submit request
3. Wait for notifications: 3min, 4.5min, 6min
4. Check `tat_alerts` table
5. Verify display in Request Detail screen
### 3. Build Frontend Reports
Use the KPI views to build:
- Dashboard cards
- Charts (pie, bar, line)
- Tables with filters
- Export to CSV
---
## 📚 Documentation
| Document | Purpose |
|----------|---------|
| `docs/KPI_REPORTING_SYSTEM.md` | Complete KPI guide with all queries |
| `docs/TAT_NOTIFICATION_SYSTEM.md` | TAT system architecture |
| `TAT_QUICK_START.md` | Quick setup for TAT |
| `START_HERE.md` | Start here for TAT setup |
| `backend_structure.txt` | Database schema reference |
---
## 🔍 Database Schema Summary
```
tat_alerts (NEW)
├─ alert_id (PK)
├─ request_id (FK → workflow_requests)
├─ level_id (FK → approval_levels)
├─ approver_id (FK → users)
├─ alert_type (TAT_50, TAT_75, TAT_100)
├─ threshold_percentage (50, 75, 100)
├─ tat_hours_allocated
├─ tat_hours_elapsed
├─ tat_hours_remaining
├─ level_start_time
├─ alert_sent_at
├─ expected_completion_time
├─ alert_message
├─ notification_sent
├─ notification_channels (array)
├─ is_breached
├─ was_completed_on_time
├─ completion_time
├─ metadata (JSONB)
└─ created_at
approval_levels (UPDATED)
├─ ... existing fields ...
├─ tat50_alert_sent (NEW)
├─ tat75_alert_sent (NEW)
├─ tat_breached (NEW)
└─ tat_start_time (NEW)
8 Views Created:
├─ vw_request_volume_summary
├─ vw_tat_compliance
├─ vw_approver_performance
├─ vw_tat_alerts_summary
├─ vw_department_summary
├─ vw_daily_kpi_metrics
├─ vw_workflow_aging
└─ vw_engagement_metrics
```
---
## ✅ Implementation Checklist
- [x] Create `tat_alerts` table
- [x] Add TAT status fields to `approval_levels`
- [x] Create 8 KPI views for reporting
- [x] Update TAT processor to log alerts
- [x] Export `TatAlert` model
- [x] Run all migrations successfully
- [x] Create comprehensive documentation
- [ ] Setup Upstash Redis (YOU DO THIS)
- [ ] Test TAT notifications (YOU DO THIS)
- [ ] Build frontend KPI dashboards (YOU DO THIS)
---
## 🎉 Status: READY TO USE!
- ✅ Database schema complete
- ✅ TAT alerts logging ready
- ✅ KPI views optimized
- ✅ All migrations applied
- ✅ Documentation complete
**Just connect Redis and you're good to go!**
---
**Last Updated**: November 4, 2025
**Team**: Royal Enfield Workflow System

View File

@ -1,120 +0,0 @@
# 🚀 Migration Quick Reference
## Daily Development Workflow
### Starting Development (Auto-runs Migrations)
```bash
npm run dev
```
✅ **This will automatically run all new migrations before starting the server!**
### Run Migrations Only
```bash
npm run migrate
```
## Adding a New Migration (3 Steps)
### 1⃣ Create Migration File
Location: `src/migrations/YYYYMMDD-description.ts`
```typescript
import { QueryInterface, DataTypes } from 'sequelize';
export async function up(queryInterface: QueryInterface): Promise<void> {
await queryInterface.addColumn('table_name', 'column_name', {
type: DataTypes.STRING,
allowNull: true,
});
console.log('✅ Migration completed');
}
export async function down(queryInterface: QueryInterface): Promise<void> {
await queryInterface.removeColumn('table_name', 'column_name');
console.log('✅ Rollback completed');
}
```
### 2⃣ Register in `src/scripts/migrate.ts`
```typescript
// Add import at top
import * as m15 from '../migrations/YYYYMMDD-description';
// Add execution in run() function
await (m15 as any).up(sequelize.getQueryInterface());
```
### 3⃣ Test
```bash
npm run migrate
```
## Common Operations
### Add Column
```typescript
await queryInterface.addColumn('table', 'column', {
type: DataTypes.STRING(100),
allowNull: false,
defaultValue: 'value'
});
```
### Add Foreign Key
```typescript
await queryInterface.addColumn('table', 'foreign_id', {
type: DataTypes.UUID,
references: { model: 'other_table', key: 'id' },
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
});
```
### Add Index
```typescript
await queryInterface.addIndex('table', ['column'], {
name: 'idx_table_column'
});
```
### Create Table
```typescript
await queryInterface.createTable('new_table', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: DataTypes.STRING(100),
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE
});
```
## What's New ✨
### Latest Migration: Skip Approver Functionality
- **File**: `20251105-add-skip-fields-to-approval-levels.ts`
- **Added Fields**:
- `is_skipped` - Boolean flag
- `skipped_at` - Timestamp
- `skipped_by` - User reference
- `skip_reason` - Text explanation
- **Index**: Partial index on `is_skipped = TRUE`
## Troubleshooting
| Issue | Solution |
|-------|----------|
| Migration fails | Check console error, fix migration file, re-run |
| Column exists error | Migration partially ran - add idempotent checks |
| Server won't start | Fix migration first, it blocks startup |
## 📚 Full Documentation
See `MIGRATION_WORKFLOW.md` for comprehensive guide.
---
**Auto-Migration**: ✅ Enabled
**Total Migrations**: 14
**Latest**: 2025-11-05

View File

@ -1,284 +0,0 @@
# Migration Workflow Guide
## Overview
This project uses a TypeScript-based migration system for database schema changes. All migrations are automatically executed when you start the development server.
## 🚀 Quick Start
### Running Development Server with Migrations
```bash
npm run dev
```
This command will:
1. ✅ Run all pending migrations automatically
2. 🚀 Start the development server with hot reload
### Running Migrations Only
```bash
npm run migrate
```
Use this when you only want to apply migrations without starting the server.
## 📝 Creating New Migrations
### Step 1: Create Migration File
Create a new TypeScript file in `src/migrations/` with the naming pattern:
```
YYYYMMDD-descriptive-name.ts
```
Example: `20251105-add-new-field.ts`
### Step 2: Migration Template
```typescript
import { QueryInterface, DataTypes } from 'sequelize';
/**
* Migration: Brief description
* Purpose: Detailed explanation
* Date: YYYY-MM-DD
*/
export async function up(queryInterface: QueryInterface): Promise<void> {
// Add your forward migration logic here
await queryInterface.addColumn('table_name', 'column_name', {
type: DataTypes.STRING,
allowNull: true,
});
console.log('✅ Migration description completed');
}
export async function down(queryInterface: QueryInterface): Promise<void> {
// Add your rollback logic here
await queryInterface.removeColumn('table_name', 'column_name');
console.log('✅ Migration rolled back');
}
```
### Step 3: Register Migration
Add your new migration to `src/scripts/migrate.ts`:
```typescript
// 1. Import at the top
import * as m15 from '../migrations/20251105-add-new-field';
// 2. Execute in the run() function
await (m15 as any).up(sequelize.getQueryInterface());
```
### Step 4: Test
```bash
npm run migrate
```
## 📋 Current Migrations
The following migrations are configured and will run in order:
1. `2025103001-create-workflow-requests` - Core workflow requests table
2. `2025103002-create-approval-levels` - Approval hierarchy structure
3. `2025103003-create-participants` - Workflow participants
4. `2025103004-create-documents` - Document attachments
5. `20251031_01_create_subscriptions` - User subscriptions
6. `20251031_02_create_activities` - Activity tracking
7. `20251031_03_create_work_notes` - Work notes/comments
8. `20251031_04_create_work_note_attachments` - Note attachments
9. `20251104-add-tat-alert-fields` - TAT alert fields
10. `20251104-create-tat-alerts` - TAT alerts table
11. `20251104-create-kpi-views` - KPI database views
12. `20251104-create-holidays` - Holiday calendar
13. `20251104-create-admin-config` - Admin configurations
14. `20251105-add-skip-fields-to-approval-levels` - Skip approver functionality
## 🔄 Migration Safety Features
### Idempotent Migrations
All migrations should be **idempotent** (safe to run multiple times). Use checks like:
```typescript
// Check if column exists before adding
const tableDescription = await queryInterface.describeTable('table_name');
if (!tableDescription.column_name) {
await queryInterface.addColumn(/* ... */);
}
// Check if table exists before creating
const tables = await queryInterface.showAllTables();
if (!tables.includes('table_name')) {
await queryInterface.createTable(/* ... */);
}
```
### Error Handling
Migrations automatically:
- ✅ Stop on first error
- ❌ Exit with error code 1 on failure
- 📝 Log detailed error messages
- 🔄 Prevent server startup if migrations fail
## 🛠️ Common Migration Operations
### Adding a Column
```typescript
await queryInterface.addColumn('table_name', 'new_column', {
type: DataTypes.STRING(100),
allowNull: false,
defaultValue: 'default_value',
comment: 'Column description'
});
```
### Adding Foreign Key
```typescript
await queryInterface.addColumn('table_name', 'foreign_key_id', {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'referenced_table',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
});
```
### Creating Index
```typescript
await queryInterface.addIndex('table_name', ['column_name'], {
name: 'idx_table_column',
unique: false
});
// Partial index with WHERE clause
await queryInterface.addIndex('table_name', ['status'], {
name: 'idx_table_active',
where: {
is_active: true
}
});
```
### Creating Table
```typescript
await queryInterface.createTable('new_table', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
});
```
### Modifying Column
```typescript
await queryInterface.changeColumn('table_name', 'column_name', {
type: DataTypes.STRING(200), // Changed from 100
allowNull: true // Changed from false
});
```
### Dropping Column
```typescript
await queryInterface.removeColumn('table_name', 'old_column');
```
### Raw SQL Queries
```typescript
await queryInterface.sequelize.query(`
CREATE OR REPLACE VIEW view_name AS
SELECT * FROM table_name WHERE condition
`);
```
## 📊 Database Structure Reference
Always refer to `backend_structure.txt` for the authoritative database structure including:
- All tables and their columns
- Data types and constraints
- Relationships and foreign keys
- Enum values
- Indexes
## 🚨 Troubleshooting
### Migration Fails with "Column Already Exists"
- The migration might have partially run
- Add idempotent checks or manually rollback the failed migration
### Server Won't Start After Migration
- Check the migration error in console
- Fix the migration file
- Run `npm run migrate` to retry
### Need to Rollback a Migration
```bash
# Manual rollback (requires implementing down() function)
ts-node src/scripts/rollback.ts
```
## 🎯 Best Practices
1. **Always test migrations** on development database first
2. **Write rollback logic** in `down()` function
3. **Use descriptive names** for migrations
4. **Add comments** explaining the purpose
5. **Keep migrations small** - one logical change per file
6. **Never modify** existing migration files after they run in production
7. **Use transactions** for complex multi-step migrations
8. **Backup production** before running new migrations
## 📝 Migration Checklist
Before running migrations in production:
- [ ] Tested on local development database
- [ ] Verified rollback functionality works
- [ ] Checked for data loss scenarios
- [ ] Reviewed index impact on performance
- [ ] Confirmed migration is idempotent
- [ ] Updated `backend_structure.txt` documentation
- [ ] Added migration to version control
- [ ] Registered in `migrate.ts`
## 🔗 Related Files
- **Migration Scripts**: `src/migrations/`
- **Migration Runner**: `src/scripts/migrate.ts`
- **Database Config**: `src/config/database.ts`
- **Database Structure**: `backend_structure.txt`
- **Package Scripts**: `package.json`
## 💡 Example: Recent Migration
The latest migration (`20251105-add-skip-fields-to-approval-levels`) demonstrates best practices:
- ✅ Descriptive naming
- ✅ Clear documentation
- ✅ Multiple related columns added together
- ✅ Foreign key relationships
- ✅ Indexed for query performance
- ✅ Includes rollback logic
- ✅ Helpful console messages
---
**Last Updated**: November 5, 2025
**Migration Count**: 14 migrations
**Auto-Run**: Enabled for `npm run dev`

View File

@ -1,220 +0,0 @@
# Quick Fix: Settings Not Editable Issue
## 🔴 Problem
Settings showing as "not editable" in the frontend.
## 🎯 Root Cause
**Field Mapping Issue:** Database uses `is_editable` (snake_case) but frontend expects `isEditable` (camelCase).
## ✅ Solution Applied
### **1. Fixed Admin Controller**
Added field mapping from snake_case to camelCase:
```typescript
// Re_Backend/src/controllers/admin.controller.ts
const configurations = rawConfigurations.map(config => ({
configId: config.config_id, // ✅ Mapped
isEditable: config.is_editable, // ✅ Mapped
isSensitive: config.is_sensitive, // ✅ Mapped
requiresRestart: config.requires_restart, // ✅ Mapped
// ... all other fields
}));
```
### **2. Database Fix Required**
**Option A: Delete and Re-seed** (Recommended if no custom configs)
```sql
-- Connect to your database
DELETE FROM admin_configurations;
-- Restart backend - auto-seeding will run
-- Check logs for: "✅ Default configurations seeded (18 settings)"
```
**Option B: Fix Existing Records** (If you have custom values)
```sql
-- Update existing records to add missing fields
UPDATE admin_configurations
SET
is_sensitive = COALESCE(is_sensitive, false),
requires_restart = COALESCE(requires_restart, false),
is_editable = COALESCE(is_editable, true)
WHERE is_sensitive IS NULL
OR requires_restart IS NULL
OR is_editable IS NULL;
-- Set requires_restart = true for settings that need it
UPDATE admin_configurations
SET requires_restart = true
WHERE config_key IN (
'WORK_START_HOUR',
'WORK_END_HOUR',
'MAX_FILE_SIZE_MB',
'ALLOWED_FILE_TYPES'
);
```
---
## 🚀 Step-by-Step Fix
### **Step 1: Stop Backend**
```bash
# Press Ctrl+C to stop the server
```
### **Step 2: Clear Configurations** (if any exist)
```sql
-- Connect to PostgreSQL
psql -U postgres -d re_workflow
-- Check if configurations exist
SELECT COUNT(*) FROM admin_configurations;
-- If count > 0, delete them
DELETE FROM admin_configurations;
-- Verify
SELECT COUNT(*) FROM admin_configurations;
-- Should show: 0
```
### **Step 3: Restart Backend** (Auto-seeds)
```bash
cd Re_Backend
npm run dev
```
### **Step 4: Verify Seeding in Logs**
Look for:
```
⚙️ System configurations initialized
✅ Default configurations seeded successfully (18 settings across 7 categories)
```
### **Step 5: Test in Frontend**
1. Login as Admin user
2. Go to **Settings → System Configuration**
3. You should see **7 category tabs**
4. Click any tab (e.g., "TAT SETTINGS")
5. All settings should now have:
- ✅ Editable input fields
- ✅ **Save** button enabled
- ✅ **Reset to Default** button
---
## 🧪 Verify Configuration Loaded Correctly
**Test API Endpoint:**
```bash
# Get all configurations
curl http://localhost:5000/api/v1/admin/configurations \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
```
**Expected Response:**
```json
{
"success": true,
"data": [
{
"configId": "uuid...",
"configKey": "DEFAULT_TAT_EXPRESS_HOURS",
"configCategory": "TAT_SETTINGS",
"configValue": "24",
"valueType": "NUMBER",
"displayName": "Default TAT for Express Priority",
"isEditable": true, // ✅ Should be true
"isSensitive": false,
"validationRules": {"min": 1, "max": 168},
"uiComponent": "number",
"sortOrder": 1,
"requiresRestart": false
},
// ... 17 more configurations
],
"count": 18
}
```
**Check the `isEditable` field - should be `true` for all!**
---
## 🐛 Common Issues & Solutions
### Issue 1: "Configurations already exist. Skipping seed."
**Cause:** Old configurations in database
**Fix:** Delete them and restart backend
### Issue 2: Settings show as gray/disabled
**Cause:** `is_editable = false` in database
**Fix:** Run SQL update to set all to `true`
### Issue 3: "Configuration not found or not editable" error when saving
**Cause:** Backend can't find the config or `is_editable = false`
**Fix:** Verify database has correct values
### Issue 4: Empty settings page
**Cause:** No configurations in database
**Fix:** Check backend logs for seeding errors, run seed manually
---
## 📊 Expected Database State
After successful seeding, your `admin_configurations` table should have:
| Count | Category | All Editable? |
|-------|----------|---------------|
| 6 | TAT_SETTINGS | ✅ Yes |
| 3 | DOCUMENT_POLICY | ✅ Yes |
| 2 | AI_CONFIGURATION | ✅ Yes |
| 3 | NOTIFICATION_RULES | ✅ Yes |
| 4 | DASHBOARD_LAYOUT | ✅ Yes |
| 3 | WORKFLOW_SHARING | ✅ Yes |
| 2 | WORKFLOW_LIMITS | ✅ Yes |
| **18 Total** | **7 Categories** | **✅ All Editable** |
Query to verify:
```sql
SELECT
config_category,
COUNT(*) as total,
SUM(CASE WHEN is_editable = true THEN 1 ELSE 0 END) as editable_count
FROM admin_configurations
GROUP BY config_category
ORDER BY config_category;
```
Should show 100% editable in all categories!
---
## ✅ After Fix - Settings UI Will Show:
```
Settings → System Configuration
┌─────────────────────────────────────────┐
│ [TAT SETTINGS] [DOCUMENT POLICY] [...] │ ← 7 tabs
├─────────────────────────────────────────┤
│ │
│ ⏰ Default TAT for Express Priority │
│ (Description...) │
│ ┌──────┐ ← EDITABLE │
│ │ 24 │ │
│ └──────┘ │
│ [💾 Save] [🔄 Reset] ← ENABLED │
│ │
│ ⏰ First TAT Reminder (%) │
│ ━━━━●━━━━ 50% ← SLIDER WORKS │
│ [💾 Save] [🔄 Reset] │
│ │
└─────────────────────────────────────────┘
```
**All inputs should be EDITABLE and Save buttons ENABLED!** ✅

View File

@ -1,253 +0,0 @@
# Quick Start: Skip & Add Approver Features
## 🚀 Setup (One-Time)
### **Step 1: Run Database Migration**
```bash
# Connect to database
psql -U postgres -d re_workflow
# Run migration
\i Re_Backend/src/migrations/add_is_skipped_to_approval_levels.sql
# Verify columns added
\d approval_levels
# Should show: is_skipped, skipped_at, skipped_by, skip_reason
```
### **Step 2: Restart Backend**
```bash
cd Re_Backend
npm run dev
```
---
## 📖 User Guide
### **How to Skip an Approver (Initiator/Approver)**
1. Go to **Request Detail****Workflow** tab
2. Find the approver who is pending/in-review
3. Click **"Skip This Approver"** button
4. Enter reason (e.g., "On vacation")
5. Click OK
**Result:**
- ✅ Approver marked as SKIPPED
- ✅ Next approver becomes active
- ✅ Notification sent to next approver
- ✅ Activity logged
---
### **How to Add New Approver (Initiator/Approver)**
1. Go to **Request Detail** → **Quick Actions**
2. Click **"Add Approver"**
3. Review **Current Levels** (shows all existing approvers with status)
4. Select **Approval Level** (where to insert new approver)
5. Enter **TAT Hours** (e.g., 48)
6. Enter **Email** (use @ to search: `@john`)
7. Click **"Add at Level X"**
**Result:**
- ✅ New approver inserted at chosen level
- ✅ Existing approvers shifted automatically
- ✅ TAT jobs scheduled if level is active
- ✅ Notification sent to new approver
- ✅ Activity logged
---
## 🎯 Examples
### **Example 1: Skip Non-Responding Approver**
**Scenario:** Mike (Level 2) hasn't responded for 3 days, deadline approaching
**Steps:**
1. Open request REQ-2025-001
2. Go to Workflow tab
3. Find Mike's card (Level 2 - In Review)
4. Click "Skip This Approver"
5. Reason: "Approver on extended leave - deadline critical"
6. Confirm
**Result:**
```
Before: After:
Level 1: Sarah ✅ Level 1: Sarah ✅
Level 2: Mike ⏳ → Level 2: Mike ⏭️ (SKIPPED)
Level 3: Lisa ⏸️ Level 3: Lisa ⏳ (ACTIVE!)
```
---
### **Example 2: Add Finance Review**
**Scenario:** Need Finance Manager approval between existing levels
**Steps:**
1. Click "Add Approver" in Quick Actions
2. See current levels:
- Level 1: Sarah (Approved)
- Level 2: Mike (In Review)
- Level 3: Lisa (Waiting)
3. Select Level: **3** (to insert before Lisa)
4. TAT Hours: **48**
5. Email: `@john` → Select "John Doe (john@finance.com)"
6. Click "Add at Level 3"
**Result:**
```
Before: After:
Level 1: Sarah ✅ Level 1: Sarah ✅
Level 2: Mike ⏳ Level 2: Mike ⏳
Level 3: Lisa ⏸️ → Level 3: John ⏸️ (NEW!)
Level 4: Lisa ⏸️ (shifted)
```
---
## ⚙️ API Reference
### **Skip Approver**
```bash
POST /api/v1/workflows/:requestId/approvals/:levelId/skip
Headers:
Authorization: Bearer <token>
Body:
{
"reason": "Approver on vacation"
}
Response:
{
"success": true,
"message": "Approver skipped successfully"
}
```
---
### **Add Approver at Level**
```bash
POST /api/v1/workflows/:requestId/approvers/at-level
Headers:
Authorization: Bearer <token>
Body:
{
"email": "john@example.com",
"tatHours": 48,
"level": 3
}
Response:
{
"success": true,
"message": "Approver added successfully",
"data": {
"levelId": "...",
"levelNumber": 3,
"approverName": "John Doe",
"tatHours": 48
}
}
```
---
## 🛡️ Permissions
| Action | Who Can Do It |
|--------|---------------|
| Skip Approver | ✅ INITIATOR, ✅ APPROVER |
| Add Approver | ✅ INITIATOR, ✅ APPROVER |
| View Skip Reason | ✅ All participants |
---
## ⚠️ Limitations
| Limitation | Reason |
|------------|--------|
| Cannot skip approved levels | Data integrity |
| Cannot skip rejected levels | Already closed |
| Cannot skip already skipped levels | Already handled |
| Cannot skip future levels | Not yet active |
| Cannot add before completed levels | Would break workflow state |
| Must provide valid TAT (1-720h) | Business rules |
---
## 📊 Dashboard Impact
### **Skipped Approvers in Reports:**
```sql
-- Count skipped approvers
SELECT COUNT(*)
FROM approval_levels
WHERE is_skipped = TRUE;
-- Find requests with skipped levels
SELECT r.request_number, al.level_number, al.approver_name, al.skip_reason
FROM workflow_requests r
JOIN approval_levels al ON r.request_id = al.request_id
WHERE al.is_skipped = TRUE;
```
### **KPIs Affected:**
- **Avg Approval Time** - Skipped levels excluded from calculation
- **Approver Response Rate** - Skipped marked separately
- **Workflow Bottlenecks** - Identify frequently skipped approvers
---
## 🔍 Troubleshooting
### **"Cannot skip approver - level is already APPROVED"**
- The level has already been approved
- You cannot skip completed levels
### **"Cannot skip future approval levels"**
- You're trying to skip a level that hasn't been reached yet
- Only current level can be skipped
### **"Cannot add approver at level X. Minimum allowed level is Y"**
- You're trying to add before a completed level
- Must add after all approved/rejected/skipped levels
### **"User is already a participant in this request"**
- The user is already an approver, initiator, or spectator
- Cannot add same user twice
---
## ✅ Testing Checklist
- [ ] Run database migration
- [ ] Restart backend server
- [ ] Create test workflow with 3 approvers
- [ ] Approve Level 1
- [ ] Skip Level 2 (test skip functionality)
- [ ] Verify Level 3 becomes active
- [ ] Add new approver at Level 3 (test add functionality)
- [ ] Verify levels shifted correctly
- [ ] Check activity log shows both actions
- [ ] Verify notifications sent correctly
---
Ready to use! 🎉

View File

@ -1,216 +0,0 @@
# ✅ Holiday Calendar & Admin Configuration - Setup Complete!
## 🎉 Successfully Implemented
### **Database Tables Created:**
1. ✅ `holidays` - Organization holiday calendar
2. ✅ `admin_configurations` - System-wide admin settings
### **API Endpoints Created:**
- ✅ `/api/admin/holidays` - CRUD operations for holidays
- ✅ `/api/admin/configurations` - Manage admin settings
### **Features Implemented:**
- ✅ Holiday management (add/edit/delete/bulk import)
- ✅ TAT calculation excludes holidays for STANDARD priority
- ✅ Automatic holiday cache with 6-hour refresh
- ✅ Admin configuration system ready for future UI
- ✅ Sample Indian holidays data (2025) prepared for import
---
## 🚀 Quick Start
### **1. Verify Tables:**
```bash
# Check if tables were created
psql -d your_database -c "\dt holidays"
psql -d your_database -c "\dt admin_configurations"
```
### **2. Start the Backend:**
```bash
npm run dev
```
**You should see:**
```
📅 Holiday calendar loaded for TAT calculations
[TAT Utils] Loaded 0 holidays into cache
```
### **3. Add Your First Holiday (via API):**
**As Admin user:**
```bash
curl -X POST http://localhost:5000/api/admin/holidays \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d '{
"holidayDate": "2025-11-05",
"holidayName": "Diwali",
"description": "Festival of Lights",
"holidayType": "NATIONAL"
}'
```
### **4. Bulk Import Indian Holidays (Optional):**
```bash
curl -X POST http://localhost:5000/api/admin/holidays/bulk-import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
-d @data/indian_holidays_2025.json
```
---
## 📊 How It Works
### **TAT Calculation with Holidays:**
**STANDARD Priority:**
- ❌ Skips **weekends** (Saturday/Sunday)
- ❌ Skips **holidays** (from holidays table)
- ✅ Only counts **working hours** (9 AM - 6 PM)
**EXPRESS Priority:**
- ✅ Includes **all days** (24/7)
- ✅ No holidays or weekends excluded
---
## 📚 Documentation
- **Full Guide:** `docs/HOLIDAY_CALENDAR_SYSTEM.md`
- **Complete Summary:** `HOLIDAY_AND_ADMIN_CONFIG_COMPLETE.md`
---
## 🎯 Next Steps
### **For Backend Developers:**
1. Test holiday API endpoints
2. Verify TAT calculations with holidays
3. Add more admin configurations as needed
### **For Frontend Developers:**
1. Build Admin Holiday Management UI
2. Create Holiday Calendar view
3. Implement Configuration Settings page
---
## 🔍 Verify Setup
### **Check Holidays Table:**
```sql
SELECT * FROM holidays;
-- Should return 0 rows (no holidays added yet)
```
### **Check Admin Configurations:**
```sql
SELECT * FROM admin_configurations;
-- Should return 0 rows (will be seeded on first use)
```
### **Test Holiday API:**
```bash
# Get all holidays for 2025
curl http://localhost:5000/api/admin/holidays?year=2025 \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
```
---
## 📋 Sample Holidays Data
**File:** `data/indian_holidays_2025.json`
Contains 14 Indian national holidays for 2025:
- Republic Day (Jan 26)
- Holi
- Independence Day (Aug 15)
- Gandhi Jayanti (Oct 2)
- Diwali
- Christmas
- And more...
---
## ✅ Setup Status
| Component | Status | Notes |
|-----------|--------|-------|
| **Holidays Table** | ✅ Created | With 4 indexes |
| **Admin Config Table** | ✅ Created | With 3 indexes |
| **Holiday Model** | ✅ Implemented | Full CRUD support |
| **Holiday Service** | ✅ Implemented | Including bulk import |
| **Admin Controller** | ✅ Implemented | All endpoints ready |
| **Admin Routes** | ✅ Implemented | Secured with admin middleware |
| **TAT Integration** | ✅ Implemented | Holidays excluded for STANDARD |
| **Holiday Cache** | ✅ Implemented | 6-hour expiry, auto-refresh |
| **Sample Data** | ✅ Created | 14 holidays for 2025 |
| **Documentation** | ✅ Complete | Full guide available |
---
## 🎓 Example Usage
### **Create Request with Holiday in TAT Period:**
```javascript
// Create STANDARD priority request
POST /api/workflows
{
"title": "Test Request",
"priority": "STANDARD",
"approvers": [
{ "email": "approver@example.com", "tatHours": 48 }
]
}
// If holidays exist between now and +48 hours:
// - Due date will be calculated skipping those holidays
// - TAT calculation will be accurate
```
---
## 🛠️ Troubleshooting
### **Holidays not excluded from TAT?**
1. Check if holidays cache is loaded:
- Look for "Loaded X holidays into cache" in server logs
2. Verify priority is STANDARD (EXPRESS doesn't use holidays)
3. Check if holiday exists and is active:
```sql
SELECT * FROM holidays WHERE holiday_date = '2025-11-05' AND is_active = true;
```
### **Cache not updating after adding holiday?**
- Cache refreshes automatically when admin adds/updates/deletes holidays
- If not working, restart backend server
- Cache also refreshes every 6 hours automatically
---
## 📞 Support
For issues or questions:
1. Check documentation in `docs/` folder
2. Review complete guide in `HOLIDAY_AND_ADMIN_CONFIG_COMPLETE.md`
3. Consult with backend team
---
**🎉 You're all set! Start adding holidays and enjoy accurate TAT calculations!**
---
**Last Updated:** November 4, 2025
**Version:** 1.0.0
**Team:** Royal Enfield Workflow System

View File

@ -1,310 +0,0 @@
# 🎉 Auto-Migration Setup Summary
## ✅ Setup Complete!
Your development environment now automatically runs all migrations when you start the server.
---
## 📋 What Changed
### 1. ✨ New Migration Created
```
src/migrations/20251105-add-skip-fields-to-approval-levels.ts
```
**Adds "Skip Approver" functionality to approval_levels table:**
- `is_skipped` - Boolean flag
- `skipped_at` - Timestamp
- `skipped_by` - User reference (FK)
- `skip_reason` - Text explanation
- Optimized index for skipped approvers
### 2. 🔧 Migration Runner Updated
```
src/scripts/migrate.ts
```
**Enhancements:**
- ✅ Added m14 migration import
- ✅ Added m14 execution
- ✅ Better console output with emojis
- ✅ Enhanced error messages
### 3. 🚀 Auto-Run on Development Start
```json
// package.json - "dev" script
"npm run migrate && nodemon --exec ts-node ..."
```
**Before**: Manual migration required
**After**: Automatic migration on `npm run dev`
### 4. 🗑️ Cleanup
```
❌ Deleted: src/migrations/add_is_skipped_to_approval_levels.sql
```
Converted SQL → TypeScript for consistency
### 5. 📚 Documentation Created
- ✅ `MIGRATION_WORKFLOW.md` - Complete guide
- ✅ `MIGRATION_QUICK_REFERENCE.md` - Quick reference
- ✅ `AUTO_MIGRATION_SETUP_COMPLETE.md` - Detailed setup docs
- ✅ `SETUP_SUMMARY.md` - This file
---
## 🎯 How to Use
### Start Development (Most Common)
```bash
npm run dev
```
**What happens:**
```
1. 📦 Connect to database
2. 🔄 Run all 14 migrations
3. ✅ Apply any new schema changes
4. 🚀 Start development server
5. ♻️ Enable hot reload
```
### Run Migrations Only
```bash
npm run migrate
```
**When to use:**
- After pulling new migration files
- Testing migrations before dev start
- Updating database without starting server
---
## 📊 Current Migration Status
| # | Migration | Date |
|---|-----------|------|
| 1 | create-workflow-requests | 2025-10-30 |
| 2 | create-approval-levels | 2025-10-30 |
| 3 | create-participants | 2025-10-30 |
| 4 | create-documents | 2025-10-30 |
| 5 | create-subscriptions | 2025-10-31 |
| 6 | create-activities | 2025-10-31 |
| 7 | create-work-notes | 2025-10-31 |
| 8 | create-work-note-attachments | 2025-10-31 |
| 9 | add-tat-alert-fields | 2025-11-04 |
| 10 | create-tat-alerts | 2025-11-04 |
| 11 | create-kpi-views | 2025-11-04 |
| 12 | create-holidays | 2025-11-04 |
| 13 | create-admin-config | 2025-11-04 |
| 14 | **add-skip-fields-to-approval-levels** | 2025-11-05 ✨ **NEW** |
**Total**: 14 migrations configured and ready
---
## 🔥 Key Features
### Automated Workflow
```
npm run dev
Runs migrations
Starts server
Ready to code! 🎉
```
### Safety Features
- ✅ **Idempotent** - Safe to run multiple times
- ✅ **Error Handling** - Stops on first error
- ✅ **Blocks Startup** - Server won't start if migration fails
- ✅ **Rollback Support** - Every migration has down() function
- ✅ **TypeScript** - Type-safe schema changes
### Developer Experience
- ✅ **Zero Manual Steps** - Everything automatic
- ✅ **Consistent State** - Everyone has same schema
- ✅ **Fast Iteration** - Quick dev cycle
- ✅ **Clear Feedback** - Visual console output
---
## 📖 Quick Reference
### File Locations
```
src/
├── migrations/ ← Migration files
│ ├── 2025103001-create-workflow-requests.ts
│ ├── ...
│ └── 20251105-add-skip-fields-to-approval-levels.ts ✨
├── scripts/
│ └── migrate.ts ← Migration runner
└── config/
└── database.ts ← Database config
Root:
├── package.json ← Dev script with auto-migration
├── backend_structure.txt ← Database schema reference
└── MIGRATION_*.md ← Documentation
```
### Common Commands
```bash
# Development with auto-migration
npm run dev
# Migrations only
npm run migrate
# Build for production
npm run build
# Type check
npm run type-check
# Linting
npm run lint
npm run lint:fix
```
---
## 🆕 Adding New Migrations
### Quick Steps
1. **Create** migration file in `src/migrations/`
2. **Register** in `src/scripts/migrate.ts`
3. **Test** with `npm run dev` or `npm run migrate`
### Detailed Guide
See `MIGRATION_WORKFLOW.md` for:
- Migration templates
- Common operations
- Best practices
- Troubleshooting
- Safety guidelines
---
## ✨ Benefits
### For You
- ✅ No more manual migration steps
- ✅ Always up-to-date database schema
- ✅ Less context switching
- ✅ Focus on feature development
### For Team
- ✅ Consistent development environment
- ✅ Easy onboarding for new developers
- ✅ Clear migration history
- ✅ Professional workflow
### For Production
- ✅ Tested migration process
- ✅ Rollback capabilities
- ✅ Version controlled schema changes
- ✅ Audit trail of database changes
---
## 🎓 Example Session
```bash
# You just pulled latest code with new migration
git pull origin main
# Start development - migrations run automatically
npm run dev
# Console output:
📦 Database connected
🔄 Running migrations...
✅ Created workflow_requests table
✅ Created approval_levels table
...
✅ Added skip-related fields to approval_levels table
✅ All migrations applied successfully
🚀 Server running on port 5000
📊 Environment: development
⏰ TAT Worker: Initialized and listening
# Your database is now up-to-date!
# Server is running!
# Ready to code! 🎉
```
---
## 🔗 Next Steps
### Immediate
1. ✅ Run `npm run dev` to test auto-migration
2. ✅ Verify all 14 migrations execute successfully
3. ✅ Check database schema for new skip fields
### When Adding Features
1. Create migration for schema changes
2. Register in migrate.ts
3. Test with `npm run dev`
4. Commit migration with feature code
### Before Production Deploy
1. Backup production database
2. Test migrations in staging
3. Review migration execution order
4. Deploy with confidence
---
## 📞 Support & Resources
| Resource | Location |
|----------|----------|
| Full Guide | `MIGRATION_WORKFLOW.md` |
| Quick Reference | `MIGRATION_QUICK_REFERENCE.md` |
| Setup Details | `AUTO_MIGRATION_SETUP_COMPLETE.md` |
| Database Schema | `backend_structure.txt` |
| Migration Files | `src/migrations/` |
| Migration Runner | `src/scripts/migrate.ts` |
---
## 🏆 Success Criteria
- ✅ Auto-migration configured
- ✅ All 14 migrations registered
- ✅ TypeScript migration created for skip fields
- ✅ SQL file converted and cleaned up
- ✅ Documentation completed
- ✅ Package.json updated
- ✅ Migration runner enhanced
- ✅ Ready for development
---
## 🎉 You're All Set!
Just run:
```bash
npm run dev
```
And watch the magic happen! ✨
All new migrations will automatically run before your server starts.
---
**Setup Date**: November 5, 2025
**Migration System**: TypeScript-based
**Auto-Run**: ✅ Enabled
**Total Migrations**: 14
**Status**: 🟢 Production Ready
**Team**: Royal Enfield .NET Expert Team
**Project**: Workflow Management System

View File

@ -1,751 +0,0 @@
# Skip Approver & Dynamic Approver Addition
## Overview
This feature allows initiators and approvers to manage approval workflows dynamically when approvers are unavailable or additional approval is needed.
### **Key Features:**
1. **Skip Approver** - Skip non-responding approvers and move to next level
2. **Add Approver at Specific Level** - Insert new approver at any position
3. **Automatic Level Shifting** - Existing approvers are automatically renumbered
4. **Smart Validation** - Cannot modify completed levels (approved/rejected/skipped)
5. **TAT Management** - New approvers get their own TAT, jobs scheduled automatically
---
## Use Cases
### **Use Case 1: Approver on Leave**
**Scenario:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (Pending) ⏳ ← On vacation, not responding
Level 3: Lisa (Waiting) ⏸️
```
**Solution:**
```
Initiator clicks "Skip This Approver" on Level 2
→ Mike is marked as SKIPPED
→ Level 3 (Lisa) becomes active
→ Lisa receives notification
→ TAT jobs cancelled for Mike, scheduled for Lisa
```
**Result:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (Skipped) ⏭️ ← Skipped
Level 3: Lisa (In Review) ⏳ ← Now active
```
---
### **Use Case 2: Add Additional Reviewer**
**Scenario:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (In Review) ⏳
Level 3: Lisa (Waiting) ⏸️
```
**Need:** Add Finance Manager (John) between Mike and Lisa
**Solution:**
```
Click "Add Approver"
→ Email: john@example.com
→ TAT: 48 hours
→ Level: 3 (between Mike and Lisa)
→ Submit
```
**Result:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (In Review) ⏳ ← Still at level 2
Level 3: John (Waiting) ⏸️ ← NEW! Inserted here
Level 4: Lisa (Waiting) ⏸️ ← Shifted from 3 to 4
```
---
### **Use Case 3: Replace Skipped Approver**
**Scenario:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (Skipped) ⏭️
Level 3: Lisa (In Review) ⏳
```
**Need:** Add replacement for Mike at level 2
**Solution:**
```
Click "Add Approver"
→ Email: john@example.com
→ TAT: 24 hours
→ Level: 2 (Mike's old position)
→ Submit
```
**Result:**
```
Level 1: Sarah (Approved) ✅
Level 2: John (Waiting) ⏸️ ← NEW! Inserted at level 2
Level 3: Mike (Skipped) ⏭️ ← Shifted from 2 to 3
Level 4: Lisa (In Review) ⏳ ← Shifted from 3 to 4
```
---
## Database Schema
### **New Fields in `approval_levels` Table:**
```sql
-- Migration: add_is_skipped_to_approval_levels.sql
ALTER TABLE approval_levels
ADD COLUMN is_skipped BOOLEAN DEFAULT FALSE,
ADD COLUMN skipped_at TIMESTAMP,
ADD COLUMN skipped_by UUID REFERENCES users(user_id),
ADD COLUMN skip_reason TEXT;
```
### **Status Enum Update:**
Already includes `SKIPPED` status:
```sql
status ENUM('PENDING', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'SKIPPED')
```
### **Example Data:**
```sql
-- Level 2 was skipped
SELECT
level_number,
approver_name,
status,
is_skipped,
skipped_at,
skip_reason
FROM approval_levels
WHERE request_id = 'xxx';
-- Results:
-- 1 | Sarah | APPROVED | FALSE | NULL | NULL
-- 2 | Mike | SKIPPED | TRUE | 2025-11-05 | On vacation
-- 3 | Lisa | PENDING | FALSE | NULL | NULL
```
---
## API Endpoints
### **1. Skip Approver**
**Endpoint:**
```
POST /api/v1/workflows/:id/approvals/:levelId/skip
```
**Request Body:**
```json
{
"reason": "Approver on vacation - deadline approaching"
}
```
**Response:**
```json
{
"success": true,
"message": "Approver skipped successfully",
"data": {
"levelId": "...",
"levelNumber": 2,
"status": "SKIPPED",
"skippedAt": "2025-11-05T10:30:00Z"
}
}
```
**Logic:**
1. ✅ Mark level as `SKIPPED`
2. ✅ Cancel TAT jobs for skipped level
3. ✅ Activate next level (move to level+1)
4. ✅ Schedule TAT jobs for next level
5. ✅ Notify next approver
6. ✅ Log activity
**Validation:**
- ❌ Cannot skip already approved/rejected/skipped levels
- ❌ Cannot skip future levels (only current level)
- ✅ Only INITIATOR or APPROVER can skip
---
### **2. Add Approver at Specific Level**
**Endpoint:**
```
POST /api/v1/workflows/:id/approvers/at-level
```
**Request Body:**
```json
{
"email": "john@example.com",
"tatHours": 48,
"level": 3
}
```
**Response:**
```json
{
"success": true,
"message": "Approver added successfully",
"data": {
"levelId": "...",
"levelNumber": 3,
"approverName": "John Doe",
"tatHours": 48,
"status": "PENDING"
}
}
```
**Logic:**
1. ✅ Find user by email
2. ✅ Validate target level (must be after completed levels)
3. ✅ Shift existing levels at and after target level (+1)
4. ✅ Create new approval level at target position
5. ✅ Add as participant (APPROVER type)
6. ✅ If new level is current level, schedule TAT jobs
7. ✅ Notify new approver
8. ✅ Log activity
**Validation:**
- ❌ User must exist in system
- ❌ User cannot be existing participant
- ❌ Level must be after completed levels (approved/rejected/skipped)
- ✅ Automatic level shifting for existing approvers
---
## Level Shifting Logic
### **Example: Add at Level 3**
**Before:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (In Review) ⏳
Level 3: Lisa (Waiting) ⏸️
Level 4: Tom (Waiting) ⏸️
```
**Action:**
```
Add John at Level 3 with 48h TAT
```
**Backend Processing:**
```typescript
// Step 1: Get levels to shift (levelNumber >= 3)
levelsToShift = [Lisa (Level 3), Tom (Level 4)]
// Step 2: Shift each level
Lisa: Level 3 → Level 4
Tom: Level 4 → Level 5
// Step 3: Insert new approver
John: Create at Level 3
// Step 4: Update workflow.totalLevels
totalLevels: 4 → 5
```
**After:**
```
Level 1: Sarah (Approved) ✅
Level 2: Mike (In Review) ⏳
Level 3: John (Waiting) ⏸️ ← NEW!
Level 4: Lisa (Waiting) ⏸️ ← Shifted from 3
Level 5: Tom (Waiting) ⏸️ ← Shifted from 4
```
---
## Frontend Implementation
### **AddApproverModal Enhancements:**
**New Props:**
```typescript
interface AddApproverModalProps {
open: boolean;
onClose: () => void;
onConfirm: (email: string, tatHours: number, level: number) => Promise<void>;
currentLevels?: ApprovalLevelInfo[]; // ✅ NEW!
}
interface ApprovalLevelInfo {
levelNumber: number;
approverName: string;
status: string;
tatHours: number;
}
```
**UI Components:**
1. **Current Levels Display** - Shows all existing levels with status badges
2. **Level Selector** - Dropdown with available levels (after completed)
3. **TAT Hours Input** - Number input for TAT (1-720 hours)
4. **Email Search** - Existing @ mention search
**Example Modal:**
```
┌─────────────────────────────────────────────────┐
│ Add Approver │
├─────────────────────────────────────────────────┤
│ Current Approval Levels │
│ ┌─────────────────────────────────────────────┐ │
│ │ [1] Sarah 50h TAT [✓] approved │ │
│ │ [2] Mike 24h TAT [⏳] pending │ │
│ │ [3] Lisa 36h TAT [⏸] waiting │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Approval Level * │
│ [Select: Level 2 (will shift existing Level 2)] │
│ │
│ TAT (Turn Around Time) * │
│ [48] hours │
│ │
│ Email Address * │
│ [@john or john@example.com] │
│ │
│ [Cancel] [Add at Level 2] │
└─────────────────────────────────────────────────┘
```
---
### **RequestDetail Skip Button:**
Added to Workflow tab for each pending/in-review level:
```tsx
{/* Skip Approver Button - Only for active levels */}
{(isActive || step.status === 'pending') && !isCompleted && !isRejected && (
<Button
variant="outline"
size="sm"
className="w-full border-orange-300 text-orange-700 hover:bg-orange-50"
onClick={() => {
const reason = prompt('Provide reason for skipping:');
if (reason !== null) {
handleSkipApprover(step.levelId, reason);
}
}}
>
<AlertCircle className="w-4 h-4 mr-2" />
Skip This Approver
</Button>
)}
```
---
## Validation Rules
### **Skip Approver Validation:**
| Rule | Validation | Error Message |
|------|-----------|---------------|
| Already completed | ❌ Cannot skip APPROVED level | "Cannot skip approver - level is already APPROVED" |
| Already rejected | ❌ Cannot skip REJECTED level | "Cannot skip approver - level is already REJECTED" |
| Already skipped | ❌ Cannot skip SKIPPED level | "Cannot skip approver - level is already SKIPPED" |
| Future level | ❌ Cannot skip level > currentLevel | "Cannot skip future approval levels" |
| Authorization | ✅ Only INITIATOR or APPROVER | 403 Forbidden |
---
### **Add Approver Validation:**
| Rule | Validation | Error Message |
|------|-----------|---------------|
| User exists | ✅ User must exist in system | "User not found with this email" |
| Already participant | ❌ Cannot add existing participant | "User is already a participant" |
| Level range | ❌ Level must be ≥ (completed levels + 1) | "Cannot add at level X. Minimum is Y" |
| TAT hours | ✅ 1 ≤ hours ≤ 720 | "TAT hours must be between 1 and 720" |
| Email format | ✅ Valid email format | "Please enter a valid email" |
| Authorization | ✅ Only INITIATOR or APPROVER | 403 Forbidden |
---
## Examples
### **Example 1: Skip Current Approver**
**Initial State:**
```
Request: REQ-2025-001
Current Level: 2
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (IN_PROGRESS) ⏳ ← Taking too long
Level 3: Lisa (PENDING) ⏸️
```
**Action:**
```bash
# Initiator skips Mike
POST /api/v1/workflows/REQ-2025-001/approvals/LEVEL-ID-2/skip
Body: { "reason": "Approver on extended leave" }
```
**Backend Processing:**
```typescript
1. Get Level 2 (Mike) → Status: IN_PROGRESS ✅
2. Validate: Not already completed ✅
3. Update Level 2:
- status: 'SKIPPED'
- is_skipped: TRUE
- skipped_at: NOW()
- skipped_by: initiator userId
- skip_reason: "Approver on extended leave"
4. Cancel TAT jobs for Level 2
5. Get Level 3 (Lisa)
6. Activate Level 3:
- status: 'IN_PROGRESS'
- levelStartTime: NOW()
- tatStartTime: NOW()
7. Schedule TAT jobs for Level 3
8. Update workflow.currentLevel = 3
9. Notify Lisa
10. Log activity: "Level 2 approver (Mike) was skipped"
```
**Final State:**
```
Request: REQ-2025-001
Current Level: 3
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (SKIPPED) ⏭️ ← Skipped!
Level 3: Lisa (IN_PROGRESS) ⏳ ← Now active!
```
---
### **Example 2: Add Approver Between Levels**
**Initial State:**
```
Request: REQ-2025-001
Current Level: 2
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (IN_PROGRESS) ⏳
Level 3: Lisa (PENDING) ⏸️
```
**Action:**
```bash
# Add John at Level 3 (between Mike and Lisa)
POST /api/v1/workflows/REQ-2025-001/approvers/at-level
Body: {
"email": "john@example.com",
"tatHours": 48,
"level": 3
}
```
**Backend Processing:**
```typescript
1. Find user: john@example.com ✅
2. Validate: Not existing participant ✅
3. Validate: Level 3 ≥ minLevel (2) ✅
4. Get levels to shift: [Lisa (Level 3)]
5. Shift Lisa:
- Level 3 → Level 4
- levelName: "Level 4"
6. Create new Level 3:
- levelNumber: 3
- approverId: John's userId
- approverEmail: john@example.com
- tatHours: 48
- status: PENDING (not current level)
7. Update workflow.totalLevels: 3 → 4
8. Add John to participants (APPROVER type)
9. Notify John
10. Log activity: "John added as approver at Level 3 with TAT of 48 hours"
```
**Final State:**
```
Request: REQ-2025-001
Current Level: 2
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (IN_PROGRESS) ⏳ ← Still working
Level 3: John (PENDING) ⏸️ ← NEW! Will review after Mike
Level 4: Lisa (PENDING) ⏸️ ← Shifted from 3 to 4
```
---
### **Example 3: Complex Scenario - Skip and Add**
**Initial State:**
```
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (APPROVED) ✅
Level 3: David (IN_PROGRESS) ⏳ ← Taking too long
Level 4: Lisa (PENDING) ⏸️
Level 5: Tom (PENDING) ⏸️
```
**Action 1: Skip David**
```
Result:
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (APPROVED) ✅
Level 3: David (SKIPPED) ⏭️
Level 4: Lisa (IN_PROGRESS) ⏳ ← Now active
Level 5: Tom (PENDING) ⏸️
```
**Action 2: Add John at Level 4 (before Tom)**
```
Result:
Level 1: Sarah (APPROVED) ✅
Level 2: Mike (APPROVED) ✅
Level 3: David (SKIPPED) ⏭️
Level 4: Lisa (IN_PROGRESS) ⏳
Level 5: John (PENDING) ⏸️ ← NEW!
Level 6: Tom (PENDING) ⏸️ ← Shifted
```
---
## UI/UX
### **RequestDetail - Workflow Tab:**
**Skip Button Visibility:**
- ✅ Shows for levels with status: `pending` or `in-review`
- ❌ Hidden for `approved`, `rejected`, `skipped`, or `waiting`
- ✅ Orange/amber styling to indicate caution
- ✅ Requires reason via prompt
**Button Appearance:**
```tsx
┌───────────────────────────────────────────┐
│ Level 2: Mike (In Review) │
│ TAT: 24h • Elapsed: 15h │
│ │
│ [⚠ Skip This Approver] │
│ Skip if approver is unavailable... │
└───────────────────────────────────────────┘
```
---
### **AddApproverModal - Enhanced UI:**
**Sections:**
1. **Current Levels** - Scrollable list showing all existing levels with status
2. **Level Selector** - Dropdown with available levels (grayed out completed levels)
3. **TAT Input** - Hours input with validation (1-720)
4. **Email Search** - @ mention search (existing)
**Features:**
- ✅ Auto-selects first available level
- ✅ Shows which existing level will be shifted
- ✅ Visual indicators for completed vs pending levels
- ✅ Prevents selecting invalid levels
- ✅ Real-time validation
---
## Activity Log Examples
### **Skip Approver Log:**
```
Action: Approver Skipped
Details: Level 2 approver (Mike Johnson) was skipped by Sarah Smith.
Reason: Approver on extended leave
Timestamp: 2025-11-05 10:30:00
User: Sarah Smith (Initiator)
```
### **Add Approver Log:**
```
Action: Added new approver
Details: John Doe (john@example.com) has been added as approver at
Level 3 with TAT of 48 hours by Sarah Smith
Timestamp: 2025-11-05 11:15:00
User: Sarah Smith (Initiator)
```
---
## Notifications
### **Skip Approver Notifications:**
**To Next Approver:**
```
Title: Request Escalated
Body: Previous approver was skipped. Request REQ-2025-001 is now
awaiting your approval.
```
---
### **Add Approver Notifications:**
**To New Approver:**
```
Title: New Request Assignment
Body: You have been added as Level 3 approver to request REQ-2025-001:
New Office Location Approval
```
---
## TAT Handling
### **Skip Approver:**
```typescript
// Skipped level's TAT jobs are cancelled
await tatSchedulerService.cancelTatJobs(requestId, skippedLevelId);
// Next level's TAT jobs are scheduled
await tatSchedulerService.scheduleTatJobs(
requestId,
nextLevelId,
nextApproverId,
nextLevelTatHours,
now,
workflowPriority
);
```
### **Add Approver:**
```typescript
// If new approver is at current level, schedule TAT immediately
if (newLevel === currentLevel) {
await tatSchedulerService.scheduleTatJobs(
requestId,
newLevelId,
newApproverId,
tatHours,
now,
workflowPriority
);
}
// Otherwise, jobs will be scheduled when level becomes active
```
---
## Testing Scenarios
### **Test 1: Skip Current Approver**
```bash
# 1. Create workflow with 3 approvers
# 2. Level 1 approves
# 3. Level 2 receives notification
# 4. Level 2 doesn't respond for extended time
# 5. Initiator clicks "Skip This Approver"
# 6. Provide reason: "On vacation"
# 7. Verify:
# ✅ Level 2 status = SKIPPED
# ✅ Level 3 status = IN_PROGRESS
# ✅ Level 3 receives notification
# ✅ TAT jobs scheduled for Level 3
# ✅ Activity logged
```
### **Test 2: Add Approver at Middle Level**
```bash
# 1. Workflow has 3 levels
# 2. Level 1 approved
# 3. Click "Add Approver"
# 4. Select Level 2 (between current levels)
# 5. Enter TAT: 48
# 6. Enter email: new@example.com
# 7. Submit
# 8. Verify:
# ✅ Old Level 2 becomes Level 3
# ✅ Old Level 3 becomes Level 4
# ✅ New approver at Level 2
# ✅ totalLevels increased by 1
# ✅ New approver receives notification
```
### **Test 3: Cannot Add Before Completed Level**
```bash
# 1. Workflow: Level 1 (Approved), Level 2 (Pending)
# 2. Try to add at Level 1
# 3. Modal shows: "Minimum allowed level is 2"
# 4. Level 1 is grayed out in selector
# 5. Cannot submit ✅
```
---
## Files Modified
### **Backend:**
1. `Re_Backend/src/migrations/add_is_skipped_to_approval_levels.sql` - Database migration
2. `Re_Backend/src/services/workflow.service.ts` - Skip and add approver logic
3. `Re_Backend/src/routes/workflow.routes.ts` - API endpoints
### **Frontend:**
4. `Re_Figma_Code/src/services/workflowApi.ts` - API client methods
5. `Re_Figma_Code/src/components/participant/AddApproverModal/AddApproverModal.tsx` - Enhanced modal
6. `Re_Figma_Code/src/pages/RequestDetail/RequestDetail.tsx` - Skip button and handlers
---
## Summary
| Feature | Description | Benefit |
|---------|-------------|---------|
| **Skip Approver** | Mark approver as skipped, move to next | Handle unavailable approvers |
| **Add at Level** | Insert approver at specific position | Flexible workflow modification |
| **Auto Shifting** | Existing levels automatically renumbered | No manual level management |
| **Smart Validation** | Cannot modify completed levels | Data integrity |
| **TAT Management** | Jobs cancelled/scheduled automatically | Accurate time tracking |
| **Activity Logging** | All actions tracked in audit trail | Full transparency |
| **Notifications** | Affected users notified automatically | Keep everyone informed |
---
## Benefits
1. ✅ **Flexibility** - Handle real-world workflow changes
2. ✅ **No Bottlenecks** - Skip unavailable approvers
3. ✅ **Dynamic Addition** - Add approvers mid-workflow
4. ✅ **Data Integrity** - Cannot modify completed levels
5. ✅ **Audit Trail** - Full history of all changes
6. ✅ **Automatic Notifications** - All affected parties notified
7. ✅ **TAT Accuracy** - Time tracking updated correctly
8. ✅ **User-Friendly** - Intuitive UI with clear feedback
The approval workflow is now fully dynamic and can adapt to changing business needs! 🚀

View File

@ -1,524 +0,0 @@
# ✅ Smart Migration System Complete
## 🎯 What You Asked For
> "Every time if I do npm run dev, migrations are running right? If that already exist then skip, if it is new tables then do migrations"
**✅ DONE!** Your migration system is now intelligent and efficient.
---
## 🧠 How It Works Now
### Smart Migration Tracking
The system now includes:
1. **🗃️ Migrations Tracking Table**
- Automatically created on first run
- Stores which migrations have been executed
- Prevents duplicate execution
2. **⏭️ Smart Detection**
- Checks which migrations already ran
- Only executes **new/pending** migrations
- Skips already-completed ones
3. **🛡️ Idempotent Migrations**
- Safe to run multiple times
- Checks if tables/columns exist before creating
- No errors if schema already matches
---
## 📊 What Happens When You Run `npm run dev`
### First Time (Fresh Database)
```
📦 Database connected
✅ Created migrations tracking table
🔄 Running 14 pending migration(s)...
⏳ Running: 2025103001-create-workflow-requests
✅ Created workflow_requests table
✅ Completed: 2025103001-create-workflow-requests
⏳ Running: 2025103002-create-approval-levels
✅ Created approval_levels table
✅ Completed: 2025103002-create-approval-levels
... (all 14 migrations run)
✅ Successfully applied 14 migration(s)
📊 Total migrations: 14
🚀 Server running on port 5000
```
### Second Time (All Migrations Already Run)
```
📦 Database connected
✅ All migrations are up-to-date (no new migrations to run)
🚀 Server running on port 5000
```
**⚡ Instant startup! No migration overhead!**
### When You Add a New Migration
```
📦 Database connected
🔄 Running 1 pending migration(s)...
⏳ Running: 20251106-new-feature
✅ Added new column
✅ Completed: 20251106-new-feature
✅ Successfully applied 1 migration(s)
📊 Total migrations: 15
🚀 Server running on port 5000
```
**Only the NEW migration runs!**
---
## 🔧 Technical Implementation
### 1. Migration Tracking Database
Automatically created table:
```sql
CREATE TABLE migrations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
```
Tracks:
- ✅ Which migrations have been executed
- ✅ When they were executed
- ✅ Prevents duplicate execution via UNIQUE constraint
### 2. Smart Migration Runner
**File**: `src/scripts/migrate.ts`
**Key Features**:
```typescript
// 1. Check what's already been run
const executedMigrations = await getExecutedMigrations();
// 2. Find only new/pending migrations
const pendingMigrations = migrations.filter(
m => !executedMigrations.includes(m.name)
);
// 3. Skip if nothing to do
if (pendingMigrations.length === 0) {
console.log('✅ All migrations up-to-date');
return;
}
// 4. Run only pending migrations
for (const migration of pendingMigrations) {
await migration.module.up(queryInterface);
await markMigrationExecuted(migration.name);
}
```
### 3. Idempotent Migrations
**Example**: `20251105-add-skip-fields-to-approval-levels.ts`
**Checks before acting**:
```typescript
// Check if table exists
const tables = await queryInterface.showAllTables();
if (!tables.includes('approval_levels')) {
return; // Skip if table doesn't exist
}
// Check if column exists
const tableDescription = await queryInterface.describeTable('approval_levels');
if (!tableDescription.is_skipped) {
await queryInterface.addColumn(/* ... */);
}
// Check if index exists
const indexes = await queryInterface.showIndex('approval_levels');
const indexExists = indexes.some(idx => idx.name === 'idx_name');
if (!indexExists) {
await queryInterface.addIndex(/* ... */);
}
```
**Safe to run multiple times!**
---
## 🚀 Usage Examples
### Daily Development Workflow
```bash
# Morning - start work
npm run dev
# ✅ All up-to-date - server starts immediately
# After pulling new code with migration
git pull origin main
npm run dev
# 🔄 Runs only the new migration
# ✅ Server starts
```
### Adding a New Migration
```bash
# 1. Create migration file
# src/migrations/20251106-add-user-preferences.ts
# 2. Register in migrate.ts
# (add import and execution)
# 3. Test
npm run dev
# 🔄 Runs only your new migration
# 4. Run again to verify idempotency
npm run dev
# ✅ All up-to-date (doesn't run again)
```
### Manual Migration Run
```bash
npm run migrate
# Same smart behavior, without starting server
```
---
## 📋 Current Migration Status
All 14 migrations are now tracked:
| # | Migration | Status |
|---|-----------|--------|
| 1 | 2025103001-create-workflow-requests | ✅ Tracked |
| 2 | 2025103002-create-approval-levels | ✅ Tracked |
| 3 | 2025103003-create-participants | ✅ Tracked |
| 4 | 2025103004-create-documents | ✅ Tracked |
| 5 | 20251031_01_create_subscriptions | ✅ Tracked |
| 6 | 20251031_02_create_activities | ✅ Tracked |
| 7 | 20251031_03_create_work_notes | ✅ Tracked |
| 8 | 20251031_04_create_work_note_attachments | ✅ Tracked |
| 9 | 20251104-add-tat-alert-fields | ✅ Tracked |
| 10 | 20251104-create-tat-alerts | ✅ Tracked |
| 11 | 20251104-create-kpi-views | ✅ Tracked |
| 12 | 20251104-create-holidays | ✅ Tracked |
| 13 | 20251104-create-admin-config | ✅ Tracked |
| 14 | 20251105-add-skip-fields-to-approval-levels | ✅ Tracked & Idempotent |
---
## ✨ Key Benefits
### For You (Developer)
- ✅ **Fast Restarts** - No waiting for already-run migrations
- ✅ **No Errors** - Safe to run `npm run dev` anytime
- ✅ **Auto-Detection** - System knows what's new
- ✅ **Zero Configuration** - Just works
### For Team
- ✅ **Consistent State** - Everyone's database in sync
- ✅ **Easy Onboarding** - New devs run once, all migrates
- ✅ **No Coordination** - No "did you run migrations?" questions
- ✅ **Pull & Run** - Git pull + npm run dev = ready
### For Production
- ✅ **Safe Deployments** - Won't break if run multiple times
- ✅ **Version Control** - Clear migration history
- ✅ **Rollback Support** - Each migration has down() function
- ✅ **Audit Trail** - migrations table shows execution history
---
## 🎓 Best Practices Implemented
### 1. Idempotency
✅ All migrations check existence before creating
✅ Safe to run multiple times
✅ No duplicate errors
### 2. Tracking
✅ Dedicated migrations table
✅ Unique constraint prevents duplicates
✅ Timestamp for audit trail
### 3. Smart Execution
✅ Only runs pending migrations
✅ Maintains execution order
✅ Fails fast on errors
### 4. Developer Experience
✅ Clear console output
✅ Progress indicators
✅ Helpful error messages
---
## 📝 Adding New Migrations
### Template for Idempotent Migrations
```typescript
import { QueryInterface, DataTypes } from 'sequelize';
export async function up(queryInterface: QueryInterface): Promise<void> {
// 1. Check if table exists (for new tables)
const tables = await queryInterface.showAllTables();
if (!tables.includes('my_table')) {
await queryInterface.createTable('my_table', {/* ... */});
console.log(' ✅ Created my_table');
return;
}
// 2. Check if column exists (for new columns)
const tableDesc = await queryInterface.describeTable('existing_table');
if (!tableDesc.new_column) {
await queryInterface.addColumn('existing_table', 'new_column', {
type: DataTypes.STRING
});
console.log(' ✅ Added new_column');
}
// 3. Check if index exists (for new indexes)
try {
const indexes: any[] = await queryInterface.showIndex('my_table') as any[];
const indexExists = Array.isArray(indexes) &&
indexes.some((idx: any) => idx.name === 'idx_name');
if (!indexExists) {
await queryInterface.addIndex('my_table', ['column'], {
name: 'idx_name'
});
console.log(' ✅ Added idx_name');
}
} catch (error) {
console.log(' Index handling skipped');
}
console.log('✅ Migration completed');
}
export async function down(queryInterface: QueryInterface): Promise<void> {
// Rollback logic
await queryInterface.removeColumn('my_table', 'new_column');
console.log('✅ Rollback completed');
}
```
### Steps to Add New Migration
1. **Create File**: `src/migrations/YYYYMMDD-description.ts`
2. **Write Migration**: Use idempotent template above
3. **Register**: Add to `src/scripts/migrate.ts`:
```typescript
import * as m15 from '../migrations/20251106-description';
const migrations: Migration[] = [
// ... existing ...
{ name: '20251106-description', module: m15 },
];
```
4. **Test**: Run `npm run dev` - only new migration executes
5. **Verify**: Run `npm run dev` again - should skip (already executed)
---
## 🧪 Testing the System
### Test 1: First Run
```bash
# Drop database (if testing)
# Then run:
npm run dev
# Expected: All 14 migrations run
# migrations table created
# Server starts
```
### Test 2: Second Run
```bash
npm run dev
# Expected: "All migrations up-to-date"
# No migrations run
# Instant server start
```
### Test 3: New Migration
```bash
# Add migration #15
npm run dev
# Expected: Only migration #15 runs
# Shows "Running 1 pending migration"
# Server starts
```
### Test 4: Verify Tracking
```bash
# In PostgreSQL:
SELECT * FROM migrations ORDER BY id;
# Should show all executed migrations with timestamps
```
---
## 🔍 Monitoring Migration Status
### Check Database Directly
```sql
-- See all executed migrations
SELECT id, name, executed_at
FROM migrations
ORDER BY id;
-- Count migrations
SELECT COUNT(*) as total_migrations FROM migrations;
-- Latest migration
SELECT name, executed_at
FROM migrations
ORDER BY id DESC
LIMIT 1;
```
### Check via Application
```bash
# Run migration script
npm run migrate
# Output shows:
# - Total migrations in code
# - Already executed count
# - Pending count
```
---
## 🚨 Troubleshooting
### Issue: "Table already exists"
**Solution**: This shouldn't happen now! But if it does:
- Migration might not be idempotent
- Add table existence check
- See idempotent template above
### Issue: "Column already exists"
**Solution**: Add column existence check:
```typescript
const tableDesc = await queryInterface.describeTable('table');
if (!tableDesc.column_name) {
await queryInterface.addColumn(/* ... */);
}
```
### Issue: Migration runs every time
**Cause**: Not being marked as executed
**Check**:
```sql
SELECT * FROM migrations WHERE name = 'migration-name';
```
If missing, the marking step failed.
### Issue: Need to rerun a migration
**Solution**:
```sql
-- Remove from tracking (use with caution!)
DELETE FROM migrations WHERE name = 'migration-name';
-- Then run
npm run migrate
```
---
## 📊 System Architecture
```
npm run dev
migrate.ts runs
Check: migrations table exists?
↓ No → Create it
↓ Yes → Continue
Query: SELECT * FROM migrations
Compare: Code migrations vs DB migrations
Pending = Code - DB
If pending = 0
↓ → "All up-to-date" → Start server
If pending > 0
For each pending migration:
Run migration.up()
INSERT INTO migrations
Mark as complete
All done → Start server
```
---
## 🎯 Summary
### What Changed
| Before | After |
|--------|-------|
| All migrations run every time | Only new migrations run |
| Errors if tables exist | Smart checks prevent errors |
| No tracking | Migrations table tracks history |
| Slow restarts | Fast restarts |
| Manual coordination needed | Automatic detection |
### What You Get
**Smart Detection** - Knows what's already been run
**Fast Execution** - Only runs new migrations
**Error Prevention** - Idempotent checks
**Clear Feedback** - Detailed console output
**Audit Trail** - migrations table for history
**Team-Friendly** - Everyone stays in sync automatically
---
## 🚀 You're Ready!
Just run:
```bash
npm run dev
```
**First time**: All migrations execute, database is set up
**Every time after**: Lightning fast, only new migrations run
**Pull new code**: Automatically detects and runs new migrations
**No manual steps. No coordination needed. Just works!** ✨
---
**System**: Smart Migration Tracking ✅
**Idempotency**: Enabled ✅
**Auto-Detect**: Active ✅
**Status**: Production Ready 🟢
**Date**: November 5, 2025

View File

@ -1,209 +0,0 @@
# 🎯 START HERE - TAT Notifications Setup
## What You Need to Do RIGHT NOW
### ⚡ 2-Minute Setup (Upstash Redis)
1. **Open this link**: https://console.upstash.com/
- Sign up with GitHub/Google (it's free)
2. **Create Redis Database**:
- Click "Create Database"
- Name: `redis-tat-dev`
- Type: Regional
- Region: Pick closest to you
- Click "Create"
3. **Copy the Redis URL**:
- You'll see: `rediss://default:AbC123xyz...@us1-mighty-12345.upstash.io:6379`
- Click the copy button 📋
4. **Open** `Re_Backend/.env` and add:
```bash
REDIS_URL=rediss://default:AbC123xyz...@us1-mighty-12345.upstash.io:6379
TAT_TEST_MODE=true
```
5. **Restart Backend**:
```bash
cd Re_Backend
npm run dev
```
6. **Look for this** in the logs:
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Initialized and listening
⏰ TAT Configuration:
- Test Mode: ENABLED (1 hour = 1 minute)
```
**DONE!** You're ready to test!
---
## Test It Now (6 Minutes)
1. **Create a workflow request** via your frontend
2. **Set TAT: 6 hours** (will become 6 minutes in test mode)
3. **Submit the request**
4. **Watch for notifications**:
- **3 minutes**: ⏳ 50% notification
- **4.5 minutes**: ⚠️ 75% warning
- **6 minutes**: ⏰ 100% breach
---
## Verify It's Working
### Check Backend Logs:
```bash
# You should see:
[TAT Scheduler] Calculating TAT milestones...
[TAT Scheduler] ✅ TAT jobs scheduled
[TAT Processor] Processing tat50...
[TAT Processor] tat50 notification sent
```
### Check Upstash Console:
1. Go to https://console.upstash.com/
2. Click your database
3. Click "CLI" tab
4. Type: `KEYS bull:tatQueue:*`
5. Should see your scheduled jobs
### Check Database:
```sql
SELECT
approver_name,
tat50_alert_sent,
tat75_alert_sent,
tat_breached,
status
FROM approval_levels
WHERE status = 'IN_PROGRESS';
```
---
## What Test Mode Does
```
Normal Mode: Test Mode:
48 hours → 48 minutes
24 hours → 24 minutes
6 hours → 6 minutes
2 hours → 2 minutes
✅ Perfect for quick testing!
✅ Turn off for production: TAT_TEST_MODE=false
```
---
## Troubleshooting
### ❌ "ECONNREFUSED" Error?
**Fix**:
1. Check your `.env` file has `REDIS_URL=rediss://...`
2. Verify the URL is correct (copy from Upstash again)
3. Make sure it starts with `rediss://` (double 's')
4. Restart backend: `npm run dev`
### ❌ No Logs About Redis?
**Fix**:
1. Check `.env` file exists in `Re_Backend/` folder
2. Make sure you restarted the backend
3. Look for any errors in console
### ❌ Jobs Not Running?
**Fix**:
1. Verify `TAT_TEST_MODE=true` in `.env`
2. Make sure request is SUBMITTED (not just created)
3. Check Upstash Console → Metrics (see if commands are running)
---
## Next Steps
Once you see the first notification working:
1. ✅ Test multi-level approvals
2. ✅ Test early approval (jobs should cancel)
3. ✅ Test rejection flow
4. ✅ Check activity logs
5. ✅ Verify database flags
---
## Documentation
- **Quick Start**: `TAT_QUICK_START.md`
- **Upstash Guide**: `docs/UPSTASH_SETUP_GUIDE.md`
- **Full System Docs**: `docs/TAT_NOTIFICATION_SYSTEM.md`
- **Testing Guide**: `docs/TAT_TESTING_GUIDE.md`
- **Quick Reference**: `UPSTASH_QUICK_REFERENCE.md`
---
## Why Upstash?
**No installation** (works on Windows immediately)
**100% free** for development
**Same setup** for production
**No maintenance** required
**Fast** (global CDN)
**Secure** (TLS by default)
---
## Production Deployment
When ready for production:
1. Keep using Upstash OR install Redis on Linux server:
```bash
sudo apt install redis-server -y
```
2. Update `.env` on server:
```bash
REDIS_URL=redis://localhost:6379 # or keep Upstash URL
TAT_TEST_MODE=false # Use real hours
```
3. Deploy and monitor!
---
## Need Help?
**Upstash Console**: https://console.upstash.com/
**Our Docs**: See `docs/` folder
**Redis Commands**: Use Upstash Console CLI tab
---
## Status Checklist
- [ ] Upstash account created
- [ ] Redis database created
- [ ] REDIS_URL copied to `.env`
- [ ] TAT_TEST_MODE=true set
- [ ] Backend restarted
- [ ] Logs show "Connected to Redis"
- [ ] Test request created and submitted
- [ ] First notification received
**All done? Congratulations!** 🎉
Your TAT notification system is now LIVE!
---
**Last Updated**: November 4, 2025
**Team**: Royal Enfield Workflow

View File

@ -1,591 +0,0 @@
# ✅ TAT Alerts Display System - Complete Implementation
## 🎉 What's Been Implemented
Your TAT notification system now **stores every alert** in the database and **displays them in the UI** exactly like your shared screenshot!
---
## 📊 Complete Flow
### 1. When Request is Submitted
```typescript
// First level approver assigned
Level 1: John (TAT: 24 hours)
TAT jobs scheduled for John:
- 50% alert (12 hours)
- 75% alert (18 hours)
- 100% breach (24 hours)
```
### 2. When Notification Fires (e.g., 50%)
**Backend (`tatProcessor.ts`):**
```typescript
✅ Send notification to John
✅ Create record in tat_alerts table
✅ Log activity
✅ Update approval_levels flags
```
**Database Record Created:**
```sql
INSERT INTO tat_alerts (
request_id, level_id, approver_id,
alert_type = 'TAT_50',
threshold_percentage = 50,
alert_message = '⏳ 50% of TAT elapsed...',
alert_sent_at = NOW(),
...
)
```
### 3. When Displayed in Frontend
**API Response** (`workflow.service.ts`):
```typescript
{
workflow: {...},
approvals: [...],
tatAlerts: [ // ← NEW!
{
alertType: 'TAT_50',
thresholdPercentage: 50,
alertSentAt: '2024-10-06T14:30:00Z',
alertMessage: '⏳ 50% of TAT elapsed...',
levelId: 'abc-123',
...
}
]
}
```
**Frontend Display** (`RequestDetail.tsx`):
```tsx
<div className="bg-yellow-50 border-yellow-200 p-3 rounded-lg">
⏳ Reminder 1
50% of SLA breach reminder have been sent
Reminder sent by system automatically
Sent at: Oct 6 at 2:30 PM
</div>
```
---
## 🎨 UI Display (Matches Your Screenshot)
### Reminder Card Styling:
**50% Alert (⏳):**
- Background: `bg-yellow-50`
- Border: `border-yellow-200`
- Icon: ⏳
**75% Alert (⚠️):**
- Background: `bg-orange-50`
- Border: `border-orange-200`
- Icon: ⚠️
**100% Breach (⏰):**
- Background: `bg-red-50`
- Border: `border-red-200`
- Icon: ⏰
### Display Format:
```
┌─────────────────────────────────────────┐
│ ⏳ Reminder 1 │
│ │
│ 50% of SLA breach reminder have been │
│ sent │
│ │
│ Reminder sent by system automatically │
│ │
│ Sent at: Oct 6 at 2:30 PM │
└─────────────────────────────────────────┘
```
---
## 📍 Where Alerts Appear
### In Workflow Tab:
Alerts appear **under each approval level card** in the workflow tab:
```
┌────────────────────────────────────────┐
│ Step 2: Lisa Wong (Finance Manager) │
│ Status: pending │
│ TAT: 12 hours │
│ │
│ ⏳ Reminder 1 │ ← TAT Alert #1
│ 50% of SLA breach reminder... │
│ Sent at: Oct 6 at 2:30 PM │
│ │
│ ⚠️ Reminder 2 │ ← TAT Alert #2
│ 75% of SLA breach reminder... │
│ Sent at: Oct 6 at 6:30 PM │
└────────────────────────────────────────┘
```
---
## 🔄 Complete Data Flow
### Backend:
1. **TAT Processor** (`tatProcessor.ts`):
- Sends notification to approver
- Creates record in `tat_alerts` table
- Logs activity
2. **Workflow Service** (`workflow.service.ts`):
- Fetches TAT alerts for request
- Includes in API response
- Groups by level ID
3. **Approval Service** (`approval.service.ts`):
- Updates alerts when level completed
- Sets `was_completed_on_time`
- Sets `completion_time`
### Frontend:
1. **Request Detail** (`RequestDetail.tsx`):
- Receives TAT alerts from API
- Filters alerts by level ID
- Displays under each approval level
- Color-codes by threshold
---
## 📊 Database Schema
### TAT Alerts Table:
```sql
SELECT
alert_type, -- TAT_50, TAT_75, TAT_100
threshold_percentage, -- 50, 75, 100
alert_sent_at, -- When alert was sent
alert_message, -- Full message text
level_id, -- Which approval level
approver_id, -- Who was notified
was_completed_on_time, -- Completed within TAT?
completion_time -- When completed
FROM tat_alerts
WHERE request_id = 'YOUR_REQUEST_ID'
ORDER BY alert_sent_at ASC;
```
---
## 🧪 Testing the Display
### Step 1: Setup Upstash Redis
See `START_HERE.md` for quick setup (2 minutes)
### Step 2: Enable Test Mode
In `.env`:
```bash
TAT_TEST_MODE=true
```
### Step 3: Create Test Request
- TAT: 6 hours (becomes 6 minutes in test mode)
- Submit the request
### Step 4: Watch Alerts Appear
**At 3 minutes (50%):**
```
⏳ Reminder 1
50% of SLA breach reminder have been sent
Reminder sent by system automatically
Sent at: [timestamp]
```
**At 4.5 minutes (75%):**
```
⚠️ Reminder 2
75% of SLA breach reminder have been sent
Reminder sent by system automatically
Sent at: [timestamp]
```
**At 6 minutes (100%):**
```
⏰ Reminder 3
100% of SLA breach reminder have been sent
Reminder sent by system automatically
Sent at: [timestamp]
```
### Step 5: Verify in Database
```sql
SELECT
threshold_percentage,
alert_sent_at,
was_completed_on_time,
completion_time
FROM tat_alerts
WHERE request_id = 'YOUR_REQUEST_ID'
ORDER BY threshold_percentage;
```
---
## 🎯 Approver-Specific Alerts
### Confirmation: Alerts are Approver-Specific
**Each level's alerts** are sent to **that level's approver only**
**Previous approver** does NOT receive alerts for next level
**Current approver** receives all their level's alerts (50%, 75%, 100%)
### Example:
```
Request Flow:
Level 1: John (TAT: 24h)
→ Alerts sent to: John
→ At: 12h, 18h, 24h
Level 2: Sarah (TAT: 12h)
→ Alerts sent to: Sarah (NOT John)
→ At: 6h, 9h, 12h
Level 3: Mike (TAT: 8h)
→ Alerts sent to: Mike (NOT Sarah, NOT John)
→ At: 4h, 6h, 8h
```
---
## 📋 KPI Queries
### Get All Alerts for a Request:
```sql
SELECT
al.level_number,
al.approver_name,
ta.threshold_percentage,
ta.alert_sent_at,
ta.was_completed_on_time
FROM tat_alerts ta
JOIN approval_levels al ON ta.level_id = al.level_id
WHERE ta.request_id = 'REQUEST_ID'
ORDER BY al.level_number, ta.threshold_percentage;
```
### TAT Compliance by Approver:
```sql
SELECT
ta.approver_id,
u.display_name,
COUNT(*) as total_alerts_received,
COUNT(CASE WHEN ta.was_completed_on_time = true THEN 1 END) as completed_on_time,
COUNT(CASE WHEN ta.was_completed_on_time = false THEN 1 END) as completed_late,
ROUND(
COUNT(CASE WHEN ta.was_completed_on_time = true THEN 1 END) * 100.0 /
NULLIF(COUNT(CASE WHEN ta.was_completed_on_time IS NOT NULL THEN 1 END), 0),
2
) as compliance_rate
FROM tat_alerts ta
JOIN users u ON ta.approver_id = u.user_id
GROUP BY ta.approver_id, u.display_name;
```
### Alert Effectiveness (Response Time After Alert):
```sql
SELECT
alert_type,
AVG(
EXTRACT(EPOCH FROM (completion_time - alert_sent_at)) / 3600
) as avg_response_hours_after_alert
FROM tat_alerts
WHERE completion_time IS NOT NULL
GROUP BY alert_type;
```
---
## 📁 Files Modified
### Backend:
- ✅ `src/models/TatAlert.ts` - TAT alert model
- ✅ `src/migrations/20251104-create-tat-alerts.ts` - Table creation
- ✅ `src/queues/tatProcessor.ts` - Create alert records
- ✅ `src/services/workflow.service.ts` - Include alerts in API response
- ✅ `src/services/approval.service.ts` - Update alerts on completion
- ✅ `src/models/index.ts` - Export TatAlert model
### Frontend:
- ✅ `src/pages/RequestDetail/RequestDetail.tsx` - Display alerts in workflow tab
### Database:
- ✅ `tat_alerts` table created with 7 indexes
- ✅ 8 KPI views created for reporting
---
## 🎨 Visual Example
Based on your screenshot, the display looks like:
```
┌──────────────────────────────────────────────────┐
│ Step 2: Lisa Wong (Finance Manager) │
│ Status: pending │
│ TAT: 12 hours │
│ │
│ ┌──────────────────────────────────────────────┐│
│ │ ⏳ Reminder 1 ││
│ │ 50% of SLA breach reminder have been sent ││
│ │ Reminder sent by system automatically ││
│ │ Sent at: Oct 6 at 2:30 PM ││
│ └──────────────────────────────────────────────┘│
│ │
│ ┌──────────────────────────────────────────────┐│
│ │ ⚠️ Reminder 2 ││
│ │ 75% of SLA breach reminder have been sent ││
│ │ Reminder sent by system automatically ││
│ │ Sent at: Oct 6 at 6:30 PM ││
│ └──────────────────────────────────────────────┘│
└──────────────────────────────────────────────────┘
```
---
## ✅ Status: READY TO TEST!
### What Works Now:
- ✅ TAT alerts stored in database
- ✅ Alerts fetched with workflow details
- ✅ Alerts grouped by approval level
- ✅ Alerts displayed in workflow tab
- ✅ Color-coded by threshold
- ✅ Formatted like your screenshot
- ✅ Completion status tracked
- ✅ KPI-ready data structure
### What You Need to Do:
1. **Setup Redis** (Upstash recommended - see `START_HERE.md`)
2. **Add to `.env`**:
```bash
REDIS_URL=rediss://default:...@upstash.io:6379
TAT_TEST_MODE=true
```
3. **Restart backend**
4. **Create test request** (6-hour TAT)
5. **Watch alerts appear** in 3, 4.5, 6 minutes!
---
## 📚 Documentation
- **Setup Guide**: `START_HERE.md`
- **Quick Start**: `TAT_QUICK_START.md`
- **Upstash Guide**: `docs/UPSTASH_SETUP_GUIDE.md`
- **KPI Reporting**: `docs/KPI_REPORTING_SYSTEM.md`
- **Full System Docs**: `docs/TAT_NOTIFICATION_SYSTEM.md`
---
## 🎯 Example API Response
```json
{
"workflow": {...},
"approvals": [
{
"levelId": "abc-123",
"levelNumber": 2,
"approverName": "Lisa Wong",
"status": "PENDING",
"tatHours": 12,
...
}
],
"tatAlerts": [
{
"levelId": "abc-123",
"alertType": "TAT_50",
"thresholdPercentage": 50,
"alertSentAt": "2024-10-06T14:30:00Z",
"alertMessage": "⏳ 50% of TAT elapsed...",
"isBreached": false,
"wasCompletedOnTime": null,
"metadata": {
"requestNumber": "REQ-2024-001",
"approverName": "Lisa Wong",
"priority": "express"
}
},
{
"levelId": "abc-123",
"alertType": "TAT_75",
"thresholdPercentage": 75,
"alertSentAt": "2024-10-06T18:30:00Z",
"alertMessage": "⚠️ 75% of TAT elapsed...",
"isBreached": false,
"wasCompletedOnTime": null,
"metadata": {...}
}
]
}
```
---
## 🔍 Verify Implementation
### Check Backend Logs:
```bash
# When notification fires:
[TAT Processor] Processing tat50 for request...
[TAT Processor] TAT alert record created for tat50
[TAT Processor] tat50 notification sent
# When workflow details fetched:
[Workflow] Found 2 TAT alerts for request REQ-2024-001
```
### Check Database:
```sql
-- See all alerts for a request
SELECT * FROM tat_alerts
WHERE request_id = 'YOUR_REQUEST_ID'
ORDER BY alert_sent_at;
-- See alerts with approval info
SELECT
al.approver_name,
al.level_number,
ta.threshold_percentage,
ta.alert_sent_at,
ta.was_completed_on_time
FROM tat_alerts ta
JOIN approval_levels al ON ta.level_id = al.level_id
WHERE ta.request_id = 'YOUR_REQUEST_ID';
```
### Check Frontend:
1. Open Request Detail
2. Click "Workflow" tab
3. Look under each approval level card
4. You should see reminder boxes with:
- ⏳ 50% reminder (yellow background)
- ⚠️ 75% reminder (orange background)
- ⏰ 100% breach (red background)
---
## 📊 KPI Reporting Ready
### All TAT alerts are now queryable for KPIs:
**TAT Compliance Rate:**
```sql
SELECT
COUNT(CASE WHEN was_completed_on_time = true THEN 1 END) * 100.0 /
NULLIF(COUNT(*), 0) as compliance_rate
FROM tat_alerts
WHERE was_completed_on_time IS NOT NULL;
```
**Approver Response Time After Alert:**
```sql
SELECT
approver_id,
alert_type,
AVG(
EXTRACT(EPOCH FROM (completion_time - alert_sent_at)) / 3600
) as avg_hours_to_respond
FROM tat_alerts
WHERE completion_time IS NOT NULL
GROUP BY approver_id, alert_type;
```
**Breach Analysis:**
```sql
SELECT
DATE(alert_sent_at) as date,
COUNT(CASE WHEN alert_type = 'TAT_50' THEN 1 END) as alerts_50,
COUNT(CASE WHEN alert_type = 'TAT_75' THEN 1 END) as alerts_75,
COUNT(CASE WHEN alert_type = 'TAT_100' THEN 1 END) as breaches
FROM tat_alerts
WHERE alert_sent_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(alert_sent_at)
ORDER BY date DESC;
```
---
## 🚀 Ready to Use!
### Complete System Features:
**Notification System** - Sends alerts to approvers
**Storage System** - All alerts stored in database
**Display System** - Alerts shown in UI (matches screenshot)
**Tracking System** - Completion status tracked
**KPI System** - Full reporting and analytics
**Test Mode** - Fast testing (1 hour = 1 minute)
---
## 🎓 Quick Test
1. **Setup Upstash** (2 minutes): https://console.upstash.com/
2. **Add to `.env`**:
```bash
REDIS_URL=rediss://...
TAT_TEST_MODE=true
```
3. **Restart backend**
4. **Create request** with 6-hour TAT
5. **Submit request**
6. **Wait 3 minutes** → See first alert in UI
7. **Wait 4.5 minutes** → See second alert
8. **Wait 6 minutes** → See third alert
---
## ✨ Benefits
1. **Full Audit Trail** - Every alert stored and queryable
2. **Visual Feedback** - Users see exactly when reminders were sent
3. **KPI Ready** - Data ready for all reporting needs
4. **Compliance Tracking** - Know who completed on time vs late
5. **Effectiveness Analysis** - Measure response time after alerts
6. **Historical Data** - All past alerts preserved
---
**🎉 Implementation Complete! Connect Redis and start testing!**
See `START_HERE.md` for immediate next steps.
---
**Last Updated**: November 4, 2025
**Status**: ✅ Production Ready
**Team**: Royal Enfield Workflow

View File

@ -1,650 +0,0 @@
# ✅ Enhanced TAT Alerts Display - Complete Guide
## 🎯 What's Been Enhanced
TAT alerts now display **detailed time tracking information** inline with each approver, making it crystal clear what's happening!
---
## 📊 Enhanced Alert Display
### **What Shows Now:**
```
┌──────────────────────────────────────────────────────┐
│ ⏳ Reminder 1 - 50% TAT Threshold [WARNING] │
│ │
│ 50% of SLA breach reminder have been sent │
│ │
│ ┌──────────────┬──────────────┐ │
│ │ Allocated: │ Elapsed: │ │
│ │ 12h │ 6.0h │ │
│ ├──────────────┼──────────────┤ │
│ │ Remaining: │ Due by: │ │
│ │ 6.0h │ Oct 7, 2024 │ │
│ └──────────────┴──────────────┘ │
│ │
│ Reminder sent by system automatically [TEST MODE] │
│ Sent at: Oct 6 at 2:30 PM │
│ Note: Test mode active (1 hour = 1 minute) │
└──────────────────────────────────────────────────────┘
```
---
## 🔑 Key Information Displayed
### **For Each Alert:**
| Field | Description | Example |
|-------|-------------|---------|
| **Reminder #** | Sequential number | "Reminder 1" |
| **Threshold** | Percentage reached | "50% TAT Threshold" |
| **Status Badge** | Warning or Breach | `WARNING` / `BREACHED` |
| **Allocated** | Total TAT hours | "12h" |
| **Elapsed** | Hours used when alert sent | "6.0h" |
| **Remaining** | Hours left when alert sent | "6.0h" |
| **Due by** | Expected completion date | "Oct 7, 2024" |
| **Sent at** | When reminder was sent | "Oct 6 at 2:30 PM" |
| **Test Mode** | If in test mode | Purple badge + note |
---
## 🎨 Color Coding
### **50% Alert (⏳):**
- Background: `bg-yellow-50`
- Border: `border-yellow-200`
- Badge: `bg-amber-100 text-amber-800`
- Icon: ⏳
### **75% Alert (⚠️):**
- Background: `bg-orange-50`
- Border: `border-orange-200`
- Badge: `bg-amber-100 text-amber-800`
- Icon: ⚠️
### **100% Breach (⏰):**
- Background: `bg-red-50`
- Border: `border-red-200`
- Badge: `bg-red-100 text-red-800`
- Icon: ⏰
- Text: Shows "BREACHED" instead of "WARNING"
---
## 🧪 Test Mode vs Production Mode
### **Test Mode (TAT_TEST_MODE=true):**
**Purpose**: Fast testing during development
**Behavior:**
- ✅ 1 hour = 1 minute
- ✅ 6-hour TAT = 6 minutes
- ✅ Purple "TEST MODE" badge shown
- ✅ Note: "Test mode active (1 hour = 1 minute)"
- ✅ All times are in working time (no weekend skip)
**Example Alert (Test Mode):**
```
⏳ Reminder 1 - 50% TAT Threshold [WARNING] [TEST MODE]
Allocated: 6h | Elapsed: 3.0h
Remaining: 3.0h | Due by: Today 2:06 PM
Note: Test mode active (1 hour = 1 minute)
Sent at: Today at 2:03 PM
```
**Timeline:**
- Submit at 2:00 PM
- 50% alert at 2:03 PM (3 minutes)
- 75% alert at 2:04:30 PM (4.5 minutes)
- 100% breach at 2:06 PM (6 minutes)
---
### **Production Mode (TAT_TEST_MODE=false):**
**Purpose**: Real-world usage
**Behavior:**
- ✅ 1 hour = 1 hour (real time)
- ✅ 48-hour TAT = 48 hours
- ✅ No "TEST MODE" badge
- ✅ No test mode note
- ✅ Respects working hours (Mon-Fri, 9 AM-6 PM)
- ✅ Skips weekends
**Example Alert (Production Mode):**
```
⏳ Reminder 1 - 50% TAT Threshold [WARNING]
Allocated: 48h | Elapsed: 24.0h
Remaining: 24.0h | Due by: Oct 8, 2024
Reminder sent by system automatically
Sent at: Oct 6 at 10:00 AM
```
**Timeline:**
- Submit Monday 10:00 AM
- 50% alert Tuesday 10:00 AM (24 hours)
- 75% alert Wednesday 10:00 AM (36 hours)
- 100% breach Thursday 10:00 AM (48 hours)
---
## 📡 New API Endpoints
### **1. Get TAT Alerts for Request**
```
GET /api/tat/alerts/request/:requestId
```
**Response:**
```json
{
"success": true,
"data": [
{
"alertId": "...",
"alertType": "TAT_50",
"thresholdPercentage": 50,
"tatHoursAllocated": 12,
"tatHoursElapsed": 6.0,
"tatHoursRemaining": 6.0,
"alertSentAt": "2024-10-06T14:30:00Z",
"level": {
"levelNumber": 2,
"approverName": "Lisa Wong",
"status": "PENDING"
}
}
]
}
```
### **2. Get TAT Compliance Summary**
```
GET /api/tat/compliance/summary?startDate=2024-10-01&endDate=2024-10-31
```
**Response:**
```json
{
"success": true,
"data": {
"total_alerts": 150,
"alerts_50": 50,
"alerts_75": 45,
"breaches": 25,
"completed_on_time": 35,
"completed_late": 15,
"compliance_percentage": 70.00
}
}
```
### **3. Get TAT Breach Report**
```
GET /api/tat/breaches
```
### **4. Get Approver Performance**
```
GET /api/tat/performance/:approverId
```
---
## 🔍 Database Fields Available
### **In `tat_alerts` Table:**
| Field | Type | Use In UI |
|-------|------|-----------|
| `alert_type` | ENUM | Determine icon (⏳/⚠️/⏰) |
| `threshold_percentage` | INT | Show "50%", "75%", "100%" |
| `tat_hours_allocated` | DECIMAL | Display "Allocated: Xh" |
| `tat_hours_elapsed` | DECIMAL | Display "Elapsed: Xh" |
| `tat_hours_remaining` | DECIMAL | Display "Remaining: Xh" (red if < 2h) |
| `level_start_time` | TIMESTAMP | Calculate time since start |
| `alert_sent_at` | TIMESTAMP | Show "Sent at: ..." |
| `expected_completion_time` | TIMESTAMP | Show "Due by: ..." |
| `alert_message` | TEXT | Full notification message |
| `is_breached` | BOOLEAN | Show "BREACHED" badge |
| `metadata` | JSONB | Test mode indicator, priority, etc. |
| `was_completed_on_time` | BOOLEAN | Show compliance status |
| `completion_time` | TIMESTAMP | Show actual completion |
---
## 💡 Production Recommendation
### **For Development/Testing:**
```bash
# .env
TAT_TEST_MODE=true
WORK_START_HOUR=9
WORK_END_HOUR=18
```
**Benefits:**
- ✅ Fast feedback (minutes instead of hours/days)
- ✅ Easy to test multiple scenarios
- ✅ Clear test mode indicators prevent confusion
### **For Production:**
```bash
# .env
TAT_TEST_MODE=false
WORK_START_HOUR=9
WORK_END_HOUR=18
```
**Benefits:**
- ✅ Real-world timing
- ✅ Accurate TAT tracking
- ✅ Meaningful metrics
---
## 📋 Complete Alert Card Template
### **Full Display Structure:**
```tsx
<div className="bg-yellow-50 border-yellow-200 p-3 rounded-lg">
{/* Header */}
<div className="flex items-center justify-between">
<span>⏳ Reminder 1 - 50% TAT Threshold</span>
<Badge>WARNING</Badge>
{testMode && <Badge>TEST MODE</Badge>}
</div>
{/* Main Message */}
<p>50% of SLA breach reminder have been sent</p>
{/* Time Grid */}
<div className="grid grid-cols-2 gap-2">
<div>Allocated: 12h</div>
<div>Elapsed: 6.0h</div>
<div>Remaining: 6.0h</div>
<div>Due by: Oct 7</div>
</div>
{/* Footer */}
<div className="border-t pt-2">
<p>Reminder sent by system automatically</p>
<p>Sent at: Oct 6 at 2:30 PM</p>
{testMode && <p>Note: Test mode (1h = 1min)</p>}
</div>
</div>
```
---
## 🎯 Key Benefits of Enhanced Display
### **1. Full Transparency**
Users see exactly:
- How much time was allocated
- How much was used when alert fired
- How much was remaining
- When it's due
### **2. Context Awareness**
- Test mode clearly indicated
- Color-coded by severity
- Badge shows warning vs breach
### **3. Actionable Information**
- "Remaining: 2.5h" → Approver knows they have 2.5h left
- "Due by: Oct 7 at 6 PM" → Clear deadline
- "Elapsed: 6h" → Understand how long it's been
### **4. Confusion Prevention**
- Test mode badge prevents misunderstanding
- Note explains "1 hour = 1 minute" in test mode
- Clear visual distinction from production
---
## 🧪 Testing Workflow
### **Step 1: Enable Detailed Logging**
In `Re_Backend/.env`:
```bash
TAT_TEST_MODE=true
LOG_LEVEL=debug
```
### **Step 2: Create Test Request**
- TAT: 6 hours
- Priority: Standard or Express
- Submit request
### **Step 3: Watch Alerts Populate**
**At 3 minutes (50%):**
```
⏳ Reminder 1 - 50% TAT Threshold [WARNING] [TEST MODE]
Allocated: 6h | Elapsed: 3.0h
Remaining: 3.0h | Due by: Today 2:06 PM
Note: Test mode active (1 hour = 1 minute)
```
**At 4.5 minutes (75%):**
```
⚠️ Reminder 2 - 75% TAT Threshold [WARNING] [TEST MODE]
Allocated: 6h | Elapsed: 4.5h
Remaining: 1.5h | Due by: Today 2:06 PM
Note: Test mode active (1 hour = 1 minute)
```
**At 6 minutes (100%):**
```
⏰ Reminder 3 - 100% TAT Threshold [BREACHED] [TEST MODE]
Allocated: 6h | Elapsed: 6.0h
Remaining: 0.0h | Due by: Today 2:06 PM (OVERDUE)
Note: Test mode active (1 hour = 1 minute)
```
---
## 📊 KPI Queries Using Alert Data
### **Average Response Time After Each Alert Type:**
```sql
SELECT
alert_type,
ROUND(AVG(tat_hours_elapsed), 2) as avg_elapsed,
ROUND(AVG(tat_hours_remaining), 2) as avg_remaining,
COUNT(*) as alert_count,
COUNT(CASE WHEN was_completed_on_time = true THEN 1 END) as completed_on_time
FROM tat_alerts
GROUP BY alert_type
ORDER BY threshold_percentage;
```
### **Approvers Who Frequently Breach:**
```sql
SELECT
u.display_name,
u.department,
COUNT(CASE WHEN ta.is_breached = true THEN 1 END) as breach_count,
AVG(ta.tat_hours_elapsed) as avg_time_taken,
COUNT(DISTINCT ta.level_id) as total_approvals
FROM tat_alerts ta
JOIN users u ON ta.approver_id = u.user_id
WHERE ta.is_breached = true
GROUP BY u.user_id, u.display_name, u.department
ORDER BY breach_count DESC
LIMIT 10;
```
### **Time-to-Action After Alert:**
```sql
SELECT
alert_type,
threshold_percentage,
ROUND(AVG(
EXTRACT(EPOCH FROM (completion_time - alert_sent_at)) / 3600
), 2) as avg_hours_to_respond_after_alert
FROM tat_alerts
WHERE completion_time IS NOT NULL
GROUP BY alert_type, threshold_percentage
ORDER BY threshold_percentage;
```
---
## 🔄 Alert Lifecycle
### **1. Alert Created (When Threshold Reached)**
```typescript
{
alertType: 'TAT_50',
thresholdPercentage: 50,
tatHoursAllocated: 12,
tatHoursElapsed: 6.0,
tatHoursRemaining: 6.0,
alertSentAt: '2024-10-06T14:30:00Z',
expectedCompletionTime: '2024-10-06T18:00:00Z',
isBreached: false,
wasCompletedOnTime: null, // Not completed yet
metadata: { testMode: true, ... }
}
```
### **2. Approver Takes Action**
```typescript
// Updated when level is approved/rejected
{
...existingFields,
wasCompletedOnTime: true, // or false
completionTime: '2024-10-06T16:00:00Z'
}
```
### **3. Displayed in UI**
```tsx
// Shows all historical alerts for that level
// Color-coded by threshold
// Shows completion status if completed
```
---
## 🎓 Understanding the Data
### **Allocated Hours (tat_hours_allocated)**
Total TAT time given to approver for this level
```
Example: 12 hours
Meaning: Approver has 12 hours to approve/reject
```
### **Elapsed Hours (tat_hours_elapsed)**
Time used when the alert was sent
```
Example: 6.0 hours (at 50% alert)
Meaning: 6 hours have passed since level started
```
### **Remaining Hours (tat_hours_remaining)**
Time left when the alert was sent
```
Example: 6.0 hours (at 50% alert)
Meaning: 6 hours remaining before TAT breach
Note: Turns red if < 2 hours
```
### **Expected Completion Time**
When the level should be completed
```
Example: Oct 6 at 6:00 PM
Meaning: Deadline for this approval level
```
---
## ⚙️ Configuration Options
### **Disable Test Mode for Production:**
Edit `.env`:
```bash
# Production settings
TAT_TEST_MODE=false
WORK_START_HOUR=9
WORK_END_HOUR=18
```
### **Adjust Working Hours:**
```bash
# Custom working hours (e.g., 8 AM - 5 PM)
WORK_START_HOUR=8
WORK_END_HOUR=17
```
### **Redis Configuration:**
```bash
# Upstash (recommended)
REDIS_URL=rediss://default:PASSWORD@host.upstash.io:6379
# Local Redis
REDIS_URL=redis://localhost:6379
# Production Redis with auth
REDIS_URL=redis://username:password@prod-redis.com:6379
```
---
## 📱 Mobile Responsive
The alert cards are responsive:
- ✅ 2-column grid on desktop
- ✅ Single column on mobile
- ✅ All information remains visible
- ✅ Touch-friendly spacing
---
## 🚀 API Endpoints Available
### **Get Alerts for Request:**
```bash
GET /api/tat/alerts/request/:requestId
```
### **Get Alerts for Level:**
```bash
GET /api/tat/alerts/level/:levelId
```
### **Get Compliance Summary:**
```bash
GET /api/tat/compliance/summary
GET /api/tat/compliance/summary?startDate=2024-10-01&endDate=2024-10-31
```
### **Get Breach Report:**
```bash
GET /api/tat/breaches
```
### **Get Approver Performance:**
```bash
GET /api/tat/performance/:approverId
```
---
## ✅ Benefits Summary
### **For Users:**
1. **Clear Visibility** - See exact time tracking
2. **No Confusion** - Test mode clearly labeled
3. **Actionable Data** - Know exactly how much time left
4. **Historical Record** - All alerts preserved
### **For Management:**
1. **KPI Ready** - All data for reporting
2. **Compliance Tracking** - On-time vs late completion
3. **Performance Analysis** - Response time after alerts
4. **Trend Analysis** - Breach patterns
### **For System:**
1. **Audit Trail** - Every alert logged
2. **Scalable** - Queue-based architecture
3. **Reliable** - Automatic retries
4. **Maintainable** - Clear configuration
---
## 🎯 Quick Switch Between Modes
### **Development (Fast Testing):**
```bash
# .env
TAT_TEST_MODE=true
```
Restart backend → Alerts fire in minutes
### **Staging (Semi-Real):**
```bash
# .env
TAT_TEST_MODE=false
# But use shorter TATs (2-4 hours instead of 48 hours)
```
Restart backend → Alerts fire in hours
### **Production (Real):**
```bash
# .env
TAT_TEST_MODE=false
# Use actual TATs (24-48 hours)
```
Restart backend → Alerts fire in days
---
## 📊 What You See in Workflow Tab
For each approval level, you'll see:
```
┌────────────────────────────────────────────┐
│ Step 2: Lisa Wong (Finance Manager) │
│ Status: pending │
│ TAT: 12 hours │
│ Elapsed: 8h │
│ │
│ [50% Alert Card with full details] │
│ [75% Alert Card with full details] │
│ │
│ Comment: (if any) │
└────────────────────────────────────────────┘
```
**Clear, informative, and actionable!**
---
## 🎉 Status: READY!
**Enhanced display** with all timing details
**Test mode indicator** to prevent confusion
**Color-coded** by severity
**Responsive** design
**API endpoints** for custom queries
**KPI-ready** data structure
---
**Just setup Upstash Redis and start testing!**
See: `START_HERE.md` for 2-minute Redis setup
---
**Last Updated**: November 4, 2025
**Team**: Royal Enfield Workflow

View File

@ -1,269 +0,0 @@
# ⏰ TAT Notifications - Quick Start Guide
## 🎯 Goal
Get TAT (Turnaround Time) notifications working in **under 5 minutes**!
---
## ✅ Step 1: Setup Redis (Required)
### 🚀 Option A: Upstash (RECOMMENDED - No Installation!)
**Best for Windows/Development - 100% Free**
1. **Sign up**: Go to https://console.upstash.com/
2. **Create Database**: Click "Create Database"
- Name: `redis-tat-dev`
- Type: Regional
- Region: Choose closest to you
- Click "Create"
3. **Copy Connection URL**: You'll get a URL like:
```
rediss://default:AbCd1234...@us1-mighty-shark-12345.upstash.io:6379
```
4. **Update `.env` in Re_Backend/**:
```bash
REDIS_URL=rediss://default:AbCd1234...@us1-mighty-shark-12345.upstash.io:6379
```
**Done!** No installation, no setup, works everywhere!
---
### Alternative: Docker (If you prefer local)
If you have Docker Desktop:
```bash
docker run -d --name redis-tat -p 6379:6379 redis:latest
```
Then in `.env`:
```bash
REDIS_URL=redis://localhost:6379
```
---
## ⚡ Step 2: Enable Test Mode (HIGHLY RECOMMENDED)
For testing, enable **fast mode** where **1 hour = 1 minute**:
### Edit `.env` file in `Re_Backend/`:
```bash
TAT_TEST_MODE=true
```
This means:
- ✅ 6-hour TAT = 6 minutes (instead of 6 hours)
- ✅ 48-hour TAT = 48 minutes (instead of 48 hours)
- ✅ Perfect for quick testing!
---
## 🚀 Step 3: Restart Backend
```bash
cd Re_Backend
npm run dev
```
### You Should See:
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Initialized and listening
⏰ TAT Configuration:
- Test Mode: ENABLED (1 hour = 1 minute)
- Redis: rediss://***@upstash.io:6379
```
💡 If you see connection errors, double-check your `REDIS_URL` in `.env`
---
## 🧪 Step 4: Test It!
### Create a Request:
1. **Frontend**: Create a new workflow request
2. **Set TAT**: 6 hours (becomes 6 minutes in test mode)
3. **Submit** the request
### Watch the Magic:
```
✨ At 3 minutes: ⏳ 50% notification
✨ At 4.5 minutes: ⚠️ 75% notification
✨ At 6 minutes: ⏰ 100% breach notification
```
### Check Logs:
```bash
# You'll see:
[TAT Scheduler] ✅ TAT jobs scheduled for request...
[TAT Processor] Processing tat50 for request...
[TAT Processor] tat50 notification sent for request...
```
---
## 📊 Verify in Database
```sql
SELECT
approver_name,
tat_hours,
tat50_alert_sent,
tat75_alert_sent,
tat_breached,
status
FROM approval_levels
WHERE status = 'IN_PROGRESS';
```
You should see the flags change as notifications are sent!
---
## ❌ Troubleshooting
### "ECONNREFUSED" or Connection Error?
**Problem**: Can't connect to Redis
**Solution**:
1. **Check `.env` file**:
```bash
# Make sure REDIS_URL is set correctly
REDIS_URL=rediss://default:YOUR_PASSWORD@YOUR_URL.upstash.io:6379
```
2. **Verify Upstash Database**:
- Go to https://console.upstash.com/
- Check database status (should be "Active")
- Copy connection URL again if needed
3. **Test Connection**:
- Use Upstash's Redis CLI in their console
- Or install `redis-cli` and test:
```bash
redis-cli -u "rediss://default:YOUR_PASSWORD@YOUR_URL.upstash.io:6379" ping
# Should return: PONG
```
### No Notifications?
**Checklist**:
- ✅ REDIS_URL set in `.env`?
- ✅ Backend restarted after setting REDIS_URL?
- ✅ TAT_TEST_MODE=true in `.env`?
- ✅ Request submitted (not just created)?
- ✅ Logs show "Connected to Redis"?
### Still Issues?
```bash
# Check detailed logs
Get-Content Re_Backend/logs/app.log -Tail 50 -Wait
# Look for:
# ✅ [TAT Queue] Connected to Redis
# ❌ [TAT Queue] Redis connection error
```
---
## 🎓 Testing Scenarios
### Quick Test (6 minutes):
```
TAT: 6 hours (6 minutes in test mode)
├─ 3 min ⏳ 50% reminder
├─ 4.5 min ⚠️ 75% warning
└─ 6 min ⏰ 100% breach
```
### Medium Test (24 minutes):
```
TAT: 24 hours (24 minutes in test mode)
├─ 12 min ⏳ 50% reminder
├─ 18 min ⚠️ 75% warning
└─ 24 min ⏰ 100% breach
```
---
## 📚 More Information
- **Full Documentation**: `Re_Backend/docs/TAT_NOTIFICATION_SYSTEM.md`
- **Testing Guide**: `Re_Backend/docs/TAT_TESTING_GUIDE.md`
- **Redis Setup**: `Re_Backend/INSTALL_REDIS.txt`
---
## 🎉 Production Mode
When ready for production:
1. **Disable Test Mode**:
```bash
# In .env
TAT_TEST_MODE=false
```
2. **Restart Backend**
3. **TAT will now use real hours**:
- 48-hour TAT = actual 48 hours
- Working hours: Mon-Fri, 9 AM - 6 PM
---
## 🆘 Need Help?
Common fixes:
### 1. Verify Upstash Connection
```bash
# In Upstash Console (https://console.upstash.com/)
# - Click your database
# - Use the "CLI" tab to test: PING
# - Should return: PONG
```
### 2. Check Environment Variables
```bash
# In Re_Backend/.env, verify:
REDIS_URL=rediss://default:YOUR_PASSWORD@YOUR_URL.upstash.io:6379
TAT_TEST_MODE=true
```
### 3. Clear Redis Queue (if needed)
```bash
# In Upstash Console CLI tab:
FLUSHALL
# This clears all jobs - use only if you need a fresh start
```
### 4. Restart Backend
```bash
cd Re_Backend
npm run dev
```
### 5. Check Logs
```bash
Get-Content logs/app.log -Tail 50 -Wait
```
---
**Status Check**:
- [ ] Upstash Redis database created
- [ ] REDIS_URL copied to `.env`
- [ ] TAT_TEST_MODE=true in `.env`
- [ ] Backend restarted
- [ ] Logs show "TAT Queue: Connected to Redis"
- [ ] Test request submitted
✅ All checked? **You're ready!**
---
**Last Updated**: November 4, 2025
**Author**: Royal Enfield Workflow Team

View File

@ -1,420 +0,0 @@
# 🔍 Troubleshooting TAT Alerts Not Showing
## Quick Diagnosis Steps
### Step 1: Check if Redis is Connected
**Look at your backend console when you start the server:**
**Good** - Redis is working:
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Worker is ready and listening
```
**Bad** - Redis is NOT working:
```
⚠️ [TAT Worker] Redis connection failed
⚠️ [TAT Queue] Redis connection failed after 3 attempts
```
**If you see the bad message:**
→ TAT alerts will NOT be created because the worker isn't running
→ You MUST setup Redis first (see `START_HERE.md`)
---
### Step 2: Verify TAT Alerts Table Exists
**Run this SQL:**
```sql
SELECT COUNT(*) FROM tat_alerts;
```
**Expected Result:**
- If table exists: You'll see a count (maybe 0)
- If table doesn't exist: Error "relation tat_alerts does not exist"
**If table doesn't exist:**
```bash
cd Re_Backend
npm run migrate
```
---
### Step 3: Check if TAT Alerts Exist in Database
**Run this SQL:**
```sql
-- Check if ANY alerts exist
SELECT
ta.alert_id,
ta.threshold_percentage,
ta.alert_sent_at,
ta.alert_message,
ta.metadata->>'requestNumber' as request_number,
ta.metadata->>'approverName' as approver_name
FROM tat_alerts ta
ORDER BY ta.alert_sent_at DESC
LIMIT 10;
```
**If query returns 0 rows:**
→ No alerts have been created yet
→ This means:
1. Redis is not connected, OR
2. No requests have been submitted, OR
3. Not enough time has passed (wait 3 min in test mode)
---
### Step 4: Check API Response
**Option A: Use Debug Endpoint**
Call this URL in your browser or Postman:
```
GET http://localhost:5000/api/debug/tat-status
```
**You'll see:**
```json
{
"success": true,
"status": {
"redis": {
"configured": true,
"url": "rediss://****@upstash.io:6379",
"testMode": true
},
"database": {
"connected": true,
"tatAlertsTableExists": true,
"totalAlerts": 0
}
}
}
```
**Option B: Check Workflow Details Response**
For a specific request:
```
GET http://localhost:5000/api/debug/workflow-details/REQ-2025-XXXXX
```
**You'll see:**
```json
{
"success": true,
"structure": {
"hasTatAlerts": true,
"tatAlertsCount": 2
},
"tatAlerts": [
{
"alertType": "TAT_50",
"thresholdPercentage": 50,
"alertSentAt": "...",
...
}
]
}
```
---
### Step 5: Check Frontend Console
**Open browser DevTools (F12) → Console**
**When you open Request Detail, you should see:**
```javascript
// Look for the API response
Object {
workflow: {...},
approvals: [...],
tatAlerts: [...] // ← Check if this exists
}
```
**If `tatAlerts` is missing or empty:**
→ Backend is not returning it (go back to Step 3)
**If `tatAlerts` exists but not showing:**
→ Frontend rendering issue (check Step 6)
---
### Step 6: Verify Frontend Code
**Check if tatAlerts are being processed:**
Open `Re_Figma_Code/src/pages/RequestDetail/RequestDetail.tsx`
**Search for this line (around line 235 and 493):**
```typescript
const tatAlerts = Array.isArray(details.tatAlerts) ? details.tatAlerts : [];
```
**This should be there!** If not, the code wasn't applied.
**Then search for (around line 271 and 531):**
```typescript
const levelAlerts = tatAlerts.filter((alert: any) => alert.levelId === levelId);
```
**And in the JSX (around line 1070):**
```tsx
{step.tatAlerts && step.tatAlerts.length > 0 && (
<div className="mt-3 space-y-2">
{step.tatAlerts.map((alert: any, alertIndex: number) => (
```
---
## 🐛 Common Issues & Fixes
### Issue 1: "TAT alerts not showing in UI"
**Cause**: Redis not connected
**Fix**:
1. Setup Upstash: https://console.upstash.com/
2. Add to `.env`:
```bash
REDIS_URL=rediss://default:...@upstash.io:6379
TAT_TEST_MODE=true
```
3. Restart backend
4. Look for "Connected to Redis" in logs
---
### Issue 2: "tat_alerts table doesn't exist"
**Cause**: Migrations not run
**Fix**:
```bash
cd Re_Backend
npm run migrate
```
---
### Issue 3: "No alerts in database"
**Cause**: No requests submitted or not enough time passed
**Fix**:
1. Create a new workflow request
2. **SUBMIT** the request (not just save as draft)
3. Wait:
- Test mode: 3 minutes for 50% alert
- Production: Depends on TAT (e.g., 12 hours for 24-hour TAT)
---
### Issue 4: "tatAlerts is undefined in API response"
**Cause**: Backend code not updated
**Fix**:
Check `Re_Backend/src/services/workflow.service.ts` line 698:
```typescript
return { workflow, approvals, participants, documents, activities, summary, tatAlerts };
// ^^^^^^^^
// Make sure tatAlerts is included!
```
---
### Issue 5: "Frontend not displaying alerts even though they exist"
**Cause**: Frontend code not applied or missing key
**Fix**:
1. Check browser console for errors
2. Verify `step.tatAlerts` is defined in approval flow
3. Check if alerts have correct `levelId` matching approval level
---
## 📊 Manual Test Steps
### Step-by-Step Debugging:
**1. Check Redis Connection:**
```bash
# Start backend and look for:
✅ [TAT Queue] Connected to Redis
```
**2. Create and Submit Request:**
```bash
# Via frontend or API:
POST /api/workflows/create
POST /api/workflows/{id}/submit
```
**3. Wait for Alert (Test Mode):**
```bash
# For 6-hour TAT in test mode:
# Wait 3 minutes for 50% alert
```
**4. Check Database:**
```sql
SELECT * FROM tat_alerts ORDER BY alert_sent_at DESC LIMIT 5;
```
**5. Check API Response:**
```bash
GET /api/workflows/{requestNumber}/details
# Look for tatAlerts array in response
```
**6. Check Frontend:**
```javascript
// Open DevTools Console
// Navigate to Request Detail
// Check the console log for API response
```
---
## 🔧 Debug Commands
### Check TAT System Status:
```bash
curl http://localhost:5000/api/debug/tat-status
```
### Check Workflow Details for Specific Request:
```bash
curl http://localhost:5000/api/debug/workflow-details/REQ-2025-XXXXX
```
### Check Database Directly:
```sql
-- Total alerts
SELECT COUNT(*) FROM tat_alerts;
-- Alerts for specific request
SELECT * FROM tat_alerts
WHERE request_id = (
SELECT request_id FROM workflow_requests
WHERE request_number = 'REQ-2025-XXXXX'
);
-- Pending levels that should get alerts
SELECT
w.request_number,
al.approver_name,
al.status,
al.tat_start_time,
CASE
WHEN al.tat_start_time IS NULL THEN 'No TAT monitoring started'
ELSE 'TAT monitoring active'
END as tat_status
FROM approval_levels al
JOIN workflow_requests w ON al.request_id = w.request_id
WHERE al.status IN ('PENDING', 'IN_PROGRESS');
```
---
## 📝 Checklist for TAT Alerts to Show
Must have ALL of these:
- [ ] Redis connected (see "Connected to Redis" in logs)
- [ ] TAT worker running (see "Worker is ready" in logs)
- [ ] Request SUBMITTED (not draft)
- [ ] Enough time passed (3 min in test mode for 50%)
- [ ] tat_alerts table exists in database
- [ ] Alert records created in tat_alerts table
- [ ] API returns tatAlerts in workflow details
- [ ] Frontend receives tatAlerts from API
- [ ] Frontend displays tatAlerts in workflow tab
---
## 🆘 Still Not Working?
### Provide These Details:
1. **Backend console output** when starting server
2. **Result of**:
```bash
curl http://localhost:5000/api/debug/tat-status
```
3. **Database query result**:
```sql
SELECT COUNT(*) FROM tat_alerts;
```
4. **Browser console** errors (F12 → Console)
5. **Request number** you're testing with
---
## 🎯 Most Common Cause
**99% of the time, TAT alerts don't show because:**
❌ **Redis is not connected**
**How to verify:**
```bash
# When you start backend, you should see:
✅ [TAT Queue] Connected to Redis
# If you see this instead:
⚠️ [TAT Queue] Redis connection failed
# Then:
# 1. Setup Upstash: https://console.upstash.com/
# 2. Add REDIS_URL to .env
# 3. Restart backend
```
---
## 🚀 Quick Fix Steps
If alerts aren't showing, do this IN ORDER:
```bash
# 1. Check .env file has Redis URL
cat Re_Backend/.env | findstr REDIS_URL
# 2. Restart backend
cd Re_Backend
npm run dev
# 3. Look for "Connected to Redis" in console
# 4. Create NEW request (don't use old ones)
# 5. SUBMIT the request
# 6. Wait 3 minutes (in test mode)
# 7. Refresh Request Detail page
# 8. Go to Workflow tab
# 9. Alerts should appear under approver card
```
---
**Need more help? Share the output of `/api/debug/tat-status` endpoint!**
---
**Last Updated**: November 4, 2025
**Team**: Royal Enfield Workflow

View File

@ -1,215 +0,0 @@
# 🚀 Upstash Redis - Quick Reference
## One-Time Setup (2 Minutes)
```
1. Visit: https://console.upstash.com/
└─ Sign up (free)
2. Create Database
└─ Name: redis-tat-dev
└─ Type: Regional
└─ Region: US-East-1 (or closest)
└─ Click "Create"
3. Copy Redis URL
└─ Format: rediss://default:PASSWORD@host.upstash.io:6379
└─ Click copy button 📋
4. Paste into .env
└─ Re_Backend/.env
└─ REDIS_URL=rediss://default:...
└─ TAT_TEST_MODE=true
5. Start Backend
└─ cd Re_Backend
└─ npm run dev
└─ ✅ See: "Connected to Redis"
```
---
## Environment Variables
```bash
# Re_Backend/.env
# Upstash Redis (paste your URL)
REDIS_URL=rediss://default:YOUR_PASSWORD@YOUR_HOST.upstash.io:6379
# Test Mode (1 hour = 1 minute)
TAT_TEST_MODE=true
# Working Hours (optional)
WORK_START_HOUR=9
WORK_END_HOUR=18
```
---
## Test TAT Notifications
```
1. Create Request
└─ TAT: 6 hours
└─ Submit request
2. Wait for Notifications (Test Mode)
└─ 3 minutes → ⏳ 50% alert
└─ 4.5 minutes → ⚠️ 75% warning
└─ 6 minutes → ⏰ 100% breach
3. Check Logs
└─ [TAT Scheduler] ✅ TAT jobs scheduled
└─ [TAT Processor] Processing tat50...
└─ [TAT Processor] tat50 notification sent
```
---
## Monitor in Upstash Console
```
1. Go to: https://console.upstash.com/
2. Click your database
3. Click "CLI" tab
4. Run commands:
PING
→ PONG
KEYS bull:tatQueue:*
→ Shows all queued TAT jobs
INFO
→ Shows Redis stats
```
---
## Troubleshooting
### ❌ Connection Error
```bash
# Check .env
REDIS_URL=rediss://... (correct URL?)
# Test in Upstash Console
# CLI tab → PING → should return PONG
# Restart backend
npm run dev
```
### ❌ No Notifications
```bash
# Checklist:
- ✅ REDIS_URL in .env?
- ✅ TAT_TEST_MODE=true?
- ✅ Backend restarted?
- ✅ Request SUBMITTED (not just created)?
- ✅ Logs show "Connected to Redis"?
```
---
## Production Setup
```bash
# Option 1: Use Upstash (same as dev)
REDIS_URL=rediss://default:PROD_PASSWORD@prod.upstash.io:6379
TAT_TEST_MODE=false
# Option 2: Linux server with native Redis
sudo apt install redis-server -y
sudo systemctl start redis-server
# Then in .env:
REDIS_URL=redis://localhost:6379
TAT_TEST_MODE=false
```
---
## Upstash Free Tier
```
✅ 10,000 commands/day (FREE forever)
✅ 256 MB storage
✅ TLS encryption
✅ Global CDN
✅ Zero maintenance
Perfect for:
- Development
- Testing
- Small production (<100 users)
```
---
## Commands Cheat Sheet
### Upstash Console CLI
```redis
# Test connection
PING
# List all keys
KEYS *
# Count keys
DBSIZE
# View queued jobs
KEYS bull:tatQueue:*
# Get job details
HGETALL bull:tatQueue:tat50-<REQUEST_ID>-<LEVEL_ID>
# Clear all data (CAREFUL!)
FLUSHALL
# Get server info
INFO
# Monitor live commands
MONITOR
```
---
## Quick Links
- **Upstash Console**: https://console.upstash.com/
- **Upstash Docs**: https://docs.upstash.com/redis
- **Full Setup Guide**: `docs/UPSTASH_SETUP_GUIDE.md`
- **TAT System Docs**: `docs/TAT_NOTIFICATION_SYSTEM.md`
- **Quick Start**: `TAT_QUICK_START.md`
---
## Support
**Connection Issues?**
1. Verify URL format: `rediss://` (double 's')
2. Check Upstash database status (should be "Active")
3. Test in Upstash Console CLI
**Need Help?**
- Check logs: `Get-Content logs/app.log -Tail 50 -Wait`
- Review docs: `docs/UPSTASH_SETUP_GUIDE.md`
---
**✅ Setup Complete? Start Testing!**
Create a 6-hour TAT request and watch notifications arrive in 3, 4.5, and 6 minutes!
---
**Last Updated**: November 4, 2025

View File

@ -1,345 +0,0 @@
# ❓ Why Are TAT Alerts Not Showing?
## 🎯 Follow These Steps IN ORDER
### ✅ Step 1: Is Redis Connected?
**Check your backend console:**
```
Look for one of these messages:
```
**✅ GOOD (Redis is working):**
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Worker is ready and listening
```
**❌ BAD (Redis NOT working):**
```
⚠️ [TAT Worker] Redis connection failed
⚠️ [TAT Queue] Redis connection failed after 3 attempts
```
**If you see the BAD message:**
**STOP HERE!** TAT alerts will NOT work without Redis!
→ **Setup Upstash Redis NOW:**
1. Go to: https://console.upstash.com/
2. Sign up (free)
3. Create database
4. Copy Redis URL
5. Add to `Re_Backend/.env`:
```bash
REDIS_URL=rediss://default:PASSWORD@host.upstash.io:6379
TAT_TEST_MODE=true
```
6. Restart backend
7. Verify you see "Connected to Redis"
---
### ✅ Step 2: Have You Submitted a Request?
**TAT monitoring starts ONLY when:**
- ✅ Request is **SUBMITTED** (not just created/saved)
- ✅ Status changes from DRAFT → PENDING
**To verify:**
```sql
SELECT
request_number,
status,
submission_date
FROM workflow_requests
WHERE request_number = 'YOUR_REQUEST_NUMBER';
```
**Check:**
- `status` should be `PENDING`, `IN_PROGRESS`, or later
- `submission_date` should NOT be NULL
**If status is DRAFT:**
→ Click "Submit" button on the request
→ TAT monitoring will start
---
### ✅ Step 3: Has Enough Time Passed?
**In TEST MODE (TAT_TEST_MODE=true):**
- 1 hour = 1 minute
- For 6-hour TAT:
- 50% alert at: **3 minutes**
- 75% alert at: **4.5 minutes**
- 100% breach at: **6 minutes**
**In PRODUCTION MODE:**
- 1 hour = 1 hour (real time)
- For 24-hour TAT:
- 50% alert at: **12 hours**
- 75% alert at: **18 hours**
- 100% breach at: **24 hours**
**Check when request was submitted:**
```sql
SELECT
request_number,
submission_date,
NOW() - submission_date as time_since_submission
FROM workflow_requests
WHERE request_number = 'YOUR_REQUEST_NUMBER';
```
---
### ✅ Step 4: Are Alerts in the Database?
**Run this SQL:**
```sql
-- Check if table exists
SELECT COUNT(*) FROM tat_alerts;
-- If table exists, check for your request
SELECT
ta.threshold_percentage,
ta.alert_sent_at,
ta.alert_message,
ta.metadata
FROM tat_alerts ta
JOIN workflow_requests w ON ta.request_id = w.request_id
WHERE w.request_number = 'YOUR_REQUEST_NUMBER'
ORDER BY ta.alert_sent_at;
```
**Expected Result:**
- **0 rows** → No alerts sent yet (wait longer OR Redis not connected)
- **1+ rows** → Alerts exist! (Go to Step 5)
**If table doesn't exist:**
```bash
cd Re_Backend
npm run migrate
```
---
### ✅ Step 5: Is API Returning tatAlerts?
**Test the API directly:**
**Method 1: Use Debug Endpoint**
```bash
curl http://localhost:5000/api/debug/workflow-details/YOUR_REQUEST_NUMBER
```
**Look for:**
```json
{
"structure": {
"hasTatAlerts": true, ← Should be true
"tatAlertsCount": 2 ← Should be > 0
},
"tatAlerts": [...] ← Should have data
}
```
**Method 2: Check Network Tab in Browser**
1. Open Request Detail page
2. Open DevTools (F12) → Network tab
3. Find the API call to `/workflows/{requestNumber}/details`
4. Click on it
5. Check Response tab
6. Look for `tatAlerts` array
---
### ✅ Step 6: Is Frontend Receiving tatAlerts?
**Open Browser Console (F12 → Console)**
**When you open Request Detail, you should see:**
```javascript
[RequestDetail] TAT Alerts received from API: 2 [Array(2)]
```
**If you see:**
```javascript
[RequestDetail] TAT Alerts received from API: 0 []
```
→ API is NOT returning alerts (go back to Step 4)
---
### ✅ Step 7: Are Alerts Being Displayed?
**In Request Detail:**
1. Click **"Workflow" tab**
2. Scroll to the approver card
3. Look under the approver's comment section
**You should see yellow/orange/red boxes with:**
```
⏳ Reminder 1 - 50% TAT Threshold
```
**If you DON'T see them:**
→ Check browser console for JavaScript errors
---
## 🔍 Quick Diagnostic
**Run ALL of these and share the results:**
### 1. Backend Status:
```bash
curl http://localhost:5000/api/debug/tat-status
```
### 2. Database Query:
```sql
SELECT COUNT(*) as total FROM tat_alerts;
```
### 3. Browser Console:
```javascript
// Open Request Detail
// Check console for:
[RequestDetail] TAT Alerts received from API: X [...]
```
### 4. Network Response:
```
DevTools → Network → workflow details call → Response tab
Look for "tatAlerts" field
```
---
## 🎯 Most Likely Issues (In Order)
### 1. Redis Not Connected (90% of cases)
**Symptom**: No "Connected to Redis" in logs
**Fix**: Setup Upstash, add REDIS_URL, restart
### 2. Request Not Submitted (5%)
**Symptom**: Request status is DRAFT
**Fix**: Click Submit button
### 3. Not Enough Time Passed (3%)
**Symptom**: Submitted < 3 minutes ago (in test mode)
**Fix**: Wait 3 minutes for first alert
### 4. TAT Worker Not Running (1%)
**Symptom**: Redis connected but no "Worker is ready" message
**Fix**: Restart backend server
### 5. Frontend Code Not Applied (1%)
**Symptom**: API returns tatAlerts but UI doesn't show them
**Fix**: Refresh browser, clear cache
---
## 🚨 Emergency Checklist
**Do this RIGHT NOW to verify everything:**
```bash
# 1. Check backend console for Redis connection
# Look for: ✅ [TAT Queue] Connected to Redis
# 2. If NOT connected, setup Upstash:
# https://console.upstash.com/
# 3. Add to .env:
# REDIS_URL=rediss://...
# TAT_TEST_MODE=true
# 4. Restart backend
npm run dev
# 5. Check you see "Connected to Redis"
# 6. Create NEW request with 6-hour TAT
# 7. SUBMIT the request
# 8. Wait 3 minutes
# 9. Open browser console (F12)
# 10. Open Request Detail page
# 11. Check console log for:
# [RequestDetail] TAT Alerts received from API: X [...]
# 12. Go to Workflow tab
# 13. Alerts should appear!
```
---
## 📞 Share These for Help
If still not working, share:
1. **Backend console output** (first 50 lines after starting)
2. **Result of**: `curl http://localhost:5000/api/debug/tat-status`
3. **SQL result**: `SELECT COUNT(*) FROM tat_alerts;`
4. **Browser console** when opening Request Detail
5. **Request number** you're testing with
6. **How long ago** was the request submitted?
---
## ✅ Working Example
**When everything works, you'll see:**
**Backend Console:**
```
✅ [TAT Queue] Connected to Redis
✅ [TAT Worker] Worker is ready
[TAT Scheduler] ✅ TAT jobs scheduled for request REQ-2025-001
```
**After 3 minutes (test mode):**
```
[TAT Processor] Processing tat50 for request REQ-2025-001
[TAT Processor] TAT alert record created for tat50
[TAT Processor] tat50 notification sent
```
**Browser Console:**
```javascript
[RequestDetail] TAT Alerts received from API: 1 [
{
alertType: "TAT_50",
thresholdPercentage: 50,
alertSentAt: "2025-11-04T...",
...
}
]
```
**UI Display:**
```
⏳ Reminder 1 - 50% TAT Threshold
50% of SLA breach reminder have been sent
...
```
---
**Most likely you just need to setup Redis! See `START_HERE.md`**
---
**Last Updated**: November 4, 2025

View File

@ -17,16 +17,21 @@ export const corsMiddleware = cors({
origin: (origin, callback) => {
const allowedOrigins = getOrigins();
// Allow requests with no origin (like mobile apps or curl requests) in development
if (!origin && process.env.NODE_ENV === 'development') {
// In development, be more permissive
if (process.env.NODE_ENV !== 'production') {
// Allow localhost on any port
if (!origin || origin.includes('localhost') || origin.includes('127.0.0.1')) {
return callback(null, true);
}
}
// Allow requests with no origin (like mobile apps or curl requests)
if (!origin) {
return callback(null, true);
}
if (origin && allowedOrigins.includes(origin)) {
callback(null, true);
} else if (!origin) {
// Allow requests with no origin
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}

View File

@ -0,0 +1,33 @@
import { tatQueue } from './tatQueue';
import logger from '@utils/logger';
async function promoteDelayedJobs() {
if (!tatQueue) return;
try {
const delayedJobs = await tatQueue.getJobs(['delayed']);
const now = Date.now();
let promotedCount = 0;
for (const job of delayedJobs) {
const readyTime = job.timestamp + (job.opts.delay || 0);
const secondsUntil = Math.round((readyTime - now) / 1000);
// Promote if ready within 15 seconds
if (secondsUntil <= 15) {
await job.promote();
promotedCount++;
}
}
if (promotedCount > 0) {
logger.info(`[TAT] Promoted ${promotedCount} jobs`);
}
} catch (error) {
logger.error('[TAT] Promoter error:', error);
}
}
// Check every 3 seconds
setInterval(promoteDelayedJobs, 3000);
logger.info('[TAT] Delayed job promoter started');

View File

@ -0,0 +1,57 @@
import IORedis from 'ioredis';
import logger from '@utils/logger';
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
const redisPassword = process.env.REDIS_PASSWORD || undefined;
const redisOptions: any = {
maxRetriesPerRequest: null, // Required for BullMQ
enableReadyCheck: false,
retryStrategy: (times: number) => {
if (times > 5) {
logger.error('[Redis] Connection failed after 5 attempts');
return null;
}
return Math.min(times * 2000, 10000);
},
connectTimeout: 30000,
commandTimeout: 20000,
keepAlive: 30000,
autoResubscribe: true,
autoResendUnfulfilledCommands: true
};
if (redisPassword) {
redisOptions.password = redisPassword;
logger.info('[Redis] Using password authentication');
}
let sharedConnection: IORedis | null = null;
// Create a SINGLE shared connection for both Queue and Worker
export const getSharedRedisConnection = (): IORedis => {
if (!sharedConnection) {
logger.info(`[Redis] Creating shared connection to ${redisUrl}`);
sharedConnection = new IORedis(redisUrl, redisOptions);
sharedConnection.on('connect', () => {
logger.info(`[Redis] ✅ Connected to ${redisUrl}`);
});
sharedConnection.on('error', (err) => {
logger.error('[Redis] Connection error:', err.message);
});
sharedConnection.on('close', () => {
logger.warn('[Redis] Connection closed');
});
}
return sharedConnection;
};
// Export for backwards compatibility
export const sharedRedisConnection = getSharedRedisConnection();
export default sharedRedisConnection;

View File

@ -21,8 +21,9 @@ interface TatJobData {
export async function handleTatJob(job: Job<TatJobData>) {
const { requestId, levelId, approverId, type, threshold } = job.data;
logger.info(`[TAT Processor] Processing ${type} (${threshold}%) for request ${requestId}`);
try {
logger.info(`[TAT Processor] Processing ${type} for request ${requestId}, level ${levelId}`);
// Get approval level and workflow details
const approvalLevel = await ApprovalLevel.findOne({
@ -30,8 +31,8 @@ export async function handleTatJob(job: Job<TatJobData>) {
});
if (!approvalLevel) {
logger.warn(`[TAT Processor] Approval level ${levelId} not found`);
return;
logger.warn(`[TAT Processor] Approval level ${levelId} not found - likely already approved/rejected`);
return; // Skip notification for non-existent level
}
// Check if level is still pending (not already approved/rejected)
@ -126,7 +127,7 @@ export async function handleTatJob(job: Job<TatJobData>) {
expectedCompletionTime,
alertMessage: message,
notificationSent: true,
notificationChannels: ['push'], // Can add 'email', 'sms' if implemented
notificationChannels: ['push'],
isBreached: type === 'breach',
metadata: {
requestNumber,
@ -140,10 +141,9 @@ export async function handleTatJob(job: Job<TatJobData>) {
}
} as any);
logger.info(`[TAT Processor] TAT alert record created for ${type}`);
} catch (alertError) {
logger.error(`[TAT Processor] Failed to create TAT alert record:`, alertError);
// Don't fail the notification if alert logging fails
logger.info(`[TAT Processor] ✅ Alert created: ${type} (${threshold}%)`);
} catch (alertError: any) {
logger.error(`[TAT Processor] ❌ Alert creation failed for ${type}: ${alertError.message}`);
}
// Send notification to approver
@ -156,15 +156,48 @@ export async function handleTatJob(job: Job<TatJobData>) {
type: type
});
// Log activity
// Log activity (skip if it fails - don't break the TAT notification)
try {
await activityService.log({
requestId,
type: 'sla_warning',
user: { userId: 'system', name: 'System' },
user: { userId: null as any, name: 'System' }, // Use null instead of 'system' for UUID field
timestamp: new Date().toISOString(),
action: type === 'breach' ? 'TAT Breached' : 'TAT Warning',
details: activityDetails
});
logger.info(`[TAT Processor] Activity logged for ${type}`);
} catch (activityError: any) {
logger.warn(`[TAT Processor] Failed to log activity (non-critical):`, activityError.message);
// Continue - activity logging failure shouldn't break TAT notification
}
// 🔥 CRITICAL: Emit TAT alert to frontend via socket.io for real-time updates
try {
const { emitToRequestRoom } = require('../realtime/socket');
if (emitToRequestRoom) {
// Fetch the newly created alert to send complete data to frontend
const newAlert = await TatAlert.findOne({
where: { requestId, levelId, alertType },
order: [['createdAt', 'DESC']]
});
if (newAlert) {
emitToRequestRoom(requestId, 'tat:alert', {
alert: newAlert,
requestId,
levelId,
type,
thresholdPercentage,
message
});
logger.info(`[TAT Processor] ✅ TAT alert emitted to frontend via socket.io for request ${requestId}`);
}
}
} catch (socketError) {
logger.error(`[TAT Processor] Failed to emit TAT alert via socket:`, socketError);
// Don't fail the job if socket emission fails
}
logger.info(`[TAT Processor] ${type} notification sent for request ${requestId}`);
} catch (error) {

View File

@ -1,80 +1,26 @@
import { Queue } from 'bullmq';
import IORedis from 'ioredis';
import { sharedRedisConnection } from './redisConnection';
import logger from '@utils/logger';
// Create Redis connection
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
const redisPassword = process.env.REDIS_PASSWORD || undefined;
let connection: IORedis | null = null;
let tatQueue: Queue | null = null;
try {
// Parse Redis URL and add password if provided
const redisOptions: any = {
maxRetriesPerRequest: null, // Required for BullMQ
enableReadyCheck: false,
lazyConnect: true, // Don't connect immediately
retryStrategy: (times: number) => {
if (times > 5) {
logger.warn('[TAT Queue] Redis connection failed after 5 attempts. TAT notifications will be disabled.');
return null; // Stop retrying
}
return Math.min(times * 2000, 10000); // Increase retry delay
},
// Increased timeouts for remote Redis server
connectTimeout: 30000, // 30 seconds (for remote server)
commandTimeout: 20000, // 20 seconds (for slow network)
// Keepalive for long-running connections
keepAlive: 30000,
// Reconnect on error
autoResubscribe: true,
autoResendUnfulfilledCommands: true
};
// Add password if provided (either from env var or from URL)
if (redisPassword) {
redisOptions.password = redisPassword;
logger.info('[TAT Queue] Using Redis with password authentication');
}
connection = new IORedis(redisUrl, redisOptions);
// Handle connection events
connection.on('connect', () => {
logger.info('[TAT Queue] Connected to Redis');
});
connection.on('error', (err) => {
logger.warn('[TAT Queue] Redis connection error - TAT notifications disabled:', err.message);
});
// Try to connect
connection.connect().then(() => {
logger.info('[TAT Queue] Redis connection established');
}).catch((err) => {
logger.warn('[TAT Queue] Could not connect to Redis. TAT notifications will be disabled.', err.message);
connection = null;
});
// Create TAT Queue only if connection is available
if (connection) {
// Use shared Redis connection for both Queue and Worker
tatQueue = new Queue('tatQueue', {
connection,
connection: sharedRedisConnection,
defaultJobOptions: {
removeOnComplete: true, // Clean up completed jobs
removeOnFail: false, // Keep failed jobs for debugging
attempts: 3, // Retry failed jobs up to 3 times
removeOnComplete: true,
removeOnFail: false,
attempts: 2,
backoff: {
type: 'exponential',
delay: 2000 // Start with 2 second delay
type: 'fixed',
delay: 5000
}
}
});
logger.info('[TAT Queue] Queue initialized');
}
} catch (error) {
logger.warn('[TAT Queue] Failed to initialize TAT queue. TAT notifications will be disabled.', error);
logger.error('[TAT Queue] Failed to initialize:', error);
tatQueue = null;
}

View File

@ -1,187 +1,46 @@
import { Worker } from 'bullmq';
import IORedis from 'ioredis';
import { sharedRedisConnection } from './redisConnection';
import { handleTatJob } from './tatProcessor';
import logger from '@utils/logger';
// Create Redis connection for worker
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
const redisPassword = process.env.REDIS_PASSWORD || undefined;
let connection: IORedis | null = null;
let tatWorker: Worker | null = null;
try {
// Parse Redis connection options
const redisOptions: any = {
maxRetriesPerRequest: null,
enableReadyCheck: false,
lazyConnect: true,
retryStrategy: (times: number) => {
if (times > 5) {
logger.warn('[TAT Worker] Redis connection failed after 5 retries. TAT worker will not start.');
return null;
}
logger.warn(`[TAT Worker] Redis connection retry attempt ${times}`);
return Math.min(times * 2000, 10000); // Increase retry delay
},
// Increased timeouts for remote Redis server
connectTimeout: 30000, // 30 seconds (for remote server)
commandTimeout: 20000, // 20 seconds (for slow network)
// Keepalive for long-running connections
keepAlive: 30000,
// Reconnect on error
autoResubscribe: true,
autoResendUnfulfilledCommands: true
};
// Add password if provided (for authenticated Redis)
if (redisPassword) {
redisOptions.password = redisPassword;
logger.info('[TAT Worker] Using Redis with password authentication');
}
connection = new IORedis(redisUrl, redisOptions);
// Handle connection errors
connection.on('error', (err) => {
logger.error('[TAT Worker] Redis connection error:', {
message: err.message,
code: (err as any).code,
errno: (err as any).errno,
syscall: (err as any).syscall,
address: (err as any).address,
port: (err as any).port
});
});
connection.on('close', () => {
logger.warn('[TAT Worker] Redis connection closed');
});
connection.on('reconnecting', (delay: number) => {
logger.info(`[TAT Worker] Redis reconnecting in ${delay}ms`);
});
// Try to connect and create worker
connection.connect().then(async () => {
logger.info(`[TAT Worker] Connected to Redis at ${redisUrl}`);
// Verify connection by pinging and check Redis version
try {
const pingResult = await connection!.ping();
logger.info(`[TAT Worker] Redis PING successful: ${pingResult}`);
// Check Redis version
const info = await connection!.info('server');
const versionMatch = info.match(/redis_version:(.+)/);
if (versionMatch) {
const version = versionMatch[1].trim();
logger.info(`[TAT Worker] Redis version: ${version}`);
// Parse version (e.g., "3.0.504" or "7.0.0")
const versionParts = version.split('.').map(Number);
const majorVersion = versionParts[0];
if (majorVersion < 5) {
logger.error(`[TAT Worker] ❌ CRITICAL: Redis version ${version} is incompatible!`);
logger.error(`[TAT Worker] BullMQ REQUIRES Redis 5.0.0 or higher. Current version: ${version}`);
logger.error(`[TAT Worker] ⚠️ TAT Worker cannot start with this Redis version.`);
logger.error(`[TAT Worker] 📖 Solution: Upgrade Redis (see docs/REDIS_SETUP_WINDOWS.md)`);
logger.error(`[TAT Worker] 💡 Recommended: Install Memurai or use Docker Redis 7.x`);
throw new Error(`Redis version ${version} is too old. BullMQ requires Redis 5.0.0+. Please upgrade Redis.`);
}
}
} catch (err: any) {
logger.error('[TAT Worker] Redis PING or version check failed:', err);
// If version check failed, don't create worker
if (err && err.message && err.message.includes('Redis version')) {
logger.warn('[TAT Worker] TAT notifications will be disabled until Redis is upgraded.');
connection = null;
tatWorker = null;
return;
}
}
// Create TAT Worker (only if version check passed)
if (connection) {
try {
// BullMQ will check Redis version internally - wrap in try-catch
tatWorker = new Worker('tatQueue', handleTatJob, {
connection: connection!,
concurrency: 5, // Process up to 5 jobs concurrently
connection: sharedRedisConnection,
concurrency: 5,
autorun: true,
limiter: {
max: 10, // Maximum 10 jobs
duration: 1000 // per second
max: 10,
duration: 1000
}
});
} catch (workerError: any) {
// Handle Redis version errors gracefully
if (workerError && (
(workerError.message && workerError.message.includes('Redis version')) ||
(workerError.message && workerError.message.includes('5.0.0'))
)) {
logger.error(`[TAT Worker] ❌ ${workerError.message || 'Redis version incompatible'}`);
logger.warn(`[TAT Worker] ⚠️ TAT notifications are DISABLED. Application will continue to work without TAT alerts.`);
logger.info(`[TAT Worker] 💡 To enable TAT notifications, upgrade Redis to version 5.0+ (see docs/REDIS_SETUP_WINDOWS.md)`);
// Clean up connection
try {
await connection!.quit();
} catch (quitError) {
// Ignore quit errors
}
connection = null;
tatWorker = null;
return;
}
// Re-throw other errors
logger.error('[TAT Worker] Unexpected error creating worker:', workerError);
throw workerError;
}
}
logger.info('[TAT Worker] Worker initialized');
// Event listeners (only if worker was created successfully)
if (tatWorker) {
tatWorker.on('ready', () => {
logger.info('[TAT Worker] Worker is ready and listening for jobs');
logger.info('[TAT Worker] Ready and listening');
});
tatWorker.on('active', (job) => {
logger.info(`[TAT Worker] Processing: ${job.name}`);
});
tatWorker.on('completed', (job) => {
logger.info(`[TAT Worker] ✅ Job ${job.id} (${job.name}) completed for request ${job.data.requestId}`);
logger.info(`[TAT Worker] Completed: ${job.name}`);
});
tatWorker.on('failed', (job, err) => {
if (job) {
logger.error(`[TAT Worker] ❌ Job ${job.id} (${job.name}) failed for request ${job.data.requestId}:`, err);
} else {
logger.error('[TAT Worker] ❌ Job failed:', err);
}
logger.error(`[TAT Worker] Failed: ${job?.name}`, err.message);
});
tatWorker.on('error', (err) => {
logger.error('[TAT Worker] Worker error:', {
message: err.message,
stack: err.stack,
name: err.name,
code: (err as any).code,
errno: (err as any).errno,
syscall: (err as any).syscall
logger.error('[TAT Worker] Error:', err.message);
});
});
tatWorker.on('stalled', (jobId) => {
logger.warn(`[TAT Worker] Job ${jobId} has stalled`);
});
logger.info('[TAT Worker] Worker initialized and listening for TAT jobs');
}
}).catch((err) => {
logger.warn('[TAT Worker] Could not connect to Redis. TAT worker will not start. TAT notifications are disabled.', err.message);
connection = null;
tatWorker = null;
});
} catch (error) {
logger.warn('[TAT Worker] Failed to initialize TAT worker. TAT notifications will be disabled.', error);
} catch (workerError: any) {
logger.error('[TAT Worker] Failed to create worker:', workerError);
tatWorker = null;
}
@ -191,9 +50,6 @@ process.on('SIGTERM', async () => {
logger.info('[TAT Worker] SIGTERM received, closing worker...');
await tatWorker.close();
}
if (connection) {
await connection.quit();
}
});
process.on('SIGINT', async () => {
@ -201,10 +57,6 @@ process.on('SIGINT', async () => {
logger.info('[TAT Worker] SIGINT received, closing worker...');
await tatWorker.close();
}
if (connection) {
await connection.quit();
}
});
export { tatWorker };

View File

@ -1,30 +1,237 @@
import { Router } from 'express';
import { authenticateToken } from '@middlewares/auth.middleware';
import {
checkTatSystemStatus,
checkWorkflowDetailsResponse
} from '@controllers/debug.controller';
import { Router, Request, Response } from 'express';
import { tatQueue } from '../queues/tatQueue';
import { TatAlert } from '@models/TatAlert';
import { ApprovalLevel } from '@models/ApprovalLevel';
import dayjs from 'dayjs';
import logger from '@utils/logger';
const router = Router();
// Debug routes (should be disabled in production)
if (process.env.NODE_ENV !== 'production') {
router.use(authenticateToken);
/**
* @route GET /api/debug/tat-status
* @desc Check TAT system configuration and status
* @access Private
/**
* Debug endpoint to check scheduled TAT jobs in the queue
*/
router.get('/tat-status', checkTatSystemStatus);
router.get('/tat-jobs/:requestId', async (req: Request, res: Response): Promise<void> => {
try {
const { requestId } = req.params;
/**
* @route GET /api/debug/workflow-details/:requestId
* @desc Check what's in workflow details response
* @access Private
if (!tatQueue) {
res.json({
error: 'TAT queue not available (Redis not connected)',
jobs: []
});
return;
}
// Get all jobs for this request
const waitingJobs = await tatQueue.getJobs(['waiting', 'delayed', 'active']);
const requestJobs = waitingJobs.filter(job => job.data.requestId === requestId);
const jobDetails = requestJobs.map(job => {
const delay = job.opts.delay || 0;
const scheduledTime = job.timestamp ? new Date(job.timestamp + delay) : null;
const now = new Date();
const timeUntilFire = scheduledTime ? Math.round((scheduledTime.getTime() - now.getTime()) / 1000 / 60) : null;
return {
jobId: job.id,
type: job.data.type,
threshold: job.data.threshold,
requestId: job.data.requestId,
levelId: job.data.levelId,
state: job.getState(),
delay: delay,
delayMinutes: Math.round(delay / 1000 / 60),
delayHours: (delay / 1000 / 60 / 60).toFixed(2),
timestamp: job.timestamp,
scheduledTime: scheduledTime?.toISOString(),
timeUntilFire: timeUntilFire ? `${timeUntilFire} minutes` : 'N/A',
processedOn: job.processedOn ? new Date(job.processedOn).toISOString() : null,
finishedOn: job.finishedOn ? new Date(job.finishedOn).toISOString() : null
};
});
// Get TAT alerts from database
const alerts = await TatAlert.findAll({
where: { requestId },
order: [['alertSentAt', 'ASC']]
});
const alertDetails = alerts.map((alert: any) => ({
alertType: alert.alertType,
thresholdPercentage: alert.thresholdPercentage,
alertSentAt: alert.alertSentAt,
levelStartTime: alert.levelStartTime,
timeSinceStart: alert.levelStartTime
? `${((new Date(alert.alertSentAt).getTime() - new Date(alert.levelStartTime).getTime()) / 1000 / 60 / 60).toFixed(2)} hours`
: 'N/A',
notificationSent: alert.notificationSent
}));
// Get approval level details
const levels = await ApprovalLevel.findAll({
where: { requestId }
});
const levelDetails = levels.map((level: any) => ({
levelId: level.levelId,
levelNumber: level.levelNumber,
status: level.status,
tatHours: level.tatHours,
levelStartTime: level.levelStartTime,
tat50AlertSent: level.tat50AlertSent,
tat75AlertSent: level.tat75AlertSent,
tatBreached: level.tatBreached,
tatPercentageUsed: level.tatPercentageUsed
}));
res.json({
requestId,
currentTime: new Date().toISOString(),
queuedJobs: jobDetails,
jobCount: jobDetails.length,
sentAlerts: alertDetails,
alertCount: alertDetails.length,
approvalLevels: levelDetails,
testMode: process.env.TAT_TEST_MODE === 'true'
});
} catch (error: any) {
logger.error('[Debug] Error checking TAT jobs:', error);
res.status(500).json({ error: error.message });
}
});
/**
* Debug endpoint to check all queued TAT jobs
*/
router.get('/workflow-details/:requestId', checkWorkflowDetailsResponse);
}
router.get('/tat-jobs', async (req: Request, res: Response): Promise<void> => {
try {
if (!tatQueue) {
res.json({
error: 'TAT queue not available (Redis not connected)',
jobs: []
});
return;
}
const waitingJobs = await tatQueue.getJobs(['waiting', 'delayed', 'active']);
const jobDetails = waitingJobs.map(job => {
const delay = job.opts.delay || 0;
const scheduledTime = job.timestamp ? new Date(job.timestamp + delay) : null;
const now = new Date();
const timeUntilFire = scheduledTime ? Math.round((scheduledTime.getTime() - now.getTime()) / 1000 / 60) : null;
return {
jobId: job.id,
type: job.data.type,
threshold: job.data.threshold,
requestId: job.data.requestId,
levelId: job.data.levelId,
state: job.getState(),
delay: delay,
delayMinutes: Math.round(delay / 1000 / 60),
delayHours: (delay / 1000 / 60 / 60).toFixed(2),
scheduledTime: scheduledTime?.toISOString(),
timeUntilFire: timeUntilFire ? `${timeUntilFire} minutes` : 'N/A'
};
});
res.json({
currentTime: new Date().toISOString(),
jobs: jobDetails,
totalJobs: jobDetails.length,
testMode: process.env.TAT_TEST_MODE === 'true'
});
} catch (error: any) {
logger.error('[Debug] Error checking all TAT jobs:', error);
res.status(500).json({ error: error.message });
}
});
/**
* Debug endpoint to check TAT time calculations
*/
router.post('/tat-calculate', async (req: Request, res: Response): Promise<void> => {
try {
const { startTime, tatHours, priority = 'STANDARD' } = req.body;
const { addWorkingHours, addWorkingHoursExpress, calculateDelay } = await import('@utils/tatTimeUtils');
const { getTatThresholds } = await import('../services/configReader.service');
const start = startTime ? new Date(startTime) : new Date();
const isExpress = priority === 'EXPRESS';
const thresholds = await getTatThresholds();
let threshold1Time: Date;
let threshold2Time: Date;
let breachTime: Date;
if (isExpress) {
const t1 = await addWorkingHoursExpress(start, tatHours * (thresholds.first / 100));
const t2 = await addWorkingHoursExpress(start, tatHours * (thresholds.second / 100));
const tBreach = await addWorkingHoursExpress(start, tatHours);
threshold1Time = t1.toDate();
threshold2Time = t2.toDate();
breachTime = tBreach.toDate();
} else {
const t1 = await addWorkingHours(start, tatHours * (thresholds.first / 100));
const t2 = await addWorkingHours(start, tatHours * (thresholds.second / 100));
const tBreach = await addWorkingHours(start, tatHours);
threshold1Time = t1.toDate();
threshold2Time = t2.toDate();
breachTime = tBreach.toDate();
}
const now = new Date();
const delays = {
threshold1: calculateDelay(threshold1Time),
threshold2: calculateDelay(threshold2Time),
breach: calculateDelay(breachTime)
};
res.json({
input: {
startTime: start.toISOString(),
tatHours,
priority,
thresholds
},
calculations: {
threshold1: {
percentage: thresholds.first,
targetTime: threshold1Time.toISOString(),
delay: delays.threshold1,
delayMinutes: Math.round(delays.threshold1 / 1000 / 60),
delayHours: (delays.threshold1 / 1000 / 60 / 60).toFixed(2),
isPast: delays.threshold1 === 0
},
threshold2: {
percentage: thresholds.second,
targetTime: threshold2Time.toISOString(),
delay: delays.threshold2,
delayMinutes: Math.round(delays.threshold2 / 1000 / 60),
delayHours: (delays.threshold2 / 1000 / 60 / 60).toFixed(2),
isPast: delays.threshold2 === 0
},
breach: {
percentage: 100,
targetTime: breachTime.toISOString(),
delay: delays.breach,
delayMinutes: Math.round(delays.breach / 1000 / 60),
delayHours: (delays.breach / 1000 / 60 / 60).toFixed(2),
isPast: delays.breach === 0
}
},
currentTime: now.toISOString(),
testMode: process.env.TAT_TEST_MODE === 'true'
});
} catch (error: any) {
logger.error('[Debug] Error calculating TAT times:', error);
res.status(500).json({ error: error.message });
}
});
export default router;

View File

@ -2,6 +2,7 @@ import app from './app';
import http from 'http';
import { initSocket } from './realtime/socket';
import './queues/tatWorker'; // Initialize TAT worker
import './queues/delayedJobPromoter'; // Initialize delayed job promoter (workaround for BullMQ + remote Redis)
import { logTatConfig } from './config/tat.config';
import { logSystemConfig } from './config/system.config';
import { initializeHolidaysCache } from './utils/tatTimeUtils';

View File

@ -91,38 +91,59 @@ export class TatSchedulerService {
}
];
// Check if test mode enabled (1 hour = 1 minute)
const isTestMode = process.env.TAT_TEST_MODE === 'true';
// Check if times collide (working hours calculation issue)
const uniqueTimes = new Set(jobs.map(j => j.targetTime.getTime()));
const hasCollision = uniqueTimes.size < jobs.length;
let jobIndex = 0;
for (const job of jobs) {
// Skip if the time has already passed
if (job.delay === 0) {
logger.warn(`[TAT Scheduler] Skipping ${job.type} (${job.threshold}%) for level ${levelId} - time already passed`);
if (job.delay < 0) {
logger.error(`[TAT Scheduler] Skipping ${job.type} - time in past`);
continue;
}
let spacedDelay: number;
if (isTestMode) {
// Test mode: times are already in minutes (tatTimeUtils converts hours to minutes)
// Just ensure they have minimum spacing for BullMQ reliability
spacedDelay = Math.max(job.delay, 5000) + (jobIndex * 5000);
} else if (hasCollision) {
// Production with collision: add 5-minute spacing
spacedDelay = job.delay + (jobIndex * 300000);
} else {
// Production without collision: use calculated delays
spacedDelay = job.delay;
}
const jobId = `tat-${job.type}-${requestId}-${levelId}`;
await tatQueue.add(
job.type,
{
type: job.type,
threshold: job.threshold, // Store actual threshold percentage in job data
threshold: job.threshold,
requestId,
levelId,
approverId
},
{
delay: job.delay,
jobId: `tat-${job.type}-${requestId}-${levelId}`, // Generic job ID
delay: spacedDelay,
jobId: jobId,
removeOnComplete: true,
removeOnFail: false
}
);
logger.info(
`[TAT Scheduler] Scheduled ${job.type} (${job.threshold}%) for level ${levelId} ` +
`(delay: ${Math.round(job.delay / 1000 / 60)} minutes, ` +
`target: ${dayjs(job.targetTime).format('YYYY-MM-DD HH:mm')})`
);
logger.info(`[TAT Scheduler] Scheduled ${job.type} (${job.threshold}%)`);
jobIndex++;
}
logger.info(`[TAT Scheduler] TAT jobs scheduled for request ${requestId}, approver ${approverId}`);
logger.info(`[TAT Scheduler] TAT jobs scheduled for request ${requestId}`);
} catch (error) {
logger.error(`[TAT Scheduler] Failed to schedule TAT jobs:`, error);
throw error;