mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
feat: build Admin Team Dashboard with scoreboard, funnel, SLA, ROI, and integration health
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
114
src/components/admin/integration-health.tsx
Normal file
114
src/components/admin/integration-health.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { BadgeWithDot } from '@/components/base/badges/badges';
|
||||
import { cx } from '@/utils/cx';
|
||||
import type { IntegrationStatus, AuthStatus, LeadIngestionSource } from '@/types/entities';
|
||||
|
||||
interface IntegrationHealthProps {
|
||||
sources: LeadIngestionSource[];
|
||||
}
|
||||
|
||||
const statusBorderMap: Record<IntegrationStatus, string> = {
|
||||
ACTIVE: 'border-secondary',
|
||||
WARNING: 'border-warning',
|
||||
ERROR: 'border-error',
|
||||
DISABLED: 'border-secondary',
|
||||
};
|
||||
|
||||
const statusBadgeColorMap: Record<IntegrationStatus, 'success' | 'warning' | 'error' | 'gray'> = {
|
||||
ACTIVE: 'success',
|
||||
WARNING: 'warning',
|
||||
ERROR: 'error',
|
||||
DISABLED: 'gray',
|
||||
};
|
||||
|
||||
const authBadgeColorMap: Record<AuthStatus, 'success' | 'warning' | 'error' | 'gray'> = {
|
||||
VALID: 'success',
|
||||
EXPIRING_SOON: 'warning',
|
||||
EXPIRED: 'error',
|
||||
NOT_CONFIGURED: 'gray',
|
||||
};
|
||||
|
||||
function formatRelativeTime(isoString: string): string {
|
||||
const diffMs = Date.now() - new Date(isoString).getTime();
|
||||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
|
||||
if (diffMinutes < 1) return 'just now';
|
||||
if (diffMinutes < 60) return `${diffMinutes} min ago`;
|
||||
|
||||
const diffHours = Math.floor(diffMinutes / 60);
|
||||
if (diffHours < 24) return `${diffHours}h ago`;
|
||||
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
return `${diffDays}d ago`;
|
||||
}
|
||||
|
||||
export const IntegrationHealth = ({ sources }: IntegrationHealthProps) => {
|
||||
return (
|
||||
<div className="rounded-2xl border border-secondary bg-primary p-5">
|
||||
<h3 className="text-sm font-bold text-primary">Integration Health</h3>
|
||||
|
||||
<div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{sources.map((source) => {
|
||||
const status = source.integrationStatus ?? 'DISABLED';
|
||||
const authStatus = source.authStatus ?? 'NOT_CONFIGURED';
|
||||
const showAuthBadge = authStatus !== 'VALID';
|
||||
|
||||
return (
|
||||
<div
|
||||
key={source.id}
|
||||
className={cx(
|
||||
'rounded-xl border bg-primary p-4',
|
||||
statusBorderMap[status],
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-semibold text-primary">
|
||||
{source.sourceName}
|
||||
</span>
|
||||
<BadgeWithDot
|
||||
size="sm"
|
||||
type="pill-color"
|
||||
color={statusBadgeColorMap[status]}
|
||||
>
|
||||
{status}
|
||||
</BadgeWithDot>
|
||||
</div>
|
||||
|
||||
<p className="mt-2 text-xs text-tertiary">
|
||||
{source.leadsReceivedLast24h ?? 0} leads in 24h
|
||||
</p>
|
||||
|
||||
{source.lastSuccessfulSyncAt && (
|
||||
<p className="mt-0.5 text-xs text-quaternary">
|
||||
Last sync: {formatRelativeTime(source.lastSuccessfulSyncAt)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{showAuthBadge && (
|
||||
<div className="mt-2">
|
||||
<BadgeWithDot
|
||||
size="sm"
|
||||
type="pill-color"
|
||||
color={authBadgeColorMap[authStatus]}
|
||||
>
|
||||
Auth: {authStatus.replace(/_/g, ' ')}
|
||||
</BadgeWithDot>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(status === 'WARNING' || status === 'ERROR') && source.lastErrorMessage && (
|
||||
<p
|
||||
className={cx(
|
||||
'mt-2 text-xs',
|
||||
status === 'ERROR' ? 'text-error-primary' : 'text-warning-primary',
|
||||
)}
|
||||
>
|
||||
{source.lastErrorMessage}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user