@retinalabsllc/zairusjs 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +194 -3
- package/dist/index.d.ts +194 -3
- package/dist/index.js +519 -3
- package/dist/index.mjs +530 -3
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1167,11 +1167,12 @@ var TextInput = ({
|
|
|
1167
1167
|
maxLength,
|
|
1168
1168
|
disabled,
|
|
1169
1169
|
readOnly,
|
|
1170
|
+
type = "text",
|
|
1170
1171
|
onClick
|
|
1171
|
-
}) => /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 flex-1 w-full", onClick }, /* @__PURE__ */ React21.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block uppercase" }, label), /* @__PURE__ */ React21.createElement(
|
|
1172
|
+
}) => /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 flex-1 w-full", onClick }, label && /* @__PURE__ */ React21.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block uppercase" }, label), /* @__PURE__ */ React21.createElement(
|
|
1172
1173
|
"input",
|
|
1173
1174
|
{
|
|
1174
|
-
type
|
|
1175
|
+
type,
|
|
1175
1176
|
value,
|
|
1176
1177
|
onChange: (e) => onChange(e.target.value.substring(0, maxLength || 100)),
|
|
1177
1178
|
placeholder,
|
|
@@ -1189,7 +1190,7 @@ var NumberInput = ({
|
|
|
1189
1190
|
placeholder,
|
|
1190
1191
|
maxLength,
|
|
1191
1192
|
disabled
|
|
1192
|
-
}) => /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 flex-1 w-full" }, /* @__PURE__ */ React21.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block uppercase" }, label), /* @__PURE__ */ React21.createElement(
|
|
1193
|
+
}) => /* @__PURE__ */ React21.createElement("div", { className: "space-y-2 flex-1 w-full" }, label && /* @__PURE__ */ React21.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block uppercase" }, label), /* @__PURE__ */ React21.createElement(
|
|
1193
1194
|
"input",
|
|
1194
1195
|
{
|
|
1195
1196
|
type: "text",
|
|
@@ -2184,6 +2185,527 @@ var UniversalMembersPage = ({
|
|
|
2184
2185
|
"Cancel"
|
|
2185
2186
|
)))), memberToDelete && canManage && /* @__PURE__ */ React29.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React29.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isDeleting && setMemberToDelete(null) }), /* @__PURE__ */ React29.createElement("div", { className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React29.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React29.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-1" }, "Remove Member"), /* @__PURE__ */ React29.createElement("p", { className: "text-[12px] text-neutral-500 leading-snug mt-2" }, "Are you sure you want to remove this member? They will lose access instantly.")), /* @__PURE__ */ React29.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React29.createElement("button", { onClick: () => setMemberToDelete(null), disabled: isDeleting, className: "flex-1 py-2 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none" }, "Cancel"), /* @__PURE__ */ React29.createElement("button", { onClick: handleDelete, disabled: isDeleting, className: "flex-1 py-2 text-[13px] text-red-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 flex justify-center outline-none" }, isDeleting ? /* @__PURE__ */ React29.createElement(ButtonSpinner3, null) : "Remove")))));
|
|
2186
2187
|
};
|
|
2188
|
+
|
|
2189
|
+
// src/components/UniversalProfileSettings.tsx
|
|
2190
|
+
import React30, { useState as useState14, useEffect as useEffect11 } from "react";
|
|
2191
|
+
import toast5 from "react-hot-toast";
|
|
2192
|
+
import { HugeiconsIcon as HugeiconsIcon16 } from "@hugeicons/react";
|
|
2193
|
+
import { ArrowDown01Icon as ArrowDown01Icon3 } from "@hugeicons/core-free-icons";
|
|
2194
|
+
var UniversalProfileSettings = ({
|
|
2195
|
+
initialFirstName,
|
|
2196
|
+
initialLastName,
|
|
2197
|
+
email,
|
|
2198
|
+
initialProfileColor,
|
|
2199
|
+
accountStatus = "GOOD",
|
|
2200
|
+
memberSince,
|
|
2201
|
+
isReadOnly = false,
|
|
2202
|
+
themeColors,
|
|
2203
|
+
onSaveProfile
|
|
2204
|
+
}) => {
|
|
2205
|
+
const [firstName, setFirstName] = useState14(initialFirstName);
|
|
2206
|
+
const [lastName, setLastName] = useState14(initialLastName);
|
|
2207
|
+
const [profileColor, setProfileColor] = useState14(initialProfileColor);
|
|
2208
|
+
const [isSubmitting, setIsSubmitting] = useState14(false);
|
|
2209
|
+
const [isColorModalOpen, setIsColorModalOpen] = useState14(false);
|
|
2210
|
+
useEffect11(() => {
|
|
2211
|
+
setFirstName(initialFirstName || "");
|
|
2212
|
+
setLastName(initialLastName || "");
|
|
2213
|
+
setProfileColor(initialProfileColor || "NONE");
|
|
2214
|
+
}, [initialFirstName, initialLastName, initialProfileColor]);
|
|
2215
|
+
const handleFirstNameChange = (val) => {
|
|
2216
|
+
setFirstName(val.replace(/[^a-zA-Z\s-]/g, "").substring(0, 50));
|
|
2217
|
+
};
|
|
2218
|
+
const handleLastNameChange = (val) => {
|
|
2219
|
+
setLastName(val.replace(/[^a-zA-Z\s-]/g, "").substring(0, 50));
|
|
2220
|
+
};
|
|
2221
|
+
const handleSave = async (e) => {
|
|
2222
|
+
e.preventDefault();
|
|
2223
|
+
if (isSubmitting || isReadOnly) return;
|
|
2224
|
+
setIsSubmitting(true);
|
|
2225
|
+
try {
|
|
2226
|
+
const res = await onSaveProfile({ firstName, lastName, profileColor });
|
|
2227
|
+
if (res.success) {
|
|
2228
|
+
toast5.success("Profile updated successfully.");
|
|
2229
|
+
setTimeout(() => window.location.reload(), 1e3);
|
|
2230
|
+
} else {
|
|
2231
|
+
toast5.error(res.error || "Failed to update profile.");
|
|
2232
|
+
setIsSubmitting(false);
|
|
2233
|
+
}
|
|
2234
|
+
} catch (error) {
|
|
2235
|
+
toast5.error("Service unavailable. Try again later.");
|
|
2236
|
+
setIsSubmitting(false);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
const hasChanges = firstName !== initialFirstName || lastName !== initialLastName || profileColor !== initialProfileColor;
|
|
2240
|
+
const isSaveDisabled = isSubmitting || isReadOnly || !hasChanges || firstName.trim().length === 0 || lastName.trim().length === 0;
|
|
2241
|
+
const selectedColorDef = themeColors.find((c) => c.id === profileColor) || themeColors[0] || { label: "Default", bgClass: "bg-neutral-100", borderClass: "border-neutral-200" };
|
|
2242
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col max-w-3xl rounded-2xl p-6 bg-white gap-8 animate-in fade-in duration-300 min-h-full" }, /* @__PURE__ */ React30.createElement(ManagedToaster, null), /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col sm:flex-row sm:items-start justify-between gap-3 sm:gap-4" }, /* @__PURE__ */ React30.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React30.createElement("h1", { className: "text-xl text-black mb-1 truncate tracking-tight" }, "Personal Settings"), /* @__PURE__ */ React30.createElement("p", { className: "text-xs text-neutral-500 truncate" }, "Manage your personal account profile.")), isReadOnly && /* @__PURE__ */ React30.createElement("span", { className: "px-3 py-1 bg-neutral-50 text-neutral-500 rounded-full text-[10px] tracking-[0.2em] shrink-0 w-fit" }, "Read Only Access")), /* @__PURE__ */ React30.createElement("div", { className: "w-full max-w-2xl" }, /* @__PURE__ */ React30.createElement("form", { className: "flex flex-col gap-8", onSubmit: handleSave, autoComplete: "off" }, /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col sm:flex-row gap-6" }, /* @__PURE__ */ React30.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React30.createElement(
|
|
2243
|
+
TextInput,
|
|
2244
|
+
{
|
|
2245
|
+
label: "First Name",
|
|
2246
|
+
value: firstName,
|
|
2247
|
+
onChange: handleFirstNameChange,
|
|
2248
|
+
disabled: isReadOnly || isSubmitting,
|
|
2249
|
+
placeholder: "System"
|
|
2250
|
+
}
|
|
2251
|
+
)), /* @__PURE__ */ React30.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React30.createElement(
|
|
2252
|
+
TextInput,
|
|
2253
|
+
{
|
|
2254
|
+
label: "Last Name",
|
|
2255
|
+
value: lastName,
|
|
2256
|
+
onChange: handleLastNameChange,
|
|
2257
|
+
disabled: isReadOnly || isSubmitting,
|
|
2258
|
+
placeholder: "Admin"
|
|
2259
|
+
}
|
|
2260
|
+
))), /* @__PURE__ */ React30.createElement("div", { className: "space-y-2 min-w-0" }, /* @__PURE__ */ React30.createElement(
|
|
2261
|
+
TextInput,
|
|
2262
|
+
{
|
|
2263
|
+
label: "Email ID",
|
|
2264
|
+
value: email,
|
|
2265
|
+
onChange: () => {
|
|
2266
|
+
},
|
|
2267
|
+
disabled: true
|
|
2268
|
+
}
|
|
2269
|
+
), /* @__PURE__ */ React30.createElement("p", { className: "text-[10px] text-neutral-400 mt-1 truncate" }, "To change your email address, please contact support.")), /* @__PURE__ */ React30.createElement("div", { className: "space-y-2 min-w-0" }, /* @__PURE__ */ React30.createElement("label", { className: "text-[10px] text-neutral-400 tracking-[0.2em] truncate block uppercase" }, "Profile Color"), /* @__PURE__ */ React30.createElement(
|
|
2270
|
+
"button",
|
|
2271
|
+
{
|
|
2272
|
+
type: "button",
|
|
2273
|
+
onClick: () => !isReadOnly && setIsColorModalOpen(true),
|
|
2274
|
+
disabled: isReadOnly || isSubmitting,
|
|
2275
|
+
className: "w-full flex items-center justify-between px-2 py-3 text-sm bg-transparent border-b border-neutral-200 text-black transition-all outline-none focus:border-black disabled:opacity-50 disabled:cursor-not-allowed text-left"
|
|
2276
|
+
},
|
|
2277
|
+
/* @__PURE__ */ React30.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React30.createElement("div", { className: `w-4 h-4 rounded-full border ${selectedColorDef.bgClass} ${selectedColorDef.borderClass}` }), /* @__PURE__ */ React30.createElement("span", null, selectedColorDef.label)),
|
|
2278
|
+
/* @__PURE__ */ React30.createElement(HugeiconsIcon16, { icon: ArrowDown01Icon3, size: 14, className: "text-neutral-400 shrink-0" })
|
|
2279
|
+
)), /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between pt-8 mt-2 gap-6 sm:gap-4 border-t border-neutral-100" }, /* @__PURE__ */ React30.createElement("div", { className: "flex items-center gap-6 min-w-0" }, /* @__PURE__ */ React30.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React30.createElement("span", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block truncate uppercase" }, "Account Status"), /* @__PURE__ */ React30.createElement("span", { className: "text-xs text-black block truncate" }, accountStatus)), /* @__PURE__ */ React30.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React30.createElement("span", { className: "text-[10px] text-neutral-400 tracking-[0.2em] block truncate uppercase" }, "Member Since"), /* @__PURE__ */ React30.createElement("span", { className: "text-xs text-black block truncate" }, memberSince ? new Date(memberSince).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" }) : "N/A"))), /* @__PURE__ */ React30.createElement("div", { className: "flex flex-col-reverse sm:flex-row items-center gap-3 sm:gap-4 w-full sm:w-auto shrink-0" }, hasChanges && !isSubmitting && !isReadOnly && /* @__PURE__ */ React30.createElement(
|
|
2280
|
+
"button",
|
|
2281
|
+
{
|
|
2282
|
+
type: "button",
|
|
2283
|
+
onClick: () => {
|
|
2284
|
+
setFirstName(initialFirstName);
|
|
2285
|
+
setLastName(initialLastName);
|
|
2286
|
+
setProfileColor(initialProfileColor);
|
|
2287
|
+
},
|
|
2288
|
+
className: "text-[11px] tracking-widest text-neutral-500 hover:text-black transition-colors w-full sm:w-auto py-2 sm:py-0 outline-none"
|
|
2289
|
+
},
|
|
2290
|
+
"Cancel"
|
|
2291
|
+
), /* @__PURE__ */ React30.createElement(
|
|
2292
|
+
ThreeDActionButton,
|
|
2293
|
+
{
|
|
2294
|
+
type: "submit",
|
|
2295
|
+
disabled: isSaveDisabled,
|
|
2296
|
+
isLoading: isSubmitting,
|
|
2297
|
+
className: "min-w-32 w-full sm:w-auto"
|
|
2298
|
+
},
|
|
2299
|
+
"Save Changes"
|
|
2300
|
+
))))), isColorModalOpen && !isReadOnly && /* @__PURE__ */ React30.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React30.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => setIsColorModalOpen(false) }), /* @__PURE__ */ React30.createElement("div", { className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React30.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React30.createElement("h3", { className: "text-[14px] text-black tracking-tight" }, "Pick Profile Color")), /* @__PURE__ */ React30.createElement("div", { className: "w-full flex flex-col pl-2 pr-2 pb-2" }, themeColors.map((colorOption) => /* @__PURE__ */ React30.createElement(
|
|
2301
|
+
"button",
|
|
2302
|
+
{
|
|
2303
|
+
type: "button",
|
|
2304
|
+
key: colorOption.id,
|
|
2305
|
+
onClick: () => {
|
|
2306
|
+
setProfileColor(colorOption.id);
|
|
2307
|
+
setIsColorModalOpen(false);
|
|
2308
|
+
},
|
|
2309
|
+
className: `text-left px-4 py-3 text-[12px] tracking-wide transition-colors rounded-full flex items-center justify-between outline-none ${profileColor === colorOption.id ? "bg-neutral-100 text-black" : "text-neutral-500 hover:bg-neutral-50 hover:text-black"}`
|
|
2310
|
+
},
|
|
2311
|
+
/* @__PURE__ */ React30.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React30.createElement("div", { className: `w-3.5 h-3.5 rounded-full border ${colorOption.bgClass} ${colorOption.borderClass}` }), /* @__PURE__ */ React30.createElement("span", { className: "truncate pr-2" }, colorOption.label)),
|
|
2312
|
+
profileColor === colorOption.id && /* @__PURE__ */ React30.createElement("div", { className: "w-1.5 h-1.5 rounded-full shrink-0 bg-black" })
|
|
2313
|
+
))), /* @__PURE__ */ React30.createElement("div", { className: "w-full flex" }, /* @__PURE__ */ React30.createElement(
|
|
2314
|
+
"button",
|
|
2315
|
+
{
|
|
2316
|
+
type: "button",
|
|
2317
|
+
onClick: () => setIsColorModalOpen(false),
|
|
2318
|
+
className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors outline-none"
|
|
2319
|
+
},
|
|
2320
|
+
"Cancel"
|
|
2321
|
+
)))));
|
|
2322
|
+
};
|
|
2323
|
+
|
|
2324
|
+
// src/components/UniversalBillingPage.tsx
|
|
2325
|
+
import React31, { useState as useState15 } from "react";
|
|
2326
|
+
import { HugeiconsIcon as HugeiconsIcon17 } from "@hugeicons/react";
|
|
2327
|
+
import {
|
|
2328
|
+
ArrowLeft01Icon as ArrowLeft01Icon4,
|
|
2329
|
+
ArrowRight01Icon as ArrowRight01Icon4,
|
|
2330
|
+
Loading03Icon as Loading03Icon7,
|
|
2331
|
+
ArrowDown01Icon as ArrowDown01Icon4
|
|
2332
|
+
} from "@hugeicons/core-free-icons";
|
|
2333
|
+
var PageSpinner3 = () => /* @__PURE__ */ React31.createElement("div", { className: "flex justify-center items-center py-12" }, /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: Loading03Icon7, size: 32, className: "animate-spin mb-4 text-neutral-400" }));
|
|
2334
|
+
var ButtonSpinner4 = () => /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: Loading03Icon7, size: 16, className: "animate-spin text-neutral-500" });
|
|
2335
|
+
var formatDate = (dateString) => {
|
|
2336
|
+
if (!dateString) return "N/A";
|
|
2337
|
+
return new Date(dateString).toLocaleDateString("en-US", {
|
|
2338
|
+
month: "long",
|
|
2339
|
+
day: "numeric",
|
|
2340
|
+
year: "numeric"
|
|
2341
|
+
});
|
|
2342
|
+
};
|
|
2343
|
+
var formatNaira = (amount) => {
|
|
2344
|
+
return `\u20A6${(amount || 0).toLocaleString("en-NG", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
2345
|
+
};
|
|
2346
|
+
var UniversalBillingPage = ({
|
|
2347
|
+
headerTitle,
|
|
2348
|
+
headerDescription,
|
|
2349
|
+
isReadOnly = false,
|
|
2350
|
+
isModMode = false,
|
|
2351
|
+
metricPrimaryLabel,
|
|
2352
|
+
metricPrimaryValue,
|
|
2353
|
+
metricPrimarySubtext,
|
|
2354
|
+
metricSecondaryLabel,
|
|
2355
|
+
metricSecondaryValue,
|
|
2356
|
+
metricSecondarySubtext,
|
|
2357
|
+
showBillingOverview = false,
|
|
2358
|
+
billingStatus,
|
|
2359
|
+
nextBillingDate,
|
|
2360
|
+
lastPaidDate,
|
|
2361
|
+
showUsageMetrics = false,
|
|
2362
|
+
usageMetrics = [],
|
|
2363
|
+
showSearchAndFilter = false,
|
|
2364
|
+
searchQuery = "",
|
|
2365
|
+
onSearchChange,
|
|
2366
|
+
timeframe = "ALL",
|
|
2367
|
+
onTimeframeChange,
|
|
2368
|
+
invoices,
|
|
2369
|
+
isLoading,
|
|
2370
|
+
currentPage,
|
|
2371
|
+
totalPages,
|
|
2372
|
+
onPageChange,
|
|
2373
|
+
onPayInvoice,
|
|
2374
|
+
onUpdateInvoiceStatus
|
|
2375
|
+
}) => {
|
|
2376
|
+
const [currentView, setCurrentView] = useState15("list");
|
|
2377
|
+
const [selectedInvoice, setSelectedInvoice] = useState15(null);
|
|
2378
|
+
const [isTimeframeModalOpen, setIsTimeframeModalOpen] = useState15(false);
|
|
2379
|
+
const [isPaying, setIsPaying] = useState15(false);
|
|
2380
|
+
const [isActionModalOpen, setIsActionModalOpen] = useState15(false);
|
|
2381
|
+
const [isUpdating, setIsUpdating] = useState15(false);
|
|
2382
|
+
const [twoFactorCode, setTwoFactorCode] = useState15("");
|
|
2383
|
+
const handlePayment = async () => {
|
|
2384
|
+
if (!selectedInvoice || isReadOnly || !onPayInvoice || isPaying) return;
|
|
2385
|
+
setIsPaying(true);
|
|
2386
|
+
try {
|
|
2387
|
+
await onPayInvoice(selectedInvoice.id);
|
|
2388
|
+
setSelectedInvoice({ ...selectedInvoice, status: "PAID" });
|
|
2389
|
+
} finally {
|
|
2390
|
+
setIsPaying(false);
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
const handleStatusUpdate = async (newStatus) => {
|
|
2394
|
+
if (!selectedInvoice || !twoFactorCode || isUpdating || !onUpdateInvoiceStatus) return;
|
|
2395
|
+
setIsUpdating(true);
|
|
2396
|
+
try {
|
|
2397
|
+
const res = await onUpdateInvoiceStatus(selectedInvoice.id, newStatus, twoFactorCode);
|
|
2398
|
+
if (res.success) {
|
|
2399
|
+
setIsActionModalOpen(false);
|
|
2400
|
+
setTwoFactorCode("");
|
|
2401
|
+
setSelectedInvoice({ ...selectedInvoice, status: newStatus });
|
|
2402
|
+
}
|
|
2403
|
+
} finally {
|
|
2404
|
+
setIsUpdating(false);
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "flex max-w-3xl rounded-2xl bg-white p-6 flex-col gap-8 animate-in fade-in duration-300 min-h-full" }, /* @__PURE__ */ React31.createElement(ManagedToaster, null), /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col sm:flex-row sm:items-start justify-between gap-3 sm:gap-4" }, currentView === "list" ? /* @__PURE__ */ React31.createElement("div", { className: "min-w-0 w-full" }, /* @__PURE__ */ React31.createElement("h1", { className: "text-xl text-black mb-1 truncate tracking-tight" }, headerTitle), /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-neutral-500 truncate mb-6" }, headerDescription), showSearchAndFilter && onSearchChange && onTimeframeChange && /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col sm:flex-row gap-4 mb-4" }, /* @__PURE__ */ React31.createElement(
|
|
2408
|
+
TextInput,
|
|
2409
|
+
{
|
|
2410
|
+
placeholder: "Search by ID or Name...",
|
|
2411
|
+
value: searchQuery,
|
|
2412
|
+
onChange: onSearchChange
|
|
2413
|
+
}
|
|
2414
|
+
), /* @__PURE__ */ React31.createElement(
|
|
2415
|
+
"button",
|
|
2416
|
+
{
|
|
2417
|
+
type: "button",
|
|
2418
|
+
onClick: () => setIsTimeframeModalOpen(true),
|
|
2419
|
+
className: "px-4 py-3 text-sm bg-transparent border-b border-neutral-200 text-black outline-none focus:border-black shrink-0 text-left"
|
|
2420
|
+
},
|
|
2421
|
+
timeframe === "ALL" ? "All Time" : timeframe === "24H" ? "Past 24 Hours" : timeframe === "7D" ? "Past 7 Days" : "Past 30 Days"
|
|
2422
|
+
))) : /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col items-start gap-3" }, /* @__PURE__ */ React31.createElement("button", { onClick: () => setCurrentView("list"), className: "text-[10px] text-neutral-400 hover:text-black tracking-[0.2em] flex items-center gap-1.5 transition-colors outline-none" }, /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: ArrowLeft01Icon4, size: 12 }), " Back")), isReadOnly && currentView === "list" && /* @__PURE__ */ React31.createElement("span", { className: "px-3 py-1 bg-neutral-50 text-neutral-500 rounded-full text-[10px] tracking-[0.2em] shrink-0 w-fit" }, "Read Only Access")), currentView === "list" && /* @__PURE__ */ React31.createElement("div", { className: "w-full max-w-2xl" }, /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col gap-8 w-full" }, (metricPrimaryLabel || metricSecondaryLabel) && /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col sm:flex-row sm:justify-between sm:items-start pb-8 border-b border-neutral-100 gap-6 sm:gap-0" }, metricPrimaryLabel && /* @__PURE__ */ React31.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-2 truncate block uppercase" }, metricPrimaryLabel), /* @__PURE__ */ React31.createElement("div", { className: "text-xl text-black tracking-tight truncate" }, formatNaira(metricPrimaryValue)), metricPrimarySubtext && /* @__PURE__ */ React31.createElement("p", { className: "text-[10px] text-neutral-500 mt-1 tracking-widest uppercase" }, metricPrimarySubtext)), metricSecondaryLabel && /* @__PURE__ */ React31.createElement("div", { className: "min-w-0 sm:text-right" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-2 truncate block uppercase" }, metricSecondaryLabel), /* @__PURE__ */ React31.createElement("div", { className: "text-xl text-emerald-600 tracking-tight truncate" }, formatNaira(metricSecondaryValue)), metricSecondarySubtext && /* @__PURE__ */ React31.createElement("p", { className: "text-[10px] text-neutral-500 mt-1 tracking-widest uppercase" }, metricSecondarySubtext))), !isReadOnly && showBillingOverview && /* @__PURE__ */ React31.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-3 gap-8 sm:gap-4" }, /* @__PURE__ */ React31.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-3 truncate block uppercase" }, "Billing Status"), /* @__PURE__ */ React31.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React31.createElement("div", { className: `w-2 h-2 rounded-full shrink-0 ${billingStatus === "ACTIVE" ? "bg-green-500" : "bg-neutral-300"}` }), /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-black tracking-widest truncate" }, billingStatus || "UNKNOWN"))), /* @__PURE__ */ React31.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-3 truncate block uppercase" }, "Next Billing Date"), /* @__PURE__ */ React31.createElement("p", { className: "text-sm text-black truncate" }, formatDate(nextBillingDate))), lastPaidDate && /* @__PURE__ */ React31.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-3 truncate block uppercase" }, "Last Paid Date"), /* @__PURE__ */ React31.createElement("p", { className: "text-sm text-neutral-600 truncate" }, formatDate(lastPaidDate)))), !isReadOnly && showUsageMetrics && usageMetrics.length > 0 && /* @__PURE__ */ React31.createElement("div", { className: "pt-8 border-t border-neutral-100" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-6 truncate block uppercase" }, "Usage & Limits"), /* @__PURE__ */ React31.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-4 text-sm text-black" }, usageMetrics.map((metric, idx) => /* @__PURE__ */ React31.createElement("div", { key: idx, className: "flex justify-between border-b border-neutral-50 pb-2" }, /* @__PURE__ */ React31.createElement("span", { className: "text-neutral-500 text-xs" }, metric.label), /* @__PURE__ */ React31.createElement("span", { className: "text-xs text-black" }, metric.value))))), /* @__PURE__ */ React31.createElement("div", { className: "pt-8 border-t border-neutral-100" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[10px] text-neutral-400 tracking-[0.2em] mb-4 truncate block uppercase" }, "Transaction History"), isLoading ? /* @__PURE__ */ React31.createElement(PageSpinner3, null) : invoices.length === 0 ? /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-neutral-500 py-4" }, "No records found.") : /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ React31.createElement("div", { className: "divide-y divide-neutral-100" }, invoices.map((inv) => /* @__PURE__ */ React31.createElement("div", { key: inv.id, onClick: () => {
|
|
2423
|
+
setSelectedInvoice(inv);
|
|
2424
|
+
setCurrentView("details");
|
|
2425
|
+
}, className: "flex items-center justify-between py-4 hover:bg-neutral-50/50 cursor-pointer group min-w-0 px-2 transition-colors" }, /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col gap-1 min-w-0 flex-1" }, /* @__PURE__ */ React31.createElement("p", { className: "text-sm text-black truncate pr-4" }, inv.name), /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-neutral-500 truncate" }, inv.subtext)), /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col items-end gap-1 shrink-0 pl-4" }, /* @__PURE__ */ React31.createElement("p", { className: "text-sm text-black" }, formatNaira(inv.amountDue)), /* @__PURE__ */ React31.createElement("span", { className: `text-[9px] tracking-widest px-2 py-0.5 rounded-full ${inv.status === "PAID" ? "bg-emerald-50 text-emerald-600" : "bg-neutral-50 text-neutral-600"}` }, inv.status))))), totalPages > 1 && /* @__PURE__ */ React31.createElement("div", { className: "flex items-center justify-between pt-4 mt-2" }, /* @__PURE__ */ React31.createElement("span", { className: "text-[10px] text-neutral-400 tracking-[0.2em]" }, "Page ", currentPage, " of ", totalPages), /* @__PURE__ */ React31.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React31.createElement("button", { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1 || isLoading, className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black outline-none disabled:opacity-30" }, /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: ArrowLeft01Icon4, size: 14 })), /* @__PURE__ */ React31.createElement("button", { onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages || isLoading, className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black outline-none disabled:opacity-30" }, /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: ArrowRight01Icon4, size: 14 })))))))), currentView === "details" && selectedInvoice && /* @__PURE__ */ React31.createElement("div", { className: "w-full max-w-2xl animate-in fade-in duration-300" }, /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col gap-6 w-full" }, /* @__PURE__ */ React31.createElement("div", null, /* @__PURE__ */ React31.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-1 uppercase" }, "Breakdown"), /* @__PURE__ */ React31.createElement("h2", { className: "text-xl text-black mb-1" }, selectedInvoice.name), /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-neutral-500" }, "Generated on ", formatDate(selectedInvoice.createdAt))), /* @__PURE__ */ React31.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6 p-6 sm:p-8 rounded-2xl border border-neutral-100" }, /* @__PURE__ */ React31.createElement("div", null, /* @__PURE__ */ React31.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-1 uppercase" }, "Amount"), /* @__PURE__ */ React31.createElement("p", { className: "text-xl text-black" }, formatNaira(selectedInvoice.amountDue))), /* @__PURE__ */ React31.createElement("div", null, /* @__PURE__ */ React31.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-1 uppercase" }, "Status"), isModMode ? /* @__PURE__ */ React31.createElement(
|
|
2426
|
+
"button",
|
|
2427
|
+
{
|
|
2428
|
+
onClick: () => !isUpdating && setIsActionModalOpen(true),
|
|
2429
|
+
disabled: isUpdating,
|
|
2430
|
+
className: `flex items-center gap-3 text-xs text-black px-3 py-1.5 mt-1 border rounded-full transition-colors disabled:opacity-50 outline-none ${isActionModalOpen ? "bg-neutral-50 border-neutral-300" : "bg-white border-neutral-200 hover:bg-neutral-50"}`
|
|
2431
|
+
},
|
|
2432
|
+
selectedInvoice.status,
|
|
2433
|
+
isUpdating ? /* @__PURE__ */ React31.createElement(ButtonSpinner4, null) : /* @__PURE__ */ React31.createElement(HugeiconsIcon17, { icon: ArrowDown01Icon4, size: 14, className: "text-neutral-400" })
|
|
2434
|
+
) : /* @__PURE__ */ React31.createElement("span", { className: `text-[10px] tracking-widest px-3 py-1 mt-1 inline-block rounded-full ${selectedInvoice.status === "PAID" ? "bg-emerald-100 text-emerald-700" : "bg-neutral-100 text-neutral-700"}` }, selectedInvoice.status)), /* @__PURE__ */ React31.createElement("div", { className: "md:col-span-2 pt-4 border-t border-neutral-200/60 mt-2" }, /* @__PURE__ */ React31.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 block mb-1 uppercase" }, "Reference ID"), /* @__PURE__ */ React31.createElement("p", { className: "text-xs text-neutral-500 bg-white px-3 py-2 rounded-full border border-neutral-200 inline-block" }, selectedInvoice.id))), /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center justify-end gap-4 pt-4" }, !isModMode && selectedInvoice.status === "SCHEDULED" && onPayInvoice && /* @__PURE__ */ React31.createElement("div", { className: "flex flex-col items-end gap-2 w-full sm:w-auto" }, /* @__PURE__ */ React31.createElement(
|
|
2435
|
+
ThreeDActionButton,
|
|
2436
|
+
{
|
|
2437
|
+
onClick: handlePayment,
|
|
2438
|
+
disabled: isReadOnly || isPaying,
|
|
2439
|
+
isLoading: isPaying,
|
|
2440
|
+
className: "w-full sm:w-auto"
|
|
2441
|
+
},
|
|
2442
|
+
"Pay Invoice"
|
|
2443
|
+
)), !isModMode && selectedInvoice.status === "PAID" && /* @__PURE__ */ React31.createElement("span", { className: "text-[11px] tracking-widest text-emerald-600 px-6 py-3 bg-emerald-50 rounded-full" }, "Invoice Completed")))), isTimeframeModalOpen && showSearchAndFilter && onTimeframeChange && /* @__PURE__ */ React31.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React31.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => setIsTimeframeModalOpen(false) }), /* @__PURE__ */ React31.createElement("div", { className: "relative w-80 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React31.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-2" }, "Select Timeframe"), /* @__PURE__ */ React31.createElement("p", { className: "text-[12px] text-neutral-500" }, "Choose the range of transactions to display.")), /* @__PURE__ */ React31.createElement("div", { className: "w-full flex flex-col pl-2 pr-2" }, [
|
|
2444
|
+
{ value: "ALL", label: "All Time" },
|
|
2445
|
+
{ value: "24H", label: "Past 24 Hours" },
|
|
2446
|
+
{ value: "7D", label: "Past 7 Days" },
|
|
2447
|
+
{ value: "30D", label: "Past 30 Days" }
|
|
2448
|
+
].map((option) => /* @__PURE__ */ React31.createElement(
|
|
2449
|
+
"button",
|
|
2450
|
+
{
|
|
2451
|
+
key: option.value,
|
|
2452
|
+
type: "button",
|
|
2453
|
+
onClick: () => {
|
|
2454
|
+
onTimeframeChange(option.value);
|
|
2455
|
+
setIsTimeframeModalOpen(false);
|
|
2456
|
+
},
|
|
2457
|
+
className: `text-left px-4 py-3 text-[12px] tracking-wide transition-colors rounded-full flex items-center justify-between outline-none ${timeframe === option.value ? "bg-neutral-100 text-black" : "text-neutral-500 hover:bg-neutral-50 hover:text-black"}`
|
|
2458
|
+
},
|
|
2459
|
+
/* @__PURE__ */ React31.createElement("span", { className: "truncate pr-2" }, option.label),
|
|
2460
|
+
timeframe === option.value && /* @__PURE__ */ React31.createElement("div", { className: "w-1.5 h-1.5 bg-black rounded-full shrink-0" })
|
|
2461
|
+
))), /* @__PURE__ */ React31.createElement("div", { className: "w-full flex mt-2" }, /* @__PURE__ */ React31.createElement("button", { onClick: () => setIsTimeframeModalOpen(false), className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors outline-none" }, "Cancel")))), isActionModalOpen && isModMode && /* @__PURE__ */ React31.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React31.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isUpdating && setIsActionModalOpen(false) }), /* @__PURE__ */ React31.createElement("div", { className: "relative w-80 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React31.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React31.createElement("h3", { className: "text-[14px] text-black tracking-tight mb-2" }, "Update Invoice Status"), /* @__PURE__ */ React31.createElement(
|
|
2462
|
+
TextInput,
|
|
2463
|
+
{
|
|
2464
|
+
placeholder: "Enter 2FA Code...",
|
|
2465
|
+
value: twoFactorCode,
|
|
2466
|
+
onChange: setTwoFactorCode,
|
|
2467
|
+
type: "password"
|
|
2468
|
+
}
|
|
2469
|
+
)), /* @__PURE__ */ React31.createElement("div", { className: "w-full flex flex-col pl-2 pr-2" }, ["SCHEDULED", "PAID", "FAILED", "CANCELED"].map((statusOption) => /* @__PURE__ */ React31.createElement(
|
|
2470
|
+
"button",
|
|
2471
|
+
{
|
|
2472
|
+
key: statusOption,
|
|
2473
|
+
onClick: () => handleStatusUpdate(statusOption),
|
|
2474
|
+
disabled: isUpdating || !twoFactorCode,
|
|
2475
|
+
className: `text-left px-4 py-3 text-[12px] tracking-wide transition-colors rounded-full flex items-center justify-between outline-none ${selectedInvoice?.status === statusOption ? "bg-neutral-100 text-black" : "text-neutral-500 hover:bg-neutral-50 hover:text-black"}`
|
|
2476
|
+
},
|
|
2477
|
+
/* @__PURE__ */ React31.createElement("span", { className: "truncate pr-2" }, statusOption),
|
|
2478
|
+
selectedInvoice?.status === statusOption && /* @__PURE__ */ React31.createElement("div", { className: "w-1.5 h-1.5 bg-black rounded-full shrink-0" })
|
|
2479
|
+
))), /* @__PURE__ */ React31.createElement("div", { className: "w-full flex mt-2" }, /* @__PURE__ */ React31.createElement("button", { onClick: () => setIsActionModalOpen(false), disabled: isUpdating, className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors disabled:opacity-50 outline-none" }, "Cancel")))));
|
|
2480
|
+
};
|
|
2481
|
+
|
|
2482
|
+
// src/components/UniversalDashboardPage.tsx
|
|
2483
|
+
import React32, { useState as useState16 } from "react";
|
|
2484
|
+
import { HugeiconsIcon as HugeiconsIcon18 } from "@hugeicons/react";
|
|
2485
|
+
import { ArrowDown01Icon as ArrowDown01Icon5, Loading03Icon as Loading03Icon8 } from "@hugeicons/core-free-icons";
|
|
2486
|
+
var UniversalDashboardPage = ({
|
|
2487
|
+
headerTitle,
|
|
2488
|
+
headerDescription,
|
|
2489
|
+
timeframes = [],
|
|
2490
|
+
activeTimeframe,
|
|
2491
|
+
onTimeframeChange,
|
|
2492
|
+
stats,
|
|
2493
|
+
lists,
|
|
2494
|
+
isLoading = false
|
|
2495
|
+
}) => {
|
|
2496
|
+
const [isTimeframeModalOpen, setIsTimeframeModalOpen] = useState16(false);
|
|
2497
|
+
const selectedTimeframe = timeframes.find((t) => t.id === activeTimeframe) || timeframes[0];
|
|
2498
|
+
if (isLoading) {
|
|
2499
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "flex justify-center items-center py-12" }, /* @__PURE__ */ React32.createElement(HugeiconsIcon18, { icon: Loading03Icon8, size: 32, className: "animate-spin mb-4 text-black" }));
|
|
2500
|
+
}
|
|
2501
|
+
return /* @__PURE__ */ React32.createElement("div", { className: "flex flex-col gap-8 animate-in fade-in duration-300 min-h-full pb-10" }, /* @__PURE__ */ React32.createElement(ManagedToaster, null), /* @__PURE__ */ React32.createElement("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4" }, /* @__PURE__ */ React32.createElement("div", null, /* @__PURE__ */ React32.createElement("h1", { className: "text-xl text-black mb-1 tracking-tight" }, headerTitle), /* @__PURE__ */ React32.createElement("p", { className: "text-xs text-neutral-500" }, headerDescription)), timeframes.length > 0 && selectedTimeframe && onTimeframeChange && /* @__PURE__ */ React32.createElement(
|
|
2502
|
+
"button",
|
|
2503
|
+
{
|
|
2504
|
+
onClick: () => setIsTimeframeModalOpen(true),
|
|
2505
|
+
className: "flex items-center gap-3 px-4 py-2 text-xs bg-white text-black outline-none rounded-full transition-colors hover:bg-neutral-50 shrink-0"
|
|
2506
|
+
},
|
|
2507
|
+
/* @__PURE__ */ React32.createElement("span", null, selectedTimeframe.label),
|
|
2508
|
+
/* @__PURE__ */ React32.createElement(HugeiconsIcon18, { icon: ArrowDown01Icon5, size: 14, className: "text-neutral-400" })
|
|
2509
|
+
)), stats.length > 0 && /* @__PURE__ */ React32.createElement("div", { className: "w-full rounded-2xl overflow-hidden bg-white" }, /* @__PURE__ */ React32.createElement("div", { className: "grid grid-cols-2 md:grid-cols-5 divide-y md:divide-y-0 md:divide-x divide-neutral-100" }, stats.map((stat, idx) => /* @__PURE__ */ React32.createElement("div", { key: idx, className: "p-6 flex flex-col gap-1 min-w-0" }, /* @__PURE__ */ React32.createElement("p", { className: "text-[10px] tracking-[0.2em] text-neutral-400 truncate uppercase" }, stat.label), /* @__PURE__ */ React32.createElement("p", { className: `text-xl tracking-tight truncate ${stat.valueClass || "text-black"}` }, stat.value))))), lists.length > 0 && /* @__PURE__ */ React32.createElement("div", { className: "flex flex-col gap-8 w-full max-w-4xl" }, lists.map((list, idx) => /* @__PURE__ */ React32.createElement("div", { key: idx, className: "bg-white rounded-2xl p-6 flex flex-col min-w-0" }, /* @__PURE__ */ React32.createElement("div", { className: "flex items-center gap-3 mb-6" }, /* @__PURE__ */ React32.createElement("div", { className: "text-neutral-400" }, list.icon), /* @__PURE__ */ React32.createElement("h2", { className: "text-[11px] text-black tracking-[0.2em] uppercase" }, list.title)), /* @__PURE__ */ React32.createElement("div", { className: "divide-y divide-neutral-100 flex-1 overflow-y-auto" }, list.items.length === 0 ? /* @__PURE__ */ React32.createElement("p", { className: "text-xs text-neutral-500 py-4" }, list.emptyMessage) : list.items.map((item) => /* @__PURE__ */ React32.createElement("div", { key: item.id, className: "flex flex-col sm:flex-row sm:items-center justify-between py-4 gap-2 min-w-0 group" }, /* @__PURE__ */ React32.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React32.createElement("p", { className: "text-sm text-black truncate pr-4" }, item.primaryText), /* @__PURE__ */ React32.createElement("p", { className: "text-[10px] text-neutral-500 truncate tracking-widest mt-0.5" }, item.secondaryText)), /* @__PURE__ */ React32.createElement("div", { className: "flex items-center gap-4 shrink-0 pl-4" }, item.rightText && /* @__PURE__ */ React32.createElement("p", { className: "text-xs text-black" }, item.rightText), item.rightBadge && /* @__PURE__ */ React32.createElement("span", { className: `text-[9px] tracking-widest px-2.5 py-1 rounded-full ${item.rightBadgeClass || "bg-neutral-50 text-neutral-600"}` }, item.rightBadge)))))))), isTimeframeModalOpen && timeframes.length > 0 && onTimeframeChange && /* @__PURE__ */ React32.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React32.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => setIsTimeframeModalOpen(false) }), /* @__PURE__ */ React32.createElement("div", { className: "relative w-72 bg-white shadow-2xl rounded-2xl flex flex-col items-center overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React32.createElement("div", { className: "p-6 text-center w-full" }, /* @__PURE__ */ React32.createElement("h3", { className: "text-[14px] text-black tracking-tight" }, "Select Timeframe")), /* @__PURE__ */ React32.createElement("div", { className: "w-full flex flex-col pl-2 pr-2 pb-2" }, timeframes.map((tf) => /* @__PURE__ */ React32.createElement(
|
|
2510
|
+
"button",
|
|
2511
|
+
{
|
|
2512
|
+
key: tf.id,
|
|
2513
|
+
onClick: () => {
|
|
2514
|
+
onTimeframeChange(tf.id);
|
|
2515
|
+
setIsTimeframeModalOpen(false);
|
|
2516
|
+
},
|
|
2517
|
+
className: `text-left px-4 py-3 text-[12px] tracking-wide transition-colors rounded-full flex items-center justify-between outline-none ${activeTimeframe === tf.id ? "bg-neutral-100 text-black" : "text-neutral-500 hover:bg-neutral-50 hover:text-black"}`
|
|
2518
|
+
},
|
|
2519
|
+
/* @__PURE__ */ React32.createElement("span", { className: "truncate pr-2" }, tf.label),
|
|
2520
|
+
activeTimeframe === tf.id && /* @__PURE__ */ React32.createElement("div", { className: "w-1.5 h-1.5 bg-black rounded-full shrink-0" })
|
|
2521
|
+
))), /* @__PURE__ */ React32.createElement("div", { className: "w-full flex border-t border-neutral-50" }, /* @__PURE__ */ React32.createElement(
|
|
2522
|
+
"button",
|
|
2523
|
+
{
|
|
2524
|
+
onClick: () => setIsTimeframeModalOpen(false),
|
|
2525
|
+
className: "w-full py-2.5 text-[13px] text-neutral-600 hover:bg-neutral-50 transition-colors outline-none"
|
|
2526
|
+
},
|
|
2527
|
+
"Cancel"
|
|
2528
|
+
)))));
|
|
2529
|
+
};
|
|
2530
|
+
|
|
2531
|
+
// src/components/UniversalAgentConsole.tsx
|
|
2532
|
+
import React33, { useState as useState17, useRef as useRef5 } from "react";
|
|
2533
|
+
import { HugeiconsIcon as HugeiconsIcon19 } from "@hugeicons/react";
|
|
2534
|
+
import {
|
|
2535
|
+
ArrowLeft01Icon as ArrowLeft01Icon5,
|
|
2536
|
+
ArrowRight01Icon as ArrowRight01Icon5,
|
|
2537
|
+
Loading03Icon as Loading03Icon9,
|
|
2538
|
+
AttachmentIcon,
|
|
2539
|
+
Cancel01Icon as Cancel01Icon2,
|
|
2540
|
+
Upload01Icon,
|
|
2541
|
+
Download01Icon,
|
|
2542
|
+
ArrowDown01Icon as ArrowDown01Icon6,
|
|
2543
|
+
Delete02Icon,
|
|
2544
|
+
File02Icon
|
|
2545
|
+
} from "@hugeicons/core-free-icons";
|
|
2546
|
+
var formatKeyName = (key) => key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
2547
|
+
var toTitleCaseSafe = (str) => {
|
|
2548
|
+
if (!str) return "";
|
|
2549
|
+
return str.toLowerCase().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
2550
|
+
};
|
|
2551
|
+
var IGNORED_KEYS = ["id", "fileName", "contentType", "fileSizeInBytes", "categoryCode", "specificCode", "iAgree"];
|
|
2552
|
+
var UniversalAgentConsole = ({
|
|
2553
|
+
headerTitle,
|
|
2554
|
+
headerDescription,
|
|
2555
|
+
stats,
|
|
2556
|
+
tabs,
|
|
2557
|
+
activeTab,
|
|
2558
|
+
onTabChange,
|
|
2559
|
+
listData,
|
|
2560
|
+
isLoadingList,
|
|
2561
|
+
currentPage,
|
|
2562
|
+
totalPages,
|
|
2563
|
+
onPageChange,
|
|
2564
|
+
onRowClick,
|
|
2565
|
+
currentView,
|
|
2566
|
+
onBackToList,
|
|
2567
|
+
selectedApp,
|
|
2568
|
+
currentAgentId,
|
|
2569
|
+
onAcceptApplication,
|
|
2570
|
+
onUpdateStatus,
|
|
2571
|
+
onUploadArchives,
|
|
2572
|
+
onDeleteArchive
|
|
2573
|
+
}) => {
|
|
2574
|
+
const archiveRef = useRef5(null);
|
|
2575
|
+
const [pendingFiles, setPendingFiles] = useState17([]);
|
|
2576
|
+
const [isUploading, setIsUploading] = useState17(false);
|
|
2577
|
+
const [isActioning, setIsActioning] = useState17(false);
|
|
2578
|
+
const [actionModal, setActionModal] = useState17(null);
|
|
2579
|
+
const [actionMessage, setActionMessage] = useState17("");
|
|
2580
|
+
const [archiveToDelete, setArchiveToDelete] = useState17(null);
|
|
2581
|
+
const handleFileSelect = (e) => {
|
|
2582
|
+
if (e.target.files && e.target.files.length > 0) {
|
|
2583
|
+
setPendingFiles((prev) => [...prev, ...Array.from(e.target.files)]);
|
|
2584
|
+
}
|
|
2585
|
+
setTimeout(() => {
|
|
2586
|
+
if (archiveRef.current) archiveRef.current.value = "";
|
|
2587
|
+
}, 0);
|
|
2588
|
+
};
|
|
2589
|
+
const executeUpload = async () => {
|
|
2590
|
+
if (pendingFiles.length === 0 || !selectedApp) return;
|
|
2591
|
+
setIsUploading(true);
|
|
2592
|
+
try {
|
|
2593
|
+
const payloads = await Promise.all(pendingFiles.map(async (file) => {
|
|
2594
|
+
const base64data = await new Promise((resolve, reject) => {
|
|
2595
|
+
const reader = new FileReader();
|
|
2596
|
+
reader.readAsDataURL(file);
|
|
2597
|
+
reader.onload = () => resolve(reader.result);
|
|
2598
|
+
reader.onerror = (error) => reject(error);
|
|
2599
|
+
});
|
|
2600
|
+
return { imageBase64: base64data, fileName: file.name, fileType: file.type, fileSize: file.size };
|
|
2601
|
+
}));
|
|
2602
|
+
const res = await onUploadArchives(selectedApp.id, payloads);
|
|
2603
|
+
if (res.success) setPendingFiles([]);
|
|
2604
|
+
} finally {
|
|
2605
|
+
setIsUploading(false);
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
const DynamicArrayAccordion = ({ items, parentKey }) => {
|
|
2609
|
+
const [activeFAQ, setActiveFAQ] = useState17(null);
|
|
2610
|
+
return /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col relative z-10 bg-transparent w-full mt-2 border-t border-neutral-100" }, items.map((item, index) => {
|
|
2611
|
+
const isOpen = activeFAQ === index;
|
|
2612
|
+
let label = `${parentKey ? formatKeyName(parentKey) : "Item"} ${index + 1}`;
|
|
2613
|
+
if (item.fullName) label = toTitleCaseSafe(item.fullName);
|
|
2614
|
+
else if (item.role) label = item.role;
|
|
2615
|
+
else if (item.proposedName) label = toTitleCaseSafe(item.proposedName);
|
|
2616
|
+
return /* @__PURE__ */ React33.createElement("div", { key: index, className: `transition-all duration-300 ${index !== items.length - 1 ? "border-b border-neutral-100" : ""}` }, /* @__PURE__ */ React33.createElement("button", { className: "flex items-center justify-between w-full gap-4 text-left py-5 md:py-6 group outline-none bg-transparent", onClick: () => setActiveFAQ(isOpen ? null : index) }, /* @__PURE__ */ React33.createElement("span", { className: `text-[13px] md:text-[14px] transition-colors ${isOpen ? "text-black" : "text-neutral-700 group-hover:text-black"}` }, label), /* @__PURE__ */ React33.createElement("div", { className: `shrink-0 flex items-center justify-center w-8 h-8 rounded-full border transition-all duration-300 ${isOpen ? "rotate-180 border-black text-black" : "border-neutral-300 text-neutral-500"}` }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: ArrowDown01Icon6, size: 16 }))), /* @__PURE__ */ React33.createElement("div", { className: `grid transition-all duration-300 ease-in-out ${isOpen ? "grid-rows-[1fr] pb-6 md:pb-8 opacity-100" : "grid-rows-[0fr] opacity-0"}` }, /* @__PURE__ */ React33.createElement("div", { className: "overflow-hidden" }, /* @__PURE__ */ React33.createElement("div", { className: "pt-2 flex flex-col gap-3 pr-4 md:pr-12" }, renderDynamicObject(item)))));
|
|
2617
|
+
}));
|
|
2618
|
+
};
|
|
2619
|
+
const renderValue = (key, value) => {
|
|
2620
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
2621
|
+
if (typeof value === "string") {
|
|
2622
|
+
if (value.startsWith("http") || value.startsWith("data:image")) return /* @__PURE__ */ React33.createElement("a", { href: value, download: true, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-2 px-5 py-2 bg-white border border-neutral-200 text-black text-[11px] tracking-widest rounded-full hover:bg-neutral-50 transition-colors w-fit outline-none mt-2" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Download01Icon, size: 14 }), " Download File");
|
|
2623
|
+
if (key.toLowerCase().includes("name") && !value.includes("@")) return /* @__PURE__ */ React33.createElement("p", { className: "text-[12px] md:text-[13px] leading-[1.8] text-neutral-600" }, toTitleCaseSafe(value));
|
|
2624
|
+
return /* @__PURE__ */ React33.createElement("p", { className: "text-[12px] md:text-[13px] leading-[1.8] text-neutral-600" }, value);
|
|
2625
|
+
}
|
|
2626
|
+
if (Array.isArray(value)) {
|
|
2627
|
+
const validItems = value.filter((item) => item !== null && item !== void 0 && item !== "");
|
|
2628
|
+
if (validItems.length === 0) return null;
|
|
2629
|
+
if (typeof validItems[0] === "string") return /* @__PURE__ */ React33.createElement("ul", { className: "list-disc pl-4 mt-1" }, validItems.map((v, i) => /* @__PURE__ */ React33.createElement("li", { key: i, className: "text-[12px] md:text-[13px] leading-[1.8] text-neutral-600" }, toTitleCaseSafe(v))));
|
|
2630
|
+
return /* @__PURE__ */ React33.createElement(DynamicArrayAccordion, { items: validItems, parentKey: key });
|
|
2631
|
+
}
|
|
2632
|
+
if (typeof value === "object") {
|
|
2633
|
+
if (Object.keys(value).length === 0) return null;
|
|
2634
|
+
const renderedObj = renderDynamicObject(value);
|
|
2635
|
+
if (!renderedObj || Array.isArray(renderedObj) && renderedObj.length === 0) return null;
|
|
2636
|
+
return /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-3 mt-1 w-full border-l border-neutral-100 pl-4 py-2" }, renderedObj);
|
|
2637
|
+
}
|
|
2638
|
+
return /* @__PURE__ */ React33.createElement("span", { className: "text-sm text-black" }, String(value));
|
|
2639
|
+
};
|
|
2640
|
+
const renderDynamicObject = (obj) => {
|
|
2641
|
+
if (!obj) return null;
|
|
2642
|
+
const entries = Object.entries(obj).filter(([k, v]) => {
|
|
2643
|
+
if (IGNORED_KEYS.includes(k) || v === null || v === "" || v === void 0) return false;
|
|
2644
|
+
if (Array.isArray(v) && v.filter((item) => item !== null && item !== "").length === 0) return false;
|
|
2645
|
+
if (typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0) return false;
|
|
2646
|
+
return true;
|
|
2647
|
+
});
|
|
2648
|
+
if (entries.length === 0) return null;
|
|
2649
|
+
return entries.map(([k, v]) => /* @__PURE__ */ React33.createElement("div", { key: k, className: "flex flex-col items-start min-w-0 wrap-break-word w-full mb-3 last:mb-0" }, /* @__PURE__ */ React33.createElement("span", { className: "text-[10px] tracking-[0.2em] text-neutral-400 mb-1 uppercase" }, formatKeyName(k)), renderValue(k, v)));
|
|
2650
|
+
};
|
|
2651
|
+
return /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-8 animate-in fade-in duration-300 min-h-full pb-10" }, /* @__PURE__ */ React33.createElement(ManagedToaster, null), currentView === "list" && /* @__PURE__ */ React33.createElement(React33.Fragment, null, /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col items-start gap-1" }, /* @__PURE__ */ React33.createElement("h1", { className: "text-xl text-black tracking-tight" }, headerTitle), /* @__PURE__ */ React33.createElement("p", { className: "text-sm text-neutral-500" }, headerDescription)), stats.length > 0 && /* @__PURE__ */ React33.createElement("div", { className: "w-full rounded-2xl max-w-3xl overflow-hidden bg-white" }, /* @__PURE__ */ React33.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 divide-y md:divide-y-0 md:divide-x divide-neutral-100" }, stats.map((stat, idx) => /* @__PURE__ */ React33.createElement("div", { key: idx, className: "p-6 flex items-center gap-4 hover:bg-neutral-50/50 transition-colors min-w-0" }, /* @__PURE__ */ React33.createElement("div", { className: "w-12 h-12 rounded-full border border-neutral-200 bg-white flex items-center justify-center text-black shrink-0" }, stat.icon), /* @__PURE__ */ React33.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React33.createElement("p", { className: "text-[10px] tracking-[0.2em] text-neutral-400 mb-1 truncate uppercase" }, stat.label), /* @__PURE__ */ React33.createElement("p", { className: "text-xl text-black tracking-tight truncate" }, stat.value)))))), tabs.length > 0 && /* @__PURE__ */ React33.createElement("div", { className: "flex items-center gap-6" }, tabs.map((tab) => /* @__PURE__ */ React33.createElement("button", { key: tab.id, onClick: () => onTabChange(tab.id), className: `pb-3 text-sm transition-colors outline-none ${activeTab === tab.id ? "text-black border-b border-black" : "text-neutral-400 hover:text-black"}` }, tab.label))), /* @__PURE__ */ React33.createElement("div", { className: "w-full bg-white rounded-2xl max-w-3xl overflow-hidden flex flex-col min-h-100" }, isLoadingList ? /* @__PURE__ */ React33.createElement("div", { className: "flex justify-center items-center py-12" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Loading03Icon9, size: 32, className: "animate-spin text-black" })) : listData.length === 0 ? /* @__PURE__ */ React33.createElement("div", { className: "flex-1 flex justify-center items-center text-neutral-500 text-sm py-20" }, "No matching applications found.") : /* @__PURE__ */ React33.createElement(React33.Fragment, null, /* @__PURE__ */ React33.createElement("div", { className: "divide-y divide-neutral-100 flex-1" }, listData.map((item) => /* @__PURE__ */ React33.createElement("div", { key: item.id, onClick: () => onRowClick(item.id), className: "flex items-center justify-between p-5 hover:bg-neutral-50/50 transition-colors cursor-pointer group min-w-0" }, /* @__PURE__ */ React33.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React33.createElement("p", { className: "text-sm text-black truncate pr-4" }, item.title), /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-neutral-500 truncate mt-1" }, item.subtitle)), /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col items-end gap-1 shrink-0 pl-4" }, /* @__PURE__ */ React33.createElement("span", { className: `text-[10px] tracking-widest px-3 py-1 rounded-full uppercase ${item.status === "COMPLETED" ? "bg-emerald-50 text-emerald-600" : "bg-neutral-50 text-neutral-600"}` }, item.status.replace(/_/g, " ")), /* @__PURE__ */ React33.createElement("span", { className: "text-[10px] text-neutral-400 mt-1" }, item.date))))), totalPages > 1 && /* @__PURE__ */ React33.createElement("div", { className: "flex items-center justify-between p-5 bg-neutral-50/50" }, /* @__PURE__ */ React33.createElement("span", { className: "text-[10px] text-neutral-400 tracking-[0.2em]" }, "Page ", currentPage, " of ", totalPages), /* @__PURE__ */ React33.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black outline-none disabled:opacity-30" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: ArrowLeft01Icon5, size: 14 })), /* @__PURE__ */ React33.createElement("button", { onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, className: "p-2 border border-neutral-200 rounded-full bg-white text-neutral-500 hover:text-black outline-none disabled:opacity-30" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: ArrowRight01Icon5, size: 14 }))))))), currentView === "details" && selectedApp && /* @__PURE__ */ React33.createElement("div", { className: "w-full bg-white rounded-2xl p-6 sm:p-10 animate-in fade-in duration-300 max-w-3xl flex flex-col" }, /* @__PURE__ */ React33.createElement("div", { className: "flex items-center justify-between gap-4 pb-6" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => {
|
|
2652
|
+
onBackToList();
|
|
2653
|
+
setPendingFiles([]);
|
|
2654
|
+
}, className: "flex items-center gap-2 text-[10px] text-neutral-400 hover:text-black tracking-widest transition-colors outline-none uppercase" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: ArrowLeft01Icon5, size: 14 }), " Back to List"), /* @__PURE__ */ React33.createElement("span", { className: `text-[10px] tracking-widest px-4 py-1.5 rounded-full uppercase ${selectedApp.status === "COMPLETED" ? "bg-emerald-50 text-emerald-600" : "bg-neutral-50 text-neutral-600"}` }, selectedApp.status.replace(/_/g, " "))), /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-2 mb-8" }, /* @__PURE__ */ React33.createElement("h2", { className: "text-xl text-black tracking-tight" }, selectedApp.name || selectedApp.title), /* @__PURE__ */ React33.createElement("p", { className: "text-sm text-neutral-500" }, selectedApp.type ? selectedApp.type.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase()) : selectedApp.subtitle)), selectedApp.agentId === currentAgentId && selectedApp.metadata && Object.keys(selectedApp.metadata).length > 0 ? /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-5" }, renderDynamicObject(selectedApp.metadata)) : selectedApp.agentId === currentAgentId ? /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-5" }, /* @__PURE__ */ React33.createElement("p", { className: "text-sm text-neutral-500" }, "No application data extracted.")) : null, selectedApp.agentId && /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-4 pt-8 mt-4 border-t border-neutral-100" }, /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col gap-1 mb-4" }, /* @__PURE__ */ React33.createElement("h3", { className: "text-sm text-black" }, "Official Archives"), /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-neutral-500 leading-relaxed" }, "Upload final certificates or documents. Once marked completed, the archive unlocks for the user.")), selectedApp.archives?.map((arch) => /* @__PURE__ */ React33.createElement("div", { key: arch.id, className: "flex items-center justify-between p-4 bg-emerald-50 rounded-full text-emerald-800 text-sm w-full" }, /* @__PURE__ */ React33.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: File02Icon, size: 18, className: "shrink-0" }), /* @__PURE__ */ React33.createElement("span", { className: "truncate pr-2" }, arch.name)), /* @__PURE__ */ React33.createElement("button", { onClick: () => setArchiveToDelete(arch), className: "text-emerald-700 hover:text-red-500 transition-colors p-1 outline-none shrink-0" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Delete02Icon, size: 16 })))), pendingFiles.map((file, idx) => /* @__PURE__ */ React33.createElement("div", { key: idx, className: "flex items-center justify-between text-neutral-600 text-sm w-full" }, /* @__PURE__ */ React33.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: AttachmentIcon, size: 18, className: "shrink-0" }), /* @__PURE__ */ React33.createElement("span", { className: "truncate pr-2" }, file.name)), /* @__PURE__ */ React33.createElement("button", { onClick: () => setPendingFiles((p) => p.filter((_, i) => i !== idx)), className: "text-neutral-400 hover:text-red-500 transition-colors p-1 outline-none shrink-0" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Cancel01Icon2, size: 16 })))), /* @__PURE__ */ React33.createElement("input", { type: "file", multiple: true, ref: archiveRef, onChange: handleFileSelect, className: "hidden", accept: "application/pdf,image/*" }), /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col sm:flex-row items-center gap-3 w-full" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => archiveRef.current?.click(), disabled: selectedApp.status !== "COMPLETED" || isUploading, className: "flex items-center justify-center gap-3 p-4 border border-neutral-200 rounded-full hover:bg-neutral-50 transition-colors text-black text-sm w-full outline-none disabled:opacity-50" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Upload01Icon, size: 18, className: "text-neutral-400" }), " Select Files to Upload"), pendingFiles.length > 0 && /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto shrink-0" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => setPendingFiles([]), disabled: isUploading, className: "px-6 py-2 text-neutral-600 text-[11px] tracking-widest rounded-full hover:bg-neutral-200 outline-none w-full sm:w-auto uppercase" }, "Clear All"), /* @__PURE__ */ React33.createElement(ThreeDActionButton, { onClick: executeUpload, disabled: isUploading, isLoading: isUploading, className: "w-full sm:w-auto" }, "Upload ", pendingFiles.length, " File(s)")))), /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col sm:flex-row items-center justify-between gap-4 mt-8 pt-6" }, !selectedApp.agentId ? /* @__PURE__ */ React33.createElement(ThreeDActionButton, { onClick: async () => {
|
|
2655
|
+
setIsActioning(true);
|
|
2656
|
+
await onAcceptApplication(selectedApp.id);
|
|
2657
|
+
setIsActioning(false);
|
|
2658
|
+
}, disabled: isActioning, isLoading: isActioning, className: "w-full sm:w-auto" }, "Accept Application") : selectedApp.agentId !== currentAgentId ? /* @__PURE__ */ React33.createElement("div", { className: "w-full p-4 border border-red-100 bg-red-50/30 rounded-xl flex items-start gap-3" }, /* @__PURE__ */ React33.createElement(HugeiconsIcon19, { icon: Cancel01Icon2, size: 16, className: "text-red-500 shrink-0 mt-0.5" }), /* @__PURE__ */ React33.createElement("div", null, /* @__PURE__ */ React33.createElement("p", { className: "text-sm text-red-700" }, "Application Taken"), /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-red-600 mt-1" }, "Currently handled by ", selectedApp.agentName || "another agent", "."))) : /* @__PURE__ */ React33.createElement("div", { className: "w-full flex flex-col sm:flex-row items-center gap-4 justify-between" }, /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-neutral-500" }, "You are the assigned agent."), /* @__PURE__ */ React33.createElement("div", { className: "flex flex-col sm:flex-row items-center justify-end gap-3 w-full sm:w-auto" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => setActionModal("query"), disabled: selectedApp.status === "COMPLETED", className: "w-full sm:w-auto px-6 py-2 bg-white border border-neutral-200 text-neutral-600 text-[11px] tracking-widest rounded-full hover:bg-neutral-50 transition-colors outline-none disabled:opacity-30 uppercase" }, "Query"), /* @__PURE__ */ React33.createElement("button", { onClick: () => setActionModal("reject"), disabled: selectedApp.status === "COMPLETED", className: "w-full sm:w-auto px-6 py-2 bg-white border border-neutral-200 text-neutral-600 text-[11px] tracking-widest rounded-full hover:bg-neutral-50 transition-colors outline-none disabled:opacity-30 uppercase" }, "Reject"), /* @__PURE__ */ React33.createElement(ThreeDActionButton, { onClick: () => setActionModal("success"), disabled: selectedApp.status === "COMPLETED", className: "w-full sm:w-auto" }, "Mark Success"))))), actionModal && /* @__PURE__ */ React33.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React33.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isActioning && setActionModal(null) }), /* @__PURE__ */ React33.createElement("div", { className: "relative w-full max-w-md bg-white rounded-3xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200 text-left" }, /* @__PURE__ */ React33.createElement("div", { className: "p-6" }, /* @__PURE__ */ React33.createElement("h3", { className: "text-lg text-black tracking-tight capitalize mb-2" }, actionModal === "success" ? "Complete Application" : `${actionModal} Application`), /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-neutral-500" }, actionModal === "success" ? "Are you sure you want to mark this application as successfully completed?" : `Please provide a clear reason for the user. They will see this message and be asked to fix their application.`)), (actionModal === "query" || actionModal === "reject") && /* @__PURE__ */ React33.createElement("div", { className: "p-6 pb-2" }, /* @__PURE__ */ React33.createElement(TextInput, { label: "Reason for Action", value: actionMessage, onChange: setActionMessage, placeholder: "Enter your reason here...", disabled: isActioning })), /* @__PURE__ */ React33.createElement("div", { className: "flex items-center p-6 pt-4 gap-3" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => setActionModal(null), disabled: isActioning, className: "flex-1 py-3 text-[11px] tracking-widest uppercase text-neutral-600 rounded-full hover:bg-neutral-50 transition-colors outline-none disabled:opacity-50" }, "Cancel"), /* @__PURE__ */ React33.createElement(ThreeDActionButton, { onClick: async () => {
|
|
2659
|
+
setIsActioning(true);
|
|
2660
|
+
await onUpdateStatus(selectedApp.id, actionModal === "success" ? "COMPLETED" : actionModal === "reject" ? "REJECTED" : "QUEUED", actionMessage);
|
|
2661
|
+
setIsActioning(false);
|
|
2662
|
+
setActionModal(null);
|
|
2663
|
+
setActionMessage("");
|
|
2664
|
+
}, disabled: isActioning || (actionModal === "query" || actionModal === "reject") && actionMessage.trim().length < 5, isLoading: isActioning, className: "flex-1" }, "Confirm ", actionModal)))), archiveToDelete && /* @__PURE__ */ React33.createElement("div", { className: "fixed inset-0 z-110 flex items-center justify-center p-4" }, /* @__PURE__ */ React33.createElement("div", { className: "absolute inset-0 bg-black/30", onClick: () => !isActioning && setArchiveToDelete(null) }), /* @__PURE__ */ React33.createElement("div", { className: "relative w-full max-w-md bg-white rounded-3xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200 text-left" }, /* @__PURE__ */ React33.createElement("div", { className: "p-6" }, /* @__PURE__ */ React33.createElement("h3", { className: "text-lg text-black tracking-tight mb-2" }, "Delete Document?"), /* @__PURE__ */ React33.createElement("p", { className: "text-xs text-neutral-500" }, 'Are you sure you want to permanently delete "', archiveToDelete.name, '"?')), /* @__PURE__ */ React33.createElement("div", { className: "flex items-center p-6 gap-3" }, /* @__PURE__ */ React33.createElement("button", { onClick: () => setArchiveToDelete(null), disabled: isActioning, className: "flex-1 py-3 text-[11px] tracking-widest uppercase text-neutral-600 rounded-full hover:bg-neutral-50 transition-colors outline-none disabled:opacity-50" }, "Cancel"), /* @__PURE__ */ React33.createElement(ThreeDActionButton, { onClick: async () => {
|
|
2665
|
+
setIsActioning(true);
|
|
2666
|
+
await onDeleteArchive(selectedApp.id, archiveToDelete.id);
|
|
2667
|
+
setIsActioning(false);
|
|
2668
|
+
setArchiveToDelete(null);
|
|
2669
|
+
}, isLoading: isActioning, className: "flex-1" }, "Delete Document")))));
|
|
2670
|
+
};
|
|
2671
|
+
|
|
2672
|
+
// src/components/UniversalOverviewPage.tsx
|
|
2673
|
+
import React34 from "react";
|
|
2674
|
+
var UniversalOverviewPage = ({
|
|
2675
|
+
headerTitle,
|
|
2676
|
+
headerDescription,
|
|
2677
|
+
quickStats,
|
|
2678
|
+
detailsTitle = "Details",
|
|
2679
|
+
details,
|
|
2680
|
+
primaryAction,
|
|
2681
|
+
alerts = []
|
|
2682
|
+
}) => {
|
|
2683
|
+
return /* @__PURE__ */ React34.createElement("div", { className: "flex flex-col gap-8 animate-in fade-in duration-300 min-h-full pb-10" }, /* @__PURE__ */ React34.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React34.createElement("div", null, /* @__PURE__ */ React34.createElement("h1", { className: "text-xl text-black mb-1 tracking-tight" }, headerTitle), /* @__PURE__ */ React34.createElement("p", { className: "text-xs text-neutral-500" }, headerDescription))), quickStats.length > 0 && /* @__PURE__ */ React34.createElement("div", { className: "w-full rounded-2xl overflow-hidden max-w-3xl bg-white" }, /* @__PURE__ */ React34.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 divide-y md:divide-y-0 md:divide-x divide-neutral-100" }, quickStats.map((stat, idx) => /* @__PURE__ */ React34.createElement("div", { key: idx, className: "p-6 flex items-center gap-4 hover:bg-neutral-50/50 transition-colors min-w-0" }, /* @__PURE__ */ React34.createElement("div", { className: "w-12 h-12 rounded-full border border-neutral-200 bg-white flex items-center justify-center text-black shrink-0" }, stat.icon), /* @__PURE__ */ React34.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React34.createElement("p", { className: "text-[10px] tracking-[0.2em] text-neutral-400 mb-1 truncate uppercase" }, stat.label), /* @__PURE__ */ React34.createElement("p", { className: "text-lg md:text-xl text-black tracking-tight truncate" }, stat.value)))))), /* @__PURE__ */ React34.createElement("div", { className: "w-full rounded-2xl max-w-3xl overflow-hidden bg-white min-w-0" }, /* @__PURE__ */ React34.createElement("div", { className: "p-6 sm:p-8 flex flex-col h-full min-w-0 gap-6" }, /* @__PURE__ */ React34.createElement("div", { className: "flex flex-wrap items-center justify-between gap-4 border-b border-neutral-100 pb-4" }, /* @__PURE__ */ React34.createElement("h3", { className: "text-[11px] text-black tracking-[0.2em] uppercase" }, detailsTitle), primaryAction && (primaryAction.href ? /* @__PURE__ */ React34.createElement(
|
|
2684
|
+
ThreeDButton,
|
|
2685
|
+
{
|
|
2686
|
+
href: primaryAction.href,
|
|
2687
|
+
onClick: primaryAction.onClick,
|
|
2688
|
+
className: "w-fit shrink-0 gap-2"
|
|
2689
|
+
},
|
|
2690
|
+
primaryAction.label,
|
|
2691
|
+
primaryAction.icon
|
|
2692
|
+
) : /* @__PURE__ */ React34.createElement(
|
|
2693
|
+
ThreeDActionButton,
|
|
2694
|
+
{
|
|
2695
|
+
onClick: primaryAction.onClick,
|
|
2696
|
+
isLoading: primaryAction.isLoading,
|
|
2697
|
+
className: "w-fit shrink-0 gap-2"
|
|
2698
|
+
},
|
|
2699
|
+
primaryAction.label,
|
|
2700
|
+
primaryAction.icon
|
|
2701
|
+
))), /* @__PURE__ */ React34.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-8" }, details.map((detail, idx) => /* @__PURE__ */ React34.createElement("div", { key: idx, className: "min-w-0" }, /* @__PURE__ */ React34.createElement("p", { className: "text-[10px] tracking-[0.2em] text-neutral-400 mb-1.5 uppercase" }, detail.label), /* @__PURE__ */ React34.createElement("p", { className: "text-[13px] text-black truncate" }, detail.value)))), alerts.map((alert, idx) => {
|
|
2702
|
+
const bgClass = alert.type === "warning" ? "bg-amber-50/50" : alert.type === "info" ? "bg-blue-50/50" : "bg-red-50/50";
|
|
2703
|
+
const textClass = alert.type === "warning" ? "text-amber-600" : alert.type === "info" ? "text-blue-600" : "text-red-600";
|
|
2704
|
+
const valClass = alert.type === "warning" ? "text-amber-700" : alert.type === "info" ? "text-blue-700" : "text-red-700";
|
|
2705
|
+
const lblClass = alert.type === "warning" ? "text-amber-400" : alert.type === "info" ? "text-blue-400" : "text-red-400";
|
|
2706
|
+
return /* @__PURE__ */ React34.createElement("div", { key: idx, className: `mt-4 p-5 rounded-xl flex flex-col gap-4 ${bgClass}` }, /* @__PURE__ */ React34.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React34.createElement("span", { className: `text-[11px] tracking-widest uppercase ${textClass}` }, alert.title)), alert.items.map((item, itemIdx) => /* @__PURE__ */ React34.createElement("div", { key: itemIdx }, /* @__PURE__ */ React34.createElement("span", { className: `text-[10px] tracking-[0.2em] block mb-1 uppercase ${lblClass}` }, item.label), /* @__PURE__ */ React34.createElement("p", { className: `text-[13px] leading-relaxed ${valClass}` }, item.text))));
|
|
2707
|
+
}))));
|
|
2708
|
+
};
|
|
2187
2709
|
export {
|
|
2188
2710
|
AITranscriptionFeature,
|
|
2189
2711
|
AppBento2,
|
|
@@ -2209,10 +2731,15 @@ export {
|
|
|
2209
2731
|
TextInput,
|
|
2210
2732
|
ThreeDActionButton,
|
|
2211
2733
|
ThreeDButton,
|
|
2734
|
+
UniversalAgentConsole,
|
|
2735
|
+
UniversalBillingPage,
|
|
2736
|
+
UniversalDashboardPage,
|
|
2212
2737
|
UniversalHeader,
|
|
2213
2738
|
UniversalIdentityPage,
|
|
2214
2739
|
UniversalMembersPage,
|
|
2215
2740
|
UniversalOrganizationPage,
|
|
2741
|
+
UniversalOverviewPage,
|
|
2742
|
+
UniversalProfileSettings,
|
|
2216
2743
|
UniversalSidebar,
|
|
2217
2744
|
ZairusAuth
|
|
2218
2745
|
};
|