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,