From f56cb70b553c82a6a74d1c6784cadbebb93453cf Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 26 Oct 2026 09:34:15 +0200 Subject: [PATCH] Fixed dshboard render crash --- .../components/dashboard/DashboardPage.tsx | 17 ++--- .../dashboard/GovernanceInfoModal.tsx | 72 ++++++------------- .../src/components/dashboard/ProjectCard.tsx | 24 ++++++- .../PartnerMessageComposerModal.tsx | 2 +- .../src/hooks/useInAppNotifications.ts | 2 +- 5 files changed, 56 insertions(+), 61 deletions(-) diff --git a/PostFundManagement.PartnerUI/src/components/dashboard/DashboardPage.tsx b/PostFundManagement.PartnerUI/src/components/dashboard/DashboardPage.tsx index c46b9b5..779dd4a 100644 --- a/PostFundManagement.PartnerUI/src/components/dashboard/DashboardPage.tsx +++ b/PostFundManagement.PartnerUI/src/components/dashboard/DashboardPage.tsx @@ -63,6 +63,7 @@ import { EvidenceType, Notification, NotificationStatus, + NotificationPlatform, } from '../../types'; import { formatZAR, formatCompactZAR, getUserEmail } from '../../lib/formatters'; import { MessageViewer } from '../notifications/MessageViewer'; @@ -462,7 +463,7 @@ export function DashboardPage() { }; const getMessageRecipientEmail = (notification: Notification | null): string => { - if (!notification) return user?.profile?.email || (user as any)?.email || 'Partner User'; + if (!notification) return (user?.email || (user as any)?.profile?.email || '') || (user as any)?.email || 'Partner User'; const dest = notification.destination || ''; let rawTo = ''; if (dest.includes('To:')) { @@ -486,13 +487,13 @@ export function DashboardPage() { } } - return user?.profile?.email || (user as any)?.email || 'Partner User'; + return (user?.email || (user as any)?.profile?.email || '') || (user as any)?.email || 'Partner User'; }; const handleReplyToMessage = (notification: Notification) => { const senderEmail = getMessageSenderEmail(notification).trim().toLowerCase(); const currentUserEmail = ( - user?.profile?.email || + (user?.email || (user as any)?.profile?.email || '') || (user as any)?.email || '' ).trim().toLowerCase(); @@ -746,7 +747,7 @@ export function DashboardPage() { className="p-3 hover:bg-white/[0.03] transition-colors cursor-pointer text-left" >

{item.subject}

-

{stripMarkdown(item.message)}

+

{stripMarkdown(item.message || '')}

)) )} @@ -778,10 +779,10 @@ export function DashboardPage() { className="flex items-center gap-2 p-1.5 pl-2 rounded-xl bg-slate-900/80 border border-white/[0.06] hover:border-white/[0.12] transition-colors cursor-pointer" >
- {getInitials(user?.profile?.name || user?.profile?.email || 'Partner')} + {getInitials((user?.name || (user as any)?.profile?.name || '') || (user?.email || (user as any)?.profile?.email || '') || 'Partner')}
- {user?.profile?.name || 'Partner Account'} + {(user?.name || (user as any)?.profile?.name || '') || 'Partner Account'} @@ -789,8 +790,8 @@ export function DashboardPage() { {showUserDropdown && (
-

{user?.profile?.name || 'Partner User'}

-

{user?.profile?.email || ''}

+

{(user?.name || (user as any)?.profile?.name || '') || 'Partner User'}

+

{(user?.email || (user as any)?.profile?.email || '') || ''}

{partnerSummary.primaryInvitedOrg && (

{partnerSummary.primaryInvitedOrg.name} diff --git a/PostFundManagement.PartnerUI/src/components/dashboard/GovernanceInfoModal.tsx b/PostFundManagement.PartnerUI/src/components/dashboard/GovernanceInfoModal.tsx index e67b3ef..0676ad4 100644 --- a/PostFundManagement.PartnerUI/src/components/dashboard/GovernanceInfoModal.tsx +++ b/PostFundManagement.PartnerUI/src/components/dashboard/GovernanceInfoModal.tsx @@ -58,42 +58,28 @@ export const GovernanceInfoModal: React.FC = ({ // 3. Fetch rules const { data: governanceRules = [], isLoading: isLoadingRules } = useQuery({ queryKey: ['governance-rules'], - queryFn: () => getRules(1, 100), + queryFn: () => getRules({ page: 1, pageSize: 100 }), enabled: isOpen, }); if (!isOpen || typeof document === 'undefined') return null; const renderComplianceStatus = (status?: number) => { - switch (status) { - case ComplianceStatus.Compliant: - return ( - - - Compliant - - ); - case ComplianceStatus.UnderReview: - return ( - - - Under Review - - ); - case ComplianceStatus.NonCompliant: - return ( - - - Non-Compliant - - ); - default: - return ( - - Pending Submission - - ); + const s = Number(status); + if (s === ComplianceStatus.Met || s === ComplianceStatus.Exceeded) { + return ( + + + {s === ComplianceStatus.Exceeded ? 'Exceeded' : 'Compliant'} + + ); } + return ( + + + Action Required + + ); }; return createPortal( @@ -201,20 +187,13 @@ export const GovernanceInfoModal: React.FC = ({ {renderComplianceStatus(item.status)}

- {item.dueDate && ( -
+
- Due: {formatDate(item.dueDate)} + Created: {formatDate(item.createdAt)}
- )} -
- {item.description && ( -

{item.description}

- )} - {item.notes && ( -
- Note: {item.notes}
+ {item.requirements && ( +

{item.requirements}

)}
)) @@ -291,20 +270,13 @@ export const GovernanceInfoModal: React.FC = ({

{rule.name || `Rule #${idx + 1}`}

- {rule.category && ( - - {rule.category} - - )} + + v{rule.version || 1} + {rule.description && (

{rule.description}

)} - {rule.condition && ( -
- Condition: {rule.condition} -
- )} )) )} diff --git a/PostFundManagement.PartnerUI/src/components/dashboard/ProjectCard.tsx b/PostFundManagement.PartnerUI/src/components/dashboard/ProjectCard.tsx index c995c5d..a4977f7 100644 --- a/PostFundManagement.PartnerUI/src/components/dashboard/ProjectCard.tsx +++ b/PostFundManagement.PartnerUI/src/components/dashboard/ProjectCard.tsx @@ -26,7 +26,10 @@ import { import { EvidenceUploadModal } from './EvidenceUploadModal'; import { GovernanceInfoModal } from './GovernanceInfoModal'; import { - FolderKanban, + FolderKanban, + AlertTriangle, + ShieldAlert, + Scale, ChevronDown, ChevronUp, Calendar, @@ -66,6 +69,12 @@ interface ProjectCardProps { item: EnrichedPartnerProject; isExpanded?: boolean; onToggleExpand?: () => void; + onUploadEvidence?: (target: any) => void; + onSubmitActivityForApproval?: ( + activity: Activity, + milestone?: Milestone | null, + evidences?: Evidence[] + ) => Promise; onOpenSecureFile: (type: 'contract' | 'evidence', id: string | number) => void; } @@ -226,6 +235,19 @@ export const ProjectCard: React.FC = ({ return new Date(a.dueAt || 0).getTime() - new Date(b.dueAt || 0).getTime(); }); + // Requirement #5: Milestone and Project Late / At Risk calculations + const nowTime = Date.now(); + const hasOverdueMilestone = effectiveMilestones.some(m => { + const d = m.dueAt ? new Date(m.dueAt).getTime() : null; + const fulfilled = Number(m.status) === ApprovalStatus.Approved; + return d !== null && d < nowTime && !fulfilled; + }); + const hasAtRiskMilestone = !hasOverdueMilestone && effectiveMilestones.some(m => { + const d = m.dueAt ? new Date(m.dueAt).getTime() : null; + const fulfilled = Number(m.status) === ApprovalStatus.Approved; + return d !== null && d >= nowTime && (d - nowTime <= 3 * 24 * 60 * 60 * 1000) && !fulfilled; + }); + const getEvidenceForActivity = (activityId: string | number): Evidence[] => { const targetNum = toIntegerId(activityId); return effectiveEvidence diff --git a/PostFundManagement.PartnerUI/src/components/notifications/PartnerMessageComposerModal.tsx b/PostFundManagement.PartnerUI/src/components/notifications/PartnerMessageComposerModal.tsx index 00cdaeb..4ab77ad 100644 --- a/PostFundManagement.PartnerUI/src/components/notifications/PartnerMessageComposerModal.tsx +++ b/PostFundManagement.PartnerUI/src/components/notifications/PartnerMessageComposerModal.tsx @@ -254,7 +254,7 @@ export const PartnerMessageComposerModal: React.FC diff --git a/PostFundManagement.PartnerUI/src/hooks/useInAppNotifications.ts b/PostFundManagement.PartnerUI/src/hooks/useInAppNotifications.ts index 62795fc..dd1189c 100644 --- a/PostFundManagement.PartnerUI/src/hooks/useInAppNotifications.ts +++ b/PostFundManagement.PartnerUI/src/hooks/useInAppNotifications.ts @@ -300,7 +300,7 @@ export function useInAppNotifications() { const sentItem: Notification = { id: `sent_${Date.now()}`, organisationId: orgId, - recipient: payload.recipientId, + recipient: payload.recipientId || payload.recipient || "", platform: targetPlatform, status: NotificationStatus.Sent, subject: payload.subject,