primus-saas-react 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles.css +568 -0
- package/package.json +15 -9
- package/DEMO.md +0 -68
- package/INTEGRATION.md +0 -702
- package/build_log.txt +0 -0
- package/postcss.config.js +0 -6
- package/src/components/ai/AICopilot.tsx +0 -88
- package/src/components/auth/PrimusLogin.tsx +0 -298
- package/src/components/auth/UserProfile.tsx +0 -26
- package/src/components/banking/accounts/AccountDashboard.tsx +0 -67
- package/src/components/banking/cards/CreditCardVisual.tsx +0 -67
- package/src/components/banking/credit/CreditScoreCard.tsx +0 -80
- package/src/components/banking/kyc/KYCVerification.tsx +0 -76
- package/src/components/banking/loans/LoanCalculator.tsx +0 -106
- package/src/components/banking/transactions/TransactionHistory.tsx +0 -74
- package/src/components/crud/PrimusDataTable.tsx +0 -220
- package/src/components/crud/PrimusModal.tsx +0 -68
- package/src/components/dashboard/PrimusDashboard.tsx +0 -145
- package/src/components/documents/DocumentViewer.tsx +0 -107
- package/src/components/featureflags/FeatureFlagToggle.tsx +0 -64
- package/src/components/insurance/agents/AgentDirectory.tsx +0 -72
- package/src/components/insurance/claims/ClaimStatusTracker.tsx +0 -78
- package/src/components/insurance/fraud/FraudDetectionDashboard.tsx +0 -68
- package/src/components/insurance/policies/PolicyCard.tsx +0 -77
- package/src/components/insurance/premium/PremiumCalculator.tsx +0 -104
- package/src/components/insurance/quotes/QuoteComparison.tsx +0 -75
- package/src/components/layout/PrimusHeader.tsx +0 -75
- package/src/components/layout/PrimusLayout.tsx +0 -47
- package/src/components/layout/PrimusSidebar.tsx +0 -102
- package/src/components/logging/LogViewer.tsx +0 -90
- package/src/components/notifications/NotificationFeed.tsx +0 -106
- package/src/components/notifications/PrimusNotificationCenter.tsx +0 -282
- package/src/components/payments/CheckoutForm.tsx +0 -167
- package/src/components/security/SecurityDashboard.tsx +0 -83
- package/src/components/shared/Button.tsx +0 -36
- package/src/components/shared/Input.tsx +0 -36
- package/src/components/storage/FileUploader.tsx +0 -79
- package/src/context/PrimusProvider.tsx +0 -156
- package/src/context/PrimusThemeProvider.tsx +0 -160
- package/src/hooks/useNotifications.ts +0 -58
- package/src/hooks/usePrimusAuth.ts +0 -3
- package/src/hooks/useRealtimeNotifications.ts +0 -114
- package/src/index.ts +0 -42
- package/tailwind.config.js +0 -18
- package/tsconfig.json +0 -28
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface StatCardProps {
|
|
4
|
-
title: string;
|
|
5
|
-
value: string | number;
|
|
6
|
-
change?: {
|
|
7
|
-
value: number;
|
|
8
|
-
type: 'increase' | 'decrease' | 'neutral';
|
|
9
|
-
};
|
|
10
|
-
icon?: React.ReactNode;
|
|
11
|
-
description?: string;
|
|
12
|
-
trend?: number[]; // Values for sparkline
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const PrimusStatCard: React.FC<StatCardProps> = ({
|
|
16
|
-
title,
|
|
17
|
-
value,
|
|
18
|
-
change,
|
|
19
|
-
icon,
|
|
20
|
-
description,
|
|
21
|
-
trend = [40, 35, 50, 45, 60, 55, 70] // Default dummy data
|
|
22
|
-
}) => {
|
|
23
|
-
const changeColors = {
|
|
24
|
-
increase: 'text-emerald-400 bg-emerald-400/10',
|
|
25
|
-
decrease: 'text-rose-400 bg-rose-400/10',
|
|
26
|
-
neutral: 'text-gray-400 bg-gray-400/10',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const changeIcons = {
|
|
30
|
-
increase: '↑',
|
|
31
|
-
decrease: '↓',
|
|
32
|
-
neutral: '→',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Render Sparkline
|
|
36
|
-
const renderSparkline = () => {
|
|
37
|
-
// Sparkline implementation simplified for visual effect
|
|
38
|
-
// In a real app, 'trend' would be normalized to SVG coordinates
|
|
39
|
-
return (
|
|
40
|
-
<svg className="w-full h-12 opacity-50 absolute bottom-0 left-0" preserveAspectRatio="none" data-trend={trend.join(',')}>
|
|
41
|
-
<path d="M0 40 Q 20 20 40 40 T 80 40 T 120 40 T 160 30 V 50 H 0 Z" fill={`url(#gradient-${title.replace(/\s/g, '')})`} stroke="none" />
|
|
42
|
-
<defs>
|
|
43
|
-
<linearGradient id={`gradient-${title.replace(/\s/g, '')}`} x1="0" y1="0" x2="0" y2="1">
|
|
44
|
-
<stop offset="0%" stopColor="currentColor" stopOpacity="0.2" />
|
|
45
|
-
<stop offset="100%" stopColor="currentColor" stopOpacity="0" />
|
|
46
|
-
</linearGradient>
|
|
47
|
-
</defs>
|
|
48
|
-
</svg>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<div className="relative overflow-hidden bg-slate-900/40 backdrop-blur-md rounded-2xl border border-white/5 hover:border-white/10 transition-all duration-300 group">
|
|
54
|
-
<div className="absolute inset-0 bg-gradient-to-br from-white/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" />
|
|
55
|
-
|
|
56
|
-
<div className="p-6 relative z-10">
|
|
57
|
-
<div className="flex items-start justify-between mb-4">
|
|
58
|
-
<div className="p-2.5 rounded-lg bg-white/5 text-gray-300 ring-1 ring-white/10 group-hover:bg-purple-500/20 group-hover:text-purple-300 transition-colors">
|
|
59
|
-
{icon || <div className="w-5 h-5 bg-current opacity-20" />}
|
|
60
|
-
</div>
|
|
61
|
-
{change && (
|
|
62
|
-
<span className={`px-2 py-0.5 text-xs font-medium rounded-full flex items-center gap-1 ${changeColors[change.type]}`}>
|
|
63
|
-
{changeIcons[change.type]} {Math.abs(change.value)}%
|
|
64
|
-
</span>
|
|
65
|
-
)}
|
|
66
|
-
</div>
|
|
67
|
-
|
|
68
|
-
<div>
|
|
69
|
-
<h3 className="text-sm font-medium text-gray-400 tracking-wide">{title}</h3>
|
|
70
|
-
<div className="mt-1 flex items-baseline gap-2">
|
|
71
|
-
<span className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-400">
|
|
72
|
-
{value}
|
|
73
|
-
</span>
|
|
74
|
-
</div>
|
|
75
|
-
{description && (
|
|
76
|
-
<p className="text-xs text-gray-500 mt-2 font-medium">{description}</p>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
{renderSparkline()}
|
|
82
|
-
|
|
83
|
-
{/* Sparkline Decor (abstract) */}
|
|
84
|
-
<div className={`absolute -bottom-2 -right-4 w-32 h-32 blur-2xl opacity-20 rounded-full transition-colors duration-500 ${change?.type === 'increase' ? 'bg-emerald-500' :
|
|
85
|
-
change?.type === 'decrease' ? 'bg-rose-500' : 'bg-blue-500'
|
|
86
|
-
}`} />
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export interface PrimusDashboardProps {
|
|
92
|
-
children: React.ReactNode;
|
|
93
|
-
title?: string;
|
|
94
|
-
subtitle?: string;
|
|
95
|
-
actions?: React.ReactNode;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const PrimusDashboard: React.FC<PrimusDashboardProps> = ({
|
|
99
|
-
children,
|
|
100
|
-
title,
|
|
101
|
-
subtitle,
|
|
102
|
-
actions,
|
|
103
|
-
}) => {
|
|
104
|
-
return (
|
|
105
|
-
<div className="space-y-8 animate-enter">
|
|
106
|
-
{(title || actions) && (
|
|
107
|
-
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 border-b border-white/5 pb-6">
|
|
108
|
-
<div>
|
|
109
|
-
{title && (
|
|
110
|
-
<h1 className="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-white via-gray-200 to-gray-400 tracking-tight">
|
|
111
|
-
{title}
|
|
112
|
-
</h1>
|
|
113
|
-
)}
|
|
114
|
-
{subtitle && <p className="text-gray-400 mt-2 text-lg font-light leading-relaxed">{subtitle}</p>}
|
|
115
|
-
</div>
|
|
116
|
-
{actions && <div className="flex items-center gap-3">{actions}</div>}
|
|
117
|
-
</div>
|
|
118
|
-
)}
|
|
119
|
-
{children}
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export interface DashboardGridProps {
|
|
125
|
-
children: React.ReactNode;
|
|
126
|
-
columns?: 1 | 2 | 3 | 4;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export const DashboardGrid: React.FC<DashboardGridProps> = ({
|
|
130
|
-
children,
|
|
131
|
-
columns = 4,
|
|
132
|
-
}) => {
|
|
133
|
-
const colClasses = {
|
|
134
|
-
1: 'grid-cols-1',
|
|
135
|
-
2: 'grid-cols-1 md:grid-cols-2',
|
|
136
|
-
3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
|
137
|
-
4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<div className={`grid gap-6 ${colClasses[columns]}`}>
|
|
142
|
-
{children}
|
|
143
|
-
</div>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Button } from '../shared/Button';
|
|
3
|
-
import {
|
|
4
|
-
DocumentArrowDownIcon,
|
|
5
|
-
// EyeIcon,
|
|
6
|
-
PrinterIcon,
|
|
7
|
-
XMarkIcon
|
|
8
|
-
} from '@heroicons/react/24/outline';
|
|
9
|
-
|
|
10
|
-
export interface Document {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
type: 'pdf' | 'image' | 'text';
|
|
14
|
-
url: string;
|
|
15
|
-
size?: string;
|
|
16
|
-
date?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface DocumentViewerProps {
|
|
20
|
-
document: Document;
|
|
21
|
-
onClose?: () => void;
|
|
22
|
-
onDownload?: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const DocumentViewer: React.FC<DocumentViewerProps> = ({
|
|
26
|
-
document,
|
|
27
|
-
onClose,
|
|
28
|
-
onDownload
|
|
29
|
-
}) => {
|
|
30
|
-
// const [isFullscreen, setIsFullscreen] = useState(false);
|
|
31
|
-
|
|
32
|
-
// Mock content display based on type
|
|
33
|
-
const renderContent = () => {
|
|
34
|
-
switch (document.type) {
|
|
35
|
-
case 'image':
|
|
36
|
-
return (
|
|
37
|
-
<img
|
|
38
|
-
src={document.url}
|
|
39
|
-
alt={document.name}
|
|
40
|
-
className="max-w-full max-h-[80vh] object-contain mx-auto shadow-lg rounded"
|
|
41
|
-
/>
|
|
42
|
-
);
|
|
43
|
-
case 'pdf':
|
|
44
|
-
return (
|
|
45
|
-
<div className="bg-gray-100 p-8 rounded-lg text-center h-[600px] flex items-center justify-center border-2 border-dashed border-gray-300">
|
|
46
|
-
<div className="text-gray-500">
|
|
47
|
-
<DocumentArrowDownIcon className="h-16 w-16 mx-auto mb-4 text-gray-400" />
|
|
48
|
-
<p className="font-medium">PDF Preview</p>
|
|
49
|
-
<p className="text-sm mt-2">{document.name}</p>
|
|
50
|
-
<Button variant="outline" size="sm" className="mt-4" onClick={onDownload}>
|
|
51
|
-
Download to view
|
|
52
|
-
</Button>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
default:
|
|
57
|
-
return (
|
|
58
|
-
<div className="bg-white p-8 rounded shadow border border-gray-200 h-[600px] overflow-auto font-mono text-sm leading-relaxed">
|
|
59
|
-
{/* Mock text content */}
|
|
60
|
-
<p>Document Content: {document.name}</p>
|
|
61
|
-
<p className="mt-4 text-gray-500">
|
|
62
|
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|
63
|
-
</p>
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<div className="flex flex-col h-full bg-gray-50 rounded-lg overflow-hidden border border-gray-200">
|
|
71
|
-
{/* Header */}
|
|
72
|
-
<div className="bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between shadow-sm">
|
|
73
|
-
<div className="flex items-center gap-3">
|
|
74
|
-
<div className="h-8 w-8 bg-primus-100 rounded flex items-center justify-center text-primus-700">
|
|
75
|
-
<span className="uppercase text-xs font-bold">{document.type}</span>
|
|
76
|
-
</div>
|
|
77
|
-
<div>
|
|
78
|
-
<h3 className="text-sm font-semibold text-gray-900">{document.name}</h3>
|
|
79
|
-
{document.size && <p className="text-xs text-gray-500">{document.size} • {document.date}</p>}
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
<div className="flex items-center gap-2">
|
|
84
|
-
<Button variant="ghost" size="sm" onClick={() => window.print()}>
|
|
85
|
-
<PrinterIcon className="h-4 w-4" />
|
|
86
|
-
</Button>
|
|
87
|
-
<Button variant="ghost" size="sm" onClick={onDownload}>
|
|
88
|
-
<DocumentArrowDownIcon className="h-4 w-4" />
|
|
89
|
-
</Button>
|
|
90
|
-
{onClose && (
|
|
91
|
-
<div className="h-6 w-px bg-gray-200 mx-1" />
|
|
92
|
-
)}
|
|
93
|
-
{onClose && (
|
|
94
|
-
<Button variant="ghost" size="sm" onClick={onClose} className="text-gray-400 hover:text-red-500">
|
|
95
|
-
<XMarkIcon className="h-5 w-5" />
|
|
96
|
-
</Button>
|
|
97
|
-
)}
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
{/* Content Area */}
|
|
102
|
-
<div className="flex-1 overflow-auto p-6 flex flex-col items-center justify-center">
|
|
103
|
-
{renderContent()}
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface FeatureFlag {
|
|
4
|
-
name: string;
|
|
5
|
-
enabled: boolean;
|
|
6
|
-
description: string;
|
|
7
|
-
rolloutPercentage?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const FeatureFlagToggle: React.FC<{ apiUrl?: string }> = ({
|
|
11
|
-
apiUrl = 'http://localhost:5221'
|
|
12
|
-
}) => {
|
|
13
|
-
const [flags, setFlags] = useState<FeatureFlag[]>([]);
|
|
14
|
-
const [loading, setLoading] = useState(true);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
fetchFlags();
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
const fetchFlags = async () => {
|
|
21
|
-
try {
|
|
22
|
-
const response = await fetch(`${apiUrl}/api/featureflags`);
|
|
23
|
-
if (response.ok) {
|
|
24
|
-
const data = await response.json();
|
|
25
|
-
setFlags(data);
|
|
26
|
-
}
|
|
27
|
-
} catch (error) {
|
|
28
|
-
console.error('Failed to fetch feature flags:', error);
|
|
29
|
-
} finally {
|
|
30
|
-
setLoading(false);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
if (loading) {
|
|
35
|
-
return <div className="p-4">Loading feature flags...</div>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div className="bg-white p-6 rounded-lg border border-gray-200 shadow-sm">
|
|
40
|
-
<h3 className="text-lg font-medium text-gray-900 mb-4">Feature Flags</h3>
|
|
41
|
-
<div className="space-y-4">
|
|
42
|
-
{flags.map((flag) => (
|
|
43
|
-
<div key={flag.name} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
|
44
|
-
<div className="flex-1">
|
|
45
|
-
<h4 className="font-medium text-gray-900">{flag.name}</h4>
|
|
46
|
-
<p className="text-sm text-gray-500">{flag.description}</p>
|
|
47
|
-
{flag.rolloutPercentage !== undefined && (
|
|
48
|
-
<p className="text-xs text-gray-400 mt-1">
|
|
49
|
-
Rollout: {flag.rolloutPercentage}%
|
|
50
|
-
</p>
|
|
51
|
-
)}
|
|
52
|
-
</div>
|
|
53
|
-
<div className={`px-3 py-1 rounded-full text-sm font-medium ${flag.enabled
|
|
54
|
-
? 'bg-green-100 text-green-800'
|
|
55
|
-
: 'bg-gray-100 text-gray-800'
|
|
56
|
-
}`}>
|
|
57
|
-
{flag.enabled ? 'Enabled' : 'Disabled'}
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
))}
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
interface Agent {
|
|
4
|
-
id: string;
|
|
5
|
-
name: string;
|
|
6
|
-
email: string;
|
|
7
|
-
phone: string;
|
|
8
|
-
specialty: string;
|
|
9
|
-
rating: number;
|
|
10
|
-
yearsExperience: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const AgentDirectory: React.FC<{ apiUrl?: string }> = ({
|
|
14
|
-
apiUrl = 'http://localhost:5221'
|
|
15
|
-
}) => {
|
|
16
|
-
const [agents, setAgents] = useState<Agent[]>([]);
|
|
17
|
-
const [loading, setLoading] = useState(true);
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
fetchAgents();
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
const fetchAgents = async () => {
|
|
24
|
-
try {
|
|
25
|
-
const response = await fetch(`${apiUrl}/api/insurance/agents`);
|
|
26
|
-
if (response.ok) {
|
|
27
|
-
const data = await response.json();
|
|
28
|
-
setAgents(data);
|
|
29
|
-
}
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error('Failed to fetch agents:', error);
|
|
32
|
-
} finally {
|
|
33
|
-
setLoading(false);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
if (loading) return <div className="p-4">Loading agents...</div>;
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div className="bg-white p-6 rounded-lg border border-gray-200 shadow-sm">
|
|
41
|
-
<h3 className="text-lg font-medium text-gray-900 mb-4">Insurance Agents</h3>
|
|
42
|
-
|
|
43
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
44
|
-
{agents.map((agent) => (
|
|
45
|
-
<div key={agent.id} className="p-4 border border-gray-200 rounded-lg">
|
|
46
|
-
<div className="flex items-start gap-3">
|
|
47
|
-
<div className="w-12 h-12 bg-primus-100 rounded-full flex items-center justify-center text-primus-600 font-bold text-lg">
|
|
48
|
-
{agent.name.split(' ').map(n => n[0]).join('')}
|
|
49
|
-
</div>
|
|
50
|
-
<div className="flex-1">
|
|
51
|
-
<h4 className="font-medium text-gray-900">{agent.name}</h4>
|
|
52
|
-
<p className="text-sm text-gray-600">{agent.specialty}</p>
|
|
53
|
-
<div className="flex items-center gap-2 mt-1">
|
|
54
|
-
<div className="flex items-center">
|
|
55
|
-
{'⭐'.repeat(Math.floor(agent.rating))}
|
|
56
|
-
<span className="text-xs text-gray-500 ml-1">{agent.rating}</span>
|
|
57
|
-
</div>
|
|
58
|
-
<span className="text-xs text-gray-400">•</span>
|
|
59
|
-
<span className="text-xs text-gray-500">{agent.yearsExperience} years</span>
|
|
60
|
-
</div>
|
|
61
|
-
<div className="mt-2 space-y-1">
|
|
62
|
-
<p className="text-xs text-gray-600">{agent.email}</p>
|
|
63
|
-
<p className="text-xs text-gray-600">{agent.phone}</p>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
))}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { clsx } from 'clsx';
|
|
3
|
-
import { CheckIcon } from '@heroicons/react/24/solid';
|
|
4
|
-
|
|
5
|
-
export interface ClaimStep {
|
|
6
|
-
id: string;
|
|
7
|
-
label: string;
|
|
8
|
-
status: 'completed' | 'current' | 'upcoming';
|
|
9
|
-
date?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ClaimStatusTrackerProps {
|
|
13
|
-
claimId: string;
|
|
14
|
-
steps: ClaimStep[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const ClaimStatusTracker: React.FC<ClaimStatusTrackerProps> = ({
|
|
18
|
-
claimId,
|
|
19
|
-
steps
|
|
20
|
-
}) => {
|
|
21
|
-
return (
|
|
22
|
-
<div className="bg-white rounded-xl border border-gray-200 shadow-sm p-6">
|
|
23
|
-
<div className="mb-8">
|
|
24
|
-
<h3 className="text-lg font-semibold text-gray-900">Claim Status</h3>
|
|
25
|
-
<p className="text-sm text-gray-500">Claim ID: #{claimId}</p>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<nav aria-label="Progress">
|
|
29
|
-
<ol role="list" className="overflow-hidden">
|
|
30
|
-
{steps.map((step, stepIdx) => (
|
|
31
|
-
<li key={step.label} className={clsx(
|
|
32
|
-
"relative pb-10",
|
|
33
|
-
stepIdx === steps.length - 1 ? "pb-0" : ""
|
|
34
|
-
)}>
|
|
35
|
-
{stepIdx !== steps.length - 1 ? (
|
|
36
|
-
<div
|
|
37
|
-
className={clsx(
|
|
38
|
-
"absolute top-4 left-4 -ml-px h-full w-0.5",
|
|
39
|
-
step.status === 'completed' ? "bg-primus-600" : "bg-gray-200"
|
|
40
|
-
)}
|
|
41
|
-
aria-hidden="true"
|
|
42
|
-
/>
|
|
43
|
-
) : null}
|
|
44
|
-
|
|
45
|
-
<div className="relative flex items-start group">
|
|
46
|
-
<span className="h-9 flex items-center">
|
|
47
|
-
<span className={clsx(
|
|
48
|
-
"relative z-10 w-8 h-8 flex items-center justify-center rounded-full ring-1 ring-white",
|
|
49
|
-
step.status === 'completed' ? "bg-primus-600" :
|
|
50
|
-
step.status === 'current' ? "bg-white border-2 border-primus-600" :
|
|
51
|
-
"bg-white border-2 border-gray-300"
|
|
52
|
-
)}>
|
|
53
|
-
{step.status === 'completed' ? (
|
|
54
|
-
<CheckIcon className="w-5 h-5 text-white" aria-hidden="true" />
|
|
55
|
-
) : step.status === 'current' ? (
|
|
56
|
-
<span className="h-2.5 w-2.5 bg-primus-600 rounded-full" />
|
|
57
|
-
) : null}
|
|
58
|
-
</span>
|
|
59
|
-
</span>
|
|
60
|
-
<span className="ml-4 min-w-0 flex flex-col">
|
|
61
|
-
<span className={clsx(
|
|
62
|
-
"text-sm font-medium",
|
|
63
|
-
step.status === 'upcoming' ? "text-gray-500" : "text-gray-900"
|
|
64
|
-
)}>
|
|
65
|
-
{step.label}
|
|
66
|
-
</span>
|
|
67
|
-
{step.date && (
|
|
68
|
-
<span className="text-xs text-gray-500 mt-1">{step.date}</span>
|
|
69
|
-
)}
|
|
70
|
-
</span>
|
|
71
|
-
</div>
|
|
72
|
-
</li>
|
|
73
|
-
))}
|
|
74
|
-
</ol>
|
|
75
|
-
</nav>
|
|
76
|
-
</div>
|
|
77
|
-
);
|
|
78
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export const FraudDetectionDashboard: React.FC<{ apiUrl?: string }> = ({
|
|
4
|
-
apiUrl = 'http://localhost:5221'
|
|
5
|
-
}) => {
|
|
6
|
-
const [alerts, setAlerts] = useState<any[]>([]);
|
|
7
|
-
const [loading, setLoading] = useState(true);
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
fetchAlerts();
|
|
11
|
-
}, []);
|
|
12
|
-
|
|
13
|
-
const fetchAlerts = async () => {
|
|
14
|
-
try {
|
|
15
|
-
const response = await fetch(`${apiUrl}/api/insurance/fraud/alerts`);
|
|
16
|
-
if (response.ok) {
|
|
17
|
-
const data = await response.json();
|
|
18
|
-
setAlerts(data);
|
|
19
|
-
}
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.error('Failed to fetch fraud alerts:', error);
|
|
22
|
-
} finally {
|
|
23
|
-
setLoading(false);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const getSeverityColor = (severity: string) => {
|
|
28
|
-
switch (severity.toLowerCase()) {
|
|
29
|
-
case 'high': return 'bg-red-100 text-red-800 border-red-200';
|
|
30
|
-
case 'medium': return 'bg-yellow-100 text-yellow-800 border-yellow-200';
|
|
31
|
-
case 'low': return 'bg-blue-100 text-blue-800 border-blue-200';
|
|
32
|
-
default: return 'bg-gray-100 text-gray-800 border-gray-200';
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
if (loading) return <div className="p-4">Loading fraud alerts...</div>;
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div className="bg-white p-6 rounded-lg border border-gray-200 shadow-sm">
|
|
40
|
-
<div className="flex justify-between items-center mb-4">
|
|
41
|
-
<h3 className="text-lg font-medium text-gray-900">Fraud Detection</h3>
|
|
42
|
-
<span className="px-3 py-1 bg-red-100 text-red-800 rounded-full text-sm font-medium">
|
|
43
|
-
{alerts.length} Active Alerts
|
|
44
|
-
</span>
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
<div className="space-y-3">
|
|
48
|
-
{alerts.map((alert) => (
|
|
49
|
-
<div key={alert.id} className={`p-4 border-2 rounded-lg ${getSeverityColor(alert.severity)}`}>
|
|
50
|
-
<div className="flex justify-between items-start mb-2">
|
|
51
|
-
<div>
|
|
52
|
-
<h4 className="font-medium">{alert.type.replace(/_/g, ' ').toUpperCase()}</h4>
|
|
53
|
-
<p className="text-sm mt-1">{alert.description}</p>
|
|
54
|
-
</div>
|
|
55
|
-
<span className="px-2 py-1 bg-white rounded text-xs font-medium">
|
|
56
|
-
{alert.severity}
|
|
57
|
-
</span>
|
|
58
|
-
</div>
|
|
59
|
-
<div className="flex justify-between items-center mt-3 text-xs">
|
|
60
|
-
<span>Claim: {alert.claimId}</span>
|
|
61
|
-
<span>{new Date(alert.detectedDate).toLocaleString()}</span>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
))}
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Button } from '../../shared/Button';
|
|
3
|
-
import { clsx } from "clsx";
|
|
4
|
-
import { ShieldCheckIcon, DocumentTextIcon } from '@heroicons/react/24/outline';
|
|
5
|
-
|
|
6
|
-
export interface PolicyProps {
|
|
7
|
-
policyNumber: string;
|
|
8
|
-
type: string;
|
|
9
|
-
status: 'active' | 'expired' | 'pending';
|
|
10
|
-
coverageAmount: number;
|
|
11
|
-
nextPaymentDate: string;
|
|
12
|
-
premiumAmount: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const PolicyCard: React.FC<PolicyProps> = ({
|
|
16
|
-
policyNumber,
|
|
17
|
-
type,
|
|
18
|
-
status,
|
|
19
|
-
coverageAmount,
|
|
20
|
-
nextPaymentDate,
|
|
21
|
-
premiumAmount
|
|
22
|
-
}) => {
|
|
23
|
-
return (
|
|
24
|
-
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden hover:shadow-md transition-shadow">
|
|
25
|
-
<div className="bg-gray-50 px-6 py-4 border-b border-gray-100 flex justify-between items-center">
|
|
26
|
-
<div className="flex items-center gap-2">
|
|
27
|
-
<ShieldCheckIcon className="h-5 w-5 text-primus-600" />
|
|
28
|
-
<span className="font-semibold text-gray-900">{type} Insurance</span>
|
|
29
|
-
</div>
|
|
30
|
-
<span className={clsx(
|
|
31
|
-
"px-2.5 py-0.5 rounded-full text-xs font-medium uppercase tracking-wide",
|
|
32
|
-
status === 'active' ? "bg-green-100 text-green-800" :
|
|
33
|
-
status === 'pending' ? "bg-yellow-100 text-yellow-800" :
|
|
34
|
-
"bg-gray-100 text-gray-800"
|
|
35
|
-
)}>
|
|
36
|
-
{status}
|
|
37
|
-
</span>
|
|
38
|
-
</div>
|
|
39
|
-
|
|
40
|
-
<div className="p-6">
|
|
41
|
-
<div className="mb-6">
|
|
42
|
-
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Policy Number</p>
|
|
43
|
-
<p className="font-mono text-lg font-medium text-gray-900">{policyNumber}</p>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<div className="grid grid-cols-2 gap-6 mb-6">
|
|
47
|
-
<div>
|
|
48
|
-
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Coverage</p>
|
|
49
|
-
<p className="text-xl font-bold text-gray-900">
|
|
50
|
-
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(coverageAmount)}
|
|
51
|
-
</p>
|
|
52
|
-
</div>
|
|
53
|
-
<div>
|
|
54
|
-
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Premium</p>
|
|
55
|
-
<p className="text-gray-900 font-medium">
|
|
56
|
-
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(premiumAmount)}
|
|
57
|
-
<span className="text-xs text-gray-500 font-normal"> / month</span>
|
|
58
|
-
</p>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
<div className="bg-blue-50 rounded-lg p-3 flex items-start gap-3 mb-6">
|
|
63
|
-
<DocumentTextIcon className="h-5 w-5 text-blue-600 mt-0.5" />
|
|
64
|
-
<div>
|
|
65
|
-
<p className="text-sm font-medium text-blue-900">Next Payment due</p>
|
|
66
|
-
<p className="text-sm text-blue-700">{nextPaymentDate}</p>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
<div className="flex gap-3">
|
|
71
|
-
<Button variant="outline" className="flex-1">View Details</Button>
|
|
72
|
-
<Button className="flex-1">File Claim</Button>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
};
|