# RE Workflow Management System - Detailed Sprint Implementation Guide
## Comprehensive Feature Breakdown for Development Team
**Version:** 1.0
**Date:** October 23, 2025
**Project:** RE Workflow Management System (Non-Templatized)
**Focus:** Core Application Features (Workflow Creation, Approvals, Work Notes, Documents)
---
## 📖 Table of Contents
1. [Sprint 0: Foundation Setup](#sprint-0-foundation-setup)
2. [Sprint 1: SSO Authentication](#sprint-1-sso-authentication)
3. [Sprint 2: Workflow Creation Wizard](#sprint-2-workflow-creation-wizard)
4. [Sprint 3: Approval Actions & TAT Tracking](#sprint-3-approval-actions--tat-tracking)
5. [Sprint 4: Documents & Work Notes](#sprint-4-documents--work-notes)
6. [Sprint 5: Dashboard & Analytics](#sprint-5-dashboard--analytics)
7. [Sprint 6: Testing & Deployment](#sprint-6-testing--deployment)
---
## Sprint 0: Foundation Setup
### Backend Foundation (3 days)
**BE-001 to BE-004:** Database, Express, Sequelize setup
- See main task document for technical setup details
- Focus: Get server running with database connection
### Frontend Foundation (4 days)
**Current Status:**
- ✅ UI Components: 80% extracted from Figma
- ❌ Redux Store: Not created (2-3 days needed)
- ❌ API Service Layer: Not configured (1 day needed)
- ❌ Route Guards: Not implemented (1 day needed)
**Critical Work This Week:**
1. Complete UI extraction (20% remaining)
2. **Setup Redux store - CANNOT SKIP**
3. **Configure Axios API layer - CANNOT SKIP**
4. Implement protected routes
---
## Sprint 1: SSO Authentication (Week 2-3)
### 🔐 SSO Integration Overview
**Important:** No login/signup/password reset screens needed!
#### Backend SSO Integration (BE-101: 1.5 days)
**SSO Flow Implementation:**
**Step 1: Configure SSO Bridge**
```typescript
// src/config/sso.ts
export const ssoConfig = {
authorizationURL: process.env.SSO_AUTHORIZATION_URL, // RE SSO Bridge URL
tokenURL: process.env.SSO_TOKEN_URL,
userInfoURL: process.env.SSO_USERINFO_URL,
clientID: process.env.SSO_CLIENT_ID,
clientSecret: process.env.SSO_CLIENT_SECRET,
callbackURL: process.env.SSO_CALLBACK_URL, // https://workflow.re.com/api/v1/auth/callback
scope: ['openid', 'profile', 'email', 'employee_info']
};
```
**Step 2: Implement SSO Auth Service**
```typescript
// src/services/auth.service.ts
import passport from 'passport';
import { Strategy as OAuth2Strategy } from 'passport-oauth2';
// Configure Passport OAuth2 strategy
passport.use('re-sso', new OAuth2Strategy({
authorizationURL: ssoConfig.authorizationURL,
tokenURL: ssoConfig.tokenURL,
clientID: ssoConfig.clientID,
clientSecret: ssoConfig.clientSecret,
callbackURL: ssoConfig.callbackURL
}, async (accessToken, refreshToken, profile, done) => {
// Get user info from SSO
const userInfo = await fetchUserInfoFromSSO(accessToken);
// Sync user to local database
const user = await syncUserFromAD(userInfo);
return done(null, user);
}));
async function syncUserFromAD(ssoUserInfo) {
// Check if user exists in local database
let user = await User.findOne({ where: { employee_id: ssoUserInfo.employeeId } });
if (!user) {
// Create new user from AD info
user = await User.create({
employee_id: ssoUserInfo.employeeId,
email: ssoUserInfo.email,
first_name: ssoUserInfo.firstName,
last_name: ssoUserInfo.lastName,
department: ssoUserInfo.department,
designation: ssoUserInfo.designation,
phone: ssoUserInfo.phone,
profile_picture_url: ssoUserInfo.profilePicture
});
} else {
// Update existing user with latest AD info
await user.update({
email: ssoUserInfo.email,
first_name: ssoUserInfo.firstName,
last_name: ssoUserInfo.lastName,
department: ssoUserInfo.department,
designation: ssoUserInfo.designation,
last_login: new Date()
});
}
return user;
}
```
**Step 3: Implement Auth Controller**
```typescript
// src/controllers/auth.controller.ts
export const authController = {
// Initiate SSO login
login: (req, res) => {
// Redirect to SSO Bridge
passport.authenticate('re-sso')(req, res);
},
// Handle SSO callback
callback: async (req, res) => {
passport.authenticate('re-sso', async (err, user) => {
if (err || !user) {
return res.redirect('/auth/error?message=authentication_failed');
}
// Generate JWT token for app session
const jwtToken = generateJWT({
userId: user.user_id,
email: user.email,
employeeId: user.employee_id
});
// Redirect to frontend with token
res.redirect(`${process.env.FRONTEND_URL}/auth/callback?token=${jwtToken}`);
})(req, res);
},
// Get current user
me: async (req, res) => {
const user = await User.findByPk(req.user.userId);
res.json({ success: true, data: user });
},
// Logout
logout: (req, res) => {
// Clear session
req.logout();
// Redirect to SSO logout
const ssoLogoutUrl = `${ssoConfig.logoutURL}?redirect_uri=${process.env.FRONTEND_URL}`;
res.json({ success: true, redirectUrl: ssoLogoutUrl });
}
};
```
#### Frontend SSO Integration (FE-101, FE-102: 1.5 days)
**Frontend Work (Minimal):**
**1. SSO Callback Handler (FE-101: 1 day)**
```typescript
// src/pages/Auth/SSOCallback.tsx
const SSOCallback = () => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const [error, setError] = useState(null);
useEffect(() => {
// Get token from URL params
const params = new URLSearchParams(window.location.search);
const token = params.get('token');
const errorMsg = params.get('error');
if (errorMsg) {
setError('Authentication failed. Please try again.');
setTimeout(() => navigate('/'), 3000);
return;
}
if (token) {
// Store JWT token
localStorage.setItem('accessToken', token);
// Update Redux state
dispatch(setAuthenticated(true));
// Fetch user details
dispatch(fetchCurrentUser());
// Redirect to dashboard
navigate('/dashboard');
}
}, []);
if (error) {
return ;
}
return (
Authenticating... Please wait.
);
};
```
**2. Protected Routes (FE-101: Included in above)**
```typescript
// src/routes/PrivateRoute.tsx
const PrivateRoute = ({ children }) => {
const { isAuthenticated } = useAppSelector(state => state.auth);
if (!isAuthenticated) {
// Redirect to SSO login
window.location.href = `${API_BASE_URL}/auth/login`;
return ;
}
return <>{children}>;
};
```
**3. Logout Button (FE-102: 0.5 days)**
```typescript
// In Header component
const handleLogout = async () => {
try {
// Call logout API
const response = await authService.logout();
// Clear local state
dispatch(clearAuth());
localStorage.removeItem('accessToken');
// Redirect to SSO logout
window.location.href = response.data.redirectUrl;
} catch (error) {
console.error('Logout failed:', error);
}
};
// Logout button in header
```
**4. User Profile Display (FE-103: 1 day)**
```typescript
// Fetch user on app load
useEffect(() => {
if (isAuthenticated) {
dispatch(fetchCurrentUser()); // GET /api/v1/auth/me
}
}, [isAuthenticated]);
// Display in header dropdown
{user.display_name}{user.email}{user.department}ID: {user.employee_id}
```
**Sprint 1 Summary:**
- ✅ SSO handles authentication (no login screens needed)
- ✅ Frontend just handles callback and redirects
- ✅ Users auto-synced from Active Directory
- ✅ Simple logout button
- **Total Frontend Work:** 1.5-2 days (not 4-5 days!)
---
## Sprint 2: Workflow Creation Wizard (Weeks 3-4)
### 🎯 Focus: Enable users to create workflow requests
This is the **CORE FEATURE** of the application!
---
### Step 1: Template Selection (FE-202: 0.5 days)
**UI Status:** ✅ Already from Figma (80%)
**What to Build:**
```typescript
// src/components/workflow/TemplateSelector.tsx
const TemplateSelector = () => {
const [selectedTemplate, setSelectedTemplate] = useState('CUSTOM');
const dispatch = useAppDispatch();
const handleSelect = (type) => {
setSelectedTemplate(type);
// Save to Redux
dispatch(setWorkflowTemplateType(type));
};
return (
handleSelect('CUSTOM')}
>
📋Custom Request (Non-Templatized)
Create a flexible workflow tailored to your specific needs.
Define approval levels, TAT, and participants dynamically.
Available📄Template Request
Use predefined workflow templates for common processes.
Coming Soon
);
};
```
**Deliverable:** User can select "Custom Request"
---
### Step 2: Basic Information (FE-203: 1 day)
**UI Status:** ✅ Already from Figma
**What to Build:**
**1. Request Title Field:**
```typescript
const [title, setTitle] = useState('');
const [titleError, setTitleError] = useState('');
const validateTitle = (value) => {
if (!value || value.trim() === '') {
setTitleError('Request title is required');
return false;
}
if (value.length > 500) {
setTitleError('Title must be less than 500 characters');
return false;
}
setTitleError('');
return true;
};
{
setTitle(e.target.value);
validateTitle(e.target.value);
}}
error={!!titleError}
helperText={titleError || `${title.length}/500 characters`}
placeholder="E.g., Approval for new office location"
required
fullWidth
/>
```
**2. Rich Text Description:**
```typescript
// Use React Quill or similar
import ReactQuill from 'react-quill';
const [description, setDescription] = useState('');
const quillModules = {
toolbar: [
['bold', 'italic', 'underline'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link'],
[{ 'table': 'insert-table' }],
]
};
{description.length} / 5000
```
**3. Priority Selection:**
```typescript
const [priority, setPriority] = useState('STANDARD');
setPriority(e.target.value)}>
}
label={
Standard
TAT calculated using working days (Mon-Fri, excluding weekends)
}
/>
}
label={
Express (Urgent)
TAT calculated using calendar days (includes weekends)
}
/>
```
**4. Save to Redux:**
```typescript
const handleNext = () => {
if (!validateTitle(title) || !description) {
toast.error('Please fill all required fields');
return;
}
// Save to Redux
dispatch(updateWorkflowBasicInfo({
title,
description,
priority
}));
// Go to next step
setActiveStep(2); // Approval Workflow step
};
```
**Deliverable:** Basic information captured and validated
---
### Step 3: Approval Workflow Builder (FE-204: 2-3 days)
**UI Status:** ✅ Already from Figma
**Complexity:** ⭐⭐⭐⭐⭐ (MOST COMPLEX SCREEN)
**What to Build:**
**1. Dynamic Approval Levels (Core Logic)**
```typescript
// State management for approval levels
const [approvalLevels, setApprovalLevels] = useState([
{
levelNumber: 1,
levelName: '',
approverId: null,
approverDetails: null,
tatValue: '',
tatUnit: 'hours' // or 'days'
}
]);
// Add new level
const handleAddLevel = () => {
if (approvalLevels.length >= 10) {
toast.error('Maximum 10 approval levels allowed');
return;
}
setApprovalLevels([
...approvalLevels,
{
levelNumber: approvalLevels.length + 1,
levelName: '',
approverId: null,
approverDetails: null,
tatValue: '',
tatUnit: 'hours'
}
]);
};
// Remove level
const handleRemoveLevel = (levelNumber) => {
if (approvalLevels.length === 1) {
toast.error('At least one approval level is required');
return;
}
setApprovalLevels(
approvalLevels
.filter(level => level.levelNumber !== levelNumber)
.map((level, index) => ({ ...level, levelNumber: index + 1 })) // Renumber
);
};
```
**2. User Search for Approver (@mention)**
```typescript
// User search component
const ApproverSearch = ({ levelIndex, onSelectUser }) => {
const [searchQuery, setSearchQuery] = useState('');
const [searchResults, setSearchResults] = useState([]);
const [loading, setLoading] = useState(false);
// Debounced search
useEffect(() => {
const timer = setTimeout(async () => {
if (searchQuery.length >= 2) {
setLoading(true);
try {
// Call user search API
const response = await userService.searchUsers(searchQuery);
setSearchResults(response.data);
} catch (error) {
console.error('User search failed:', error);
} finally {
setLoading(false);
}
} else {
setSearchResults([]);
}
}, 300);
return () => clearTimeout(timer);
}, [searchQuery]);
return (