@hanzo/ui 5.3.36 → 5.3.39
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/assets/crypto.d.ts.map +1 -1
- package/dist/assets/file.d.ts.map +1 -1
- package/dist/assets/general.d.ts.map +1 -1
- package/dist/assets/hanzo-logo.d.ts.map +1 -1
- package/dist/assets/llm-provider.d.ts.map +1 -1
- package/dist/components/cal-embed.d.ts.map +1 -1
- package/dist/frameworks/react/hooks/index.d.ts.map +1 -1
- package/dist/frameworks/react-native/utils.d.ts.map +1 -1
- package/dist/frameworks/svelte/utils.d.ts.map +1 -1
- package/dist/frameworks/vue/utils.d.ts.map +1 -1
- package/dist/helpers/file.d.ts.map +1 -1
- package/dist/helpers/memoization.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/models/index.js +35 -0
- package/dist/models/index.mjs +35 -0
- package/dist/navigation/index.js +2 -1
- package/dist/navigation/index.mjs +2 -1
- package/dist/primitives/alert.d.ts +1 -1
- package/dist/primitives/cal-embed.d.ts.map +1 -1
- package/dist/primitives/chart.d.ts.map +1 -1
- package/dist/primitives/chat/chat-input.d.ts.map +1 -1
- package/dist/primitives/combobox.d.ts.map +1 -1
- package/dist/primitives/command.d.ts.map +1 -1
- package/dist/primitives/copy-to-clipboard-icon.d.ts.map +1 -1
- package/dist/primitives/dots-loader.d.ts.map +1 -1
- package/dist/primitives/error-message.d.ts.map +1 -1
- package/dist/primitives/file-uploader.d.ts.map +1 -1
- package/dist/primitives/form.d.ts.map +1 -1
- package/dist/primitives/input-otp.d.ts.map +1 -1
- package/dist/primitives/markdown-preview.d.ts.map +1 -1
- package/dist/primitives/pretty-json-print.d.ts.map +1 -1
- package/dist/primitives/resizable.d.ts +4 -20
- package/dist/primitives/resizable.d.ts.map +1 -1
- package/dist/primitives/resizable.js +4 -4
- package/dist/primitives/sonner.d.ts.map +1 -1
- package/dist/primitives/text-link.d.ts.map +1 -1
- package/dist/primitives/textfield.d.ts.map +1 -1
- package/dist/primitives/toast.d.ts.map +1 -1
- package/dist/resizable.js +1 -1
- package/dist/resizable.mjs +1 -1
- package/dist/src/billing/components/account-members.d.ts +10 -0
- package/dist/src/billing/components/account-members.d.ts.map +1 -0
- package/dist/src/billing/components/account-members.js +63 -0
- package/dist/src/billing/components/account-switcher.d.ts +9 -0
- package/dist/src/billing/components/account-switcher.d.ts.map +1 -0
- package/dist/src/billing/components/account-switcher.js +52 -0
- package/dist/src/billing/components/animated-card.d.ts +9 -0
- package/dist/src/billing/components/animated-card.d.ts.map +1 -0
- package/dist/src/billing/components/animated-card.js +161 -0
- package/dist/src/billing/components/billing-settings.d.ts +26 -0
- package/dist/src/billing/components/billing-settings.d.ts.map +1 -0
- package/dist/src/billing/components/billing-settings.js +23 -0
- package/dist/src/billing/components/business-profile-panel.d.ts.map +1 -1
- package/dist/src/billing/components/business-profile-panel.js +9 -8
- package/dist/src/billing/components/card-form.d.ts +18 -0
- package/dist/src/billing/components/card-form.d.ts.map +1 -0
- package/dist/src/billing/components/card-form.js +139 -0
- package/dist/src/billing/components/cost-explorer.d.ts +7 -0
- package/dist/src/billing/components/cost-explorer.d.ts.map +1 -0
- package/dist/src/billing/components/cost-explorer.js +73 -0
- package/dist/src/billing/components/credits-panel.d.ts +8 -0
- package/dist/src/billing/components/credits-panel.d.ts.map +1 -0
- package/dist/src/billing/components/credits-panel.js +58 -0
- package/dist/src/billing/components/guided-setup.d.ts +13 -0
- package/dist/src/billing/components/guided-setup.d.ts.map +1 -0
- package/dist/src/billing/components/guided-setup.js +44 -0
- package/dist/src/billing/components/index.d.ts +34 -0
- package/dist/src/billing/components/index.d.ts.map +1 -1
- package/dist/src/billing/components/index.js +17 -0
- package/dist/src/billing/components/invoice-manager.js +13 -13
- package/dist/src/billing/components/invoices-payments.d.ts +7 -0
- package/dist/src/billing/components/invoices-payments.d.ts.map +1 -0
- package/dist/src/billing/components/invoices-payments.js +44 -0
- package/dist/src/billing/components/overview-dashboard.d.ts +31 -0
- package/dist/src/billing/components/overview-dashboard.d.ts.map +1 -0
- package/dist/src/billing/components/overview-dashboard.js +105 -0
- package/dist/src/billing/components/payment-manager.d.ts.map +1 -1
- package/dist/src/billing/components/payment-manager.js +267 -83
- package/dist/src/billing/components/promotions-panel.d.ts +7 -0
- package/dist/src/billing/components/promotions-panel.d.ts.map +1 -0
- package/dist/src/billing/components/promotions-panel.js +48 -0
- package/dist/src/billing/components/spend-alerts.d.ts +9 -0
- package/dist/src/billing/components/spend-alerts.d.ts.map +1 -0
- package/dist/src/billing/components/spend-alerts.js +99 -0
- package/dist/src/billing/components/square-card-form.d.ts +32 -0
- package/dist/src/billing/components/square-card-form.d.ts.map +1 -0
- package/dist/src/billing/components/square-card-form.js +179 -0
- package/dist/src/billing/components/status-bar.d.ts +12 -0
- package/dist/src/billing/components/status-bar.d.ts.map +1 -0
- package/dist/src/billing/components/status-bar.js +32 -0
- package/dist/src/billing/components/subscription-portal.d.ts +2 -1
- package/dist/src/billing/components/subscription-portal.d.ts.map +1 -1
- package/dist/src/billing/components/subscription-portal.js +123 -26
- package/dist/src/billing/components/support-tiers-panel.d.ts +6 -0
- package/dist/src/billing/components/support-tiers-panel.d.ts.map +1 -0
- package/dist/src/billing/components/support-tiers-panel.js +90 -0
- package/dist/src/billing/components/tax-compliance-panel.js +5 -5
- package/dist/src/billing/components/transactions-panel.d.ts +8 -0
- package/dist/src/billing/components/transactions-panel.d.ts.map +1 -0
- package/dist/src/billing/components/transactions-panel.js +64 -0
- package/dist/src/billing/components/usage-panel.d.ts +7 -0
- package/dist/src/billing/components/usage-panel.d.ts.map +1 -0
- package/dist/src/billing/components/usage-panel.js +64 -0
- package/dist/src/billing/types/index.d.ts +136 -1
- package/dist/src/billing/types/index.d.ts.map +1 -1
- package/dist/src/form/form.d.ts.map +1 -1
- package/dist/src/hooks/use-copy-clipboard.d.ts.map +1 -1
- package/dist/src/hooks/use-fill-ids.d.ts.map +1 -1
- package/dist/src/hooks/use-scroll-restoration.d.ts.map +1 -1
- package/dist/src/models/ModelCard.d.ts +25 -0
- package/dist/src/models/ModelCard.d.ts.map +1 -0
- package/dist/src/models/ModelCard.js +73 -0
- package/dist/src/models/ModelLibrary.d.ts +41 -0
- package/dist/src/models/ModelLibrary.d.ts.map +1 -0
- package/dist/src/models/ModelLibrary.js +63 -0
- package/dist/src/models/ModelTable.d.ts +17 -0
- package/dist/src/models/ModelTable.d.ts.map +1 -0
- package/dist/src/models/ModelTable.js +35 -0
- package/dist/src/models/ZenEnso.d.ts +17 -0
- package/dist/src/models/ZenEnso.d.ts.map +1 -0
- package/dist/src/models/ZenEnso.js +50 -0
- package/dist/src/models/index.d.ts +23 -0
- package/dist/src/models/index.d.ts.map +1 -0
- package/dist/src/models/index.js +18 -0
- package/dist/src/models/types.d.ts +40 -0
- package/dist/src/models/types.d.ts.map +1 -0
- package/dist/src/models/types.js +7 -0
- package/dist/src/navigation/hanzo-shell/AppSwitcher.d.ts +8 -0
- package/dist/src/navigation/hanzo-shell/AppSwitcher.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/AppSwitcher.js +19 -0
- package/dist/src/navigation/hanzo-shell/BeamAvatar.d.ts +9 -0
- package/dist/src/navigation/hanzo-shell/BeamAvatar.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/BeamAvatar.js +38 -0
- package/dist/src/navigation/hanzo-shell/HanzoCommandPalette.d.ts +28 -0
- package/dist/src/navigation/hanzo-shell/HanzoCommandPalette.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/HanzoCommandPalette.js +124 -0
- package/dist/src/navigation/hanzo-shell/HanzoHeader.d.ts +37 -0
- package/dist/src/navigation/hanzo-shell/HanzoHeader.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/HanzoHeader.js +43 -0
- package/dist/src/navigation/hanzo-shell/HanzoMark.d.ts +17 -0
- package/dist/src/navigation/hanzo-shell/HanzoMark.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/HanzoMark.js +57 -0
- package/dist/src/navigation/hanzo-shell/UserAvatar.d.ts +15 -0
- package/dist/src/navigation/hanzo-shell/UserAvatar.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/UserAvatar.js +44 -0
- package/dist/src/navigation/hanzo-shell/UserOrgDropdown.d.ts +11 -0
- package/dist/src/navigation/hanzo-shell/UserOrgDropdown.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/UserOrgDropdown.js +26 -0
- package/dist/src/navigation/hanzo-shell/index.d.ts +14 -0
- package/dist/src/navigation/hanzo-shell/index.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/index.js +9 -0
- package/dist/src/navigation/hanzo-shell/types.d.ts +49 -0
- package/dist/src/navigation/hanzo-shell/types.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/types.js +32 -0
- package/dist/src/navigation/hanzo-shell/useHanzoAuth.d.ts +16 -0
- package/dist/src/navigation/hanzo-shell/useHanzoAuth.d.ts.map +1 -0
- package/dist/src/navigation/hanzo-shell/useHanzoAuth.js +93 -0
- package/dist/src/navigation/index.d.ts +2 -0
- package/dist/src/navigation/index.d.ts.map +1 -1
- package/dist/src/navigation/index.js +2 -0
- package/dist/src/ui/banner.d.ts +1 -1
- package/dist/util/blob.d.ts.map +1 -1
- package/dist/util/blob.js +1 -1
- package/dist/util/date.d.ts.map +1 -1
- package/dist/util/debounce.d.ts.map +1 -1
- package/dist/util/file.d.ts.map +1 -1
- package/dist/util/format-and-abbreviate-as-currency.d.ts +7 -1
- package/dist/util/format-and-abbreviate-as-currency.d.ts.map +1 -1
- package/dist/util/format-text.d.ts.map +1 -1
- package/dist/util/format-to-max-char.d.ts +7 -1
- package/dist/util/format-to-max-char.d.ts.map +1 -1
- package/dist/util/index.mjs +1 -1
- package/dist/util/number-abbreviate.d.ts.map +1 -1
- package/dist/util/specifier.d.ts.map +1 -1
- package/dist/util/step-animation.d.ts.map +1 -1
- package/package.json +20 -7
- package/dist/tailwind/typo-plugin/get-plugin-styles.d.ts +0 -595
- package/dist/tailwind/typo-plugin/get-plugin-styles.d.ts.map +0 -1
- package/dist/tailwind/typo-plugin/get-plugin-styles.js +0 -661
- package/dist/tailwind/typo-plugin/index.d.ts +0 -3
- package/dist/tailwind/typo-plugin/index.d.ts.map +0 -1
- package/dist/tailwind/typo-plugin/index.js +0 -102
- package/dist/tailwind/typo-plugin/utils.d.ts +0 -6
- package/dist/tailwind/typo-plugin/utils.d.ts.map +0 -1
- package/dist/tailwind/typo-plugin/utils.js +0 -47
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SpendAlert } from '../types';
|
|
2
|
+
export interface SpendAlertsPanelProps {
|
|
3
|
+
alerts: SpendAlert[];
|
|
4
|
+
onCreateAlert?: (title: string, threshold: number) => Promise<void>;
|
|
5
|
+
onUpdateAlert?: (id: string, title: string, threshold: number) => Promise<void>;
|
|
6
|
+
onDeleteAlert?: (id: string) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export declare function SpendAlertsPanel(props: SpendAlertsPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=spend-alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-alerts.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/spend-alerts.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnE,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/E,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C;AAqBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAmL5D"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
function formatCurrency(value, currency = 'USD') {
|
|
5
|
+
return new Intl.NumberFormat('en-US', {
|
|
6
|
+
style: 'currency',
|
|
7
|
+
currency,
|
|
8
|
+
minimumFractionDigits: 0,
|
|
9
|
+
maximumFractionDigits: 0,
|
|
10
|
+
}).format(value);
|
|
11
|
+
}
|
|
12
|
+
function formatDate(dateStr) {
|
|
13
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
14
|
+
month: 'short',
|
|
15
|
+
day: 'numeric',
|
|
16
|
+
year: 'numeric',
|
|
17
|
+
hour: 'numeric',
|
|
18
|
+
minute: '2-digit',
|
|
19
|
+
}).format(new Date(dateStr));
|
|
20
|
+
}
|
|
21
|
+
export function SpendAlertsPanel(props) {
|
|
22
|
+
const { alerts, onCreateAlert, onUpdateAlert, onDeleteAlert } = props;
|
|
23
|
+
const [showForm, setShowForm] = React.useState(false);
|
|
24
|
+
const [editingId, setEditingId] = React.useState(null);
|
|
25
|
+
const [title, setTitle] = React.useState('');
|
|
26
|
+
const [threshold, setThreshold] = React.useState('');
|
|
27
|
+
const [busy, setBusy] = React.useState(false);
|
|
28
|
+
const [busyDeleteId, setBusyDeleteId] = React.useState(null);
|
|
29
|
+
const [error, setError] = React.useState(null);
|
|
30
|
+
const canSubmit = title.trim().length > 0 && parseFloat(threshold) > 0;
|
|
31
|
+
const resetForm = React.useCallback(() => {
|
|
32
|
+
setTitle('');
|
|
33
|
+
setThreshold('');
|
|
34
|
+
setEditingId(null);
|
|
35
|
+
setShowForm(false);
|
|
36
|
+
setError(null);
|
|
37
|
+
}, []);
|
|
38
|
+
const handleCreate = React.useCallback(async () => {
|
|
39
|
+
if (!canSubmit || !onCreateAlert)
|
|
40
|
+
return;
|
|
41
|
+
setBusy(true);
|
|
42
|
+
setError(null);
|
|
43
|
+
try {
|
|
44
|
+
await onCreateAlert(title.trim(), parseFloat(threshold));
|
|
45
|
+
resetForm();
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
setError(err instanceof Error ? err.message : 'Failed to create alert');
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
setBusy(false);
|
|
52
|
+
}
|
|
53
|
+
}, [canSubmit, onCreateAlert, title, threshold, resetForm]);
|
|
54
|
+
const handleUpdate = React.useCallback(async () => {
|
|
55
|
+
if (!canSubmit || !editingId || !onUpdateAlert)
|
|
56
|
+
return;
|
|
57
|
+
setBusy(true);
|
|
58
|
+
setError(null);
|
|
59
|
+
try {
|
|
60
|
+
await onUpdateAlert(editingId, title.trim(), parseFloat(threshold));
|
|
61
|
+
resetForm();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
setError(err instanceof Error ? err.message : 'Failed to update alert');
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
setBusy(false);
|
|
68
|
+
}
|
|
69
|
+
}, [canSubmit, editingId, onUpdateAlert, title, threshold, resetForm]);
|
|
70
|
+
const handleDelete = React.useCallback(async (id) => {
|
|
71
|
+
if (!onDeleteAlert)
|
|
72
|
+
return;
|
|
73
|
+
setBusyDeleteId(id);
|
|
74
|
+
try {
|
|
75
|
+
await onDeleteAlert(id);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
setBusyDeleteId(null);
|
|
79
|
+
}
|
|
80
|
+
}, [onDeleteAlert]);
|
|
81
|
+
const startEdit = React.useCallback((alert) => {
|
|
82
|
+
setEditingId(alert.id);
|
|
83
|
+
setTitle(alert.title);
|
|
84
|
+
setThreshold(String(alert.threshold));
|
|
85
|
+
setShowForm(true);
|
|
86
|
+
setError(null);
|
|
87
|
+
}, []);
|
|
88
|
+
return (_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "overflow-hidden rounded-xl border border-border bg-bg-card", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-border p-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-semibold text-text", children: "Spend alerts" }), _jsx("p", { className: "text-sm text-text-muted", children: "Get notified when your spending crosses a threshold. Alerts check every 90 minutes." })] }), _jsx("button", { type: "button", onClick: () => {
|
|
89
|
+
if (showForm) {
|
|
90
|
+
resetForm();
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
setShowForm(true);
|
|
94
|
+
}
|
|
95
|
+
}, className: "rounded-lg bg-text px-3 py-2 text-sm font-medium text-bg transition hover:opacity-80", children: showForm ? 'Cancel' : 'New alert' })] }), showForm && (_jsxs("div", { className: "border-b border-border p-4", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [_jsx("input", { value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Alert name (e.g. Monthly budget)", className: "rounded-lg border border-border bg-bg-input px-3 py-2 text-sm text-text outline-none transition focus:border-brand md:col-span-2" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-dim", children: "$" }), _jsx("input", { type: "number", min: "1", step: "1", value: threshold, onChange: (e) => setThreshold(e.target.value), placeholder: "Threshold", className: "w-full rounded-lg border border-border bg-bg-input py-2 pl-7 pr-3 text-sm text-text outline-none transition focus:border-brand" })] }), _jsx("button", { type: "button", disabled: !canSubmit || busy, onClick: editingId ? handleUpdate : handleCreate, className: "rounded-lg bg-text px-4 py-2 text-sm font-medium text-bg transition hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-60", children: busy ? 'Saving...' : editingId ? 'Update' : 'Create' })] })] }), error && _jsx("p", { className: "mt-2 text-sm text-danger", children: error })] })), _jsx("div", { className: "divide-y divide-border", children: alerts.length === 0 ? (_jsx("div", { className: "p-6 text-sm text-text-muted", children: "No spend alerts configured." })) : (alerts.map((alert) => {
|
|
96
|
+
const isDeleting = busyDeleteId === alert.id;
|
|
97
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 p-4", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-medium text-text", children: alert.title }), alert.triggeredAt && (_jsx("span", { className: "rounded-full bg-amber-500/10 px-2 py-0.5 text-[10px] font-semibold text-amber-500 ring-1 ring-amber-500/20", children: "Triggered" }))] }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-xs text-text-muted", children: [_jsxs("span", { children: ["Threshold: ", formatCurrency(alert.threshold, alert.currency)] }), _jsxs("span", { children: ["Created ", formatDate(alert.createdAt)] }), alert.triggeredAt && (_jsxs("span", { children: ["Triggered ", formatDate(alert.triggeredAt)] }))] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => startEdit(alert), disabled: isDeleting, className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-text-secondary transition hover:bg-bg-elevated disabled:cursor-not-allowed disabled:opacity-60", children: "Edit" }), _jsx("button", { type: "button", onClick: () => handleDelete(alert.id), disabled: isDeleting, className: "rounded-md border border-rose-500/30 px-3 py-1.5 text-xs font-medium text-rose-500 transition hover:bg-rose-500/10 disabled:cursor-not-allowed disabled:opacity-60", children: isDeleting ? 'Deleting...' : 'Delete' })] })] }, alert.id));
|
|
98
|
+
})) })] }) }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
interface SquareCard {
|
|
2
|
+
attach(selector: string): Promise<void>;
|
|
3
|
+
tokenize(): Promise<{
|
|
4
|
+
status: string;
|
|
5
|
+
token?: string;
|
|
6
|
+
errors?: Array<{
|
|
7
|
+
message: string;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
10
|
+
destroy(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
interface SquarePayments {
|
|
13
|
+
card(options?: Record<string, unknown>): Promise<SquareCard>;
|
|
14
|
+
}
|
|
15
|
+
interface SquareSDK {
|
|
16
|
+
payments(appId: string, locationId: string): Promise<SquarePayments>;
|
|
17
|
+
}
|
|
18
|
+
declare global {
|
|
19
|
+
interface Window {
|
|
20
|
+
Square?: SquareSDK;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export interface SquareCardFormProps {
|
|
24
|
+
/** Called with the Square sourceId token when tokenization succeeds */
|
|
25
|
+
onToken: (sourceId: string) => Promise<void>;
|
|
26
|
+
/** Called when cvv input is focused (for card flip animation) */
|
|
27
|
+
onCvvFocus?: (focused: boolean) => void;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function SquareCardForm({ onToken, onCvvFocus, disabled }: SquareCardFormProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=square-card-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"square-card-form.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/square-card-form.tsx"],"names":[],"mappings":"AAMA,UAAU,UAAU;IAClB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,QAAQ,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAA;IAC5F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,UAAU,cAAc;IACtB,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAC7D;AAED,UAAU,SAAS;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CACrE;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,CAAC,EAAE,SAAS,CAAA;KACnB;CACF;AAID,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAiED,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CA4JpF"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
// -- Square environment config --
|
|
5
|
+
const SQUARE_APP_ID = process.env.NEXT_PUBLIC_SQUARE_APPLICATION_ID ?? '';
|
|
6
|
+
const SQUARE_LOCATION_ID = process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID ?? '';
|
|
7
|
+
const SQUARE_ENV = process.env.NEXT_PUBLIC_SQUARE_ENVIRONMENT ?? 'sandbox';
|
|
8
|
+
const SQUARE_SCRIPT_URL = SQUARE_ENV === 'production'
|
|
9
|
+
? 'https://web.squarecdn.com/v1/square.js'
|
|
10
|
+
: 'https://sandbox.web.squarecdn.com/v1/square.js';
|
|
11
|
+
// -- Script loader --
|
|
12
|
+
let scriptLoadPromise = null;
|
|
13
|
+
function loadSquareScript() {
|
|
14
|
+
if (scriptLoadPromise)
|
|
15
|
+
return scriptLoadPromise;
|
|
16
|
+
if (typeof window !== 'undefined' && window.Square) {
|
|
17
|
+
return (scriptLoadPromise = Promise.resolve());
|
|
18
|
+
}
|
|
19
|
+
scriptLoadPromise = new Promise((resolve, reject) => {
|
|
20
|
+
const existing = document.querySelector(`script[src="${SQUARE_SCRIPT_URL}"]`);
|
|
21
|
+
if (existing) {
|
|
22
|
+
existing.addEventListener('load', () => resolve());
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const script = document.createElement('script');
|
|
26
|
+
script.src = SQUARE_SCRIPT_URL;
|
|
27
|
+
script.async = true;
|
|
28
|
+
script.onload = () => resolve();
|
|
29
|
+
script.onerror = () => reject(new Error('Failed to load Square Web Payments SDK'));
|
|
30
|
+
document.head.appendChild(script);
|
|
31
|
+
});
|
|
32
|
+
return scriptLoadPromise;
|
|
33
|
+
}
|
|
34
|
+
// -- Square card appearance (dark theme) --
|
|
35
|
+
const SQUARE_STYLE = {
|
|
36
|
+
'.input-wrapper': {
|
|
37
|
+
backgroundColor: 'transparent',
|
|
38
|
+
},
|
|
39
|
+
input: {
|
|
40
|
+
backgroundColor: 'transparent',
|
|
41
|
+
color: '#fafafa',
|
|
42
|
+
fontFamily: 'ui-sans-serif, system-ui, sans-serif',
|
|
43
|
+
fontSize: '14px',
|
|
44
|
+
},
|
|
45
|
+
'input::placeholder': {
|
|
46
|
+
color: '#52525b',
|
|
47
|
+
},
|
|
48
|
+
'.message-text': {
|
|
49
|
+
color: '#f87171',
|
|
50
|
+
},
|
|
51
|
+
'.message-icon': {
|
|
52
|
+
color: '#f87171',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// -- Component --
|
|
56
|
+
const CONTAINER_ID = 'sq-card-container';
|
|
57
|
+
export function SquareCardForm({ onToken, onCvvFocus, disabled }) {
|
|
58
|
+
const cardRef = React.useRef(null);
|
|
59
|
+
const [loading, setLoading] = React.useState(true);
|
|
60
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
61
|
+
const [error, setError] = React.useState(null);
|
|
62
|
+
const [ready, setReady] = React.useState(false);
|
|
63
|
+
const mountedRef = React.useRef(true);
|
|
64
|
+
React.useEffect(() => {
|
|
65
|
+
mountedRef.current = true;
|
|
66
|
+
let card = null;
|
|
67
|
+
async function init() {
|
|
68
|
+
try {
|
|
69
|
+
await loadSquareScript();
|
|
70
|
+
if (!mountedRef.current)
|
|
71
|
+
return;
|
|
72
|
+
if (!window.Square)
|
|
73
|
+
throw new Error('Square SDK not available');
|
|
74
|
+
if (!SQUARE_APP_ID)
|
|
75
|
+
throw new Error('NEXT_PUBLIC_SQUARE_APPLICATION_ID is not set');
|
|
76
|
+
if (!SQUARE_LOCATION_ID)
|
|
77
|
+
throw new Error('NEXT_PUBLIC_SQUARE_LOCATION_ID is not set');
|
|
78
|
+
const payments = await window.Square.payments(SQUARE_APP_ID, SQUARE_LOCATION_ID);
|
|
79
|
+
card = await payments.card({ style: SQUARE_STYLE });
|
|
80
|
+
await card.attach(`#${CONTAINER_ID}`);
|
|
81
|
+
if (mountedRef.current) {
|
|
82
|
+
cardRef.current = card;
|
|
83
|
+
setReady(true);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
await card.destroy();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (mountedRef.current) {
|
|
91
|
+
setError(err instanceof Error ? err.message : 'Failed to initialize payment form');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (mountedRef.current)
|
|
96
|
+
setLoading(false);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
void init();
|
|
100
|
+
return () => {
|
|
101
|
+
mountedRef.current = false;
|
|
102
|
+
cardRef.current?.destroy().catch(() => undefined);
|
|
103
|
+
cardRef.current = null;
|
|
104
|
+
};
|
|
105
|
+
}, []);
|
|
106
|
+
const handleSubmit = React.useCallback(async () => {
|
|
107
|
+
if (!cardRef.current || !ready || submitting || disabled)
|
|
108
|
+
return;
|
|
109
|
+
setSubmitting(true);
|
|
110
|
+
setError(null);
|
|
111
|
+
try {
|
|
112
|
+
const result = await cardRef.current.tokenize();
|
|
113
|
+
if (result.status === 'OK' && result.token) {
|
|
114
|
+
await onToken(result.token);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const msg = result.errors?.map(e => e.message).join('; ') ?? 'Tokenization failed';
|
|
118
|
+
setError(msg);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
setError(err instanceof Error ? err.message : 'Payment processing error');
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
if (mountedRef.current)
|
|
126
|
+
setSubmitting(false);
|
|
127
|
+
}
|
|
128
|
+
}, [onToken, ready, submitting, disabled]);
|
|
129
|
+
// Wire up CVV focus detection via DOM event listener on the Square iframe container.
|
|
130
|
+
React.useEffect(() => {
|
|
131
|
+
if (!onCvvFocus)
|
|
132
|
+
return;
|
|
133
|
+
const container = document.getElementById(CONTAINER_ID);
|
|
134
|
+
if (!container)
|
|
135
|
+
return;
|
|
136
|
+
let cvvFrame = null;
|
|
137
|
+
function findAndAttach() {
|
|
138
|
+
const frames = container.querySelectorAll('iframe');
|
|
139
|
+
frames.forEach(frame => {
|
|
140
|
+
const name = frame.getAttribute('name') ?? frame.id ?? '';
|
|
141
|
+
if (name.toLowerCase().includes('cvv') || name.toLowerCase().includes('cvc')) {
|
|
142
|
+
if (cvvFrame !== frame) {
|
|
143
|
+
cvvFrame = frame;
|
|
144
|
+
try {
|
|
145
|
+
frame.contentWindow?.document.body?.addEventListener('focus', () => onCvvFocus(true), true);
|
|
146
|
+
frame.contentWindow?.document.body?.addEventListener('blur', () => onCvvFocus(false), true);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// cross-origin access blocked -- Square SDK handles its own events
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const observer = new MutationObserver(findAndAttach);
|
|
156
|
+
observer.observe(container, { childList: true, subtree: true });
|
|
157
|
+
findAndAttach();
|
|
158
|
+
return () => observer.disconnect();
|
|
159
|
+
}, [onCvvFocus, ready]);
|
|
160
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { id: CONTAINER_ID, style: {
|
|
161
|
+
minHeight: loading ? '120px' : undefined,
|
|
162
|
+
borderRadius: '8px',
|
|
163
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
164
|
+
backgroundColor: 'rgba(255,255,255,0.04)',
|
|
165
|
+
padding: '12px',
|
|
166
|
+
transition: 'border-color 0.2s',
|
|
167
|
+
}, children: loading && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', height: '96px' }, children: [_jsx("div", { style: { width: '20px', height: '20px', border: '2px solid rgba(255,255,255,0.15)', borderTopColor: 'rgba(255,255,255,0.7)', borderRadius: '50%', animation: 'spin 0.8s linear infinite' } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })] })) }), error && (_jsx("p", { className: "text-sm", style: { color: '#f87171' }, children: error })), _jsx("button", { type: "button", disabled: !ready || submitting || !!disabled, onClick: handleSubmit, style: {
|
|
168
|
+
width: '100%',
|
|
169
|
+
borderRadius: '8px',
|
|
170
|
+
backgroundColor: ready && !submitting && !disabled ? 'rgb(250,250,250)' : 'rgba(250,250,250,0.3)',
|
|
171
|
+
color: ready && !submitting && !disabled ? 'rgb(9,9,11)' : 'rgba(9,9,11,0.5)',
|
|
172
|
+
padding: '10px 16px',
|
|
173
|
+
fontSize: '14px',
|
|
174
|
+
fontWeight: '500',
|
|
175
|
+
cursor: ready && !submitting && !disabled ? 'pointer' : 'not-allowed',
|
|
176
|
+
border: 'none',
|
|
177
|
+
transition: 'opacity 0.15s',
|
|
178
|
+
}, children: submitting ? 'Processing\u2026' : 'Add card' }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '11px', color: 'rgba(255,255,255,0.35)' }, children: [_jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }), "Secured by Square ", '\u2014', " PCI DSS compliant tokenization"] })] }));
|
|
179
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Subscription, SubscriptionPlan, PaymentMethod } from '../types';
|
|
2
|
+
export type BillingSection = 'overview' | 'usage' | 'invoices' | 'pricing' | 'credits' | 'transactions' | 'alerts' | 'payment' | 'team' | 'settings';
|
|
3
|
+
export interface StatusBarProps {
|
|
4
|
+
subscription: Subscription;
|
|
5
|
+
currentPlan: SubscriptionPlan | undefined;
|
|
6
|
+
mtdSpend: number;
|
|
7
|
+
currency: string;
|
|
8
|
+
paymentMethods: PaymentMethod[];
|
|
9
|
+
onNavigate: (target: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function StatusBar({ subscription, currentPlan, mtdSpend, currency, paymentMethods, onNavigate }: StatusBarProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=status-bar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-bar.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/status-bar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE7E,MAAM,MAAM,cAAc,GACtB,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAC7C,SAAS,GAAG,cAAc,GAAG,QAAQ,GACrC,SAAS,GAAG,MAAM,GAAG,UAAU,CAAA;AAEnC,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,YAAY,CAAA;IAC1B,WAAW,EAAE,gBAAgB,GAAG,SAAS,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,aAAa,EAAE,CAAA;IAC/B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAUD,wBAAgB,SAAS,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,cAAc,2CAyEtH"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
function fmt(amount, currency = 'usd') {
|
|
4
|
+
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount);
|
|
5
|
+
}
|
|
6
|
+
function fmtDate(d) {
|
|
7
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
8
|
+
}
|
|
9
|
+
export function StatusBar({ subscription, currentPlan, mtdSpend, currency, paymentMethods, onNavigate }) {
|
|
10
|
+
const planName = currentPlan?.name ?? 'Pay-as-you-go';
|
|
11
|
+
const hasActivePlan = !!subscription.id && subscription.status === 'active';
|
|
12
|
+
const renewalDate = subscription.currentPeriodEnd ? fmtDate(new Date(subscription.currentPeriodEnd)) : null;
|
|
13
|
+
const defaultPm = paymentMethods.find(m => m.is_default || m.isDefault);
|
|
14
|
+
const hasPm = paymentMethods.length > 0;
|
|
15
|
+
const isPastDue = subscription.status === 'past_due';
|
|
16
|
+
// Dynamic CTA
|
|
17
|
+
let ctaLabel;
|
|
18
|
+
let ctaSection;
|
|
19
|
+
if (!hasPm) {
|
|
20
|
+
ctaLabel = 'Add payment method';
|
|
21
|
+
ctaSection = 'invoices:payment-methods';
|
|
22
|
+
}
|
|
23
|
+
else if (!hasActivePlan) {
|
|
24
|
+
ctaLabel = 'Choose a plan';
|
|
25
|
+
ctaSection = 'overview';
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
ctaLabel = 'View costs';
|
|
29
|
+
ctaSection = 'usage';
|
|
30
|
+
}
|
|
31
|
+
return (_jsx("div", { className: "rounded-2xl border border-border bg-bg-card p-1", children: _jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 divide-x divide-border", children: [_jsxs("div", { className: "p-4", children: [_jsx("p", { className: "text-[11px] uppercase tracking-wide text-text-dim mb-1", children: "Plan" }), _jsx("p", { className: "text-sm font-semibold text-text", children: planName }), renewalDate && hasActivePlan && (_jsxs("p", { className: "text-xs text-text-muted mt-0.5", children: [subscription.cancelAt ? 'Cancels' : 'Renews', " ", renewalDate] }))] }), _jsxs("div", { className: "p-4", children: [_jsx("p", { className: "text-[11px] uppercase tracking-wide text-text-dim mb-1", children: "This Period" }), _jsx("p", { className: "text-sm font-semibold text-text", children: fmt(mtdSpend, currency) }), _jsx("p", { className: "text-xs text-text-muted mt-0.5", children: "Month-to-date" })] }), _jsxs("div", { className: "p-4", children: [_jsx("p", { className: "text-[11px] uppercase tracking-wide text-text-dim mb-1", children: "Payment" }), isPastDue ? (_jsx("p", { className: "text-sm font-semibold text-warning", children: "Past due" })) : hasPm ? (_jsx("p", { className: "text-sm font-semibold text-text", children: defaultPm?.card ? `${defaultPm.card.brand} \u00B7\u00B7\u00B7\u00B7${defaultPm.card.last4}` : 'Active' })) : (_jsx("p", { className: "text-sm font-semibold text-danger", children: "No payment method" })), hasPm && !isPastDue && (_jsx("p", { className: "text-xs text-success mt-0.5", children: "Healthy" }))] }), _jsx("div", { className: "p-4 flex items-center justify-center", children: _jsx("button", { type: "button", onClick: () => onNavigate(ctaSection), className: "w-full rounded-lg bg-brand px-4 py-2 text-sm font-medium text-brand-foreground transition hover:bg-brand-hover", children: ctaLabel }) })] }) }));
|
|
32
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BillingMetric, Subscription, SubscriptionPlan, RetentionOffer, SubscriptionHistory } from '../types';
|
|
1
|
+
import type { BillingMetric, Subscription, SubscriptionPlan, RetentionOffer, SubscriptionHistory, DiscountCode } from '../types';
|
|
2
2
|
export interface SubscriptionPortalProps {
|
|
3
3
|
subscription: Subscription;
|
|
4
4
|
availablePlans: SubscriptionPlan[];
|
|
@@ -9,6 +9,7 @@ export interface SubscriptionPortalProps {
|
|
|
9
9
|
onDowngrade?: (planId: string) => Promise<void>;
|
|
10
10
|
onCancel?: (reason: string, feedback?: string) => Promise<void>;
|
|
11
11
|
onAcceptOffer?: (offerId: string) => Promise<void>;
|
|
12
|
+
onApplyDiscount?: (code: string) => Promise<DiscountCode | null>;
|
|
12
13
|
showHistory?: boolean;
|
|
13
14
|
defaultView?: 'overview' | 'plans' | 'history';
|
|
14
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription-portal.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/subscription-portal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,mBAAmB,
|
|
1
|
+
{"version":3,"file":"subscription-portal.d.ts","sourceRoot":"","sources":["../../../../src/billing/components/subscription-portal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,YAAY,EACb,MAAM,UAAU,CAAA;AAEjB,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,EAAE,gBAAgB,EAAE,CAAA;IAClC,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;IAC9B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;IAClC,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC3C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAChE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,SAAS,CAAA;CAC/C;AAgDD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CA+chE"}
|