172 lines
4.2 KiB
TypeScript
172 lines
4.2 KiB
TypeScript
import { humanize } from './humanize';
|
|
|
|
export const useCaseStatusMeta = {
|
|
draft: {
|
|
label: 'Draft',
|
|
className: 'bg-slate-100 text-slate-700 border-slate-200',
|
|
},
|
|
submitted: {
|
|
label: 'Submitted',
|
|
className: 'bg-blue-100 text-blue-700 border-blue-200',
|
|
},
|
|
risk_review: {
|
|
label: 'GC review',
|
|
className: 'bg-amber-100 text-amber-800 border-amber-200',
|
|
},
|
|
security_review: {
|
|
label: 'Security review',
|
|
className: 'bg-indigo-100 text-indigo-700 border-indigo-200',
|
|
},
|
|
ethics_review: {
|
|
label: 'Ethics review',
|
|
className: 'bg-violet-100 text-violet-700 border-violet-200',
|
|
},
|
|
approved: {
|
|
label: 'Approved',
|
|
className: 'bg-emerald-100 text-emerald-700 border-emerald-200',
|
|
},
|
|
rejected: {
|
|
label: 'Rejected',
|
|
className: 'bg-rose-100 text-rose-700 border-rose-200',
|
|
},
|
|
needs_changes: {
|
|
label: 'Needs changes',
|
|
className: 'bg-orange-100 text-orange-700 border-orange-200',
|
|
},
|
|
retired: {
|
|
label: 'Retired',
|
|
className: 'bg-slate-100 text-slate-600 border-slate-200',
|
|
},
|
|
};
|
|
|
|
export const riskLevelMeta = {
|
|
low: {
|
|
label: 'Low',
|
|
className: 'bg-emerald-100 text-emerald-700 border-emerald-200',
|
|
},
|
|
medium: {
|
|
label: 'Medium',
|
|
className: 'bg-amber-100 text-amber-800 border-amber-200',
|
|
},
|
|
high: {
|
|
label: 'High',
|
|
className: 'bg-orange-100 text-orange-700 border-orange-200',
|
|
},
|
|
critical: {
|
|
label: 'Critical',
|
|
className: 'bg-rose-100 text-rose-700 border-rose-200',
|
|
},
|
|
};
|
|
|
|
export const approvalStepMeta = {
|
|
partner: {
|
|
label: 'Partner / governance lead',
|
|
description: 'Business sponsor confirms value and ownership.',
|
|
},
|
|
general_counsel: {
|
|
label: 'General counsel',
|
|
description: 'Legal leadership confirms risk posture and client impact.',
|
|
},
|
|
it_security: {
|
|
label: 'IT / security',
|
|
description: 'Security posture, retention, SSO, and controls are validated.',
|
|
},
|
|
ethics_risk: {
|
|
label: 'Ethics / risk',
|
|
description: 'Human review and professional responsibility safeguards are confirmed.',
|
|
},
|
|
};
|
|
|
|
export function getBadgeClasses(className = 'bg-slate-100 text-slate-700 border-slate-200') {
|
|
return `inline-flex items-center rounded-full border px-2.5 py-1 text-xs font-semibold ${className}`;
|
|
}
|
|
|
|
export function getStatusBadge(value?: string | null) {
|
|
if (!value) {
|
|
return {
|
|
label: 'Not set',
|
|
className: getBadgeClasses(),
|
|
};
|
|
}
|
|
|
|
const meta = useCaseStatusMeta[value as keyof typeof useCaseStatusMeta] || {
|
|
label: humanize(value),
|
|
className: 'bg-slate-100 text-slate-700 border-slate-200',
|
|
};
|
|
|
|
return {
|
|
label: meta.label,
|
|
className: getBadgeClasses(meta.className),
|
|
};
|
|
}
|
|
|
|
export function getRiskBadge(value?: string | null) {
|
|
if (!value) {
|
|
return {
|
|
label: 'Risk pending',
|
|
className: getBadgeClasses('bg-slate-100 text-slate-700 border-slate-200'),
|
|
};
|
|
}
|
|
|
|
const meta = riskLevelMeta[value as keyof typeof riskLevelMeta] || {
|
|
label: humanize(value),
|
|
className: 'bg-slate-100 text-slate-700 border-slate-200',
|
|
};
|
|
|
|
return {
|
|
label: meta.label,
|
|
className: getBadgeClasses(meta.className),
|
|
};
|
|
}
|
|
|
|
export function getDecisionBadge(value?: string | null) {
|
|
if (!value || value === 'pending') {
|
|
return {
|
|
label: 'Pending',
|
|
className: getBadgeClasses('bg-slate-100 text-slate-700 border-slate-200'),
|
|
};
|
|
}
|
|
|
|
if (value === 'approved') {
|
|
return {
|
|
label: 'Approved',
|
|
className: getBadgeClasses('bg-emerald-100 text-emerald-700 border-emerald-200'),
|
|
};
|
|
}
|
|
|
|
if (value === 'rejected') {
|
|
return {
|
|
label: 'Rejected',
|
|
className: getBadgeClasses('bg-rose-100 text-rose-700 border-rose-200'),
|
|
};
|
|
}
|
|
|
|
if (value === 'needs_changes') {
|
|
return {
|
|
label: 'Needs changes',
|
|
className: getBadgeClasses('bg-orange-100 text-orange-700 border-orange-200'),
|
|
};
|
|
}
|
|
|
|
return {
|
|
label: humanize(value),
|
|
className: getBadgeClasses(),
|
|
};
|
|
}
|
|
|
|
export function formatNumber(value?: number | string | null) {
|
|
if (value === null || value === undefined || value === '') {
|
|
return '—';
|
|
}
|
|
|
|
const numericValue = Number(value);
|
|
|
|
if (Number.isNaN(numericValue)) {
|
|
return '—';
|
|
}
|
|
|
|
return new Intl.NumberFormat('en-US', {
|
|
maximumFractionDigits: 1,
|
|
}).format(numericValue);
|
|
}
|