diff --git a/src/components/layout/PageLayout/PageLayout.tsx b/src/components/layout/PageLayout/PageLayout.tsx index b1ef5b9..994481d 100644 --- a/src/components/layout/PageLayout/PageLayout.tsx +++ b/src/components/layout/PageLayout/PageLayout.tsx @@ -19,6 +19,7 @@ import { ReLogo } from '@/assets'; import notificationApi, { Notification } from '@/services/notificationApi'; import { getSocket, joinUserRoom } from '@/utils/socket'; import { formatDistanceToNow } from 'date-fns'; +import { TokenManager } from '@/utils/tokenManager'; interface PageLayoutProps { children: React.ReactNode; @@ -36,6 +37,17 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on const [notificationsOpen, setNotificationsOpen] = useState(false); const { user } = useAuth(); + // Check if user is a Dealer + const isDealer = useMemo(() => { + try { + const userData = TokenManager.getUserData(); + return userData?.jobTitle === 'Dealer'; + } catch (error) { + console.error('[PageLayout] Error checking dealer status:', error); + return false; + } + }, []); + // Get user initials for avatar const getUserInitials = () => { try { @@ -63,16 +75,19 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on { id: 'requests', label: 'All Requests', icon: List }, ]; - // Add remaining menu items + // Add remaining menu items (exclude "My Requests" for dealers) + if (!isDealer) { + items.push({ id: 'my-requests', label: 'My Requests', icon: User }); + } + items.push( - { id: 'my-requests', label: 'My Requests', icon: User }, { id: 'open-requests', label: 'Open Requests', icon: FileText }, { id: 'closed-requests', label: 'Closed Requests', icon: CheckCircle }, { id: 'shared-summaries', label: 'Shared Summary', icon: Share2 } ); return items; - }, []); + }, [isDealer]); const toggleSidebar = () => { setSidebarOpen(!sidebarOpen); @@ -256,16 +271,18 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on {/* Quick Action in Sidebar - Right below menu items */} -
- -
+ {!isDealer && ( +
+ +
+ )} @@ -294,14 +311,16 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on
- + {!isDealer && ( + + )} diff --git a/src/components/workflow/CreateRequest/TemplateSelectionStep.tsx b/src/components/workflow/CreateRequest/TemplateSelectionStep.tsx index ad611d2..e1d86bc 100644 --- a/src/components/workflow/CreateRequest/TemplateSelectionStep.tsx +++ b/src/components/workflow/CreateRequest/TemplateSelectionStep.tsx @@ -1,9 +1,8 @@ -import { motion, AnimatePresence } from 'framer-motion'; +import { motion } from 'framer-motion'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; -import { Label } from '@/components/ui/label'; import { Separator } from '@/components/ui/separator'; -import { Check, Clock, Users, Info, Flame, Target, TrendingUp } from 'lucide-react'; +import { Check, Clock, Users, Flame, Target, TrendingUp } from 'lucide-react'; import { RequestTemplate } from '@/hooks/useCreateRequestForm'; interface TemplateSelectionStepProps { @@ -142,63 +141,6 @@ export function TemplateSelectionStep({ ))}
- - {/* Template Details Card */} - - {selectedTemplate && ( - - - - - - {selectedTemplate.name} - Template Details - - - -
-
- -

{selectedTemplate.suggestedSLA} days

-
-
- -
- {getPriorityIcon(selectedTemplate.priority)} - {selectedTemplate.priority} -
-
-
- -

{selectedTemplate.estimatedTime}

-
-
-
- -
- {selectedTemplate.commonApprovers.map((approver, index) => ( - - {approver} - - ))} -
-
-
-
-
- )} -
); } diff --git a/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx b/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx index 059277c..7fc5543 100644 --- a/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx +++ b/src/dealer-claim/components/request-creation/ClaimManagementWizard.tsx @@ -251,7 +251,7 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar if (!verifiedDealer.isLoggedIn) { toast.error( - `Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" (${verifiedDealer.dealerCode}) has not logged in to the system. Please ask them to log in first.`, + `Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" (${verifiedDealer.dealerCode}) is not mapped to the system.`, { duration: 5000 } ); // Clear the selection @@ -277,9 +277,9 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar setDealerSearchInput(verifiedDealer.dealerName || verifiedDealer.displayName); setDealerSearchResults([]); - toast.success(`Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" verified and logged in`); + toast.success(`Dealer "${verifiedDealer.dealerName || verifiedDealer.displayName}" verified and mapped to the System`); } catch (error: any) { - const errorMessage = error.message || 'Failed to verify dealer login'; + const errorMessage = 'Dealer is not mapped to the system' toast.error(errorMessage, { duration: 5000 }); // Clear the selection setDealerSearchInput(''); @@ -537,18 +537,6 @@ export function ClaimManagementWizard({ onBack, onSubmit }: ClaimManagementWizar )} -
- Status: -
- - Logged in -
- -
- - Not logged in -
-
{formData.dealerCode && (

diff --git a/src/dealer-claim/components/request-detail/WorkflowTab.tsx b/src/dealer-claim/components/request-detail/WorkflowTab.tsx index c51bf3a..63c407a 100644 --- a/src/dealer-claim/components/request-detail/WorkflowTab.tsx +++ b/src/dealer-claim/components/request-detail/WorkflowTab.tsx @@ -395,10 +395,18 @@ export function DealerClaimWorkflowTab({ return `Step ${stepNumber} approval required.`; }; + // Get backend currentLevel to determine which steps are active vs waiting + // This needs to be calculated before mapping steps so we can use it in status normalization + // Convert to number to ensure proper comparison + const backendCurrentLevel = request?.currentLevel || request?.current_level || request?.currentStep; + const currentLevelNumber = backendCurrentLevel !== undefined && backendCurrentLevel !== null + ? Number(backendCurrentLevel) + : null; + // Transform approval flow to dealer claim workflow steps const workflowSteps: WorkflowStep[] = approvalFlow.map((step: any, index: number) => { - // Get actual step number from levelNumber or step field - const actualStepNumber = step.levelNumber || step.level_number || step.step || index + 1; + // Get actual step number from levelNumber or step field - ensure it's a number + const actualStepNumber = Number(step.levelNumber || step.level_number || step.step || index + 1); // Get levelName from the approval level if available const levelName = step.levelName || step.level_name; @@ -467,26 +475,75 @@ export function DealerClaimWorkflowTab({ } } - // Normalize status - handle "in-review" and other variations - // Check both step.status and approval.status (approval status is more accurate after approval) - const stepStatus = step.status || approval?.status || 'waiting'; - let normalizedStatus = stepStatus.toLowerCase(); + // Normalize status - ALWAYS check step position first, then use approval/step status + // This ensures future steps are always 'waiting' regardless of approval/step status + let normalizedStatus: string; - // Handle status variations - if (normalizedStatus === 'in-review' || normalizedStatus === 'in_review' || normalizedStatus === 'in review') { - normalizedStatus = 'in_progress'; - } - - // If approval exists and has a status, prefer that (it's more up-to-date) - if (approval?.status) { - const approvalStatus = approval.status.toLowerCase(); - // Map backend status values to frontend status values - if (approvalStatus === 'approved') { - normalizedStatus = 'approved'; - } else if (approvalStatus === 'rejected') { - normalizedStatus = 'rejected'; - } else if (approvalStatus === 'pending' || approvalStatus === 'in_progress' || approvalStatus === 'in-progress') { - normalizedStatus = 'in_progress'; + // First, check step position relative to currentLevel (this is the source of truth) + // Use currentLevelNumber which is guaranteed to be a number or null + if (currentLevelNumber !== null && currentLevelNumber > 0) { + if (actualStepNumber > currentLevelNumber) { + // Future step - MUST be waiting (ignore approval status and step.status) + normalizedStatus = 'waiting'; + } else if (actualStepNumber < currentLevelNumber) { + // Past step - should be approved/rejected + // Use approval status if available, otherwise default to waiting + if (approval?.status) { + const approvalStatus = approval.status.toLowerCase(); + if (approvalStatus === 'approved') { + normalizedStatus = 'approved'; + } else if (approvalStatus === 'rejected') { + normalizedStatus = 'rejected'; + } else { + normalizedStatus = 'waiting'; // Past step with unexpected status + } + } else { + normalizedStatus = 'waiting'; // Past step with no approval + } + } else { + // Current step - use approval status if available, otherwise use step.status + if (approval?.status) { + const approvalStatus = approval.status.toLowerCase(); + if (approvalStatus === 'approved') { + normalizedStatus = 'approved'; + } else if (approvalStatus === 'rejected') { + normalizedStatus = 'rejected'; + } else if (approvalStatus === 'pending' || approvalStatus === 'in_progress' || approvalStatus === 'in-progress') { + normalizedStatus = 'in_progress'; + } else { + normalizedStatus = 'in_progress'; // Default for current step + } + } else { + // No approval object - normalize step.status + const stepStatus = (step.status || 'pending').toLowerCase(); + if (stepStatus === 'in-review' || stepStatus === 'in_review' || stepStatus === 'in review' || stepStatus === 'pending') { + normalizedStatus = 'in_progress'; + } else { + normalizedStatus = stepStatus; + } + } + } + } else { + // No backend currentLevel - use approval status if available, otherwise step.status + if (approval?.status) { + const approvalStatus = approval.status.toLowerCase(); + if (approvalStatus === 'approved') { + normalizedStatus = 'approved'; + } else if (approvalStatus === 'rejected') { + normalizedStatus = 'rejected'; + } else if (approvalStatus === 'pending' || approvalStatus === 'in_progress' || approvalStatus === 'in-progress') { + normalizedStatus = 'in_progress'; + } else { + normalizedStatus = (step.status || 'waiting').toLowerCase(); + } + } else { + // No approval and no currentLevel - normalize step.status + const stepStatus = (step.status || 'waiting').toLowerCase(); + if (stepStatus === 'in-review' || stepStatus === 'in_review' || stepStatus === 'in review' || stepStatus === 'pending') { + normalizedStatus = 'in_progress'; + } else { + normalizedStatus = stepStatus; + } } } @@ -522,10 +579,12 @@ export function DealerClaimWorkflowTab({ // IMPORTANT: Use the workflow's currentLevel from backend (most accurate) // Fallback to finding first pending step if currentLevel not available // Note: Status normalization already handled in workflowSteps mapping above - const backendCurrentLevel = request?.currentLevel || request?.current_level || request?.currentStep; + // backendCurrentLevel is already calculated above before the map function // Find the step that matches backend's currentLevel - const activeStepFromBackend = workflowSteps.find(s => s.step === backendCurrentLevel); + const activeStepFromBackend = currentLevelNumber !== null + ? workflowSteps.find(s => s.step === currentLevelNumber) + : null; // If backend currentLevel exists and step is pending/in_progress, use it // Otherwise, find first pending/in_progress step @@ -537,7 +596,7 @@ export function DealerClaimWorkflowTab({ return status === 'pending' || status === 'in_progress' || status === 'in-review' || status === 'in_review'; }); - const currentStep = activeStep ? activeStep.step : (backendCurrentLevel || request?.currentStep || 1); + const currentStep = activeStep ? activeStep.step : (currentLevelNumber || request?.currentStep || 1); // Check if current user is the dealer (for steps 1 and 5) const userEmail = (user as any)?.email?.toLowerCase() || ''; diff --git a/src/pages/Auth/Auth.tsx b/src/pages/Auth/Auth.tsx index 5028b81..08f1498 100644 --- a/src/pages/Auth/Auth.tsx +++ b/src/pages/Auth/Auth.tsx @@ -89,7 +89,7 @@ export function Auth() { ) : ( <> - Login with OKTA + RE Employee Login )} @@ -119,7 +119,7 @@ export function Auth() { ) : ( <> - Login with Tanflow + Dealer Login )} diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx index 3427884..ac2b26f 100644 --- a/src/pages/Dashboard/Dashboard.tsx +++ b/src/pages/Dashboard/Dashboard.tsx @@ -4,6 +4,7 @@ import { type DateRange } from '@/services/dashboard.service'; import { useAuth, hasManagementAccess } from '@/contexts/AuthContext'; import { useAppSelector, useAppDispatch } from '@/redux/hooks'; import { setViewAsUser } from './redux/dashboardSlice'; +import { TokenManager } from '@/utils/tokenManager'; // Custom Hooks import { useDashboardFilters } from './hooks/useDashboardFilters'; @@ -161,8 +162,19 @@ export function Dashboard({ onNavigate, onNewRequest }: DashboardProps) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [dateRange, customStartDate, customEndDate, viewAsUser]); + // Check if user is a Dealer + const isDealer = useMemo(() => { + try { + const userData = TokenManager.getUserData(); + return userData?.jobTitle === 'Dealer'; + } catch (error) { + console.error('[Dashboard] Error checking dealer status:', error); + return false; + } + }, []); + // Quick actions - const quickActions = useMemo(() => getQuickActions(isAdmin, onNewRequest, onNavigate), [isAdmin, onNewRequest, onNavigate]); + const quickActions = useMemo(() => getQuickActions(isAdmin, onNewRequest, onNavigate, isDealer), [isAdmin, onNewRequest, onNavigate, isDealer]); // KPI click handler const handleKPIClick = useCallback((filters: { diff --git a/src/pages/Dashboard/utils/dashboardNavigation.ts b/src/pages/Dashboard/utils/dashboardNavigation.ts index af10c2a..74c35cd 100644 --- a/src/pages/Dashboard/utils/dashboardNavigation.ts +++ b/src/pages/Dashboard/utils/dashboardNavigation.ts @@ -50,16 +50,23 @@ export interface QuickAction { export function getQuickActions( isAdmin: boolean, onNewRequest?: () => void, - onNavigate?: (page: string) => void + onNavigate?: (page: string) => void, + isDealer?: boolean ): QuickAction[] { - const actions: QuickAction[] = [ - { label: 'New Request', icon: FileText, action: () => onNewRequest?.(), color: 'bg-emerald-600 hover:bg-emerald-700' }, + const actions: QuickAction[] = []; + + // Only add "New Request" action if user is not a dealer + if (!isDealer) { + actions.push({ label: 'New Request', icon: FileText, action: () => onNewRequest?.(), color: 'bg-emerald-600 hover:bg-emerald-700' }); + } + + actions.push( { label: 'View Pending', icon: Clock, action: () => onNavigate?.('open-requests'), color: 'bg-blue-600 hover:bg-blue-700' }, { label: 'Settings', icon: Settings, action: () => onNavigate?.('settings'), color: 'bg-slate-600 hover:bg-slate-700' } - ]; + ); if (isAdmin) { - actions.splice(2, 0, { label: 'Reports', icon: Activity, action: () => onNavigate?.('detailed-reports'), color: 'bg-purple-600 hover:bg-purple-700' }); + actions.splice(actions.length - 1, 0, { label: 'Reports', icon: Activity, action: () => onNavigate?.('detailed-reports'), color: 'bg-purple-600 hover:bg-purple-700' }); } return actions;