From ec70f1d3f11d7deeabb0c68233b3a0c40142fbcd Mon Sep 17 00:00:00 2001 From: Laxman Date: Wed, 13 May 2026 20:45:33 +0530 Subject: [PATCH] system log table added and feew bugs coverd from the tracker --- package-lock.json | 1 - src/App.tsx | 6 + src/api/API.ts | 15 + src/components/admin/ApprovalPoliciesPage.tsx | 2 +- src/components/admin/QuestionnaireBuilder.tsx | 8 +- src/components/layout/Sidebar.tsx | 4 +- src/components/ui/scroll-area.tsx | 8 +- src/components/ui/table.tsx | 2 +- src/features/admin/pages/SystemLogsPage.tsx | 612 ++++++++++++++++++ .../pages/ConstitutionalChangeDetails.tsx | 2 +- .../dashboard/pages/FDDDashboardPage.tsx | 2 +- .../components/InterviewConfigManagement.tsx | 10 +- .../ApplicationDetailsSidebar.tsx | 66 ++ .../ApplicationDetailsTabs.tsx | 6 +- .../hooks/useApplicationDetailsUIState.ts | 2 + .../onboarding/pages/ApplicationDetails.tsx | 16 + .../pages/RelocationRequestDetails.tsx | 2 +- .../resignation/pages/ResignationDetails.tsx | 365 +++++------ .../termination/pages/TerminationDetails.tsx | 288 ++++----- src/styles/globals.css | 16 +- 20 files changed, 1065 insertions(+), 368 deletions(-) create mode 100644 src/features/admin/pages/SystemLogsPage.tsx diff --git a/package-lock.json b/package-lock.json index 22e574c..120870d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12222,7 +12222,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, diff --git a/src/App.tsx b/src/App.tsx index 87182f8..0385b18 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -47,6 +47,7 @@ import { DealerRelocationPage } from '@/features/relocation/pages/DealerRelocati import QuestionnaireBuilder from '@/components/admin/QuestionnaireBuilder'; import QuestionnaireList from '@/components/admin/QuestionnaireList'; import InterviewConfigManagement from '@/features/master/components/InterviewConfigManagement'; +import { SystemLogsPage } from '@/features/admin/pages/SystemLogsPage'; import { WorkNotesPage } from '@/features/onboarding/pages/WorkNotesPage'; import { NotificationsPage } from '@/pages/NotificationsPage'; import { Toaster } from '@/components/ui/sonner'; @@ -275,6 +276,11 @@ export default function App() { ? : } /> + + : + } /> {/* HR/Finance Modules (Simplified for brevity, following pattern) */} client.get(`/fdd/${applicationId}`), assignFddAgency: (data: any) => client.post('/fdd/assign', data), flagNonResponsive: (data: any) => client.post('/flag', data), + + // System Audit Logs (segregated `system_audit_logs` table — Super Admin only) + getSystemAuditLogs: (params?: { + module?: string; + entityType?: string; + entityId?: string; + action?: string; + userId?: string; + search?: string; + dateFrom?: string; + dateTo?: string; + page?: number; + limit?: number; + }) => client.get('/audit/system-logs', params), + getSystemAuditSummary: () => client.get('/audit/system-summary'), }; export default API; diff --git a/src/components/admin/ApprovalPoliciesPage.tsx b/src/components/admin/ApprovalPoliciesPage.tsx index 3424aa6..493b03f 100644 --- a/src/components/admin/ApprovalPoliciesPage.tsx +++ b/src/components/admin/ApprovalPoliciesPage.tsx @@ -197,7 +197,7 @@ export function ApprovalPoliciesPage() { Configured Stages -
+
diff --git a/src/components/admin/QuestionnaireBuilder.tsx b/src/components/admin/QuestionnaireBuilder.tsx index 714b49b..acbe918 100644 --- a/src/components/admin/QuestionnaireBuilder.tsx +++ b/src/components/admin/QuestionnaireBuilder.tsx @@ -294,14 +294,14 @@ const QuestionnaireBuilder: React.FC = () => { onChange={(e) => updateQuestion(index, 'inputType', e.target.value as any)} className="w-full border border-slate-300 p-2.5 rounded-lg focus:ring-2 focus:ring-red-500 outline-none bg-white" > - + - + - - + + diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 2b69eaf..65ca18a 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -14,7 +14,8 @@ import { RefreshCcw, MapPin, ClipboardList, - ListChecks + ListChecks, + Activity } from 'lucide-react'; import { useState, useRef, useCallback, useEffect } from 'react'; import ReactDOM from 'react-dom'; @@ -115,6 +116,7 @@ export function Sidebar({ onLogout }: SidebarProps) { menuItems.push({ id: 'users', label: 'User Management', icon: Users }); menuItems.push({ id: 'questionnaires', label: 'Questionnaire Templates', icon: ClipboardList }); menuItems.push({ id: 'interview-configs', label: 'Interview Configs', icon: ListChecks }); + menuItems.push({ id: 'system-logs', label: 'System Logs', icon: Activity }); } const handleSearch = (e: React.FormEvent) => { diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx index f791725..796b651 100644 --- a/src/components/ui/scroll-area.tsx +++ b/src/components/ui/scroll-area.tsx @@ -42,14 +42,18 @@ function ScrollBar({ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent", orientation === "horizontal" && - "h-2.5 flex-col border-t border-t-transparent", + "h-1 flex-col border-t border-t-transparent", className, )} {...props} > ); diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx index 59e4dcc..06aea62 100644 --- a/src/components/ui/table.tsx +++ b/src/components/ui/table.tsx @@ -8,7 +8,7 @@ function Table({ className, ...props }: React.ComponentProps<"table">) { return (
= { + CREATED: 'bg-emerald-100 text-emerald-700 border-emerald-200', + UPDATED: 'bg-blue-100 text-blue-700 border-blue-200', + DELETED: 'bg-rose-100 text-rose-700 border-rose-200', + ACTIVATED: 'bg-emerald-100 text-emerald-700 border-emerald-200', + DEACTIVATED: 'bg-slate-200 text-slate-700 border-slate-300', + INITIALIZED: 'bg-amber-100 text-amber-700 border-amber-200', + SUBMITTED: 'bg-indigo-100 text-indigo-700 border-indigo-200', + ASSIGNED: 'bg-violet-100 text-violet-700 border-violet-200', + UNASSIGNED: 'bg-slate-200 text-slate-700 border-slate-300', + REORDERED: 'bg-sky-100 text-sky-700 border-sky-200' +}; + +const formatTimestamp = (ts: string) => { + try { + const d = new Date(ts); + return d.toLocaleString('en-IN', { + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); + } catch { + return ts; + } +}; + +const JsonBlock = ({ value }: { value: any }) => { + if (value === null || value === undefined) { + return

; + } + return ( +
+            {JSON.stringify(value, null, 2)}
+        
+ ); +}; + +export const SystemLogsPage: React.FC = () => { + const [logs, setLogs] = useState([]); + const [summary, setSummary] = useState(null); + const [loading, setLoading] = useState(false); + const [page, setPage] = useState(1); + const [limit] = useState(25); + const [totalPages, setTotalPages] = useState(1); + const [totalEntries, setTotalEntries] = useState(0); + + const [moduleFilter, setModuleFilter] = useState('__all__'); + const [actionFilter, setActionFilter] = useState('__all__'); + const [search, setSearch] = useState(''); + const [dateFrom, setDateFrom] = useState(''); + const [dateTo, setDateTo] = useState(''); + const [appliedSearch, setAppliedSearch] = useState(''); + + const [selected, setSelected] = useState(null); + + const queryParams = useMemo( + () => ({ + module: moduleFilter !== '__all__' ? moduleFilter : undefined, + action: actionFilter !== '__all__' ? actionFilter : undefined, + search: appliedSearch || undefined, + dateFrom: dateFrom || undefined, + dateTo: dateTo || undefined, + page, + limit + }), + [moduleFilter, actionFilter, appliedSearch, dateFrom, dateTo, page, limit] + ); + + const fetchLogs = useCallback(async () => { + setLoading(true); + try { + const res = (await API.getSystemAuditLogs(queryParams)) as any; + const body = res?.data; + if (res?.ok && body?.success) { + setLogs(body.data || []); + setTotalPages(body.pagination?.totalPages || 1); + setTotalEntries(body.pagination?.total || 0); + } else { + toast.error(body?.message || 'Unable to load system logs'); + setLogs([]); + } + } catch (err) { + console.error('[SystemLogsPage] fetchLogs error:', err); + toast.error('Failed to load system logs'); + setLogs([]); + } finally { + setLoading(false); + } + }, [queryParams]); + + const fetchSummary = useCallback(async () => { + try { + const res = (await API.getSystemAuditSummary()) as any; + const body = res?.data; + if (res?.ok && body?.success) { + setSummary(body.data || null); + } + } catch (err) { + console.error('[SystemLogsPage] fetchSummary error:', err); + } + }, []); + + useEffect(() => { + fetchLogs(); + }, [fetchLogs]); + + useEffect(() => { + fetchSummary(); + }, [fetchSummary]); + + const applySearch = () => { + setPage(1); + setAppliedSearch(search.trim()); + }; + + const clearFilters = () => { + setModuleFilter('__all__'); + setActionFilter('__all__'); + setSearch(''); + setAppliedSearch(''); + setDateFrom(''); + setDateTo(''); + setPage(1); + }; + + return ( +
+
+
+

+ + System Activity Logs +

+

+ Configuration-level changes across questionnaires, interview configs, master hierarchy, system / SLA configs, and role assignments. +

+
+ +
+ + {/* Summary */} +
+ + + + Total Events + + + +

{summary?.totalEntries ?? '—'}

+

Lifetime

+
+
+ {(summary?.byModule || []).slice(0, 4).map((row) => ( + + + + {row.moduleLabel} + + + +

{row.total}

+ +
+
+ ))} +
+ + {/* Filters */} + + +
+
+ +
+ setSearch(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') applySearch(); + }} + /> + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + { + setDateFrom(e.target.value); + setPage(1); + }} + /> +
+ +
+ + { + setDateTo(e.target.value); + setPage(1); + }} + /> +
+
+ +
+
+ + Showing {logs.length} of {totalEntries} matching event(s) +
+ +
+
+
+ + {/* Logs table */} + +
+
+ + + + + + + + + + + + + {loading && ( + + + + )} + {!loading && logs.length === 0 && ( + + + + )} + {!loading && + logs.map((log) => ( + setSelected(log)} + > + + + + + + + + + ))} + +
WhenModuleActionEntityActorDescription
+ + Loading system logs… +
+ No events match the current filters. +
+ {formatTimestamp(log.timestamp)} + + + {log.moduleLabel} + + + + {log.actionLabel} + + +
+ {log.entityLabel || `${log.entityType}`} +
+ {log.entityId && ( +
+ {log.entityId} +
+ )} +
+
+
+ {(log.actor?.name || 'S').charAt(0).toUpperCase()} +
+
+
{log.actor?.name || 'System'}
+ {log.actor?.role && ( +
+ {log.actor.role} +
+ )} +
+
+
+
+ {log.description} +
+
+ +
+
+ + {/* Pagination */} +
+
+ Page {page} of {totalPages} +
+
+ + +
+
+ + + {/* Detail dialog */} + !open && setSelected(null)}> + + {selected && ( + <> + + + + {selected.moduleLabel} · {selected.actionLabel} + + + {selected.description} + + + +
+
+

+ Entity +

+

{selected.entityLabel || selected.entityType}

+ {selected.entityId && ( +

+ {selected.entityId} +

+ )} +
+
+

+ Actor +

+

+ + {selected.actor?.name || 'System'} +

+ {selected.actor?.role && ( +

+ {selected.actor.role} +

+ )} + {selected.actor?.email && ( +

+ {selected.actor.email} +

+ )} +
+
+

+ When +

+

{formatTimestamp(selected.timestamp)}

+
+
+

+ Source +

+

{selected.ipAddress || '—'}

+

+ {selected.userAgent || ''} +

+
+
+ +
+
+

+ Previous values +

+ +
+
+

+ New values +

+ +
+
+ + {selected.metadata && ( +
+

+ Metadata +

+ +
+ )} + + )} +
+
+
+ ); +}; + +export default SystemLogsPage; diff --git a/src/features/constitutional/pages/ConstitutionalChangeDetails.tsx b/src/features/constitutional/pages/ConstitutionalChangeDetails.tsx index b3e0a26..c17a293 100644 --- a/src/features/constitutional/pages/ConstitutionalChangeDetails.tsx +++ b/src/features/constitutional/pages/ConstitutionalChangeDetails.tsx @@ -701,7 +701,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }: -
+
Workflow Progress Documents diff --git a/src/features/dashboard/pages/FDDDashboardPage.tsx b/src/features/dashboard/pages/FDDDashboardPage.tsx index ba82d79..678c073 100644 --- a/src/features/dashboard/pages/FDDDashboardPage.tsx +++ b/src/features/dashboard/pages/FDDDashboardPage.tsx @@ -147,7 +147,7 @@ export function FDDDashboardPage() {

) : ( -
+
diff --git a/src/features/master/components/InterviewConfigManagement.tsx b/src/features/master/components/InterviewConfigManagement.tsx index 36602ec..020adfe 100644 --- a/src/features/master/components/InterviewConfigManagement.tsx +++ b/src/features/master/components/InterviewConfigManagement.tsx @@ -404,7 +404,7 @@ const InterviewConfigManagement: React.FC = () => { {/* Items Area with Horizontal Guard */} -
+
@@ -449,9 +449,9 @@ const InterviewConfigManagement: React.FC = () => { - Selection - Text - Comment + Options + One Liner + Paragraph Numeric @@ -535,7 +535,7 @@ const InterviewConfigManagement: React.FC = () => {

-

Selection Choices Profile +
Options

+ + + Assign FDD Agency + + Select an FDD partner agency to perform the financial due diligence audit for this application. + + +
+
+ + +
+ +
+
+ + )} + {activeInterviewForUser && !hasSubmittedFeedback && ( diff --git a/src/features/onboarding/components/application-details/ApplicationDetailsTabs.tsx b/src/features/onboarding/components/application-details/ApplicationDetailsTabs.tsx index 385099c..c8519e3 100644 --- a/src/features/onboarding/components/application-details/ApplicationDetailsTabs.tsx +++ b/src/features/onboarding/components/application-details/ApplicationDetailsTabs.tsx @@ -126,7 +126,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) { -
+
Questionnaire Progress @@ -525,7 +525,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
-
+
@@ -581,7 +581,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {

Scheduled Interviews

-
+
diff --git a/src/features/onboarding/hooks/useApplicationDetailsUIState.ts b/src/features/onboarding/hooks/useApplicationDetailsUIState.ts index ed13a90..9893f4c 100644 --- a/src/features/onboarding/hooks/useApplicationDetailsUIState.ts +++ b/src/features/onboarding/hooks/useApplicationDetailsUIState.ts @@ -62,6 +62,7 @@ export function useApplicationDetailsUIState({ initialTab = 'questionnaire' }: U const [fddAgencies, setFddAgencies] = useState([]); const [selectedAgencyId, setSelectedAgencyId] = useState(''); const [isAssigningAgency, setIsAssigningAgency] = useState(false); + const [showAssignFddModal, setShowAssignFddModal] = useState(false); const [isApproving, setIsApproving] = useState(false); const [isRejecting, setIsRejecting] = useState(false); const [ktMatrixScores, setKtMatrixScores] = useState>({}); @@ -142,6 +143,7 @@ export function useApplicationDetailsUIState({ initialTab = 'questionnaire' }: U fddAgencies, setFddAgencies, selectedAgencyId, setSelectedAgencyId, isAssigningAgency, setIsAssigningAgency, + showAssignFddModal, setShowAssignFddModal, isApproving, setIsApproving, isRejecting, setIsRejecting, ktMatrixScores, setKtMatrixScores, diff --git a/src/features/onboarding/pages/ApplicationDetails.tsx b/src/features/onboarding/pages/ApplicationDetails.tsx index eb32d88..7b10a2d 100644 --- a/src/features/onboarding/pages/ApplicationDetails.tsx +++ b/src/features/onboarding/pages/ApplicationDetails.tsx @@ -105,6 +105,7 @@ export const ApplicationDetails = () => { fddAgencies, setFddAgencies, selectedAgencyId, setSelectedAgencyId, isAssigningAgency, setIsAssigningAgency, + showAssignFddModal, setShowAssignFddModal, isApproving, setIsApproving, isRejecting, setIsRejecting, ktMatrixScores, setKtMatrixScores, @@ -498,6 +499,21 @@ export const ApplicationDetails = () => { currentUser={currentUser} handleGenerateDealerCodes={handleGenerateDealerCodes} onOpenAssignArchitectureModal={() => setShowAssignArchitectureModal(true)} + onOpenAssignFdd={() => { + setSelectedAgencyId(''); + fetchFddAgencies(); + setShowAssignFddModal(true); + }} + showAssignFddModal={showAssignFddModal} + setShowAssignFddModal={setShowAssignFddModal} + fddAgencies={fddAgencies} + selectedAgencyId={selectedAgencyId} + setSelectedAgencyId={setSelectedAgencyId} + isAssigningAgency={isAssigningAgency} + handleAssignAgency={async () => { + await handleAssignAgency(); + setShowAssignFddModal(false); + }} activeInterviewForUser={activeInterviewForUser} hasSubmittedFeedback={hasSubmittedFeedback} setSelectedInterviewForFeedback={setSelectedInterviewForFeedback} diff --git a/src/features/relocation/pages/RelocationRequestDetails.tsx b/src/features/relocation/pages/RelocationRequestDetails.tsx index 42047f6..8c9f7bf 100644 --- a/src/features/relocation/pages/RelocationRequestDetails.tsx +++ b/src/features/relocation/pages/RelocationRequestDetails.tsx @@ -681,7 +681,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel -
+
Workflow Progress Documents diff --git a/src/features/resignation/pages/ResignationDetails.tsx b/src/features/resignation/pages/ResignationDetails.tsx index 7dc7a89..62de72e 100644 --- a/src/features/resignation/pages/ResignationDetails.tsx +++ b/src/features/resignation/pages/ResignationDetails.tsx @@ -9,6 +9,7 @@ import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { Separator } from '@/components/ui/separator'; import { useState, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { User as UserType } from '@/lib/mock-data'; @@ -479,149 +480,6 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
- {/* Action Bar - Professional Layout */} - - -
- {/* Primary Actions Row */} -
-
- Workflow Actions: - {/* Debug for PPT button visibility */} - {(() => { - const roleNormalized = String(currentUser?.roleCode || currentUser?.role || '').trim().toUpperCase(); - const isDDLeadUser = roleNormalized === 'DD LEAD' || roleNormalized === 'DD_LEAD'; - const isDDLeadStageCurrent = ['DD Lead', 'DD Lead Review', 'DDL Review'].includes(resignationData?.currentStage); - - if (isDDLeadUser && isDDLeadStageCurrent) { - return ( - - ); - } - return null; - })()} - {permissions.canApprove && ( - - )} - {permissions.canSendBack && ( - - )} - {permissions.canWithdraw && ( - - )} - {permissions.canRevoke && ( - - )} -
- - {/* Secondary Actions */} -
- {permissions.canPushToFnF && ( - - )} - {permissions.canAssign && ( - - )} -
-
- - {/* Work Notes Button - Independent Section */} -
-
- - Communication & Notes -
- -
-
-
-
- - {/* Tabs */} Details @@ -633,8 +491,55 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig )} +
+
+ {/* Details Tab */} + + + Resignation Details + + +
+
+
+ +

{resignationData?.resignationType}

+
+
+ +

{resignationData?.reason}

+
+
+
+
+ +

{resignationData?.lastOperationalDateSales ? formatDateTime(resignationData.lastOperationalDateSales, 'date') : 'N/A'}

+
+
+ +

{resignationData?.lastOperationalDateServices ? formatDateTime(resignationData.lastOperationalDateServices, 'date') : 'N/A'}

+
+
+
+ +

{resignationData?.additionalInfo || 'No additional info provided'}

+
+
+
+ +

{resignationData?.submittedOn ? formatDateTime(resignationData.submittedOn) : 'N/A'}

+
+
+ +

{resignationData?.currentStage}

+
+
+
+
+
+ Request Information @@ -706,51 +611,6 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
- - - - - Resignation Details - - -
-
-
- -

{resignationData?.resignationType}

-
-
- -

{resignationData?.reason}

-
-
-
-
- -

{resignationData?.lastOperationalDateSales ? formatDateTime(resignationData.lastOperationalDateSales, 'date') : 'N/A'}

-
-
- -

{resignationData?.lastOperationalDateServices ? formatDateTime(resignationData.lastOperationalDateServices, 'date') : 'N/A'}

-
-
-
- -

{resignationData?.additionalInfo || 'No additional info provided'}

-
-
-
- -

{resignationData?.submittedOn ? formatDateTime(resignationData.submittedOn) : 'N/A'}

-
-
- -

{resignationData?.currentStage}

-
-
-
-
-
{/* Progress Tab */} @@ -1073,6 +933,135 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig )} +
+ + {/* Sidebar */} +
+ + + Actions + + + {(() => { + const roleNormalized = String(currentUser?.roleCode || currentUser?.role || '').trim().toUpperCase(); + const isDDLeadUser = roleNormalized === 'DD LEAD' || roleNormalized === 'DD_LEAD'; + const isDDLeadStageCurrent = ['DD Lead', 'DD Lead Review', 'DDL Review'].includes(resignationData?.currentStage); + + if (isDDLeadUser && isDDLeadStageCurrent) { + return ( + + ); + } + return null; + })()} + + {permissions.canApprove && ( + + )} + + {permissions.canSendBack && ( + + )} + + {permissions.canWithdraw && ( + + )} + + {permissions.canRevoke && ( + + )} + + {permissions.canPushToFnF && ( + + )} + + {permissions.canAssign && ( + + )} + + + + + + +
+
{/* Action Dialogs */} diff --git a/src/features/termination/pages/TerminationDetails.tsx b/src/features/termination/pages/TerminationDetails.tsx index 3be64b0..5c4e4e9 100644 --- a/src/features/termination/pages/TerminationDetails.tsx +++ b/src/features/termination/pages/TerminationDetails.tsx @@ -9,6 +9,7 @@ import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { Separator } from '@/components/ui/separator'; import { useState, useEffect } from 'react'; import { User } from '@/lib/mock-data'; import { toast } from 'sonner'; @@ -580,157 +581,6 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi - {/* Action Bar - Professional Layout */} - - -
- {(request.currentStage === 'Evaluation of Dealer SCN Response' || request.currentStage === 'Personal Hearing') && ( - - Joint Review Stage - - This stage requires a joint evaluation of the SCN response by the DD-Lead, ZBH, RBM, and DD-Head. - The case will only advance to NBH Final Approval once all four stakeholders have recorded their review. - - - )} - {/* Primary Actions Row */} -
-
- Termination Actions: - {currentUser?.role !== 'Dealer' && ( - <> - {!permissions.canFinalize && ( - <> - {permissions.canApprove && ( - - )} - {permissions.canIssueSCN && ( - - )} - {permissions.canUploadSCNResponse && ( - - )} - {permissions.canApprove && ( - - )} - - )} - {permissions.canHold && ( - - )} - {permissions.canFinalize && ( - - )} - - )} -
- - {/* Secondary Actions */} -
- {permissions.canPushToFnF && ( - - )} - {!permissions.isFinalState && ( - - )} -
-
- - {/* Work Notes Button - Independent Section */} -
-
- - Communication & Notes -
- -
-
-
-
- - {/* Tabs */} Details @@ -739,6 +589,9 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi Audit Trail +
+
+ {/* Details Tab */} @@ -1149,6 +1002,139 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi +
+ + {/* Sidebar */} +
+ + + Actions + + + {(request.currentStage === 'Evaluation of Dealer SCN Response' || request.currentStage === 'Personal Hearing') && ( + + Joint Review Stage + + Joint evaluation by DD-Lead, ZBH, RBM, and DD-Head required before advancing to NBH Final Approval. + + + )} + + {currentUser?.role !== 'Dealer' && ( + <> + {!permissions.canFinalize && ( + <> + {permissions.canApprove && ( + + )} + {permissions.canIssueSCN && ( + + )} + {permissions.canUploadSCNResponse && ( + + )} + {permissions.canApprove && ( + + )} + + )} + {permissions.canHold && ( + + )} + {permissions.canFinalize && ( + + )} + + )} + + {permissions.canPushToFnF && ( + + )} + + {!permissions.isFinalState && ( + + )} + + + + + + +
+
{/* Action Dialogs */} diff --git a/src/styles/globals.css b/src/styles/globals.css index fbb4ee1..5a51ff0 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -283,7 +283,7 @@ html { /* Thin, light horizontal scrollbar (e.g. tab strips with overflow-x) */ .custom-scrollbar-x::-webkit-scrollbar { - height: 4px; + height: 2px; } .custom-scrollbar-x::-webkit-scrollbar-track { @@ -291,22 +291,22 @@ html { } .custom-scrollbar-x::-webkit-scrollbar-thumb { - background: #e2e8f0; + background: #f1f5f9; border-radius: 9999px; } .custom-scrollbar-x::-webkit-scrollbar-thumb:hover { - background: #cbd5e1; + background: #e2e8f0; } .custom-scrollbar-x { scrollbar-width: thin; - scrollbar-color: #e2e8f0 transparent; + scrollbar-color: #f1f5f9 transparent; } /* Extra-thin, subtle horizontal scrollbar (documents modal tables) */ .custom-scrollbar-x-slim::-webkit-scrollbar { - height: 3px; + height: 2px; } .custom-scrollbar-x-slim::-webkit-scrollbar-track { @@ -314,17 +314,17 @@ html { } .custom-scrollbar-x-slim::-webkit-scrollbar-thumb { - background: #f1f5f9; + background: #f8fafc; border-radius: 9999px; } .custom-scrollbar-x-slim::-webkit-scrollbar-thumb:hover { - background: #e2e8f0; + background: #f1f5f9; } .custom-scrollbar-x-slim { scrollbar-width: thin; - scrollbar-color: #f1f5f9 transparent; + scrollbar-color: #f8fafc transparent; } /* Extra-thin, light vertical scrollbar (e.g. modals) */