ui modification based on bug reported
This commit is contained in:
parent
826c0eedea
commit
8c7965e469
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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! 🎉
|
||||
|
||||
129
FIXES_APPLIED.md
129
FIXES_APPLIED.md
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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!** ✅
|
||||
|
||||
@ -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! 🎉
|
||||
|
||||
@ -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
|
||||
|
||||
310
SETUP_SUMMARY.md
310
SETUP_SUMMARY.md
@ -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
|
||||
|
||||
@ -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! 🚀
|
||||
|
||||
@ -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
|
||||
|
||||
209
START_HERE.md
209
START_HERE.md
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'));
|
||||
}
|
||||
|
||||
33
src/queues/delayedJobPromoter.ts
Normal file
33
src/queues/delayedJobPromoter.ts
Normal 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');
|
||||
57
src/queues/redisConnection.ts
Normal file
57
src/queues/redisConnection.ts
Normal 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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 };
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user