primus-react-ui 1.0.9
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/README.md +146 -0
- package/dist/index.d.mts +485 -0
- package/dist/index.d.ts +485 -0
- package/dist/index.js +2597 -0
- package/dist/index.mjs +2527 -0
- package/package.json +43 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2527 @@
|
|
|
1
|
+
// src/components/banking/accounts/AccountDashboard.tsx
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var AccountDashboard = ({
|
|
5
|
+
apiUrl = "http://localhost:5221"
|
|
6
|
+
}) => {
|
|
7
|
+
const [accounts, setAccounts] = useState([]);
|
|
8
|
+
const [loading, setLoading] = useState(true);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
fetchAccounts();
|
|
11
|
+
}, []);
|
|
12
|
+
const fetchAccounts = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(`${apiUrl}/api/banking/accounts`);
|
|
15
|
+
if (response.ok) {
|
|
16
|
+
const data = await response.json();
|
|
17
|
+
setAccounts(data);
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error("Failed to fetch accounts:", error);
|
|
21
|
+
} finally {
|
|
22
|
+
setLoading(false);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
if (loading) return /* @__PURE__ */ jsx("div", { className: "p-4", children: "Loading accounts..." });
|
|
26
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
27
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Bank Accounts" }),
|
|
28
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-4", children: accounts.map((account) => /* @__PURE__ */ jsxs("div", { className: "p-4 bg-gray-50 rounded-lg border border-gray-200", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start", children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
31
|
+
/* @__PURE__ */ jsx("h4", { className: "font-medium text-gray-900", children: account.type }),
|
|
32
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: account.accountNumber })
|
|
33
|
+
] }),
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { className: "text-right", children: [
|
|
35
|
+
/* @__PURE__ */ jsxs("p", { className: "text-2xl font-bold text-gray-900", children: [
|
|
36
|
+
"$",
|
|
37
|
+
account.balance.toLocaleString()
|
|
38
|
+
] }),
|
|
39
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: account.currency })
|
|
40
|
+
] })
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("span", { className: `px-2 py-1 rounded text-xs font-medium ${account.status === "active" ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`, children: account.status }) })
|
|
43
|
+
] }, account.id)) })
|
|
44
|
+
] });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/components/banking/kyc/KYCVerification.tsx
|
|
48
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
49
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
50
|
+
var KYCVerification = ({
|
|
51
|
+
userId,
|
|
52
|
+
apiUrl = "http://localhost:5221"
|
|
53
|
+
}) => {
|
|
54
|
+
const [status, setStatus] = useState2(null);
|
|
55
|
+
const [loading, setLoading] = useState2(true);
|
|
56
|
+
useEffect2(() => {
|
|
57
|
+
fetchStatus();
|
|
58
|
+
}, [userId]);
|
|
59
|
+
const fetchStatus = async () => {
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(`${apiUrl}/api/banking/kyc/status/${userId}`);
|
|
62
|
+
if (response.ok) {
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
setStatus(data);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("Failed to fetch KYC status:", error);
|
|
68
|
+
} finally {
|
|
69
|
+
setLoading(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (loading) return /* @__PURE__ */ jsx2("div", { className: "p-4", children: "Loading KYC status..." });
|
|
73
|
+
if (!status) return /* @__PURE__ */ jsx2("div", { className: "p-4", children: "No KYC data found" });
|
|
74
|
+
return /* @__PURE__ */ jsxs2("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
75
|
+
/* @__PURE__ */ jsx2("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "KYC Verification Status" }),
|
|
76
|
+
/* @__PURE__ */ jsxs2("div", { className: "mb-6", children: [
|
|
77
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
78
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-gray-600", children: "Status:" }),
|
|
79
|
+
/* @__PURE__ */ jsx2("span", { className: `px-3 py-1 rounded-full text-sm font-medium ${status.status === "verified" ? "bg-green-100 text-green-800" : "bg-yellow-100 text-yellow-800"}`, children: status.status })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
82
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-gray-600", children: "Risk Level:" }),
|
|
83
|
+
/* @__PURE__ */ jsx2("span", { className: `px-3 py-1 rounded-full text-sm font-medium ${status.riskLevel === "low" ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`, children: status.riskLevel })
|
|
84
|
+
] })
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
87
|
+
/* @__PURE__ */ jsx2("h4", { className: "font-medium text-gray-900 mb-3", children: "Documents" }),
|
|
88
|
+
/* @__PURE__ */ jsx2("div", { className: "space-y-2", children: status.documents.map((doc, index) => /* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center p-3 bg-gray-50 rounded", children: [
|
|
89
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-gray-900", children: doc.type }),
|
|
90
|
+
/* @__PURE__ */ jsx2("span", { className: `px-2 py-1 rounded text-xs font-medium ${doc.status === "approved" ? "bg-green-100 text-green-800" : "bg-yellow-100 text-yellow-800"}`, children: doc.status })
|
|
91
|
+
] }, index)) })
|
|
92
|
+
] })
|
|
93
|
+
] });
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// src/components/banking/loans/LoanCalculator.tsx
|
|
97
|
+
import { useState as useState3 } from "react";
|
|
98
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
99
|
+
var LoanCalculator = ({
|
|
100
|
+
apiUrl = "http://localhost:5221"
|
|
101
|
+
}) => {
|
|
102
|
+
const [amount, setAmount] = useState3(25e3);
|
|
103
|
+
const [rate, setRate] = useState3(5.5);
|
|
104
|
+
const [term, setTerm] = useState3(60);
|
|
105
|
+
const [result, setResult] = useState3(null);
|
|
106
|
+
const [loading, setLoading] = useState3(false);
|
|
107
|
+
const calculate = async () => {
|
|
108
|
+
setLoading(true);
|
|
109
|
+
try {
|
|
110
|
+
const response = await fetch(`${apiUrl}/api/banking/loans/calculate`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: { "Content-Type": "application/json" },
|
|
113
|
+
body: JSON.stringify({ amount, interestRate: rate, term })
|
|
114
|
+
});
|
|
115
|
+
if (response.ok) {
|
|
116
|
+
const data = await response.json();
|
|
117
|
+
setResult(data);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error("Failed to calculate loan:", error);
|
|
121
|
+
} finally {
|
|
122
|
+
setLoading(false);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return /* @__PURE__ */ jsxs3("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
126
|
+
/* @__PURE__ */ jsx3("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Loan Calculator" }),
|
|
127
|
+
/* @__PURE__ */ jsxs3("div", { className: "space-y-4 mb-6", children: [
|
|
128
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
129
|
+
/* @__PURE__ */ jsxs3("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
130
|
+
"Loan Amount: $",
|
|
131
|
+
amount.toLocaleString()
|
|
132
|
+
] }),
|
|
133
|
+
/* @__PURE__ */ jsx3(
|
|
134
|
+
"input",
|
|
135
|
+
{
|
|
136
|
+
type: "range",
|
|
137
|
+
min: "1000",
|
|
138
|
+
max: "100000",
|
|
139
|
+
step: "1000",
|
|
140
|
+
value: amount,
|
|
141
|
+
onChange: (e) => setAmount(Number(e.target.value)),
|
|
142
|
+
className: "w-full"
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
] }),
|
|
146
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
147
|
+
/* @__PURE__ */ jsxs3("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
148
|
+
"Interest Rate: ",
|
|
149
|
+
rate,
|
|
150
|
+
"%"
|
|
151
|
+
] }),
|
|
152
|
+
/* @__PURE__ */ jsx3(
|
|
153
|
+
"input",
|
|
154
|
+
{
|
|
155
|
+
type: "range",
|
|
156
|
+
min: "1",
|
|
157
|
+
max: "15",
|
|
158
|
+
step: "0.1",
|
|
159
|
+
value: rate,
|
|
160
|
+
onChange: (e) => setRate(Number(e.target.value)),
|
|
161
|
+
className: "w-full"
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
166
|
+
/* @__PURE__ */ jsxs3("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
167
|
+
"Term: ",
|
|
168
|
+
term,
|
|
169
|
+
" months"
|
|
170
|
+
] }),
|
|
171
|
+
/* @__PURE__ */ jsx3(
|
|
172
|
+
"input",
|
|
173
|
+
{
|
|
174
|
+
type: "range",
|
|
175
|
+
min: "12",
|
|
176
|
+
max: "360",
|
|
177
|
+
step: "12",
|
|
178
|
+
value: term,
|
|
179
|
+
onChange: (e) => setTerm(Number(e.target.value)),
|
|
180
|
+
className: "w-full"
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
] })
|
|
184
|
+
] }),
|
|
185
|
+
/* @__PURE__ */ jsx3(
|
|
186
|
+
"button",
|
|
187
|
+
{
|
|
188
|
+
onClick: calculate,
|
|
189
|
+
disabled: loading,
|
|
190
|
+
className: "w-full bg-primus-600 text-white py-2 px-4 rounded hover:bg-primus-700 disabled:opacity-50",
|
|
191
|
+
children: loading ? "Calculating..." : "Calculate"
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
result && /* @__PURE__ */ jsx3("div", { className: "mt-6 p-4 bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
195
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
196
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-600", children: "Monthly Payment" }),
|
|
197
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-2xl font-bold text-gray-900", children: [
|
|
198
|
+
"$",
|
|
199
|
+
result.monthlyPayment
|
|
200
|
+
] })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
203
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-600", children: "Total Interest" }),
|
|
204
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-2xl font-bold text-gray-900", children: [
|
|
205
|
+
"$",
|
|
206
|
+
result.totalInterest
|
|
207
|
+
] })
|
|
208
|
+
] })
|
|
209
|
+
] }) })
|
|
210
|
+
] });
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/components/banking/credit/CreditScoreCard.tsx
|
|
214
|
+
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
215
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
216
|
+
var CreditScoreCard = ({
|
|
217
|
+
userId,
|
|
218
|
+
apiUrl = "http://localhost:5221"
|
|
219
|
+
}) => {
|
|
220
|
+
const [score, setScore] = useState4(null);
|
|
221
|
+
const [loading, setLoading] = useState4(true);
|
|
222
|
+
useEffect3(() => {
|
|
223
|
+
fetchScore();
|
|
224
|
+
}, [userId]);
|
|
225
|
+
const fetchScore = async () => {
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(`${apiUrl}/api/banking/credit-score/${userId}`);
|
|
228
|
+
if (response.ok) {
|
|
229
|
+
const data = await response.json();
|
|
230
|
+
setScore(data);
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error("Failed to fetch credit score:", error);
|
|
234
|
+
} finally {
|
|
235
|
+
setLoading(false);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
if (loading) return /* @__PURE__ */ jsx4("div", { className: "p-4", children: "Loading credit score..." });
|
|
239
|
+
if (!score) return /* @__PURE__ */ jsx4("div", { className: "p-4", children: "No credit score data" });
|
|
240
|
+
const getScoreColor = (score2) => {
|
|
241
|
+
if (score2 >= 740) return "text-green-600";
|
|
242
|
+
if (score2 >= 670) return "text-blue-600";
|
|
243
|
+
if (score2 >= 580) return "text-yellow-600";
|
|
244
|
+
return "text-red-600";
|
|
245
|
+
};
|
|
246
|
+
return /* @__PURE__ */ jsxs4("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
247
|
+
/* @__PURE__ */ jsx4("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Credit Score" }),
|
|
248
|
+
/* @__PURE__ */ jsxs4("div", { className: "text-center mb-6", children: [
|
|
249
|
+
/* @__PURE__ */ jsx4("div", { className: `text-6xl font-bold ${getScoreColor(score.score)}`, children: score.score }),
|
|
250
|
+
/* @__PURE__ */ jsx4("div", { className: "text-lg text-gray-600 mt-2", children: score.rating }),
|
|
251
|
+
/* @__PURE__ */ jsxs4("div", { className: "text-sm text-gray-400 mt-1", children: [
|
|
252
|
+
"Updated: ",
|
|
253
|
+
new Date(score.lastUpdated).toLocaleDateString()
|
|
254
|
+
] })
|
|
255
|
+
] }),
|
|
256
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
257
|
+
/* @__PURE__ */ jsx4("h4", { className: "font-medium text-gray-900 mb-3", children: "Score Factors" }),
|
|
258
|
+
/* @__PURE__ */ jsx4("div", { className: "space-y-2", children: score.factors.map((factor, index) => /* @__PURE__ */ jsxs4("div", { className: "flex justify-between items-center p-2 bg-gray-50 rounded", children: [
|
|
259
|
+
/* @__PURE__ */ jsx4("span", { className: "text-sm text-gray-900", children: factor.factor }),
|
|
260
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
261
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-xs text-gray-500", children: [
|
|
262
|
+
factor.weight,
|
|
263
|
+
"%"
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsx4("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${factor.impact === "positive" ? "bg-green-100 text-green-800" : factor.impact === "negative" ? "bg-red-100 text-red-800" : "bg-gray-100 text-gray-800"}`, children: factor.impact })
|
|
266
|
+
] })
|
|
267
|
+
] }, index)) })
|
|
268
|
+
] })
|
|
269
|
+
] });
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/components/insurance/quotes/QuoteComparison.tsx
|
|
273
|
+
import { useState as useState5, useEffect as useEffect4 } from "react";
|
|
274
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
275
|
+
var QuoteComparison = ({
|
|
276
|
+
apiUrl = "http://localhost:5221"
|
|
277
|
+
}) => {
|
|
278
|
+
const [quotes, setQuotes] = useState5([]);
|
|
279
|
+
const [loading, setLoading] = useState5(true);
|
|
280
|
+
useEffect4(() => {
|
|
281
|
+
fetchQuotes();
|
|
282
|
+
}, []);
|
|
283
|
+
const fetchQuotes = async () => {
|
|
284
|
+
try {
|
|
285
|
+
const response = await fetch(`${apiUrl}/api/insurance/quotes`);
|
|
286
|
+
if (response.ok) {
|
|
287
|
+
const data = await response.json();
|
|
288
|
+
setQuotes(data);
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error("Failed to fetch quotes:", error);
|
|
292
|
+
} finally {
|
|
293
|
+
setLoading(false);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
if (loading) return /* @__PURE__ */ jsx5("div", { className: "p-4", children: "Loading quotes..." });
|
|
297
|
+
return /* @__PURE__ */ jsxs5("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
298
|
+
/* @__PURE__ */ jsx5("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Insurance Quotes" }),
|
|
299
|
+
/* @__PURE__ */ jsx5("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: quotes.map((quote) => /* @__PURE__ */ jsxs5("div", { className: "p-4 border-2 border-gray-200 rounded-lg hover:border-primus-500 transition-colors", children: [
|
|
300
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex justify-between items-start mb-3", children: [
|
|
301
|
+
/* @__PURE__ */ jsx5("h4", { className: "font-medium text-gray-900", children: quote.type }),
|
|
302
|
+
/* @__PURE__ */ jsx5("span", { className: `px-2 py-1 rounded text-xs font-medium ${quote.status === "active" ? "bg-green-100 text-green-800" : "bg-blue-100 text-blue-800"}`, children: quote.status })
|
|
303
|
+
] }),
|
|
304
|
+
/* @__PURE__ */ jsxs5("div", { className: "mb-3", children: [
|
|
305
|
+
/* @__PURE__ */ jsxs5("div", { className: "text-3xl font-bold text-gray-900", children: [
|
|
306
|
+
"$",
|
|
307
|
+
quote.monthlyPremium,
|
|
308
|
+
/* @__PURE__ */ jsx5("span", { className: "text-sm font-normal text-gray-500", children: "/mo" })
|
|
309
|
+
] }),
|
|
310
|
+
/* @__PURE__ */ jsxs5("div", { className: "text-sm text-gray-600", children: [
|
|
311
|
+
"Coverage: $",
|
|
312
|
+
quote.coverageAmount.toLocaleString()
|
|
313
|
+
] })
|
|
314
|
+
] }),
|
|
315
|
+
/* @__PURE__ */ jsxs5("div", { className: "text-xs text-gray-500", children: [
|
|
316
|
+
"Valid until: ",
|
|
317
|
+
new Date(quote.validUntil).toLocaleDateString()
|
|
318
|
+
] }),
|
|
319
|
+
/* @__PURE__ */ jsx5("button", { className: "mt-3 w-full px-4 py-2 bg-primus-600 text-white rounded hover:bg-primus-700", children: "Accept Quote" })
|
|
320
|
+
] }, quote.id)) })
|
|
321
|
+
] });
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/components/insurance/agents/AgentDirectory.tsx
|
|
325
|
+
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
326
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
327
|
+
var AgentDirectory = ({
|
|
328
|
+
apiUrl = "http://localhost:5221"
|
|
329
|
+
}) => {
|
|
330
|
+
const [agents, setAgents] = useState6([]);
|
|
331
|
+
const [loading, setLoading] = useState6(true);
|
|
332
|
+
useEffect5(() => {
|
|
333
|
+
fetchAgents();
|
|
334
|
+
}, []);
|
|
335
|
+
const fetchAgents = async () => {
|
|
336
|
+
try {
|
|
337
|
+
const response = await fetch(`${apiUrl}/api/insurance/agents`);
|
|
338
|
+
if (response.ok) {
|
|
339
|
+
const data = await response.json();
|
|
340
|
+
setAgents(data);
|
|
341
|
+
}
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error("Failed to fetch agents:", error);
|
|
344
|
+
} finally {
|
|
345
|
+
setLoading(false);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
if (loading) return /* @__PURE__ */ jsx6("div", { className: "p-4", children: "Loading agents..." });
|
|
349
|
+
return /* @__PURE__ */ jsxs6("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
350
|
+
/* @__PURE__ */ jsx6("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Insurance Agents" }),
|
|
351
|
+
/* @__PURE__ */ jsx6("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: agents.map((agent) => /* @__PURE__ */ jsx6("div", { className: "p-4 border border-gray-200 rounded-lg", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-start gap-3", children: [
|
|
352
|
+
/* @__PURE__ */ jsx6("div", { className: "w-12 h-12 bg-primus-100 rounded-full flex items-center justify-center text-primus-600 font-bold text-lg", children: agent.name.split(" ").map((n) => n[0]).join("") }),
|
|
353
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex-1", children: [
|
|
354
|
+
/* @__PURE__ */ jsx6("h4", { className: "font-medium text-gray-900", children: agent.name }),
|
|
355
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm text-gray-600", children: agent.specialty }),
|
|
356
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 mt-1", children: [
|
|
357
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center", children: [
|
|
358
|
+
"\u2B50".repeat(Math.floor(agent.rating)),
|
|
359
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs text-gray-500 ml-1", children: agent.rating })
|
|
360
|
+
] }),
|
|
361
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs text-gray-400", children: "\u2022" }),
|
|
362
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-xs text-gray-500", children: [
|
|
363
|
+
agent.yearsExperience,
|
|
364
|
+
" years"
|
|
365
|
+
] })
|
|
366
|
+
] }),
|
|
367
|
+
/* @__PURE__ */ jsxs6("div", { className: "mt-2 space-y-1", children: [
|
|
368
|
+
/* @__PURE__ */ jsx6("p", { className: "text-xs text-gray-600", children: agent.email }),
|
|
369
|
+
/* @__PURE__ */ jsx6("p", { className: "text-xs text-gray-600", children: agent.phone })
|
|
370
|
+
] })
|
|
371
|
+
] })
|
|
372
|
+
] }) }, agent.id)) })
|
|
373
|
+
] });
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/components/insurance/fraud/FraudDetectionDashboard.tsx
|
|
377
|
+
import { useState as useState7, useEffect as useEffect6 } from "react";
|
|
378
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
379
|
+
var FraudDetectionDashboard = ({
|
|
380
|
+
apiUrl = "http://localhost:5221"
|
|
381
|
+
}) => {
|
|
382
|
+
const [alerts, setAlerts] = useState7([]);
|
|
383
|
+
const [loading, setLoading] = useState7(true);
|
|
384
|
+
useEffect6(() => {
|
|
385
|
+
fetchAlerts();
|
|
386
|
+
}, []);
|
|
387
|
+
const fetchAlerts = async () => {
|
|
388
|
+
try {
|
|
389
|
+
const response = await fetch(`${apiUrl}/api/insurance/fraud/alerts`);
|
|
390
|
+
if (response.ok) {
|
|
391
|
+
const data = await response.json();
|
|
392
|
+
setAlerts(data);
|
|
393
|
+
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error("Failed to fetch fraud alerts:", error);
|
|
396
|
+
} finally {
|
|
397
|
+
setLoading(false);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const getSeverityColor = (severity) => {
|
|
401
|
+
switch (severity.toLowerCase()) {
|
|
402
|
+
case "high":
|
|
403
|
+
return "bg-red-100 text-red-800 border-red-200";
|
|
404
|
+
case "medium":
|
|
405
|
+
return "bg-yellow-100 text-yellow-800 border-yellow-200";
|
|
406
|
+
case "low":
|
|
407
|
+
return "bg-blue-100 text-blue-800 border-blue-200";
|
|
408
|
+
default:
|
|
409
|
+
return "bg-gray-100 text-gray-800 border-gray-200";
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
if (loading) return /* @__PURE__ */ jsx7("div", { className: "p-4", children: "Loading fraud alerts..." });
|
|
413
|
+
return /* @__PURE__ */ jsxs7("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
414
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex justify-between items-center mb-4", children: [
|
|
415
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-lg font-medium text-gray-900", children: "Fraud Detection" }),
|
|
416
|
+
/* @__PURE__ */ jsxs7("span", { className: "px-3 py-1 bg-red-100 text-red-800 rounded-full text-sm font-medium", children: [
|
|
417
|
+
alerts.length,
|
|
418
|
+
" Active Alerts"
|
|
419
|
+
] })
|
|
420
|
+
] }),
|
|
421
|
+
/* @__PURE__ */ jsx7("div", { className: "space-y-3", children: alerts.map((alert) => /* @__PURE__ */ jsxs7("div", { className: `p-4 border-2 rounded-lg ${getSeverityColor(alert.severity)}`, children: [
|
|
422
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex justify-between items-start mb-2", children: [
|
|
423
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
424
|
+
/* @__PURE__ */ jsx7("h4", { className: "font-medium", children: alert.type.replace(/_/g, " ").toUpperCase() }),
|
|
425
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm mt-1", children: alert.description })
|
|
426
|
+
] }),
|
|
427
|
+
/* @__PURE__ */ jsx7("span", { className: "px-2 py-1 bg-white rounded text-xs font-medium", children: alert.severity })
|
|
428
|
+
] }),
|
|
429
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex justify-between items-center mt-3 text-xs", children: [
|
|
430
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
431
|
+
"Claim: ",
|
|
432
|
+
alert.claimId
|
|
433
|
+
] }),
|
|
434
|
+
/* @__PURE__ */ jsx7("span", { children: new Date(alert.detectedDate).toLocaleString() })
|
|
435
|
+
] })
|
|
436
|
+
] }, alert.id)) })
|
|
437
|
+
] });
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// src/components/insurance/premium/PremiumCalculator.tsx
|
|
441
|
+
import { useState as useState8 } from "react";
|
|
442
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
443
|
+
var PremiumCalculator = ({
|
|
444
|
+
apiUrl = "http://localhost:5221"
|
|
445
|
+
}) => {
|
|
446
|
+
const [coverageAmount, setCoverageAmount] = useState8(1e5);
|
|
447
|
+
const [age, setAge] = useState8(35);
|
|
448
|
+
const [riskScore, setRiskScore] = useState8(30);
|
|
449
|
+
const [result, setResult] = useState8(null);
|
|
450
|
+
const [loading, setLoading] = useState8(false);
|
|
451
|
+
const calculate = async () => {
|
|
452
|
+
setLoading(true);
|
|
453
|
+
try {
|
|
454
|
+
const response = await fetch(`${apiUrl}/api/insurance/premium/calculate`, {
|
|
455
|
+
method: "POST",
|
|
456
|
+
headers: { "Content-Type": "application/json" },
|
|
457
|
+
body: JSON.stringify({ coverageAmount, age, riskScore })
|
|
458
|
+
});
|
|
459
|
+
if (response.ok) {
|
|
460
|
+
const data = await response.json();
|
|
461
|
+
setResult(data);
|
|
462
|
+
}
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error("Failed to calculate premium:", error);
|
|
465
|
+
} finally {
|
|
466
|
+
setLoading(false);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
return /* @__PURE__ */ jsxs8("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
470
|
+
/* @__PURE__ */ jsx8("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Premium Calculator" }),
|
|
471
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-4 mb-6", children: [
|
|
472
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
473
|
+
/* @__PURE__ */ jsxs8("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
474
|
+
"Coverage Amount: $",
|
|
475
|
+
coverageAmount.toLocaleString()
|
|
476
|
+
] }),
|
|
477
|
+
/* @__PURE__ */ jsx8(
|
|
478
|
+
"input",
|
|
479
|
+
{
|
|
480
|
+
type: "range",
|
|
481
|
+
min: "10000",
|
|
482
|
+
max: "1000000",
|
|
483
|
+
step: "10000",
|
|
484
|
+
value: coverageAmount,
|
|
485
|
+
onChange: (e) => setCoverageAmount(Number(e.target.value)),
|
|
486
|
+
className: "w-full"
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
] }),
|
|
490
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
491
|
+
/* @__PURE__ */ jsxs8("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
492
|
+
"Age: ",
|
|
493
|
+
age
|
|
494
|
+
] }),
|
|
495
|
+
/* @__PURE__ */ jsx8(
|
|
496
|
+
"input",
|
|
497
|
+
{
|
|
498
|
+
type: "range",
|
|
499
|
+
min: "18",
|
|
500
|
+
max: "80",
|
|
501
|
+
value: age,
|
|
502
|
+
onChange: (e) => setAge(Number(e.target.value)),
|
|
503
|
+
className: "w-full"
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
508
|
+
/* @__PURE__ */ jsxs8("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
|
|
509
|
+
"Risk Score: ",
|
|
510
|
+
riskScore
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ jsx8(
|
|
513
|
+
"input",
|
|
514
|
+
{
|
|
515
|
+
type: "range",
|
|
516
|
+
min: "0",
|
|
517
|
+
max: "100",
|
|
518
|
+
value: riskScore,
|
|
519
|
+
onChange: (e) => setRiskScore(Number(e.target.value)),
|
|
520
|
+
className: "w-full"
|
|
521
|
+
}
|
|
522
|
+
)
|
|
523
|
+
] })
|
|
524
|
+
] }),
|
|
525
|
+
/* @__PURE__ */ jsx8(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
onClick: calculate,
|
|
529
|
+
disabled: loading,
|
|
530
|
+
className: "w-full bg-primus-600 text-white py-2 px-4 rounded hover:bg-primus-700 disabled:opacity-50",
|
|
531
|
+
children: loading ? "Calculating..." : "Calculate Premium"
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
result && /* @__PURE__ */ jsx8("div", { className: "mt-6 p-4 bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
535
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
536
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-gray-600", children: "Monthly Premium" }),
|
|
537
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-2xl font-bold text-gray-900", children: [
|
|
538
|
+
"$",
|
|
539
|
+
result.monthlyPremium
|
|
540
|
+
] })
|
|
541
|
+
] }),
|
|
542
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
543
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm text-gray-600", children: "Annual Premium" }),
|
|
544
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-2xl font-bold text-gray-900", children: [
|
|
545
|
+
"$",
|
|
546
|
+
result.annualPremium
|
|
547
|
+
] })
|
|
548
|
+
] })
|
|
549
|
+
] }) })
|
|
550
|
+
] });
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/components/ai/AICopilot.tsx
|
|
554
|
+
import { useState as useState9 } from "react";
|
|
555
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
556
|
+
var AICopilot = ({
|
|
557
|
+
apiUrl = "http://localhost:5221"
|
|
558
|
+
}) => {
|
|
559
|
+
const [messages, setMessages] = useState9([]);
|
|
560
|
+
const [input, setInput] = useState9("");
|
|
561
|
+
const [loading, setLoading] = useState9(false);
|
|
562
|
+
const sendMessage = async () => {
|
|
563
|
+
if (!input.trim()) return;
|
|
564
|
+
const userMessage = { role: "user", content: input };
|
|
565
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
566
|
+
setInput("");
|
|
567
|
+
setLoading(true);
|
|
568
|
+
try {
|
|
569
|
+
const response = await fetch(`${apiUrl}/api/ai/chat`, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
headers: { "Content-Type": "application/json" },
|
|
572
|
+
body: JSON.stringify({ message: input })
|
|
573
|
+
});
|
|
574
|
+
if (response.ok) {
|
|
575
|
+
const data = await response.json();
|
|
576
|
+
setMessages((prev) => [...prev, { role: "assistant", content: data.message }]);
|
|
577
|
+
}
|
|
578
|
+
} catch (error) {
|
|
579
|
+
console.error("Failed to send message:", error);
|
|
580
|
+
} finally {
|
|
581
|
+
setLoading(false);
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
return /* @__PURE__ */ jsxs9("div", { className: "bg-white rounded-lg border border-gray-200 shadow-sm flex flex-col h-[600px]", children: [
|
|
585
|
+
/* @__PURE__ */ jsx9("div", { className: "p-4 border-b border-gray-200", children: /* @__PURE__ */ jsx9("h3", { className: "text-lg font-medium text-gray-900", children: "AI Copilot" }) }),
|
|
586
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [
|
|
587
|
+
messages.map((msg, index) => /* @__PURE__ */ jsx9("div", { className: `flex ${msg.role === "user" ? "justify-end" : "justify-start"}`, children: /* @__PURE__ */ jsx9("div", { className: `max-w-[70%] p-3 rounded-lg ${msg.role === "user" ? "bg-primus-600 text-white" : "bg-gray-100 text-gray-900"}`, children: msg.content }) }, index)),
|
|
588
|
+
loading && /* @__PURE__ */ jsx9("div", { className: "flex justify-start", children: /* @__PURE__ */ jsx9("div", { className: "bg-gray-100 p-3 rounded-lg", children: /* @__PURE__ */ jsxs9("div", { className: "flex gap-1", children: [
|
|
589
|
+
/* @__PURE__ */ jsx9("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce" }),
|
|
590
|
+
/* @__PURE__ */ jsx9("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "0.1s" } }),
|
|
591
|
+
/* @__PURE__ */ jsx9("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "0.2s" } })
|
|
592
|
+
] }) }) })
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsx9("div", { className: "p-4 border-t border-gray-200", children: /* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
595
|
+
/* @__PURE__ */ jsx9(
|
|
596
|
+
"input",
|
|
597
|
+
{
|
|
598
|
+
type: "text",
|
|
599
|
+
value: input,
|
|
600
|
+
onChange: (e) => setInput(e.target.value),
|
|
601
|
+
onKeyPress: (e) => e.key === "Enter" && sendMessage(),
|
|
602
|
+
placeholder: "Ask me anything...",
|
|
603
|
+
className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primus-500",
|
|
604
|
+
disabled: loading
|
|
605
|
+
}
|
|
606
|
+
),
|
|
607
|
+
/* @__PURE__ */ jsx9(
|
|
608
|
+
"button",
|
|
609
|
+
{
|
|
610
|
+
onClick: sendMessage,
|
|
611
|
+
disabled: loading || !input.trim(),
|
|
612
|
+
className: "px-4 py-2 bg-primus-600 text-white rounded-lg hover:bg-primus-700 disabled:opacity-50",
|
|
613
|
+
children: "Send"
|
|
614
|
+
}
|
|
615
|
+
)
|
|
616
|
+
] }) })
|
|
617
|
+
] });
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// src/components/storage/FileUploader.tsx
|
|
621
|
+
import { useState as useState10 } from "react";
|
|
622
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
623
|
+
var FileUploader = ({
|
|
624
|
+
apiUrl = "http://localhost:5221",
|
|
625
|
+
onUploadComplete
|
|
626
|
+
}) => {
|
|
627
|
+
const [uploading, setUploading] = useState10(false);
|
|
628
|
+
const [dragActive, setDragActive] = useState10(false);
|
|
629
|
+
const handleUpload = async (file) => {
|
|
630
|
+
setUploading(true);
|
|
631
|
+
const formData = new FormData();
|
|
632
|
+
formData.append("file", file);
|
|
633
|
+
try {
|
|
634
|
+
const response = await fetch(`${apiUrl}/api/storage/upload`, {
|
|
635
|
+
method: "POST",
|
|
636
|
+
body: formData
|
|
637
|
+
});
|
|
638
|
+
if (response.ok) {
|
|
639
|
+
const data = await response.json();
|
|
640
|
+
onUploadComplete?.(data);
|
|
641
|
+
}
|
|
642
|
+
} catch (error) {
|
|
643
|
+
console.error("Upload failed:", error);
|
|
644
|
+
} finally {
|
|
645
|
+
setUploading(false);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const handleDrop = (e) => {
|
|
649
|
+
e.preventDefault();
|
|
650
|
+
setDragActive(false);
|
|
651
|
+
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
|
|
652
|
+
handleUpload(e.dataTransfer.files[0]);
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
return /* @__PURE__ */ jsxs10("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
656
|
+
/* @__PURE__ */ jsx10("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Upload File" }),
|
|
657
|
+
/* @__PURE__ */ jsx10(
|
|
658
|
+
"div",
|
|
659
|
+
{
|
|
660
|
+
className: `border-2 border-dashed rounded-lg p-8 text-center ${dragActive ? "border-primus-500 bg-primus-50" : "border-gray-300"}`,
|
|
661
|
+
onDragOver: (e) => {
|
|
662
|
+
e.preventDefault();
|
|
663
|
+
setDragActive(true);
|
|
664
|
+
},
|
|
665
|
+
onDragLeave: () => setDragActive(false),
|
|
666
|
+
onDrop: handleDrop,
|
|
667
|
+
children: uploading ? /* @__PURE__ */ jsx10("div", { className: "text-gray-600", children: "Uploading..." }) : /* @__PURE__ */ jsxs10(Fragment, { children: [
|
|
668
|
+
/* @__PURE__ */ jsx10("svg", { className: "mx-auto h-12 w-12 text-gray-400", stroke: "currentColor", fill: "none", viewBox: "0 0 48 48", children: /* @__PURE__ */ jsx10("path", { d: "M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
669
|
+
/* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-gray-600", children: "Drag and drop a file here, or click to select" }),
|
|
670
|
+
/* @__PURE__ */ jsx10(
|
|
671
|
+
"input",
|
|
672
|
+
{
|
|
673
|
+
type: "file",
|
|
674
|
+
className: "hidden",
|
|
675
|
+
id: "file-upload",
|
|
676
|
+
onChange: (e) => e.target.files?.[0] && handleUpload(e.target.files[0])
|
|
677
|
+
}
|
|
678
|
+
),
|
|
679
|
+
/* @__PURE__ */ jsx10(
|
|
680
|
+
"label",
|
|
681
|
+
{
|
|
682
|
+
htmlFor: "file-upload",
|
|
683
|
+
className: "mt-4 inline-block px-4 py-2 bg-primus-600 text-white rounded cursor-pointer hover:bg-primus-700",
|
|
684
|
+
children: "Select File"
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
] })
|
|
688
|
+
}
|
|
689
|
+
)
|
|
690
|
+
] });
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
// src/components/security/SecurityDashboard.tsx
|
|
694
|
+
import { useState as useState11, useEffect as useEffect7 } from "react";
|
|
695
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
696
|
+
var SecurityDashboard = ({
|
|
697
|
+
apiUrl = "http://localhost:5221"
|
|
698
|
+
}) => {
|
|
699
|
+
const [vulnerabilities, setVulnerabilities] = useState11([]);
|
|
700
|
+
const [loading, setLoading] = useState11(true);
|
|
701
|
+
useEffect7(() => {
|
|
702
|
+
fetchVulnerabilities();
|
|
703
|
+
}, []);
|
|
704
|
+
const fetchVulnerabilities = async () => {
|
|
705
|
+
try {
|
|
706
|
+
const response = await fetch(`${apiUrl}/api/security/vulnerabilities`);
|
|
707
|
+
if (response.ok) {
|
|
708
|
+
const data = await response.json();
|
|
709
|
+
setVulnerabilities(data);
|
|
710
|
+
}
|
|
711
|
+
} catch (error) {
|
|
712
|
+
console.error("Failed to fetch vulnerabilities:", error);
|
|
713
|
+
} finally {
|
|
714
|
+
setLoading(false);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
const getSeverityColor = (severity) => {
|
|
718
|
+
switch (severity.toLowerCase()) {
|
|
719
|
+
case "critical":
|
|
720
|
+
return "bg-red-100 text-red-800";
|
|
721
|
+
case "high":
|
|
722
|
+
return "bg-orange-100 text-orange-800";
|
|
723
|
+
case "medium":
|
|
724
|
+
return "bg-yellow-100 text-yellow-800";
|
|
725
|
+
case "low":
|
|
726
|
+
return "bg-blue-100 text-blue-800";
|
|
727
|
+
default:
|
|
728
|
+
return "bg-gray-100 text-gray-800";
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
if (loading) return /* @__PURE__ */ jsx11("div", { className: "p-4", children: "Scanning for vulnerabilities..." });
|
|
732
|
+
return /* @__PURE__ */ jsxs11("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
733
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex justify-between items-center mb-4", children: [
|
|
734
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-lg font-medium text-gray-900", children: "Security Dashboard" }),
|
|
735
|
+
/* @__PURE__ */ jsx11(
|
|
736
|
+
"button",
|
|
737
|
+
{
|
|
738
|
+
onClick: fetchVulnerabilities,
|
|
739
|
+
className: "px-3 py-1 bg-primus-600 text-white rounded text-sm hover:bg-primus-700",
|
|
740
|
+
children: "Rescan"
|
|
741
|
+
}
|
|
742
|
+
)
|
|
743
|
+
] }),
|
|
744
|
+
/* @__PURE__ */ jsx11("div", { className: "space-y-3", children: vulnerabilities.map((vuln) => /* @__PURE__ */ jsxs11("div", { className: "p-4 border border-gray-200 rounded-lg", children: [
|
|
745
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex justify-between items-start mb-2", children: [
|
|
746
|
+
/* @__PURE__ */ jsx11("h4", { className: "font-medium text-gray-900", children: vuln.title }),
|
|
747
|
+
/* @__PURE__ */ jsx11("span", { className: `px-2 py-1 rounded text-xs font-medium ${getSeverityColor(vuln.severity)}`, children: vuln.severity })
|
|
748
|
+
] }),
|
|
749
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm text-gray-600 mb-2", children: vuln.description }),
|
|
750
|
+
vuln.file && /* @__PURE__ */ jsxs11("p", { className: "text-xs text-gray-500 mb-2", children: [
|
|
751
|
+
vuln.file,
|
|
752
|
+
vuln.line ? `:${vuln.line}` : ""
|
|
753
|
+
] }),
|
|
754
|
+
/* @__PURE__ */ jsxs11("p", { className: "text-sm text-primus-600", children: [
|
|
755
|
+
"\u{1F4A1} ",
|
|
756
|
+
vuln.recommendation
|
|
757
|
+
] })
|
|
758
|
+
] }, vuln.id)) })
|
|
759
|
+
] });
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// src/components/auth/PrimusLogin.tsx
|
|
763
|
+
import { useState as useState13 } from "react";
|
|
764
|
+
|
|
765
|
+
// src/context/PrimusThemeProvider.tsx
|
|
766
|
+
import { createContext, useContext, useState as useState12, useCallback } from "react";
|
|
767
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
768
|
+
var themeColors = {
|
|
769
|
+
light: {
|
|
770
|
+
bg: "bg-white",
|
|
771
|
+
bgSecondary: "bg-gray-50",
|
|
772
|
+
bgTertiary: "bg-gray-100",
|
|
773
|
+
bgHover: "hover:bg-gray-100",
|
|
774
|
+
border: "border-gray-200",
|
|
775
|
+
borderSecondary: "border-gray-100",
|
|
776
|
+
text: "text-gray-900",
|
|
777
|
+
textSecondary: "text-gray-600",
|
|
778
|
+
textMuted: "text-gray-400",
|
|
779
|
+
accent: "bg-violet-600",
|
|
780
|
+
accentHover: "hover:bg-violet-500",
|
|
781
|
+
accentText: "text-violet-600",
|
|
782
|
+
badge: "bg-orange-500",
|
|
783
|
+
success: "text-emerald-600",
|
|
784
|
+
warning: "text-amber-600",
|
|
785
|
+
error: "text-rose-600",
|
|
786
|
+
info: "text-blue-600"
|
|
787
|
+
},
|
|
788
|
+
dark: {
|
|
789
|
+
bg: "bg-gray-900",
|
|
790
|
+
bgSecondary: "bg-gray-800",
|
|
791
|
+
bgTertiary: "bg-gray-800/50",
|
|
792
|
+
bgHover: "hover:bg-gray-800",
|
|
793
|
+
border: "border-gray-700",
|
|
794
|
+
borderSecondary: "border-gray-800",
|
|
795
|
+
text: "text-white",
|
|
796
|
+
textSecondary: "text-gray-400",
|
|
797
|
+
textMuted: "text-gray-500",
|
|
798
|
+
accent: "bg-blue-600",
|
|
799
|
+
accentHover: "hover:bg-blue-500",
|
|
800
|
+
accentText: "text-blue-400",
|
|
801
|
+
badge: "bg-rose-500",
|
|
802
|
+
success: "text-emerald-400",
|
|
803
|
+
warning: "text-amber-400",
|
|
804
|
+
error: "text-rose-400",
|
|
805
|
+
info: "text-blue-400"
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
var PrimusThemeContext = createContext(void 0);
|
|
809
|
+
function PrimusThemeProvider({ children, defaultTheme = "dark" }) {
|
|
810
|
+
const [theme, setTheme] = useState12(defaultTheme);
|
|
811
|
+
const toggleTheme = useCallback(() => {
|
|
812
|
+
setTheme((prev) => prev === "dark" ? "light" : "dark");
|
|
813
|
+
}, []);
|
|
814
|
+
const value = {
|
|
815
|
+
theme,
|
|
816
|
+
colors: themeColors[theme],
|
|
817
|
+
setTheme,
|
|
818
|
+
toggleTheme
|
|
819
|
+
};
|
|
820
|
+
return /* @__PURE__ */ jsx12(PrimusThemeContext.Provider, { value, children });
|
|
821
|
+
}
|
|
822
|
+
function usePrimusTheme() {
|
|
823
|
+
const context = useContext(PrimusThemeContext);
|
|
824
|
+
if (!context) {
|
|
825
|
+
return {
|
|
826
|
+
theme: "light",
|
|
827
|
+
colors: themeColors.light,
|
|
828
|
+
setTheme: () => console.warn("PrimusThemeProvider not found"),
|
|
829
|
+
toggleTheme: () => console.warn("PrimusThemeProvider not found")
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
return context;
|
|
833
|
+
}
|
|
834
|
+
function PrimusThemeToggle() {
|
|
835
|
+
const { theme, toggleTheme, colors } = usePrimusTheme();
|
|
836
|
+
return /* @__PURE__ */ jsx12(
|
|
837
|
+
"button",
|
|
838
|
+
{
|
|
839
|
+
onClick: toggleTheme,
|
|
840
|
+
className: `p-2 rounded-lg ${colors.bgHover} transition-colors`,
|
|
841
|
+
title: `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
|
|
842
|
+
children: theme === "dark" ? /* @__PURE__ */ jsx12("svg", { className: "h-5 w-5 text-yellow-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx12("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) }) : /* @__PURE__ */ jsx12("svg", { className: "h-5 w-5 text-gray-600", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx12("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) })
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/components/auth/PrimusLogin.tsx
|
|
848
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
849
|
+
var providerConfigs = {
|
|
850
|
+
google: {
|
|
851
|
+
name: "Google",
|
|
852
|
+
icon: /* @__PURE__ */ jsxs12("svg", { className: "h-5 w-5", viewBox: "0 0 24 24", children: [
|
|
853
|
+
/* @__PURE__ */ jsx13("path", { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" }),
|
|
854
|
+
/* @__PURE__ */ jsx13("path", { fill: "#34A853", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }),
|
|
855
|
+
/* @__PURE__ */ jsx13("path", { fill: "#FBBC05", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }),
|
|
856
|
+
/* @__PURE__ */ jsx13("path", { fill: "#EA4335", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" })
|
|
857
|
+
] }),
|
|
858
|
+
colors: { light: "bg-white hover:bg-gray-50 text-gray-700 border-gray-300", dark: "bg-white hover:bg-gray-100 text-gray-700 border-gray-300" }
|
|
859
|
+
},
|
|
860
|
+
azure: {
|
|
861
|
+
name: "Microsoft",
|
|
862
|
+
icon: /* @__PURE__ */ jsxs12("svg", { className: "h-5 w-5", viewBox: "0 0 23 23", children: [
|
|
863
|
+
/* @__PURE__ */ jsx13("path", { fill: "#f35325", d: "M1 1h10v10H1z" }),
|
|
864
|
+
/* @__PURE__ */ jsx13("path", { fill: "#81bc06", d: "M12 1h10v10H12z" }),
|
|
865
|
+
/* @__PURE__ */ jsx13("path", { fill: "#05a6f0", d: "M1 12h10v10H1z" }),
|
|
866
|
+
/* @__PURE__ */ jsx13("path", { fill: "#ffba08", d: "M12 12h10v10H12z" })
|
|
867
|
+
] }),
|
|
868
|
+
colors: { light: "bg-white hover:bg-gray-50 text-gray-700 border-gray-300", dark: "bg-white hover:bg-gray-100 text-gray-700 border-gray-300" }
|
|
869
|
+
},
|
|
870
|
+
github: {
|
|
871
|
+
name: "GitHub",
|
|
872
|
+
icon: /* @__PURE__ */ jsx13("svg", { className: "h-5 w-5", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { fillRule: "evenodd", d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z", clipRule: "evenodd" }) }),
|
|
873
|
+
colors: { light: "bg-gray-900 hover:bg-gray-800 text-white border-gray-900", dark: "bg-white hover:bg-gray-100 text-gray-900 border-gray-300" }
|
|
874
|
+
},
|
|
875
|
+
microsoft: {
|
|
876
|
+
name: "Microsoft",
|
|
877
|
+
icon: /* @__PURE__ */ jsxs12("svg", { className: "h-5 w-5", viewBox: "0 0 24 24", children: [
|
|
878
|
+
/* @__PURE__ */ jsx13("path", { fill: "#F25022", d: "M1 1h10v10H1z" }),
|
|
879
|
+
/* @__PURE__ */ jsx13("path", { fill: "#00A4EF", d: "M1 13h10v10H1z" }),
|
|
880
|
+
/* @__PURE__ */ jsx13("path", { fill: "#7FBA00", d: "M13 1h10v10H13z" }),
|
|
881
|
+
/* @__PURE__ */ jsx13("path", { fill: "#FFB900", d: "M13 13h10v10H13z" })
|
|
882
|
+
] }),
|
|
883
|
+
colors: { light: "bg-white hover:bg-gray-50 text-gray-700 border-gray-300", dark: "bg-white hover:bg-gray-100 text-gray-700 border-gray-300" }
|
|
884
|
+
},
|
|
885
|
+
apple: {
|
|
886
|
+
name: "Apple",
|
|
887
|
+
icon: /* @__PURE__ */ jsx13("svg", { className: "h-5 w-5", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { d: "M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" }) }),
|
|
888
|
+
colors: { light: "bg-black hover:bg-gray-900 text-white border-black", dark: "bg-white hover:bg-gray-100 text-black border-gray-300" }
|
|
889
|
+
},
|
|
890
|
+
facebook: {
|
|
891
|
+
name: "Facebook",
|
|
892
|
+
icon: /* @__PURE__ */ jsx13("svg", { className: "h-5 w-5", fill: "white", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx13("path", { d: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" }) }),
|
|
893
|
+
colors: { light: "bg-[#1877F2] hover:bg-[#166FE5] text-white border-[#1877F2]", dark: "bg-[#1877F2] hover:bg-[#166FE5] text-white border-[#1877F2]" }
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
function PrimusLogin({
|
|
897
|
+
onLogin,
|
|
898
|
+
socialProviders = [],
|
|
899
|
+
onSocialLogin,
|
|
900
|
+
authEndpoint = "/api/auth",
|
|
901
|
+
showEmailLogin = true,
|
|
902
|
+
title = "Welcome Back",
|
|
903
|
+
subtitle = "Enter your credentials to continue",
|
|
904
|
+
logo,
|
|
905
|
+
theme: themeOverride
|
|
906
|
+
}) {
|
|
907
|
+
const themeContext = usePrimusTheme();
|
|
908
|
+
const theme = themeOverride || themeContext.theme;
|
|
909
|
+
const isLight = theme === "light";
|
|
910
|
+
const [username, setUsername] = useState13("");
|
|
911
|
+
const [password, setPassword] = useState13("");
|
|
912
|
+
const [loading, setLoading] = useState13(false);
|
|
913
|
+
const handleSubmit = async (e) => {
|
|
914
|
+
e.preventDefault();
|
|
915
|
+
if (!username || !password) return;
|
|
916
|
+
setLoading(true);
|
|
917
|
+
try {
|
|
918
|
+
onLogin?.({ username, password });
|
|
919
|
+
} finally {
|
|
920
|
+
setLoading(false);
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const handleSocialClick = (provider) => {
|
|
924
|
+
if (onSocialLogin) {
|
|
925
|
+
onSocialLogin(provider);
|
|
926
|
+
} else {
|
|
927
|
+
window.location.href = `${authEndpoint}/${provider}`;
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
const bgGradient = isLight ? "bg-gradient-to-br from-gray-50 via-white to-violet-50" : "bg-gray-950";
|
|
931
|
+
const cardBg = isLight ? "bg-white border-gray-200 shadow-xl" : "bg-gray-900/50 border-white/10";
|
|
932
|
+
const headingColor = isLight ? "text-gray-900" : "text-white";
|
|
933
|
+
const subtextColor = isLight ? "text-gray-500" : "text-slate-400";
|
|
934
|
+
const labelColor = isLight ? "text-gray-600" : "text-slate-300";
|
|
935
|
+
const inputClasses = isLight ? "bg-gray-50 border-gray-300 text-gray-900 placeholder:text-gray-400 focus:border-violet-500 focus:ring-violet-500" : "bg-gray-950/50 border-slate-700 text-white placeholder:text-slate-600 focus:border-blue-500 focus:ring-blue-500";
|
|
936
|
+
const buttonGradient = isLight ? "bg-gradient-to-r from-violet-600 to-purple-600" : "bg-gradient-to-r from-blue-600 to-indigo-600";
|
|
937
|
+
const logoBg = isLight ? "bg-violet-600 shadow-violet-500/20" : "bg-blue-600 shadow-blue-500/20";
|
|
938
|
+
const dividerColor = isLight ? "border-gray-200" : "border-slate-700";
|
|
939
|
+
const hasSocial = socialProviders.length > 0;
|
|
940
|
+
return /* @__PURE__ */ jsxs12("div", { className: `min-h-screen ${bgGradient} flex items-center justify-center p-4`, children: [
|
|
941
|
+
!isLight && /* @__PURE__ */ jsxs12("div", { className: "absolute inset-0 overflow-hidden", children: [
|
|
942
|
+
/* @__PURE__ */ jsx13("div", { className: "absolute -top-[20%] -left-[10%] w-[50%] h-[50%] bg-blue-600/20 rounded-full blur-[120px]" }),
|
|
943
|
+
/* @__PURE__ */ jsx13("div", { className: "absolute -bottom-[20%] -right-[10%] w-[50%] h-[50%] bg-purple-600/20 rounded-full blur-[120px]" })
|
|
944
|
+
] }),
|
|
945
|
+
/* @__PURE__ */ jsxs12("div", { className: `w-full max-w-md ${cardBg} p-8 rounded-2xl border backdrop-blur-xl relative z-10`, children: [
|
|
946
|
+
/* @__PURE__ */ jsxs12("div", { className: "text-center mb-8", children: [
|
|
947
|
+
logo ? typeof logo === "string" ? /* @__PURE__ */ jsx13("img", { src: logo, alt: "Logo", className: "h-12 w-12 mx-auto mb-4 rounded-xl" }) : /* @__PURE__ */ jsx13("div", { className: "mb-4", children: logo }) : /* @__PURE__ */ jsx13("div", { className: `h-12 w-12 ${logoBg} rounded-xl mx-auto flex items-center justify-center mb-4 shadow-lg`, children: /* @__PURE__ */ jsx13("span", { className: "font-bold text-xl text-white", children: "P" }) }),
|
|
948
|
+
/* @__PURE__ */ jsx13("h1", { className: `text-2xl font-bold ${headingColor} tracking-tight`, children: title }),
|
|
949
|
+
/* @__PURE__ */ jsx13("p", { className: `${subtextColor} text-sm mt-2`, children: subtitle })
|
|
950
|
+
] }),
|
|
951
|
+
hasSocial && /* @__PURE__ */ jsx13("div", { className: "space-y-3 mb-6", children: socialProviders.map((provider) => {
|
|
952
|
+
const config = providerConfigs[provider];
|
|
953
|
+
if (!config) return null;
|
|
954
|
+
return /* @__PURE__ */ jsxs12(
|
|
955
|
+
"button",
|
|
956
|
+
{
|
|
957
|
+
onClick: () => handleSocialClick(provider),
|
|
958
|
+
className: `w-full flex items-center justify-center gap-3 px-4 py-2.5 border rounded-lg font-medium text-sm transition-all duration-200 ${isLight ? config.colors.light : config.colors.dark}`,
|
|
959
|
+
children: [
|
|
960
|
+
config.icon,
|
|
961
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
962
|
+
"Continue with ",
|
|
963
|
+
config.name
|
|
964
|
+
] })
|
|
965
|
+
]
|
|
966
|
+
},
|
|
967
|
+
provider
|
|
968
|
+
);
|
|
969
|
+
}) }),
|
|
970
|
+
hasSocial && showEmailLogin && /* @__PURE__ */ jsxs12("div", { className: "relative my-6", children: [
|
|
971
|
+
/* @__PURE__ */ jsx13("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx13("div", { className: `w-full border-t ${dividerColor}` }) }),
|
|
972
|
+
/* @__PURE__ */ jsx13("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsx13("span", { className: `${isLight ? "bg-white" : "bg-gray-900/50"} px-2 ${subtextColor}`, children: "Or continue with email" }) })
|
|
973
|
+
] }),
|
|
974
|
+
showEmailLogin && /* @__PURE__ */ jsxs12("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
975
|
+
/* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
|
|
976
|
+
/* @__PURE__ */ jsx13("label", { className: `text-xs font-medium ${labelColor} ml-1`, children: "EMAIL" }),
|
|
977
|
+
/* @__PURE__ */ jsx13(
|
|
978
|
+
"input",
|
|
979
|
+
{
|
|
980
|
+
type: "text",
|
|
981
|
+
value: username,
|
|
982
|
+
onChange: (e) => setUsername(e.target.value),
|
|
983
|
+
className: `w-full px-4 py-2.5 ${inputClasses} border rounded-lg focus:outline-none focus:ring-1 transition-all text-sm`,
|
|
984
|
+
placeholder: "you@example.com"
|
|
985
|
+
}
|
|
986
|
+
)
|
|
987
|
+
] }),
|
|
988
|
+
/* @__PURE__ */ jsxs12("div", { className: "space-y-2", children: [
|
|
989
|
+
/* @__PURE__ */ jsx13("label", { className: `text-xs font-medium ${labelColor} ml-1`, children: "PASSWORD" }),
|
|
990
|
+
/* @__PURE__ */ jsx13(
|
|
991
|
+
"input",
|
|
992
|
+
{
|
|
993
|
+
type: "password",
|
|
994
|
+
value: password,
|
|
995
|
+
onChange: (e) => setPassword(e.target.value),
|
|
996
|
+
className: `w-full px-4 py-2.5 ${inputClasses} border rounded-lg focus:outline-none focus:ring-1 transition-all text-sm`,
|
|
997
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
|
|
998
|
+
}
|
|
999
|
+
)
|
|
1000
|
+
] }),
|
|
1001
|
+
/* @__PURE__ */ jsx13(
|
|
1002
|
+
"button",
|
|
1003
|
+
{
|
|
1004
|
+
type: "submit",
|
|
1005
|
+
disabled: loading,
|
|
1006
|
+
className: `w-full mt-4 py-2.5 ${buttonGradient} text-white font-medium rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50`,
|
|
1007
|
+
children: loading ? "Signing in..." : "Sign In"
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
] })
|
|
1011
|
+
] })
|
|
1012
|
+
] });
|
|
1013
|
+
}
|
|
1014
|
+
var LoginPage = PrimusLogin;
|
|
1015
|
+
|
|
1016
|
+
// src/context/PrimusProvider.tsx
|
|
1017
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect8, useState as useState14, useCallback as useCallback2 } from "react";
|
|
1018
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1019
|
+
var PrimusAuthContext = createContext2(void 0);
|
|
1020
|
+
var PrimusProvider = ({
|
|
1021
|
+
children,
|
|
1022
|
+
authority,
|
|
1023
|
+
clientId,
|
|
1024
|
+
onLoginSuccess,
|
|
1025
|
+
onLoginError
|
|
1026
|
+
}) => {
|
|
1027
|
+
void clientId;
|
|
1028
|
+
const [isAuthenticated, setIsAuthenticated] = useState14(false);
|
|
1029
|
+
const [user, setUser] = useState14(null);
|
|
1030
|
+
const [token, setToken] = useState14(null);
|
|
1031
|
+
const [isLoading, setIsLoading] = useState14(false);
|
|
1032
|
+
useEffect8(() => {
|
|
1033
|
+
const storedToken = localStorage.getItem("primus_token");
|
|
1034
|
+
const storedUser = localStorage.getItem("primus_user");
|
|
1035
|
+
if (storedToken && storedUser) {
|
|
1036
|
+
try {
|
|
1037
|
+
setToken(storedToken);
|
|
1038
|
+
setUser(JSON.parse(storedUser));
|
|
1039
|
+
setIsAuthenticated(true);
|
|
1040
|
+
} catch {
|
|
1041
|
+
localStorage.removeItem("primus_token");
|
|
1042
|
+
localStorage.removeItem("primus_user");
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}, []);
|
|
1046
|
+
const login = useCallback2(async (credentials) => {
|
|
1047
|
+
setIsLoading(true);
|
|
1048
|
+
try {
|
|
1049
|
+
let endpoint;
|
|
1050
|
+
let body;
|
|
1051
|
+
if ("provider" in credentials) {
|
|
1052
|
+
endpoint = `${authority}/auth/${credentials.provider}`;
|
|
1053
|
+
body = {};
|
|
1054
|
+
} else {
|
|
1055
|
+
endpoint = `${authority}/auth/local`;
|
|
1056
|
+
body = { email: credentials.email, password: credentials.password };
|
|
1057
|
+
}
|
|
1058
|
+
const response = await fetch(endpoint, {
|
|
1059
|
+
method: "POST",
|
|
1060
|
+
headers: { "Content-Type": "application/json" },
|
|
1061
|
+
body: JSON.stringify(body)
|
|
1062
|
+
});
|
|
1063
|
+
if (!response.ok) {
|
|
1064
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1065
|
+
const errorMsg = errorData.message || `Login failed (${response.status})`;
|
|
1066
|
+
onLoginError?.(errorMsg);
|
|
1067
|
+
return { success: false, error: errorMsg };
|
|
1068
|
+
}
|
|
1069
|
+
const data = await response.json();
|
|
1070
|
+
const accessToken = data.access_token || data.token;
|
|
1071
|
+
const userData = data.user || {
|
|
1072
|
+
name: "Authenticated User",
|
|
1073
|
+
email: "provider" in credentials ? `${credentials.provider}@primus.local` : credentials.email
|
|
1074
|
+
};
|
|
1075
|
+
localStorage.setItem("primus_token", accessToken);
|
|
1076
|
+
localStorage.setItem("primus_user", JSON.stringify(userData));
|
|
1077
|
+
setToken(accessToken);
|
|
1078
|
+
setUser(userData);
|
|
1079
|
+
setIsAuthenticated(true);
|
|
1080
|
+
onLoginSuccess?.(userData, accessToken);
|
|
1081
|
+
return { success: true };
|
|
1082
|
+
} catch (err) {
|
|
1083
|
+
const errorMsg = err instanceof Error ? err.message : "Network error";
|
|
1084
|
+
onLoginError?.(errorMsg);
|
|
1085
|
+
return { success: false, error: errorMsg };
|
|
1086
|
+
} finally {
|
|
1087
|
+
setIsLoading(false);
|
|
1088
|
+
}
|
|
1089
|
+
}, [authority, onLoginSuccess, onLoginError]);
|
|
1090
|
+
const logout = useCallback2(async () => {
|
|
1091
|
+
localStorage.removeItem("primus_token");
|
|
1092
|
+
localStorage.removeItem("primus_user");
|
|
1093
|
+
setToken(null);
|
|
1094
|
+
setIsAuthenticated(false);
|
|
1095
|
+
setUser(null);
|
|
1096
|
+
}, []);
|
|
1097
|
+
return /* @__PURE__ */ jsx14(PrimusAuthContext.Provider, { value: { isAuthenticated, user, login, logout, token, isLoading }, children });
|
|
1098
|
+
};
|
|
1099
|
+
var usePrimusAuth = () => {
|
|
1100
|
+
const context = useContext2(PrimusAuthContext);
|
|
1101
|
+
if (context === void 0) {
|
|
1102
|
+
return {
|
|
1103
|
+
isAuthenticated: false,
|
|
1104
|
+
user: null,
|
|
1105
|
+
login: async () => ({ success: false, error: "PrimusProvider not configured" }),
|
|
1106
|
+
logout: async () => {
|
|
1107
|
+
},
|
|
1108
|
+
token: null,
|
|
1109
|
+
isLoading: false
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
return context;
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
// src/components/shared/Button.tsx
|
|
1116
|
+
import React13 from "react";
|
|
1117
|
+
import { clsx } from "clsx";
|
|
1118
|
+
import { twMerge } from "tailwind-merge";
|
|
1119
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1120
|
+
function cn(...inputs) {
|
|
1121
|
+
return twMerge(clsx(inputs));
|
|
1122
|
+
}
|
|
1123
|
+
var Button = React13.forwardRef(
|
|
1124
|
+
({ className, variant = "primary", size = "md", ...props }, ref) => {
|
|
1125
|
+
return /* @__PURE__ */ jsx15(
|
|
1126
|
+
"button",
|
|
1127
|
+
{
|
|
1128
|
+
ref,
|
|
1129
|
+
className: cn(
|
|
1130
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
1131
|
+
{
|
|
1132
|
+
"bg-gray-900 text-gray-50 hover:bg-gray-900/90": variant === "primary",
|
|
1133
|
+
"bg-gray-100 text-gray-900 hover:bg-gray-100/80": variant === "secondary",
|
|
1134
|
+
"border border-gray-200 bg-white hover:bg-gray-100 hover:text-gray-900": variant === "outline",
|
|
1135
|
+
"hover:bg-gray-100 hover:text-gray-900": variant === "ghost",
|
|
1136
|
+
"h-9 px-3": size === "sm",
|
|
1137
|
+
"h-10 px-4 py-2": size === "md",
|
|
1138
|
+
"h-11 px-8": size === "lg"
|
|
1139
|
+
},
|
|
1140
|
+
className
|
|
1141
|
+
),
|
|
1142
|
+
...props
|
|
1143
|
+
}
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
);
|
|
1147
|
+
|
|
1148
|
+
// src/components/auth/UserProfile.tsx
|
|
1149
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1150
|
+
var UserProfile = () => {
|
|
1151
|
+
const { user, logout, isAuthenticated } = usePrimusAuth();
|
|
1152
|
+
if (!isAuthenticated || !user) {
|
|
1153
|
+
return null;
|
|
1154
|
+
}
|
|
1155
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-4 p-4 rounded-lg bg-gray-50 border border-gray-200", children: [
|
|
1156
|
+
/* @__PURE__ */ jsx16("div", { className: "h-10 w-10 rounded-full bg-primus-100 flex items-center justify-center text-primus-600 font-bold", children: user.name?.charAt(0) || "U" }),
|
|
1157
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex-1", children: [
|
|
1158
|
+
/* @__PURE__ */ jsx16("p", { className: "text-sm font-medium text-gray-900", children: user.name }),
|
|
1159
|
+
/* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-500", children: user.email })
|
|
1160
|
+
] }),
|
|
1161
|
+
/* @__PURE__ */ jsx16(Button, { variant: "outline", size: "sm", onClick: () => logout(), children: "Sign out" })
|
|
1162
|
+
] });
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
// src/hooks/useNotifications.ts
|
|
1166
|
+
import { useState as useState15, useEffect as useEffect9 } from "react";
|
|
1167
|
+
var useNotifications = (apiUrl = "http://localhost:5221") => {
|
|
1168
|
+
const [notifications, setNotifications] = useState15([]);
|
|
1169
|
+
const [unreadCount, setUnreadCount] = useState15(0);
|
|
1170
|
+
const [loading, setLoading] = useState15(true);
|
|
1171
|
+
useEffect9(() => {
|
|
1172
|
+
fetchNotifications();
|
|
1173
|
+
const interval = setInterval(fetchNotifications, 3e3);
|
|
1174
|
+
return () => clearInterval(interval);
|
|
1175
|
+
}, []);
|
|
1176
|
+
const fetchNotifications = async () => {
|
|
1177
|
+
try {
|
|
1178
|
+
const response = await fetch(`${apiUrl}/notifications`);
|
|
1179
|
+
if (response.ok) {
|
|
1180
|
+
const data = await response.json();
|
|
1181
|
+
setNotifications(data);
|
|
1182
|
+
setUnreadCount(data.filter((n) => !n.read).length);
|
|
1183
|
+
}
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
console.error("Failed to fetch notifications:", error);
|
|
1186
|
+
} finally {
|
|
1187
|
+
setLoading(false);
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
const markAsRead = (id) => {
|
|
1191
|
+
setNotifications((prev) => prev.map((n) => n.id === id ? { ...n, read: true } : n));
|
|
1192
|
+
setUnreadCount((prev) => Math.max(0, prev - 1));
|
|
1193
|
+
};
|
|
1194
|
+
const markAllAsRead = () => {
|
|
1195
|
+
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
|
|
1196
|
+
setUnreadCount(0);
|
|
1197
|
+
};
|
|
1198
|
+
return {
|
|
1199
|
+
notifications,
|
|
1200
|
+
unreadCount,
|
|
1201
|
+
loading,
|
|
1202
|
+
markAsRead,
|
|
1203
|
+
markAllAsRead,
|
|
1204
|
+
refresh: fetchNotifications
|
|
1205
|
+
};
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
// src/components/notifications/NotificationFeed.tsx
|
|
1209
|
+
import { BellIcon, CheckCircleIcon, ExclamationTriangleIcon, InformationCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
|
1210
|
+
import { Popover, Transition } from "@headlessui/react";
|
|
1211
|
+
import { Fragment as Fragment2 } from "react";
|
|
1212
|
+
import { clsx as clsx2 } from "clsx";
|
|
1213
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1214
|
+
var NotificationIcon = ({ type }) => {
|
|
1215
|
+
switch (type) {
|
|
1216
|
+
case "success":
|
|
1217
|
+
return /* @__PURE__ */ jsx17(CheckCircleIcon, { className: "h-6 w-6 text-green-500" });
|
|
1218
|
+
case "warning":
|
|
1219
|
+
return /* @__PURE__ */ jsx17(ExclamationTriangleIcon, { className: "h-6 w-6 text-yellow-500" });
|
|
1220
|
+
case "error":
|
|
1221
|
+
return /* @__PURE__ */ jsx17(XCircleIcon, { className: "h-6 w-6 text-red-500" });
|
|
1222
|
+
default:
|
|
1223
|
+
return /* @__PURE__ */ jsx17(InformationCircleIcon, { className: "h-6 w-6 text-blue-500" });
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
var NotificationFeed = () => {
|
|
1227
|
+
const { notifications, unreadCount, markAsRead, markAllAsRead } = useNotifications();
|
|
1228
|
+
return /* @__PURE__ */ jsx17(Popover, { className: "relative", children: ({ open }) => /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
1229
|
+
/* @__PURE__ */ jsxs14(Popover.Button, { className: clsx2(
|
|
1230
|
+
"relative p-2 rounded-full hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primus-500",
|
|
1231
|
+
open && "bg-gray-100"
|
|
1232
|
+
), children: [
|
|
1233
|
+
/* @__PURE__ */ jsx17(BellIcon, { className: "h-6 w-6 text-gray-600", "aria-hidden": "true" }),
|
|
1234
|
+
unreadCount > 0 && /* @__PURE__ */ jsx17("span", { className: "absolute top-1 right-1 block h-2.5 w-2.5 rounded-full bg-red-500 ring-2 ring-white" })
|
|
1235
|
+
] }),
|
|
1236
|
+
/* @__PURE__ */ jsx17(
|
|
1237
|
+
Transition,
|
|
1238
|
+
{
|
|
1239
|
+
as: Fragment2,
|
|
1240
|
+
enter: "transition ease-out duration-200",
|
|
1241
|
+
enterFrom: "opacity-0 translate-y-1",
|
|
1242
|
+
enterTo: "opacity-100 translate-y-0",
|
|
1243
|
+
leave: "transition ease-in duration-150",
|
|
1244
|
+
leaveFrom: "opacity-100 translate-y-0",
|
|
1245
|
+
leaveTo: "opacity-0 translate-y-1",
|
|
1246
|
+
children: /* @__PURE__ */ jsxs14(Popover.Panel, { className: "absolute right-0 z-10 mt-2 w-80 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:w-96", children: [
|
|
1247
|
+
/* @__PURE__ */ jsxs14("div", { className: "px-4 py-3 flex items-center justify-between border-b border-gray-100", children: [
|
|
1248
|
+
/* @__PURE__ */ jsx17("h3", { className: "text-sm font-semibold text-gray-900", children: "Notifications" }),
|
|
1249
|
+
unreadCount > 0 && /* @__PURE__ */ jsx17(
|
|
1250
|
+
"button",
|
|
1251
|
+
{
|
|
1252
|
+
onClick: () => markAllAsRead(),
|
|
1253
|
+
className: "text-xs font-medium text-primus-600 hover:text-primus-500",
|
|
1254
|
+
children: "Mark all as read"
|
|
1255
|
+
}
|
|
1256
|
+
)
|
|
1257
|
+
] }),
|
|
1258
|
+
/* @__PURE__ */ jsx17("div", { className: "max-h-96 overflow-y-auto", children: notifications.length === 0 ? /* @__PURE__ */ jsx17("div", { className: "px-4 py-6 text-center text-sm text-gray-500", children: "No new notifications" }) : /* @__PURE__ */ jsx17("div", { className: "divide-y divide-gray-100", children: notifications.map((notification) => /* @__PURE__ */ jsxs14(
|
|
1259
|
+
"div",
|
|
1260
|
+
{
|
|
1261
|
+
className: clsx2(
|
|
1262
|
+
"flex gap-3 px-4 py-4 hover:bg-gray-50 transition-colors cursor-pointer",
|
|
1263
|
+
!notification.read && "bg-blue-50/50"
|
|
1264
|
+
),
|
|
1265
|
+
onClick: () => markAsRead(notification.id),
|
|
1266
|
+
children: [
|
|
1267
|
+
/* @__PURE__ */ jsx17("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx17(NotificationIcon, { type: notification.type }) }),
|
|
1268
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex-1 min-w-0", children: [
|
|
1269
|
+
/* @__PURE__ */ jsx17("p", { className: "text-sm font-medium text-gray-900", children: notification.title }),
|
|
1270
|
+
/* @__PURE__ */ jsx17("p", { className: "text-sm text-gray-500 truncate", children: notification.message }),
|
|
1271
|
+
/* @__PURE__ */ jsx17("p", { className: "mt-1 text-xs text-gray-400", children: new Date(notification.timestamp).toLocaleDateString() })
|
|
1272
|
+
] }),
|
|
1273
|
+
!notification.read && /* @__PURE__ */ jsx17("div", { className: "flex-shrink-0 self-center", children: /* @__PURE__ */ jsx17("span", { className: "block h-2 w-2 rounded-full bg-primus-500" }) })
|
|
1274
|
+
]
|
|
1275
|
+
},
|
|
1276
|
+
notification.id
|
|
1277
|
+
)) }) }),
|
|
1278
|
+
/* @__PURE__ */ jsx17("div", { className: "border-t border-gray-100 px-4 py-2", children: /* @__PURE__ */ jsx17("a", { href: "#", className: "block text-center text-sm font-medium text-primus-600 hover:text-primus-500", children: "View all notifications" }) })
|
|
1279
|
+
] })
|
|
1280
|
+
}
|
|
1281
|
+
)
|
|
1282
|
+
] }) });
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
// src/components/notifications/PrimusNotificationCenter.tsx
|
|
1286
|
+
import { useState as useState16, useEffect as useEffect10, useCallback as useCallback3 } from "react";
|
|
1287
|
+
import { Bell, Send, X, Check, AlertTriangle, Info, AlertCircle } from "lucide-react";
|
|
1288
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1289
|
+
var API_URL = "http://localhost:5222";
|
|
1290
|
+
function PrimusNotificationCenter({ theme: themeOverride, apiUrl = API_URL }) {
|
|
1291
|
+
const themeContext = usePrimusTheme();
|
|
1292
|
+
const theme = themeOverride || themeContext.theme;
|
|
1293
|
+
const t = themeColors[theme];
|
|
1294
|
+
const [notifications, setNotifications] = useState16([]);
|
|
1295
|
+
const [isOpen, setIsOpen] = useState16(false);
|
|
1296
|
+
const [showSendForm, setShowSendForm] = useState16(false);
|
|
1297
|
+
const [unreadCount, setUnreadCount] = useState16(0);
|
|
1298
|
+
const [newTitle, setNewTitle] = useState16("");
|
|
1299
|
+
const [newMessage, setNewMessage] = useState16("");
|
|
1300
|
+
const [newType, setNewType] = useState16("info");
|
|
1301
|
+
const [sending, setSending] = useState16(false);
|
|
1302
|
+
const fetchNotifications = useCallback3(async () => {
|
|
1303
|
+
try {
|
|
1304
|
+
const response = await fetch(`${apiUrl}/api/notifications`);
|
|
1305
|
+
if (response.ok) {
|
|
1306
|
+
const data = await response.json();
|
|
1307
|
+
setNotifications(data);
|
|
1308
|
+
setUnreadCount(data.filter((n) => !n.read).length);
|
|
1309
|
+
}
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
}
|
|
1312
|
+
}, [apiUrl]);
|
|
1313
|
+
useEffect10(() => {
|
|
1314
|
+
fetchNotifications();
|
|
1315
|
+
const interval = setInterval(fetchNotifications, 3e3);
|
|
1316
|
+
return () => clearInterval(interval);
|
|
1317
|
+
}, [fetchNotifications]);
|
|
1318
|
+
const sendNotification = async () => {
|
|
1319
|
+
if (!newTitle.trim() || !newMessage.trim()) return;
|
|
1320
|
+
setSending(true);
|
|
1321
|
+
try {
|
|
1322
|
+
const response = await fetch(`${apiUrl}/api/notifications`, {
|
|
1323
|
+
method: "POST",
|
|
1324
|
+
headers: { "Content-Type": "application/json" },
|
|
1325
|
+
body: JSON.stringify({ title: newTitle, message: newMessage, type: newType })
|
|
1326
|
+
});
|
|
1327
|
+
if (response.ok) {
|
|
1328
|
+
setNewTitle("");
|
|
1329
|
+
setNewMessage("");
|
|
1330
|
+
setNewType("info");
|
|
1331
|
+
setShowSendForm(false);
|
|
1332
|
+
await fetchNotifications();
|
|
1333
|
+
}
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
console.error("Failed to send notification");
|
|
1336
|
+
}
|
|
1337
|
+
setSending(false);
|
|
1338
|
+
};
|
|
1339
|
+
const markAsRead = async (id) => {
|
|
1340
|
+
try {
|
|
1341
|
+
await fetch(`${apiUrl}/api/notifications/${id}/read`, { method: "PUT" });
|
|
1342
|
+
setNotifications((prev) => prev.map((n) => n.id === id ? { ...n, read: true } : n));
|
|
1343
|
+
setUnreadCount((prev) => Math.max(0, prev - 1));
|
|
1344
|
+
} catch {
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
const getTypeIcon = (type) => {
|
|
1348
|
+
const iconClass = "h-4 w-4";
|
|
1349
|
+
switch (type) {
|
|
1350
|
+
case "success":
|
|
1351
|
+
return /* @__PURE__ */ jsx18(Check, { className: `${iconClass} ${t.success}` });
|
|
1352
|
+
case "warning":
|
|
1353
|
+
return /* @__PURE__ */ jsx18(AlertTriangle, { className: `${iconClass} ${t.warning}` });
|
|
1354
|
+
case "error":
|
|
1355
|
+
return /* @__PURE__ */ jsx18(AlertCircle, { className: `${iconClass} ${t.error}` });
|
|
1356
|
+
default:
|
|
1357
|
+
return /* @__PURE__ */ jsx18(Info, { className: `${iconClass} ${t.info}` });
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
const getTypeBg = (type) => {
|
|
1361
|
+
if (theme === "light") {
|
|
1362
|
+
switch (type) {
|
|
1363
|
+
case "success":
|
|
1364
|
+
return "bg-emerald-50 border-emerald-200";
|
|
1365
|
+
case "warning":
|
|
1366
|
+
return "bg-amber-50 border-amber-200";
|
|
1367
|
+
case "error":
|
|
1368
|
+
return "bg-rose-50 border-rose-200";
|
|
1369
|
+
default:
|
|
1370
|
+
return "bg-blue-50 border-blue-200";
|
|
1371
|
+
}
|
|
1372
|
+
} else {
|
|
1373
|
+
switch (type) {
|
|
1374
|
+
case "success":
|
|
1375
|
+
return "bg-emerald-900/20 border-emerald-800";
|
|
1376
|
+
case "warning":
|
|
1377
|
+
return "bg-amber-900/20 border-amber-800";
|
|
1378
|
+
case "error":
|
|
1379
|
+
return "bg-rose-900/20 border-rose-800";
|
|
1380
|
+
default:
|
|
1381
|
+
return "bg-blue-900/20 border-blue-800";
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
return /* @__PURE__ */ jsxs15("div", { className: "relative font-sans", children: [
|
|
1386
|
+
/* @__PURE__ */ jsxs15(
|
|
1387
|
+
"button",
|
|
1388
|
+
{
|
|
1389
|
+
onClick: () => setIsOpen(!isOpen),
|
|
1390
|
+
className: `relative p-2.5 rounded-xl ${t.bgHover} transition-all duration-200 group`,
|
|
1391
|
+
children: [
|
|
1392
|
+
/* @__PURE__ */ jsx18(Bell, { className: `h-5 w-5 ${t.textSecondary} group-hover:${t.accentText} transition-colors` }),
|
|
1393
|
+
unreadCount > 0 && /* @__PURE__ */ jsx18("span", { className: `absolute -top-0.5 -right-0.5 h-5 w-5 ${t.badge} rounded-full flex items-center justify-center text-white text-xs font-bold shadow-lg animate-pulse`, children: unreadCount > 9 ? "9+" : unreadCount })
|
|
1394
|
+
]
|
|
1395
|
+
}
|
|
1396
|
+
),
|
|
1397
|
+
isOpen && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
1398
|
+
/* @__PURE__ */ jsx18("div", { className: "fixed inset-0 z-40", onClick: () => setIsOpen(false) }),
|
|
1399
|
+
/* @__PURE__ */ jsxs15("div", { className: `absolute right-0 mt-3 w-96 ${t.bg} border ${t.border} rounded-2xl shadow-2xl z-50 overflow-hidden`, children: [
|
|
1400
|
+
/* @__PURE__ */ jsxs15("div", { className: `px-5 py-4 border-b ${t.border} flex items-center justify-between`, children: [
|
|
1401
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-3", children: [
|
|
1402
|
+
/* @__PURE__ */ jsx18("div", { className: `p-2 rounded-xl ${t.bgTertiary}`, children: /* @__PURE__ */ jsx18(Bell, { className: `h-5 w-5 ${t.accentText}` }) }),
|
|
1403
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1404
|
+
/* @__PURE__ */ jsx18("h3", { className: `font-semibold ${t.text}`, children: "Notifications" }),
|
|
1405
|
+
/* @__PURE__ */ jsx18("p", { className: `text-xs ${t.textMuted}`, children: unreadCount > 0 ? `${unreadCount} unread` : "All caught up!" })
|
|
1406
|
+
] })
|
|
1407
|
+
] }),
|
|
1408
|
+
/* @__PURE__ */ jsx18("button", { onClick: () => setIsOpen(false), className: `p-1.5 rounded-lg ${t.bgHover} transition-colors`, children: /* @__PURE__ */ jsx18(X, { className: `h-4 w-4 ${t.textSecondary}` }) })
|
|
1409
|
+
] }),
|
|
1410
|
+
/* @__PURE__ */ jsxs15("div", { className: `p-4 border-b ${t.border} ${t.bgSecondary}`, children: [
|
|
1411
|
+
/* @__PURE__ */ jsxs15(
|
|
1412
|
+
"button",
|
|
1413
|
+
{
|
|
1414
|
+
onClick: () => setShowSendForm(!showSendForm),
|
|
1415
|
+
className: `w-full px-4 py-2.5 ${t.accent} ${t.accentHover} rounded-xl text-white text-sm font-medium flex items-center justify-center gap-2 transition-all duration-200 shadow-lg`,
|
|
1416
|
+
children: [
|
|
1417
|
+
/* @__PURE__ */ jsx18(Send, { className: "h-4 w-4" }),
|
|
1418
|
+
"Send New Notification"
|
|
1419
|
+
]
|
|
1420
|
+
}
|
|
1421
|
+
),
|
|
1422
|
+
showSendForm && /* @__PURE__ */ jsxs15("div", { className: "mt-4 space-y-3", children: [
|
|
1423
|
+
/* @__PURE__ */ jsx18(
|
|
1424
|
+
"input",
|
|
1425
|
+
{
|
|
1426
|
+
type: "text",
|
|
1427
|
+
placeholder: "Notification title...",
|
|
1428
|
+
value: newTitle,
|
|
1429
|
+
onChange: (e) => setNewTitle(e.target.value),
|
|
1430
|
+
className: `w-full px-4 py-2.5 ${t.bg} border ${t.border} rounded-xl ${t.text} text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`
|
|
1431
|
+
}
|
|
1432
|
+
),
|
|
1433
|
+
/* @__PURE__ */ jsx18(
|
|
1434
|
+
"input",
|
|
1435
|
+
{
|
|
1436
|
+
type: "text",
|
|
1437
|
+
placeholder: "Message content...",
|
|
1438
|
+
value: newMessage,
|
|
1439
|
+
onChange: (e) => setNewMessage(e.target.value),
|
|
1440
|
+
className: `w-full px-4 py-2.5 ${t.bg} border ${t.border} rounded-xl ${t.text} text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`
|
|
1441
|
+
}
|
|
1442
|
+
),
|
|
1443
|
+
/* @__PURE__ */ jsx18("div", { className: "flex gap-2", children: ["info", "success", "warning", "error"].map((type) => /* @__PURE__ */ jsx18(
|
|
1444
|
+
"button",
|
|
1445
|
+
{
|
|
1446
|
+
onClick: () => setNewType(type),
|
|
1447
|
+
className: `flex-1 py-2 px-3 rounded-lg text-xs font-medium capitalize transition-all border ${newType === type ? `${getTypeBg(type)} ${t.text}` : `${t.bg} ${t.textSecondary} ${t.border} ${t.bgHover}`}`,
|
|
1448
|
+
children: type
|
|
1449
|
+
},
|
|
1450
|
+
type
|
|
1451
|
+
)) }),
|
|
1452
|
+
/* @__PURE__ */ jsx18(
|
|
1453
|
+
"button",
|
|
1454
|
+
{
|
|
1455
|
+
onClick: sendNotification,
|
|
1456
|
+
disabled: sending || !newTitle.trim() || !newMessage.trim(),
|
|
1457
|
+
className: `w-full py-2.5 ${t.accent} ${t.accentHover} disabled:opacity-50 rounded-xl text-white text-sm font-medium`,
|
|
1458
|
+
children: sending ? "Sending..." : "Send Notification"
|
|
1459
|
+
}
|
|
1460
|
+
)
|
|
1461
|
+
] })
|
|
1462
|
+
] }),
|
|
1463
|
+
/* @__PURE__ */ jsx18("div", { className: "max-h-80 overflow-y-auto", children: notifications.length === 0 ? /* @__PURE__ */ jsxs15("div", { className: "px-5 py-12 text-center", children: [
|
|
1464
|
+
/* @__PURE__ */ jsx18("div", { className: `mx-auto w-12 h-12 rounded-full ${t.bgSecondary} flex items-center justify-center mb-3`, children: /* @__PURE__ */ jsx18(Bell, { className: `h-6 w-6 ${t.textMuted}` }) }),
|
|
1465
|
+
/* @__PURE__ */ jsx18("p", { className: `${t.textSecondary} text-sm`, children: "No notifications yet" })
|
|
1466
|
+
] }) : /* @__PURE__ */ jsx18("div", { className: `divide-y ${t.borderSecondary}`, children: notifications.map((notification) => /* @__PURE__ */ jsx18(
|
|
1467
|
+
"div",
|
|
1468
|
+
{
|
|
1469
|
+
onClick: () => markAsRead(notification.id),
|
|
1470
|
+
className: `px-5 py-4 ${t.bgHover} transition-colors cursor-pointer ${!notification.read ? t.bgTertiary : ""}`,
|
|
1471
|
+
children: /* @__PURE__ */ jsxs15("div", { className: "flex gap-3", children: [
|
|
1472
|
+
/* @__PURE__ */ jsx18("div", { className: `mt-0.5 p-2 rounded-lg border ${getTypeBg(notification.type)}`, children: getTypeIcon(notification.type) }),
|
|
1473
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-0", children: [
|
|
1474
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
1475
|
+
/* @__PURE__ */ jsx18("p", { className: `text-sm font-medium ${t.text}`, children: notification.title }),
|
|
1476
|
+
!notification.read && /* @__PURE__ */ jsx18("span", { className: `h-2 w-2 rounded-full ${t.badge}` })
|
|
1477
|
+
] }),
|
|
1478
|
+
/* @__PURE__ */ jsx18("p", { className: `text-sm ${t.textSecondary} mt-0.5 line-clamp-2`, children: notification.message }),
|
|
1479
|
+
/* @__PURE__ */ jsx18("p", { className: `text-xs ${t.textMuted} mt-1.5`, children: new Date(notification.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
|
|
1480
|
+
] })
|
|
1481
|
+
] })
|
|
1482
|
+
},
|
|
1483
|
+
notification.id
|
|
1484
|
+
)) }) }),
|
|
1485
|
+
notifications.length > 0 && /* @__PURE__ */ jsxs15("div", { className: `px-5 py-3 border-t ${t.border} ${t.bgSecondary} flex items-center justify-between`, children: [
|
|
1486
|
+
/* @__PURE__ */ jsx18("button", { onClick: fetchNotifications, className: `text-sm ${t.accentText} font-medium`, children: "Refresh" }),
|
|
1487
|
+
/* @__PURE__ */ jsx18("button", { className: `text-sm ${t.textSecondary}`, children: "View all \u2192" })
|
|
1488
|
+
] })
|
|
1489
|
+
] })
|
|
1490
|
+
] })
|
|
1491
|
+
] });
|
|
1492
|
+
}
|
|
1493
|
+
var NotificationBell = PrimusNotificationCenter;
|
|
1494
|
+
var PrimusNotifications = PrimusNotificationCenter;
|
|
1495
|
+
|
|
1496
|
+
// src/hooks/useRealtimeNotifications.ts
|
|
1497
|
+
import { useState as useState17, useEffect as useEffect11, useCallback as useCallback4 } from "react";
|
|
1498
|
+
var useRealtimeNotifications = (options) => {
|
|
1499
|
+
const { apiUrl, pollInterval = 3e3, userId } = options;
|
|
1500
|
+
const [notifications, setNotifications] = useState17([]);
|
|
1501
|
+
const [unreadCount, setUnreadCount] = useState17(0);
|
|
1502
|
+
const [loading, setLoading] = useState17(true);
|
|
1503
|
+
const [error, setError] = useState17(null);
|
|
1504
|
+
const baseUrl = `${apiUrl}/api/notifications`;
|
|
1505
|
+
const fetchNotifications = useCallback4(async () => {
|
|
1506
|
+
try {
|
|
1507
|
+
const url = userId ? `${baseUrl}?userId=${userId}` : baseUrl;
|
|
1508
|
+
const response = await fetch(url);
|
|
1509
|
+
if (response.ok) {
|
|
1510
|
+
const data = await response.json();
|
|
1511
|
+
setNotifications(data);
|
|
1512
|
+
setUnreadCount(data.filter((n) => !n.read).length);
|
|
1513
|
+
setError(null);
|
|
1514
|
+
}
|
|
1515
|
+
} catch (err) {
|
|
1516
|
+
setError("Failed to connect to notification service");
|
|
1517
|
+
} finally {
|
|
1518
|
+
setLoading(false);
|
|
1519
|
+
}
|
|
1520
|
+
}, [baseUrl, userId]);
|
|
1521
|
+
const sendNotification = useCallback4(async (title, message, type = "info") => {
|
|
1522
|
+
try {
|
|
1523
|
+
const response = await fetch(baseUrl, {
|
|
1524
|
+
method: "POST",
|
|
1525
|
+
headers: { "Content-Type": "application/json" },
|
|
1526
|
+
body: JSON.stringify({ title, message, type, userId })
|
|
1527
|
+
});
|
|
1528
|
+
if (response.ok) {
|
|
1529
|
+
await fetchNotifications();
|
|
1530
|
+
return true;
|
|
1531
|
+
}
|
|
1532
|
+
return false;
|
|
1533
|
+
} catch {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
}, [baseUrl, userId, fetchNotifications]);
|
|
1537
|
+
const markAsRead = useCallback4(async (id) => {
|
|
1538
|
+
try {
|
|
1539
|
+
await fetch(`${baseUrl}/${id}/read`, { method: "PUT" });
|
|
1540
|
+
setNotifications((prev) => prev.map((n) => n.id === id ? { ...n, read: true } : n));
|
|
1541
|
+
setUnreadCount((prev) => Math.max(0, prev - 1));
|
|
1542
|
+
} catch {
|
|
1543
|
+
}
|
|
1544
|
+
}, [baseUrl]);
|
|
1545
|
+
const markAllAsRead = useCallback4(async () => {
|
|
1546
|
+
const unread = notifications.filter((n) => !n.read);
|
|
1547
|
+
await Promise.all(unread.map((n) => fetch(`${baseUrl}/${n.id}/read`, { method: "PUT" })));
|
|
1548
|
+
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
|
|
1549
|
+
setUnreadCount(0);
|
|
1550
|
+
}, [notifications, baseUrl]);
|
|
1551
|
+
const deleteNotification = useCallback4(async (id) => {
|
|
1552
|
+
try {
|
|
1553
|
+
await fetch(`${baseUrl}/${id}`, { method: "DELETE" });
|
|
1554
|
+
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
}, [baseUrl]);
|
|
1558
|
+
useEffect11(() => {
|
|
1559
|
+
fetchNotifications();
|
|
1560
|
+
const interval = setInterval(fetchNotifications, pollInterval);
|
|
1561
|
+
return () => clearInterval(interval);
|
|
1562
|
+
}, [fetchNotifications, pollInterval]);
|
|
1563
|
+
return {
|
|
1564
|
+
notifications,
|
|
1565
|
+
unreadCount,
|
|
1566
|
+
loading,
|
|
1567
|
+
error,
|
|
1568
|
+
sendNotification,
|
|
1569
|
+
markAsRead,
|
|
1570
|
+
markAllAsRead,
|
|
1571
|
+
deleteNotification,
|
|
1572
|
+
refresh: fetchNotifications
|
|
1573
|
+
};
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1576
|
+
// src/components/payments/CheckoutForm.tsx
|
|
1577
|
+
import { useState as useState18 } from "react";
|
|
1578
|
+
|
|
1579
|
+
// src/components/shared/Input.tsx
|
|
1580
|
+
import React14 from "react";
|
|
1581
|
+
import { clsx as clsx3 } from "clsx";
|
|
1582
|
+
import { twMerge as twMerge2 } from "tailwind-merge";
|
|
1583
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1584
|
+
function cn2(...inputs) {
|
|
1585
|
+
return twMerge2(clsx3(inputs));
|
|
1586
|
+
}
|
|
1587
|
+
var Input = React14.forwardRef(
|
|
1588
|
+
({ className, label, error, ...props }, ref) => {
|
|
1589
|
+
return /* @__PURE__ */ jsxs16("div", { className: "w-full", children: [
|
|
1590
|
+
label && /* @__PURE__ */ jsx19("label", { className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-2 block text-gray-700", children: label }),
|
|
1591
|
+
/* @__PURE__ */ jsx19(
|
|
1592
|
+
"input",
|
|
1593
|
+
{
|
|
1594
|
+
ref,
|
|
1595
|
+
className: cn2(
|
|
1596
|
+
"flex h-10 w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1597
|
+
error && "border-red-500 focus-visible:ring-red-500",
|
|
1598
|
+
className
|
|
1599
|
+
),
|
|
1600
|
+
...props
|
|
1601
|
+
}
|
|
1602
|
+
),
|
|
1603
|
+
error && /* @__PURE__ */ jsx19("p", { className: "text-sm text-red-500 mt-1", children: error })
|
|
1604
|
+
] });
|
|
1605
|
+
}
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
// src/components/payments/CheckoutForm.tsx
|
|
1609
|
+
import { LockClosedIcon, CheckCircleIcon as CheckCircleIcon2, XCircleIcon as XCircleIcon2 } from "@heroicons/react/24/solid";
|
|
1610
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1611
|
+
var CheckoutForm = ({
|
|
1612
|
+
amount,
|
|
1613
|
+
currency = "USD",
|
|
1614
|
+
apiUrl = "http://localhost:5221",
|
|
1615
|
+
onSuccess,
|
|
1616
|
+
onError
|
|
1617
|
+
}) => {
|
|
1618
|
+
const [loading, setLoading] = useState18(false);
|
|
1619
|
+
const [cardholderName, setCardholderName] = useState18("");
|
|
1620
|
+
const [cardNumber, setCardNumber] = useState18("");
|
|
1621
|
+
const [expiry, setExpiry] = useState18("");
|
|
1622
|
+
const [cvc, setCvc] = useState18("");
|
|
1623
|
+
const [error, setError] = useState18(null);
|
|
1624
|
+
const [success, setSuccess] = useState18(false);
|
|
1625
|
+
const handleSubmit = async (e) => {
|
|
1626
|
+
e.preventDefault();
|
|
1627
|
+
setError(null);
|
|
1628
|
+
setSuccess(false);
|
|
1629
|
+
setLoading(true);
|
|
1630
|
+
try {
|
|
1631
|
+
const response = await fetch(`${apiUrl}/api/payments/charge`, {
|
|
1632
|
+
method: "POST",
|
|
1633
|
+
headers: { "Content-Type": "application/json" },
|
|
1634
|
+
body: JSON.stringify({
|
|
1635
|
+
amount,
|
|
1636
|
+
currency,
|
|
1637
|
+
cardholderName,
|
|
1638
|
+
cardNumber: cardNumber.replace(/\s/g, ""),
|
|
1639
|
+
expiry,
|
|
1640
|
+
cvc
|
|
1641
|
+
})
|
|
1642
|
+
});
|
|
1643
|
+
const data = await response.json();
|
|
1644
|
+
if (!response.ok) {
|
|
1645
|
+
throw new Error(data.error || "Payment failed");
|
|
1646
|
+
}
|
|
1647
|
+
setSuccess(true);
|
|
1648
|
+
onSuccess?.(data);
|
|
1649
|
+
setTimeout(() => {
|
|
1650
|
+
setCardholderName("");
|
|
1651
|
+
setCardNumber("");
|
|
1652
|
+
setExpiry("");
|
|
1653
|
+
setCvc("");
|
|
1654
|
+
setSuccess(false);
|
|
1655
|
+
}, 3e3);
|
|
1656
|
+
} catch (err) {
|
|
1657
|
+
const errorMessage = err.message || "Payment processing failed";
|
|
1658
|
+
setError(errorMessage);
|
|
1659
|
+
onError?.(errorMessage);
|
|
1660
|
+
} finally {
|
|
1661
|
+
setLoading(false);
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
return /* @__PURE__ */ jsxs17("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
1665
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex justify-between items-center mb-6", children: [
|
|
1666
|
+
/* @__PURE__ */ jsx20("h3", { className: "text-lg font-medium text-gray-900", children: "Payment Details" }),
|
|
1667
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex items-center text-gray-500 text-sm", children: [
|
|
1668
|
+
/* @__PURE__ */ jsx20(LockClosedIcon, { className: "h-4 w-4 mr-1" }),
|
|
1669
|
+
"Secure Payment"
|
|
1670
|
+
] })
|
|
1671
|
+
] }),
|
|
1672
|
+
/* @__PURE__ */ jsxs17("div", { className: "mb-6 p-4 bg-gray-50 rounded-lg flex justify-between items-center", children: [
|
|
1673
|
+
/* @__PURE__ */ jsx20("span", { className: "text-gray-600", children: "Total to pay" }),
|
|
1674
|
+
/* @__PURE__ */ jsx20("span", { className: "text-2xl font-bold text-gray-900", children: new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount) })
|
|
1675
|
+
] }),
|
|
1676
|
+
error && /* @__PURE__ */ jsxs17("div", { className: "mb-4 p-3 bg-red-50 border border-red-200 rounded-lg flex items-start gap-2", children: [
|
|
1677
|
+
/* @__PURE__ */ jsx20(XCircleIcon2, { className: "h-5 w-5 text-red-500 flex-shrink-0 mt-0.5" }),
|
|
1678
|
+
/* @__PURE__ */ jsx20("p", { className: "text-sm text-red-700", children: error })
|
|
1679
|
+
] }),
|
|
1680
|
+
success && /* @__PURE__ */ jsxs17("div", { className: "mb-4 p-3 bg-green-50 border border-green-200 rounded-lg flex items-start gap-2", children: [
|
|
1681
|
+
/* @__PURE__ */ jsx20(CheckCircleIcon2, { className: "h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" }),
|
|
1682
|
+
/* @__PURE__ */ jsx20("p", { className: "text-sm text-green-700", children: "Payment successful!" })
|
|
1683
|
+
] }),
|
|
1684
|
+
/* @__PURE__ */ jsxs17("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
1685
|
+
/* @__PURE__ */ jsx20(
|
|
1686
|
+
Input,
|
|
1687
|
+
{
|
|
1688
|
+
label: "Cardholder Name",
|
|
1689
|
+
placeholder: "John Doe",
|
|
1690
|
+
value: cardholderName,
|
|
1691
|
+
onChange: (e) => setCardholderName(e.target.value),
|
|
1692
|
+
required: true,
|
|
1693
|
+
disabled: loading
|
|
1694
|
+
}
|
|
1695
|
+
),
|
|
1696
|
+
/* @__PURE__ */ jsxs17("div", { className: "relative", children: [
|
|
1697
|
+
/* @__PURE__ */ jsx20(
|
|
1698
|
+
Input,
|
|
1699
|
+
{
|
|
1700
|
+
label: "Card Number",
|
|
1701
|
+
placeholder: "4242 4242 4242 4242",
|
|
1702
|
+
value: cardNumber,
|
|
1703
|
+
onChange: (e) => setCardNumber(e.target.value),
|
|
1704
|
+
required: true,
|
|
1705
|
+
disabled: loading
|
|
1706
|
+
}
|
|
1707
|
+
),
|
|
1708
|
+
/* @__PURE__ */ jsxs17("div", { className: "absolute right-3 top-[34px] flex gap-1", children: [
|
|
1709
|
+
/* @__PURE__ */ jsx20("div", { className: "h-5 w-8 bg-gray-200 rounded" }),
|
|
1710
|
+
/* @__PURE__ */ jsx20("div", { className: "h-5 w-8 bg-gray-200 rounded" })
|
|
1711
|
+
] })
|
|
1712
|
+
] }),
|
|
1713
|
+
/* @__PURE__ */ jsxs17("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1714
|
+
/* @__PURE__ */ jsx20(
|
|
1715
|
+
Input,
|
|
1716
|
+
{
|
|
1717
|
+
label: "Expiry Date",
|
|
1718
|
+
placeholder: "MM/YY",
|
|
1719
|
+
value: expiry,
|
|
1720
|
+
onChange: (e) => setExpiry(e.target.value),
|
|
1721
|
+
maxLength: 5,
|
|
1722
|
+
required: true,
|
|
1723
|
+
disabled: loading
|
|
1724
|
+
}
|
|
1725
|
+
),
|
|
1726
|
+
/* @__PURE__ */ jsx20(
|
|
1727
|
+
Input,
|
|
1728
|
+
{
|
|
1729
|
+
label: "CVC",
|
|
1730
|
+
placeholder: "123",
|
|
1731
|
+
value: cvc,
|
|
1732
|
+
onChange: (e) => setCvc(e.target.value),
|
|
1733
|
+
maxLength: 4,
|
|
1734
|
+
required: true,
|
|
1735
|
+
disabled: loading
|
|
1736
|
+
}
|
|
1737
|
+
)
|
|
1738
|
+
] }),
|
|
1739
|
+
/* @__PURE__ */ jsxs17("div", { className: "pt-4", children: [
|
|
1740
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: "w-full bg-primus-600 hover:bg-primus-700", size: "lg", disabled: loading, children: loading ? "Processing..." : `Pay ${new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount)}` }),
|
|
1741
|
+
/* @__PURE__ */ jsx20("p", { className: "text-center text-xs text-gray-400 mt-3", children: "Powered by Primus Payments. Your data is encrypted." }),
|
|
1742
|
+
/* @__PURE__ */ jsx20("p", { className: "text-center text-xs text-gray-500 mt-2", children: "Test card: 4242 4242 4242 4242" })
|
|
1743
|
+
] })
|
|
1744
|
+
] })
|
|
1745
|
+
] });
|
|
1746
|
+
};
|
|
1747
|
+
|
|
1748
|
+
// src/components/documents/DocumentViewer.tsx
|
|
1749
|
+
import {
|
|
1750
|
+
DocumentArrowDownIcon,
|
|
1751
|
+
PrinterIcon,
|
|
1752
|
+
XMarkIcon
|
|
1753
|
+
} from "@heroicons/react/24/outline";
|
|
1754
|
+
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1755
|
+
var DocumentViewer = ({
|
|
1756
|
+
document,
|
|
1757
|
+
onClose,
|
|
1758
|
+
onDownload
|
|
1759
|
+
}) => {
|
|
1760
|
+
const renderContent = () => {
|
|
1761
|
+
switch (document.type) {
|
|
1762
|
+
case "image":
|
|
1763
|
+
return /* @__PURE__ */ jsx21(
|
|
1764
|
+
"img",
|
|
1765
|
+
{
|
|
1766
|
+
src: document.url,
|
|
1767
|
+
alt: document.name,
|
|
1768
|
+
className: "max-w-full max-h-[80vh] object-contain mx-auto shadow-lg rounded"
|
|
1769
|
+
}
|
|
1770
|
+
);
|
|
1771
|
+
case "pdf":
|
|
1772
|
+
return /* @__PURE__ */ jsx21("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", children: /* @__PURE__ */ jsxs18("div", { className: "text-gray-500", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx21(DocumentArrowDownIcon, { className: "h-16 w-16 mx-auto mb-4 text-gray-400" }),
|
|
1774
|
+
/* @__PURE__ */ jsx21("p", { className: "font-medium", children: "PDF Preview" }),
|
|
1775
|
+
/* @__PURE__ */ jsx21("p", { className: "text-sm mt-2", children: document.name }),
|
|
1776
|
+
/* @__PURE__ */ jsx21(Button, { variant: "outline", size: "sm", className: "mt-4", onClick: onDownload, children: "Download to view" })
|
|
1777
|
+
] }) });
|
|
1778
|
+
default:
|
|
1779
|
+
return /* @__PURE__ */ jsxs18("div", { className: "bg-white p-8 rounded shadow border border-gray-200 h-[600px] overflow-auto font-mono text-sm leading-relaxed", children: [
|
|
1780
|
+
/* @__PURE__ */ jsxs18("p", { children: [
|
|
1781
|
+
"Document Content: ",
|
|
1782
|
+
document.name
|
|
1783
|
+
] }),
|
|
1784
|
+
/* @__PURE__ */ jsx21("p", { className: "mt-4 text-gray-500", children: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." })
|
|
1785
|
+
] });
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col h-full bg-gray-50 rounded-lg overflow-hidden border border-gray-200", children: [
|
|
1789
|
+
/* @__PURE__ */ jsxs18("div", { className: "bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between shadow-sm", children: [
|
|
1790
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-3", children: [
|
|
1791
|
+
/* @__PURE__ */ jsx21("div", { className: "h-8 w-8 bg-primus-100 rounded flex items-center justify-center text-primus-700", children: /* @__PURE__ */ jsx21("span", { className: "uppercase text-xs font-bold", children: document.type }) }),
|
|
1792
|
+
/* @__PURE__ */ jsxs18("div", { children: [
|
|
1793
|
+
/* @__PURE__ */ jsx21("h3", { className: "text-sm font-semibold text-gray-900", children: document.name }),
|
|
1794
|
+
document.size && /* @__PURE__ */ jsxs18("p", { className: "text-xs text-gray-500", children: [
|
|
1795
|
+
document.size,
|
|
1796
|
+
" \u2022 ",
|
|
1797
|
+
document.date
|
|
1798
|
+
] })
|
|
1799
|
+
] })
|
|
1800
|
+
] }),
|
|
1801
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx21(Button, { variant: "ghost", size: "sm", onClick: () => window.print(), children: /* @__PURE__ */ jsx21(PrinterIcon, { className: "h-4 w-4" }) }),
|
|
1803
|
+
/* @__PURE__ */ jsx21(Button, { variant: "ghost", size: "sm", onClick: onDownload, children: /* @__PURE__ */ jsx21(DocumentArrowDownIcon, { className: "h-4 w-4" }) }),
|
|
1804
|
+
onClose && /* @__PURE__ */ jsx21("div", { className: "h-6 w-px bg-gray-200 mx-1" }),
|
|
1805
|
+
onClose && /* @__PURE__ */ jsx21(Button, { variant: "ghost", size: "sm", onClick: onClose, className: "text-gray-400 hover:text-red-500", children: /* @__PURE__ */ jsx21(XMarkIcon, { className: "h-5 w-5" }) })
|
|
1806
|
+
] })
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ jsx21("div", { className: "flex-1 overflow-auto p-6 flex flex-col items-center justify-center", children: renderContent() })
|
|
1809
|
+
] });
|
|
1810
|
+
};
|
|
1811
|
+
|
|
1812
|
+
// src/components/banking/cards/CreditCardVisual.tsx
|
|
1813
|
+
import { clsx as clsx4 } from "clsx";
|
|
1814
|
+
import { WifiIcon } from "@heroicons/react/24/outline";
|
|
1815
|
+
import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1816
|
+
var CreditCardVisual = ({
|
|
1817
|
+
cardHolder,
|
|
1818
|
+
last4,
|
|
1819
|
+
expiry,
|
|
1820
|
+
brand = "visa",
|
|
1821
|
+
variant = "black"
|
|
1822
|
+
}) => {
|
|
1823
|
+
const getBackground = () => {
|
|
1824
|
+
switch (variant) {
|
|
1825
|
+
case "blue":
|
|
1826
|
+
return "bg-gradient-to-br from-blue-600 to-blue-800";
|
|
1827
|
+
case "gold":
|
|
1828
|
+
return "bg-gradient-to-br from-yellow-500 to-yellow-700";
|
|
1829
|
+
case "platinum":
|
|
1830
|
+
return "bg-gradient-to-br from-gray-300 to-gray-500";
|
|
1831
|
+
default:
|
|
1832
|
+
return "bg-gradient-to-br from-gray-900 to-gray-800";
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
return /* @__PURE__ */ jsxs19("div", { className: clsx4(
|
|
1836
|
+
"w-80 h-48 rounded-2xl p-6 text-white shadow-xl relative overflow-hidden transition-transform hover:scale-105 duration-300",
|
|
1837
|
+
getBackground()
|
|
1838
|
+
), children: [
|
|
1839
|
+
/* @__PURE__ */ jsx22("div", { className: "absolute top-0 right-0 -mr-10 -mt-10 w-40 h-40 bg-white opacity-5 rounded-full blur-xl" }),
|
|
1840
|
+
/* @__PURE__ */ jsx22("div", { className: "absolute bottom-0 left-0 -ml-10 -mb-10 w-40 h-40 bg-white opacity-5 rounded-full blur-xl" }),
|
|
1841
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex justify-between items-start mb-8", children: [
|
|
1842
|
+
/* @__PURE__ */ jsx22("div", { className: "w-12 h-9 bg-yellow-400 rounded-md opacity-80 flex items-center justify-center", children: /* @__PURE__ */ jsx22("div", { className: "w-8 h-6 border border-yellow-600 rounded opacity-50" }) }),
|
|
1843
|
+
/* @__PURE__ */ jsx22(WifiIcon, { className: "h-6 w-6 rotate-90 opacity-70" })
|
|
1844
|
+
] }),
|
|
1845
|
+
/* @__PURE__ */ jsx22("div", { className: "mb-6", children: /* @__PURE__ */ jsxs19("p", { className: "font-mono text-xl tracking-widest text-shadow", children: [
|
|
1846
|
+
"\u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 ",
|
|
1847
|
+
last4
|
|
1848
|
+
] }) }),
|
|
1849
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex justify-between items-end", children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs19("div", { children: [
|
|
1851
|
+
/* @__PURE__ */ jsx22("p", { className: "text-[10px] uppercase text-gray-300 tracking-wider mb-1", children: "Card Holder" }),
|
|
1852
|
+
/* @__PURE__ */ jsx22("p", { className: "font-medium tracking-wide uppercase text-sm truncate max-w-[160px]", children: cardHolder })
|
|
1853
|
+
] }),
|
|
1854
|
+
/* @__PURE__ */ jsxs19("div", { children: [
|
|
1855
|
+
/* @__PURE__ */ jsx22("p", { className: "text-[10px] uppercase text-gray-300 tracking-wider mb-1", children: "Expires" }),
|
|
1856
|
+
/* @__PURE__ */ jsx22("p", { className: "font-medium tracking-wide text-sm", children: expiry })
|
|
1857
|
+
] }),
|
|
1858
|
+
/* @__PURE__ */ jsx22("div", { className: "font-bold text-lg italic opacity-90", children: brand.toUpperCase() })
|
|
1859
|
+
] })
|
|
1860
|
+
] });
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1863
|
+
// src/components/banking/transactions/TransactionHistory.tsx
|
|
1864
|
+
import {
|
|
1865
|
+
ArrowDownLeftIcon,
|
|
1866
|
+
ArrowUpRightIcon,
|
|
1867
|
+
ShoppingBagIcon
|
|
1868
|
+
} from "@heroicons/react/24/outline";
|
|
1869
|
+
import { clsx as clsx5 } from "clsx";
|
|
1870
|
+
import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1871
|
+
var TransactionHistory = ({
|
|
1872
|
+
transactions,
|
|
1873
|
+
onTransactionClick
|
|
1874
|
+
}) => {
|
|
1875
|
+
const getIcon = (t) => {
|
|
1876
|
+
if (t.type === "credit") return /* @__PURE__ */ jsx23(ArrowDownLeftIcon, { className: "h-5 w-5 text-green-600" });
|
|
1877
|
+
if (t.category === "shopping") return /* @__PURE__ */ jsx23(ShoppingBagIcon, { className: "h-5 w-5 text-blue-600" });
|
|
1878
|
+
return /* @__PURE__ */ jsx23(ArrowUpRightIcon, { className: "h-5 w-5 text-red-600" });
|
|
1879
|
+
};
|
|
1880
|
+
return /* @__PURE__ */ jsxs20("div", { className: "bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden", children: [
|
|
1881
|
+
/* @__PURE__ */ jsxs20("div", { className: "px-6 py-4 border-b border-gray-100 flex justify-between items-center", children: [
|
|
1882
|
+
/* @__PURE__ */ jsx23("h3", { className: "font-semibold text-gray-900", children: "Recent Transactions" }),
|
|
1883
|
+
/* @__PURE__ */ jsx23("button", { className: "text-sm text-primus-600 hover:text-primus-700 font-medium", children: "View All" })
|
|
1884
|
+
] }),
|
|
1885
|
+
/* @__PURE__ */ jsx23("div", { className: "divide-y divide-gray-100", children: transactions.map((t) => /* @__PURE__ */ jsxs20(
|
|
1886
|
+
"div",
|
|
1887
|
+
{
|
|
1888
|
+
onClick: () => onTransactionClick?.(t.id),
|
|
1889
|
+
className: "px-6 py-4 flex items-center justify-between hover:bg-gray-50 cursor-pointer transition-colors",
|
|
1890
|
+
children: [
|
|
1891
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-4", children: [
|
|
1892
|
+
/* @__PURE__ */ jsx23("div", { className: clsx5(
|
|
1893
|
+
"h-10 w-10 rounded-full flex items-center justify-center",
|
|
1894
|
+
t.type === "credit" ? "bg-green-100" : "bg-gray-100"
|
|
1895
|
+
), children: getIcon(t) }),
|
|
1896
|
+
/* @__PURE__ */ jsxs20("div", { children: [
|
|
1897
|
+
/* @__PURE__ */ jsx23("p", { className: "text-sm font-medium text-gray-900", children: t.description }),
|
|
1898
|
+
/* @__PURE__ */ jsx23("p", { className: "text-xs text-gray-500", children: t.date })
|
|
1899
|
+
] })
|
|
1900
|
+
] }),
|
|
1901
|
+
/* @__PURE__ */ jsxs20("div", { className: "text-right", children: [
|
|
1902
|
+
/* @__PURE__ */ jsxs20("p", { className: clsx5(
|
|
1903
|
+
"text-sm font-semibold",
|
|
1904
|
+
t.type === "credit" ? "text-green-600" : "text-gray-900"
|
|
1905
|
+
), children: [
|
|
1906
|
+
t.type === "credit" ? "+" : "-",
|
|
1907
|
+
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(t.amount)
|
|
1908
|
+
] }),
|
|
1909
|
+
/* @__PURE__ */ jsx23("p", { className: "text-xs text-gray-400 capitalize", children: t.status || "completed" })
|
|
1910
|
+
] })
|
|
1911
|
+
]
|
|
1912
|
+
},
|
|
1913
|
+
t.id
|
|
1914
|
+
)) })
|
|
1915
|
+
] });
|
|
1916
|
+
};
|
|
1917
|
+
|
|
1918
|
+
// src/components/insurance/policies/PolicyCard.tsx
|
|
1919
|
+
import { clsx as clsx6 } from "clsx";
|
|
1920
|
+
import { ShieldCheckIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
|
|
1921
|
+
import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1922
|
+
var PolicyCard = ({
|
|
1923
|
+
policyNumber,
|
|
1924
|
+
type,
|
|
1925
|
+
status,
|
|
1926
|
+
coverageAmount,
|
|
1927
|
+
nextPaymentDate,
|
|
1928
|
+
premiumAmount
|
|
1929
|
+
}) => {
|
|
1930
|
+
return /* @__PURE__ */ jsxs21("div", { className: "bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden hover:shadow-md transition-shadow", children: [
|
|
1931
|
+
/* @__PURE__ */ jsxs21("div", { className: "bg-gray-50 px-6 py-4 border-b border-gray-100 flex justify-between items-center", children: [
|
|
1932
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", children: [
|
|
1933
|
+
/* @__PURE__ */ jsx24(ShieldCheckIcon, { className: "h-5 w-5 text-primus-600" }),
|
|
1934
|
+
/* @__PURE__ */ jsxs21("span", { className: "font-semibold text-gray-900", children: [
|
|
1935
|
+
type,
|
|
1936
|
+
" Insurance"
|
|
1937
|
+
] })
|
|
1938
|
+
] }),
|
|
1939
|
+
/* @__PURE__ */ jsx24("span", { className: clsx6(
|
|
1940
|
+
"px-2.5 py-0.5 rounded-full text-xs font-medium uppercase tracking-wide",
|
|
1941
|
+
status === "active" ? "bg-green-100 text-green-800" : status === "pending" ? "bg-yellow-100 text-yellow-800" : "bg-gray-100 text-gray-800"
|
|
1942
|
+
), children: status })
|
|
1943
|
+
] }),
|
|
1944
|
+
/* @__PURE__ */ jsxs21("div", { className: "p-6", children: [
|
|
1945
|
+
/* @__PURE__ */ jsxs21("div", { className: "mb-6", children: [
|
|
1946
|
+
/* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 uppercase tracking-wider mb-1", children: "Policy Number" }),
|
|
1947
|
+
/* @__PURE__ */ jsx24("p", { className: "font-mono text-lg font-medium text-gray-900", children: policyNumber })
|
|
1948
|
+
] }),
|
|
1949
|
+
/* @__PURE__ */ jsxs21("div", { className: "grid grid-cols-2 gap-6 mb-6", children: [
|
|
1950
|
+
/* @__PURE__ */ jsxs21("div", { children: [
|
|
1951
|
+
/* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 uppercase tracking-wider mb-1", children: "Coverage" }),
|
|
1952
|
+
/* @__PURE__ */ jsx24("p", { className: "text-xl font-bold text-gray-900", children: new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(coverageAmount) })
|
|
1953
|
+
] }),
|
|
1954
|
+
/* @__PURE__ */ jsxs21("div", { children: [
|
|
1955
|
+
/* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 uppercase tracking-wider mb-1", children: "Premium" }),
|
|
1956
|
+
/* @__PURE__ */ jsxs21("p", { className: "text-gray-900 font-medium", children: [
|
|
1957
|
+
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(premiumAmount),
|
|
1958
|
+
/* @__PURE__ */ jsx24("span", { className: "text-xs text-gray-500 font-normal", children: " / month" })
|
|
1959
|
+
] })
|
|
1960
|
+
] })
|
|
1961
|
+
] }),
|
|
1962
|
+
/* @__PURE__ */ jsxs21("div", { className: "bg-blue-50 rounded-lg p-3 flex items-start gap-3 mb-6", children: [
|
|
1963
|
+
/* @__PURE__ */ jsx24(DocumentTextIcon, { className: "h-5 w-5 text-blue-600 mt-0.5" }),
|
|
1964
|
+
/* @__PURE__ */ jsxs21("div", { children: [
|
|
1965
|
+
/* @__PURE__ */ jsx24("p", { className: "text-sm font-medium text-blue-900", children: "Next Payment due" }),
|
|
1966
|
+
/* @__PURE__ */ jsx24("p", { className: "text-sm text-blue-700", children: nextPaymentDate })
|
|
1967
|
+
] })
|
|
1968
|
+
] }),
|
|
1969
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex gap-3", children: [
|
|
1970
|
+
/* @__PURE__ */ jsx24(Button, { variant: "outline", className: "flex-1", children: "View Details" }),
|
|
1971
|
+
/* @__PURE__ */ jsx24(Button, { className: "flex-1", children: "File Claim" })
|
|
1972
|
+
] })
|
|
1973
|
+
] })
|
|
1974
|
+
] });
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
// src/components/insurance/claims/ClaimStatusTracker.tsx
|
|
1978
|
+
import { clsx as clsx7 } from "clsx";
|
|
1979
|
+
import { CheckIcon } from "@heroicons/react/24/solid";
|
|
1980
|
+
import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1981
|
+
var ClaimStatusTracker = ({
|
|
1982
|
+
claimId,
|
|
1983
|
+
steps
|
|
1984
|
+
}) => {
|
|
1985
|
+
return /* @__PURE__ */ jsxs22("div", { className: "bg-white rounded-xl border border-gray-200 shadow-sm p-6", children: [
|
|
1986
|
+
/* @__PURE__ */ jsxs22("div", { className: "mb-8", children: [
|
|
1987
|
+
/* @__PURE__ */ jsx25("h3", { className: "text-lg font-semibold text-gray-900", children: "Claim Status" }),
|
|
1988
|
+
/* @__PURE__ */ jsxs22("p", { className: "text-sm text-gray-500", children: [
|
|
1989
|
+
"Claim ID: #",
|
|
1990
|
+
claimId
|
|
1991
|
+
] })
|
|
1992
|
+
] }),
|
|
1993
|
+
/* @__PURE__ */ jsx25("nav", { "aria-label": "Progress", children: /* @__PURE__ */ jsx25("ol", { role: "list", className: "overflow-hidden", children: steps.map((step, stepIdx) => /* @__PURE__ */ jsxs22("li", { className: clsx7(
|
|
1994
|
+
"relative pb-10",
|
|
1995
|
+
stepIdx === steps.length - 1 ? "pb-0" : ""
|
|
1996
|
+
), children: [
|
|
1997
|
+
stepIdx !== steps.length - 1 ? /* @__PURE__ */ jsx25(
|
|
1998
|
+
"div",
|
|
1999
|
+
{
|
|
2000
|
+
className: clsx7(
|
|
2001
|
+
"absolute top-4 left-4 -ml-px h-full w-0.5",
|
|
2002
|
+
step.status === "completed" ? "bg-primus-600" : "bg-gray-200"
|
|
2003
|
+
),
|
|
2004
|
+
"aria-hidden": "true"
|
|
2005
|
+
}
|
|
2006
|
+
) : null,
|
|
2007
|
+
/* @__PURE__ */ jsxs22("div", { className: "relative flex items-start group", children: [
|
|
2008
|
+
/* @__PURE__ */ jsx25("span", { className: "h-9 flex items-center", children: /* @__PURE__ */ jsx25("span", { className: clsx7(
|
|
2009
|
+
"relative z-10 w-8 h-8 flex items-center justify-center rounded-full ring-1 ring-white",
|
|
2010
|
+
step.status === "completed" ? "bg-primus-600" : step.status === "current" ? "bg-white border-2 border-primus-600" : "bg-white border-2 border-gray-300"
|
|
2011
|
+
), children: step.status === "completed" ? /* @__PURE__ */ jsx25(CheckIcon, { className: "w-5 h-5 text-white", "aria-hidden": "true" }) : step.status === "current" ? /* @__PURE__ */ jsx25("span", { className: "h-2.5 w-2.5 bg-primus-600 rounded-full" }) : null }) }),
|
|
2012
|
+
/* @__PURE__ */ jsxs22("span", { className: "ml-4 min-w-0 flex flex-col", children: [
|
|
2013
|
+
/* @__PURE__ */ jsx25("span", { className: clsx7(
|
|
2014
|
+
"text-sm font-medium",
|
|
2015
|
+
step.status === "upcoming" ? "text-gray-500" : "text-gray-900"
|
|
2016
|
+
), children: step.label }),
|
|
2017
|
+
step.date && /* @__PURE__ */ jsx25("span", { className: "text-xs text-gray-500 mt-1", children: step.date })
|
|
2018
|
+
] })
|
|
2019
|
+
] })
|
|
2020
|
+
] }, step.label)) }) })
|
|
2021
|
+
] });
|
|
2022
|
+
};
|
|
2023
|
+
|
|
2024
|
+
// src/components/featureflags/FeatureFlagToggle.tsx
|
|
2025
|
+
import { useState as useState19, useEffect as useEffect12 } from "react";
|
|
2026
|
+
import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2027
|
+
var FeatureFlagToggle = ({
|
|
2028
|
+
apiUrl = "http://localhost:5221"
|
|
2029
|
+
}) => {
|
|
2030
|
+
const [flags, setFlags] = useState19([]);
|
|
2031
|
+
const [loading, setLoading] = useState19(true);
|
|
2032
|
+
useEffect12(() => {
|
|
2033
|
+
fetchFlags();
|
|
2034
|
+
}, []);
|
|
2035
|
+
const fetchFlags = async () => {
|
|
2036
|
+
try {
|
|
2037
|
+
const response = await fetch(`${apiUrl}/api/featureflags`);
|
|
2038
|
+
if (response.ok) {
|
|
2039
|
+
const data = await response.json();
|
|
2040
|
+
setFlags(data);
|
|
2041
|
+
}
|
|
2042
|
+
} catch (error) {
|
|
2043
|
+
console.error("Failed to fetch feature flags:", error);
|
|
2044
|
+
} finally {
|
|
2045
|
+
setLoading(false);
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
if (loading) {
|
|
2049
|
+
return /* @__PURE__ */ jsx26("div", { className: "p-4", children: "Loading feature flags..." });
|
|
2050
|
+
}
|
|
2051
|
+
return /* @__PURE__ */ jsxs23("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
2052
|
+
/* @__PURE__ */ jsx26("h3", { className: "text-lg font-medium text-gray-900 mb-4", children: "Feature Flags" }),
|
|
2053
|
+
/* @__PURE__ */ jsx26("div", { className: "space-y-4", children: flags.map((flag) => /* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between p-4 bg-gray-50 rounded-lg", children: [
|
|
2054
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex-1", children: [
|
|
2055
|
+
/* @__PURE__ */ jsx26("h4", { className: "font-medium text-gray-900", children: flag.name }),
|
|
2056
|
+
/* @__PURE__ */ jsx26("p", { className: "text-sm text-gray-500", children: flag.description }),
|
|
2057
|
+
flag.rolloutPercentage !== void 0 && /* @__PURE__ */ jsxs23("p", { className: "text-xs text-gray-400 mt-1", children: [
|
|
2058
|
+
"Rollout: ",
|
|
2059
|
+
flag.rolloutPercentage,
|
|
2060
|
+
"%"
|
|
2061
|
+
] })
|
|
2062
|
+
] }),
|
|
2063
|
+
/* @__PURE__ */ jsx26("div", { className: `px-3 py-1 rounded-full text-sm font-medium ${flag.enabled ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`, children: flag.enabled ? "Enabled" : "Disabled" })
|
|
2064
|
+
] }, flag.name)) })
|
|
2065
|
+
] });
|
|
2066
|
+
};
|
|
2067
|
+
|
|
2068
|
+
// src/components/logging/LogViewer.tsx
|
|
2069
|
+
import { useState as useState20, useEffect as useEffect13 } from "react";
|
|
2070
|
+
import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2071
|
+
var LogViewer = ({
|
|
2072
|
+
apiUrl = "http://localhost:5221"
|
|
2073
|
+
}) => {
|
|
2074
|
+
const [logs, setLogs] = useState20([]);
|
|
2075
|
+
const [filter, setFilter] = useState20("");
|
|
2076
|
+
const [loading, setLoading] = useState20(true);
|
|
2077
|
+
useEffect13(() => {
|
|
2078
|
+
fetchLogs();
|
|
2079
|
+
const interval = setInterval(fetchLogs, 1e4);
|
|
2080
|
+
return () => clearInterval(interval);
|
|
2081
|
+
}, [filter]);
|
|
2082
|
+
const fetchLogs = async () => {
|
|
2083
|
+
try {
|
|
2084
|
+
const url = filter ? `${apiUrl}/api/logs?level=${filter}` : `${apiUrl}/api/logs`;
|
|
2085
|
+
const response = await fetch(url);
|
|
2086
|
+
if (response.ok) {
|
|
2087
|
+
const data = await response.json();
|
|
2088
|
+
setLogs(data);
|
|
2089
|
+
}
|
|
2090
|
+
} catch (error) {
|
|
2091
|
+
console.error("Failed to fetch logs:", error);
|
|
2092
|
+
} finally {
|
|
2093
|
+
setLoading(false);
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
const getLevelColor = (level) => {
|
|
2097
|
+
switch (level.toUpperCase()) {
|
|
2098
|
+
case "ERROR":
|
|
2099
|
+
return "text-red-600 bg-red-50";
|
|
2100
|
+
case "WARNING":
|
|
2101
|
+
return "text-yellow-600 bg-yellow-50";
|
|
2102
|
+
case "INFO":
|
|
2103
|
+
return "text-blue-600 bg-blue-50";
|
|
2104
|
+
default:
|
|
2105
|
+
return "text-gray-600 bg-gray-50";
|
|
2106
|
+
}
|
|
2107
|
+
};
|
|
2108
|
+
return /* @__PURE__ */ jsxs24("div", { className: "bg-white p-6 rounded-lg border border-gray-200 shadow-sm", children: [
|
|
2109
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex justify-between items-center mb-4", children: [
|
|
2110
|
+
/* @__PURE__ */ jsx27("h3", { className: "text-lg font-medium text-gray-900", children: "System Logs" }),
|
|
2111
|
+
/* @__PURE__ */ jsxs24(
|
|
2112
|
+
"select",
|
|
2113
|
+
{
|
|
2114
|
+
value: filter,
|
|
2115
|
+
onChange: (e) => setFilter(e.target.value),
|
|
2116
|
+
className: "px-3 py-1 border border-gray-300 rounded-md text-sm",
|
|
2117
|
+
children: [
|
|
2118
|
+
/* @__PURE__ */ jsx27("option", { value: "", children: "All Levels" }),
|
|
2119
|
+
/* @__PURE__ */ jsx27("option", { value: "INFO", children: "Info" }),
|
|
2120
|
+
/* @__PURE__ */ jsx27("option", { value: "WARNING", children: "Warning" }),
|
|
2121
|
+
/* @__PURE__ */ jsx27("option", { value: "ERROR", children: "Error" })
|
|
2122
|
+
]
|
|
2123
|
+
}
|
|
2124
|
+
)
|
|
2125
|
+
] }),
|
|
2126
|
+
loading ? /* @__PURE__ */ jsx27("div", { className: "text-center py-8 text-gray-500", children: "Loading logs..." }) : /* @__PURE__ */ jsx27("div", { className: "space-y-2 max-h-96 overflow-y-auto", children: logs.map((log) => /* @__PURE__ */ jsx27("div", { className: "p-3 bg-gray-50 rounded border border-gray-200", children: /* @__PURE__ */ jsxs24("div", { className: "flex items-start gap-3", children: [
|
|
2127
|
+
/* @__PURE__ */ jsx27("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${getLevelColor(log.level)}`, children: log.level }),
|
|
2128
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex-1 min-w-0", children: [
|
|
2129
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm text-gray-900", children: log.message }),
|
|
2130
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex gap-4 mt-1 text-xs text-gray-500", children: [
|
|
2131
|
+
/* @__PURE__ */ jsx27("span", { children: new Date(log.timestamp).toLocaleString() }),
|
|
2132
|
+
/* @__PURE__ */ jsx27("span", { children: log.source })
|
|
2133
|
+
] })
|
|
2134
|
+
] })
|
|
2135
|
+
] }) }, log.id)) })
|
|
2136
|
+
] });
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
// src/components/layout/PrimusLayout.tsx
|
|
2140
|
+
import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2141
|
+
var PrimusLayout = ({
|
|
2142
|
+
children,
|
|
2143
|
+
sidebar,
|
|
2144
|
+
header
|
|
2145
|
+
}) => {
|
|
2146
|
+
return /* @__PURE__ */ jsxs25("div", { className: `min-h-screen flex text-white relative`, children: [
|
|
2147
|
+
sidebar && /* @__PURE__ */ jsx28("aside", { className: "w-72 flex-shrink-0 flex flex-col fixed inset-y-0 left-0 z-50", children: sidebar }),
|
|
2148
|
+
/* @__PURE__ */ jsxs25("div", { className: `flex-1 flex flex-col min-h-screen ${sidebar ? "pl-72" : ""}`, children: [
|
|
2149
|
+
header && /* @__PURE__ */ jsx28("header", { className: "h-20 flex items-center px-8 fixed top-0 right-0 left-72 z-40 bg-slate-900/10 backdrop-blur-md border-b border-white/5 transition-all duration-300", children: header }),
|
|
2150
|
+
/* @__PURE__ */ jsx28("main", { className: "flex-1 p-8 pt-28 overflow-x-hidden", children: /* @__PURE__ */ jsx28("div", { className: "max-w-7xl mx-auto space-y-8 animate-enter", children }) })
|
|
2151
|
+
] })
|
|
2152
|
+
] });
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
// src/components/layout/PrimusSidebar.tsx
|
|
2156
|
+
import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2157
|
+
var PrimusSidebar = ({
|
|
2158
|
+
logo,
|
|
2159
|
+
items,
|
|
2160
|
+
footer,
|
|
2161
|
+
onItemClick,
|
|
2162
|
+
activeId
|
|
2163
|
+
}) => {
|
|
2164
|
+
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col h-full bg-slate-900/50 backdrop-blur-xl border-r border-white/10 relative overflow-hidden", children: [
|
|
2165
|
+
/* @__PURE__ */ jsx29("div", { className: "absolute top-0 left-0 w-full h-64 bg-purple-500/10 blur-[60px] pointer-events-none" }),
|
|
2166
|
+
logo && /* @__PURE__ */ jsx29("div", { className: "h-20 flex items-center px-6 relative z-10 border-b border-white/5", children: logo }),
|
|
2167
|
+
/* @__PURE__ */ jsx29("nav", { className: "flex-1 py-6 px-3 overflow-y-auto relative z-10", children: /* @__PURE__ */ jsx29("ul", { className: "space-y-1", children: items.map((item) => {
|
|
2168
|
+
const isActive = activeId === item.id || item.active;
|
|
2169
|
+
return /* @__PURE__ */ jsx29("li", { children: /* @__PURE__ */ jsxs26(
|
|
2170
|
+
"button",
|
|
2171
|
+
{
|
|
2172
|
+
onClick: () => {
|
|
2173
|
+
item.onClick?.();
|
|
2174
|
+
onItemClick?.(item);
|
|
2175
|
+
},
|
|
2176
|
+
className: `group relative w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-medium transition-all duration-300 ${isActive ? "text-white shadow-lg shadow-purple-500/20 ring-1 ring-white/10" : "text-gray-400 hover:text-white hover:bg-white/5"}`,
|
|
2177
|
+
children: [
|
|
2178
|
+
isActive && /* @__PURE__ */ jsx29("div", { className: "absolute inset-0 rounded-xl bg-gradient-to-r from-purple-600/90 to-pink-600/90 opacity-100 transition-opacity" }),
|
|
2179
|
+
/* @__PURE__ */ jsx29("span", { className: "relative z-10 w-5 h-5 flex items-center justify-center transition-transform group-hover:scale-110 duration-300", children: item.icon }),
|
|
2180
|
+
/* @__PURE__ */ jsx29("span", { className: "relative z-10 flex-1 text-left", children: item.label }),
|
|
2181
|
+
item.badge && /* @__PURE__ */ jsx29("span", { className: `relative z-10 px-2 py-0.5 text-xs font-semibold rounded-full border ${isActive ? "bg-white/20 border-white/20 text-white" : "bg-gray-800 border-gray-700 text-gray-300 group-hover:bg-gray-700"}`, children: item.badge }),
|
|
2182
|
+
isActive && /* @__PURE__ */ jsx29("div", { className: "absolute left-0 top-1/2 -translate-y-1/2 w-1 h-8 bg-white rounded-r-full shadow-[0_0_12px_rgba(255,255,255,0.6)] hidden" })
|
|
2183
|
+
]
|
|
2184
|
+
}
|
|
2185
|
+
) }, item.id);
|
|
2186
|
+
}) }) }),
|
|
2187
|
+
footer && /* @__PURE__ */ jsx29("div", { className: "p-4 border-t border-white/5 bg-black/20 backdrop-blur-md relative z-10", children: footer })
|
|
2188
|
+
] });
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
// src/components/layout/PrimusHeader.tsx
|
|
2192
|
+
import React18 from "react";
|
|
2193
|
+
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2194
|
+
var PrimusHeader = ({
|
|
2195
|
+
title,
|
|
2196
|
+
breadcrumbs,
|
|
2197
|
+
actions,
|
|
2198
|
+
user,
|
|
2199
|
+
onUserClick
|
|
2200
|
+
}) => {
|
|
2201
|
+
return /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between w-full", children: [
|
|
2202
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-4", children: [
|
|
2203
|
+
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx30("nav", { className: "flex items-center gap-2 text-sm", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs27(React18.Fragment, { children: [
|
|
2204
|
+
index > 0 && /* @__PURE__ */ jsx30("span", { className: "text-gray-500", children: "/" }),
|
|
2205
|
+
/* @__PURE__ */ jsx30("span", { className: index === breadcrumbs.length - 1 ? "text-white" : "text-gray-400 hover:text-white cursor-pointer", children: crumb.label })
|
|
2206
|
+
] }, index)) }),
|
|
2207
|
+
title && !breadcrumbs && /* @__PURE__ */ jsx30("h1", { className: "text-xl font-semibold", children: title })
|
|
2208
|
+
] }),
|
|
2209
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-4", children: [
|
|
2210
|
+
actions,
|
|
2211
|
+
user && /* @__PURE__ */ jsxs27(
|
|
2212
|
+
"button",
|
|
2213
|
+
{
|
|
2214
|
+
onClick: onUserClick,
|
|
2215
|
+
className: "flex items-center gap-3 px-3 py-1.5 rounded-lg hover:bg-gray-700/50 transition-colors",
|
|
2216
|
+
children: [
|
|
2217
|
+
/* @__PURE__ */ jsxs27("div", { className: "text-right hidden sm:block", children: [
|
|
2218
|
+
/* @__PURE__ */ jsx30("p", { className: "text-sm font-medium", children: user.name }),
|
|
2219
|
+
user.email && /* @__PURE__ */ jsx30("p", { className: "text-xs text-gray-400", children: user.email })
|
|
2220
|
+
] }),
|
|
2221
|
+
user.avatar ? /* @__PURE__ */ jsx30("img", { src: user.avatar, alt: user.name, className: "w-8 h-8 rounded-full" }) : /* @__PURE__ */ jsx30("div", { className: "w-8 h-8 rounded-full bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center text-sm font-medium", children: user.name.charAt(0).toUpperCase() })
|
|
2222
|
+
]
|
|
2223
|
+
}
|
|
2224
|
+
)
|
|
2225
|
+
] })
|
|
2226
|
+
] });
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
// src/components/crud/PrimusDataTable.tsx
|
|
2230
|
+
import { useState as useState21, useMemo } from "react";
|
|
2231
|
+
import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2232
|
+
function PrimusDataTable({
|
|
2233
|
+
data,
|
|
2234
|
+
columns,
|
|
2235
|
+
rowKey,
|
|
2236
|
+
loading = false,
|
|
2237
|
+
selectable = false,
|
|
2238
|
+
selectedKeys = [],
|
|
2239
|
+
onSelectionChange,
|
|
2240
|
+
onRowClick,
|
|
2241
|
+
actions,
|
|
2242
|
+
emptyMessage = "No data available",
|
|
2243
|
+
searchPlaceholder = "Filter...",
|
|
2244
|
+
searchable = true
|
|
2245
|
+
}) {
|
|
2246
|
+
const [search, setSearch] = useState21("");
|
|
2247
|
+
const [sortKey, setSortKey] = useState21(null);
|
|
2248
|
+
const [sortDir, setSortDir] = useState21("asc");
|
|
2249
|
+
const filteredData = useMemo(() => {
|
|
2250
|
+
let result = [...data];
|
|
2251
|
+
if (search) {
|
|
2252
|
+
const lowerSearch = search.toLowerCase();
|
|
2253
|
+
result = result.filter(
|
|
2254
|
+
(row) => Object.values(row).some(
|
|
2255
|
+
(val) => String(val).toLowerCase().includes(lowerSearch)
|
|
2256
|
+
)
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
if (sortKey) {
|
|
2260
|
+
result.sort((a, b) => {
|
|
2261
|
+
const aVal = a[sortKey];
|
|
2262
|
+
const bVal = b[sortKey];
|
|
2263
|
+
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
2264
|
+
return sortDir === "asc" ? cmp : -cmp;
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
return result;
|
|
2268
|
+
}, [data, search, sortKey, sortDir]);
|
|
2269
|
+
const handleSort = (key) => {
|
|
2270
|
+
if (sortKey === key) {
|
|
2271
|
+
setSortDir(sortDir === "asc" ? "desc" : "asc");
|
|
2272
|
+
} else {
|
|
2273
|
+
setSortKey(key);
|
|
2274
|
+
setSortDir("asc");
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
const toggleSelect = (key) => {
|
|
2278
|
+
const newKeys = selectedKeys.includes(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
|
|
2279
|
+
onSelectionChange?.(newKeys);
|
|
2280
|
+
};
|
|
2281
|
+
const toggleSelectAll = () => {
|
|
2282
|
+
if (selectedKeys.length === filteredData.length) {
|
|
2283
|
+
onSelectionChange?.([]);
|
|
2284
|
+
} else {
|
|
2285
|
+
onSelectionChange?.(filteredData.map((row) => row[rowKey]));
|
|
2286
|
+
}
|
|
2287
|
+
};
|
|
2288
|
+
return /* @__PURE__ */ jsxs28("div", { className: "flex flex-col space-y-4", children: [
|
|
2289
|
+
searchable && /* @__PURE__ */ jsx31("div", { className: "flex items-center justify-between px-1", children: /* @__PURE__ */ jsxs28("div", { className: "relative group", children: [
|
|
2290
|
+
/* @__PURE__ */ jsx31("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx31("svg", { className: "h-4 w-4 text-gray-500 group-focus-within:text-purple-400 transition-colors", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx31("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) }),
|
|
2291
|
+
/* @__PURE__ */ jsx31(
|
|
2292
|
+
"input",
|
|
2293
|
+
{
|
|
2294
|
+
type: "text",
|
|
2295
|
+
placeholder: searchPlaceholder,
|
|
2296
|
+
value: search,
|
|
2297
|
+
onChange: (e) => setSearch(e.target.value),
|
|
2298
|
+
className: "w-64 pl-10 pr-4 py-2 bg-white/5 border border-white/10 rounded-lg text-sm text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-purple-500/50 focus:bg-white/10 transition-all hover:bg-white/10"
|
|
2299
|
+
}
|
|
2300
|
+
)
|
|
2301
|
+
] }) }),
|
|
2302
|
+
/* @__PURE__ */ jsx31("div", { className: `overflow-hidden rounded-xl border border-white/5 bg-slate-900/40 backdrop-blur-md shadow-xl ring-1 ring-white/5 ${loading ? "opacity-80" : ""}`, children: /* @__PURE__ */ jsx31("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs28("table", { className: "w-full text-left border-collapse", children: [
|
|
2303
|
+
/* @__PURE__ */ jsx31("thead", { className: "bg-white/5 border-b border-white/5 backdrop-blur-md sticky top-0 z-20", children: /* @__PURE__ */ jsxs28("tr", { children: [
|
|
2304
|
+
selectable && /* @__PURE__ */ jsx31("th", { className: "w-12 px-6 py-4", children: /* @__PURE__ */ jsx31(
|
|
2305
|
+
"input",
|
|
2306
|
+
{
|
|
2307
|
+
type: "checkbox",
|
|
2308
|
+
checked: selectedKeys.length === filteredData.length && filteredData.length > 0,
|
|
2309
|
+
onChange: toggleSelectAll,
|
|
2310
|
+
className: "rounded border-gray-600 bg-gray-800 text-purple-600 focus:ring-offset-gray-900 focus:ring-purple-500 transition-colors cursor-pointer"
|
|
2311
|
+
}
|
|
2312
|
+
) }),
|
|
2313
|
+
columns.map((col) => /* @__PURE__ */ jsx31(
|
|
2314
|
+
"th",
|
|
2315
|
+
{
|
|
2316
|
+
className: `px-6 py-4 text-xs font-semibold text-gray-400 uppercase tracking-wider ${col.sortable ? "cursor-pointer hover:text-white group" : ""}`,
|
|
2317
|
+
style: { width: col.width },
|
|
2318
|
+
onClick: () => col.sortable && handleSort(String(col.key)),
|
|
2319
|
+
children: /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-2", children: [
|
|
2320
|
+
col.header,
|
|
2321
|
+
col.sortable && /* @__PURE__ */ jsx31("span", { className: `transition-opacity duration-200 ${sortKey === col.key ? "opacity-100 text-purple-400" : "opacity-0 group-hover:opacity-50"}`, children: sortDir === "asc" ? "\u2191" : "\u2193" })
|
|
2322
|
+
] })
|
|
2323
|
+
},
|
|
2324
|
+
String(col.key)
|
|
2325
|
+
)),
|
|
2326
|
+
actions && /* @__PURE__ */ jsx31("th", { className: "w-24 px-6 py-4 text-right text-xs font-semibold text-gray-400 uppercase tracking-wider" })
|
|
2327
|
+
] }) }),
|
|
2328
|
+
/* @__PURE__ */ jsx31("tbody", { className: "divide-y divide-white/5", children: loading ? /* @__PURE__ */ jsx31("tr", { children: /* @__PURE__ */ jsx31("td", { colSpan: columns.length + (selectable ? 1 : 0) + (actions ? 1 : 0), className: "px-6 py-24 text-center text-gray-500", children: /* @__PURE__ */ jsxs28("div", { className: "flex flex-col items-center justify-center gap-3", children: [
|
|
2329
|
+
/* @__PURE__ */ jsx31("div", { className: "w-6 h-6 border-2 border-purple-500/50 border-t-purple-500 rounded-full animate-spin" }),
|
|
2330
|
+
/* @__PURE__ */ jsx31("span", { className: "text-sm font-medium", children: "Loading data..." })
|
|
2331
|
+
] }) }) }) : filteredData.length === 0 ? /* @__PURE__ */ jsx31("tr", { children: /* @__PURE__ */ jsx31("td", { colSpan: columns.length + (selectable ? 1 : 0) + (actions ? 1 : 0), className: "px-6 py-24 text-center text-gray-500", children: /* @__PURE__ */ jsx31("p", { className: "text-sm", children: emptyMessage }) }) }) : filteredData.map((row) => /* @__PURE__ */ jsxs28(
|
|
2332
|
+
"tr",
|
|
2333
|
+
{
|
|
2334
|
+
onClick: () => onRowClick?.(row),
|
|
2335
|
+
className: `
|
|
2336
|
+
group transition-colors duration-150
|
|
2337
|
+
${onRowClick ? "cursor-pointer hover:bg-white/5" : ""}
|
|
2338
|
+
${selectedKeys.includes(row[rowKey]) ? "bg-purple-500/10 hover:bg-purple-500/20" : ""}
|
|
2339
|
+
`,
|
|
2340
|
+
children: [
|
|
2341
|
+
selectable && /* @__PURE__ */ jsx31("td", { className: "px-6 py-4", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx31(
|
|
2342
|
+
"input",
|
|
2343
|
+
{
|
|
2344
|
+
type: "checkbox",
|
|
2345
|
+
checked: selectedKeys.includes(row[rowKey]),
|
|
2346
|
+
onChange: () => toggleSelect(row[rowKey]),
|
|
2347
|
+
className: "rounded border-gray-600 bg-gray-800 text-purple-600 focus:ring-offset-gray-900 focus:ring-purple-500 cursor-pointer"
|
|
2348
|
+
}
|
|
2349
|
+
) }),
|
|
2350
|
+
columns.map((col) => /* @__PURE__ */ jsx31("td", { className: "px-6 py-4 text-sm text-gray-300 group-hover:text-white transition-colors", children: col.render ? col.render(row[col.key], row) : String(row[col.key] ?? "") }, String(col.key))),
|
|
2351
|
+
actions && /* @__PURE__ */ jsx31("td", { className: "px-6 py-4 text-right", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx31("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity transform translate-x-2 group-hover:translate-x-0", children: actions(row) }) })
|
|
2352
|
+
]
|
|
2353
|
+
},
|
|
2354
|
+
String(row[rowKey])
|
|
2355
|
+
)) })
|
|
2356
|
+
] }) }) })
|
|
2357
|
+
] });
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// src/components/crud/PrimusModal.tsx
|
|
2361
|
+
import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2362
|
+
var PrimusModal = ({
|
|
2363
|
+
open,
|
|
2364
|
+
onClose,
|
|
2365
|
+
title,
|
|
2366
|
+
children,
|
|
2367
|
+
footer,
|
|
2368
|
+
size = "md"
|
|
2369
|
+
}) => {
|
|
2370
|
+
if (!open) return null;
|
|
2371
|
+
const sizeClasses = {
|
|
2372
|
+
sm: "max-w-sm",
|
|
2373
|
+
md: "max-w-md",
|
|
2374
|
+
lg: "max-w-lg",
|
|
2375
|
+
xl: "max-w-xl"
|
|
2376
|
+
};
|
|
2377
|
+
return /* @__PURE__ */ jsxs29("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
2378
|
+
/* @__PURE__ */ jsx32(
|
|
2379
|
+
"div",
|
|
2380
|
+
{
|
|
2381
|
+
className: "absolute inset-0 bg-black/60 backdrop-blur-sm",
|
|
2382
|
+
onClick: onClose
|
|
2383
|
+
}
|
|
2384
|
+
),
|
|
2385
|
+
/* @__PURE__ */ jsxs29("div", { className: `relative w-full ${sizeClasses[size]} mx-4 bg-gray-800 rounded-xl border border-gray-700 shadow-2xl`, children: [
|
|
2386
|
+
title && /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between px-6 py-4 border-b border-gray-700", children: [
|
|
2387
|
+
/* @__PURE__ */ jsx32("h2", { className: "text-lg font-semibold text-white", children: title }),
|
|
2388
|
+
/* @__PURE__ */ jsx32(
|
|
2389
|
+
"button",
|
|
2390
|
+
{
|
|
2391
|
+
onClick: onClose,
|
|
2392
|
+
className: "text-gray-400 hover:text-white transition-colors",
|
|
2393
|
+
children: /* @__PURE__ */ jsx32("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx32("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
2394
|
+
}
|
|
2395
|
+
)
|
|
2396
|
+
] }),
|
|
2397
|
+
/* @__PURE__ */ jsx32("div", { className: "p-6", children }),
|
|
2398
|
+
footer && /* @__PURE__ */ jsx32("div", { className: "flex justify-end gap-3 px-6 py-4 border-t border-gray-700 bg-gray-800/50", children: footer })
|
|
2399
|
+
] })
|
|
2400
|
+
] });
|
|
2401
|
+
};
|
|
2402
|
+
|
|
2403
|
+
// src/components/dashboard/PrimusDashboard.tsx
|
|
2404
|
+
import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2405
|
+
var PrimusStatCard = ({
|
|
2406
|
+
title,
|
|
2407
|
+
value,
|
|
2408
|
+
change,
|
|
2409
|
+
icon,
|
|
2410
|
+
description,
|
|
2411
|
+
trend = [40, 35, 50, 45, 60, 55, 70]
|
|
2412
|
+
// Default dummy data
|
|
2413
|
+
}) => {
|
|
2414
|
+
const changeColors = {
|
|
2415
|
+
increase: "text-emerald-400 bg-emerald-400/10",
|
|
2416
|
+
decrease: "text-rose-400 bg-rose-400/10",
|
|
2417
|
+
neutral: "text-gray-400 bg-gray-400/10"
|
|
2418
|
+
};
|
|
2419
|
+
const changeIcons = {
|
|
2420
|
+
increase: "\u2191",
|
|
2421
|
+
decrease: "\u2193",
|
|
2422
|
+
neutral: "\u2192"
|
|
2423
|
+
};
|
|
2424
|
+
const renderSparkline = () => {
|
|
2425
|
+
return /* @__PURE__ */ jsxs30("svg", { className: "w-full h-12 opacity-50 absolute bottom-0 left-0", preserveAspectRatio: "none", "data-trend": trend.join(","), children: [
|
|
2426
|
+
/* @__PURE__ */ jsx33("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" }),
|
|
2427
|
+
/* @__PURE__ */ jsx33("defs", { children: /* @__PURE__ */ jsxs30("linearGradient", { id: `gradient-${title.replace(/\s/g, "")}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
2428
|
+
/* @__PURE__ */ jsx33("stop", { offset: "0%", stopColor: "currentColor", stopOpacity: "0.2" }),
|
|
2429
|
+
/* @__PURE__ */ jsx33("stop", { offset: "100%", stopColor: "currentColor", stopOpacity: "0" })
|
|
2430
|
+
] }) })
|
|
2431
|
+
] });
|
|
2432
|
+
};
|
|
2433
|
+
return /* @__PURE__ */ jsxs30("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", children: [
|
|
2434
|
+
/* @__PURE__ */ jsx33("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" }),
|
|
2435
|
+
/* @__PURE__ */ jsxs30("div", { className: "p-6 relative z-10", children: [
|
|
2436
|
+
/* @__PURE__ */ jsxs30("div", { className: "flex items-start justify-between mb-4", children: [
|
|
2437
|
+
/* @__PURE__ */ jsx33("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", children: icon || /* @__PURE__ */ jsx33("div", { className: "w-5 h-5 bg-current opacity-20" }) }),
|
|
2438
|
+
change && /* @__PURE__ */ jsxs30("span", { className: `px-2 py-0.5 text-xs font-medium rounded-full flex items-center gap-1 ${changeColors[change.type]}`, children: [
|
|
2439
|
+
changeIcons[change.type],
|
|
2440
|
+
" ",
|
|
2441
|
+
Math.abs(change.value),
|
|
2442
|
+
"%"
|
|
2443
|
+
] })
|
|
2444
|
+
] }),
|
|
2445
|
+
/* @__PURE__ */ jsxs30("div", { children: [
|
|
2446
|
+
/* @__PURE__ */ jsx33("h3", { className: "text-sm font-medium text-gray-400 tracking-wide", children: title }),
|
|
2447
|
+
/* @__PURE__ */ jsx33("div", { className: "mt-1 flex items-baseline gap-2", children: /* @__PURE__ */ jsx33("span", { className: "text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-400", children: value }) }),
|
|
2448
|
+
description && /* @__PURE__ */ jsx33("p", { className: "text-xs text-gray-500 mt-2 font-medium", children: description })
|
|
2449
|
+
] })
|
|
2450
|
+
] }),
|
|
2451
|
+
renderSparkline(),
|
|
2452
|
+
/* @__PURE__ */ jsx33("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" : change?.type === "decrease" ? "bg-rose-500" : "bg-blue-500"}` })
|
|
2453
|
+
] });
|
|
2454
|
+
};
|
|
2455
|
+
var PrimusDashboard = ({
|
|
2456
|
+
children,
|
|
2457
|
+
title,
|
|
2458
|
+
subtitle,
|
|
2459
|
+
actions
|
|
2460
|
+
}) => {
|
|
2461
|
+
return /* @__PURE__ */ jsxs30("div", { className: "space-y-8 animate-enter", children: [
|
|
2462
|
+
(title || actions) && /* @__PURE__ */ jsxs30("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-4 border-b border-white/5 pb-6", children: [
|
|
2463
|
+
/* @__PURE__ */ jsxs30("div", { children: [
|
|
2464
|
+
title && /* @__PURE__ */ jsx33("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", children: title }),
|
|
2465
|
+
subtitle && /* @__PURE__ */ jsx33("p", { className: "text-gray-400 mt-2 text-lg font-light leading-relaxed", children: subtitle })
|
|
2466
|
+
] }),
|
|
2467
|
+
actions && /* @__PURE__ */ jsx33("div", { className: "flex items-center gap-3", children: actions })
|
|
2468
|
+
] }),
|
|
2469
|
+
children
|
|
2470
|
+
] });
|
|
2471
|
+
};
|
|
2472
|
+
var DashboardGrid = ({
|
|
2473
|
+
children,
|
|
2474
|
+
columns = 4
|
|
2475
|
+
}) => {
|
|
2476
|
+
const colClasses = {
|
|
2477
|
+
1: "grid-cols-1",
|
|
2478
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
2479
|
+
3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
|
2480
|
+
4: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4"
|
|
2481
|
+
};
|
|
2482
|
+
return /* @__PURE__ */ jsx33("div", { className: `grid gap-6 ${colClasses[columns]}`, children });
|
|
2483
|
+
};
|
|
2484
|
+
export {
|
|
2485
|
+
AICopilot,
|
|
2486
|
+
AccountDashboard,
|
|
2487
|
+
AgentDirectory,
|
|
2488
|
+
CheckoutForm,
|
|
2489
|
+
ClaimStatusTracker,
|
|
2490
|
+
CreditCardVisual,
|
|
2491
|
+
CreditScoreCard,
|
|
2492
|
+
DashboardGrid,
|
|
2493
|
+
DocumentViewer,
|
|
2494
|
+
FeatureFlagToggle,
|
|
2495
|
+
FileUploader,
|
|
2496
|
+
FraudDetectionDashboard,
|
|
2497
|
+
KYCVerification,
|
|
2498
|
+
LoanCalculator,
|
|
2499
|
+
LogViewer,
|
|
2500
|
+
LoginPage,
|
|
2501
|
+
NotificationBell,
|
|
2502
|
+
NotificationFeed,
|
|
2503
|
+
PolicyCard,
|
|
2504
|
+
PremiumCalculator,
|
|
2505
|
+
PrimusDashboard,
|
|
2506
|
+
PrimusDataTable,
|
|
2507
|
+
PrimusHeader,
|
|
2508
|
+
PrimusLayout,
|
|
2509
|
+
PrimusLogin,
|
|
2510
|
+
PrimusModal,
|
|
2511
|
+
PrimusNotificationCenter,
|
|
2512
|
+
PrimusNotifications,
|
|
2513
|
+
PrimusProvider,
|
|
2514
|
+
PrimusSidebar,
|
|
2515
|
+
PrimusStatCard,
|
|
2516
|
+
PrimusThemeProvider,
|
|
2517
|
+
PrimusThemeToggle,
|
|
2518
|
+
QuoteComparison,
|
|
2519
|
+
SecurityDashboard,
|
|
2520
|
+
TransactionHistory,
|
|
2521
|
+
UserProfile,
|
|
2522
|
+
themeColors,
|
|
2523
|
+
useNotifications,
|
|
2524
|
+
usePrimusAuth,
|
|
2525
|
+
usePrimusTheme,
|
|
2526
|
+
useRealtimeNotifications
|
|
2527
|
+
};
|