@burdenoff/microfe-subscriberbot 2026.629.1 → 2026.706.2
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/subscriberbot/components/Modal.js +51 -0
- package/dist/subscriberbot/components/Modal.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +355 -159
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +339 -66
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.js +496 -73
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +136 -150
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +197 -58
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +2 -0
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +538 -71
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +2 -0
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +664 -126
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +18 -16
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"sourcesContent":["/**\n * Invoices\n * @module subscriberbot/pages\n *\n * The Universal Billing Layer's invoice ledger across every relationship.\n * Filter by status, mark issued/overdue invoices paid. Reads\n * `subListInvoices` (a SubInvoiceConnection); writes `subMarkInvoicePaid`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Receipt, FileText } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport { useInvoices, useMarkInvoicePaid } from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatDate } from '../utils/formatters';\nimport type { SubInvoiceStatus } from '../types';\n\nconst PAGE_SIZE = 25;\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubInvoiceStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ISSUED', label: 'Issued' },\n { value: 'OVERDUE', label: 'Overdue' },\n { value: 'PAID', label: 'Paid' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'CANCELLED', label: 'Cancelled' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst PAYABLE = new Set<SubInvoiceStatus>(['ISSUED', 'OVERDUE', 'FAILED']);\n\nexport function InvoicesPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [status, setStatus] = useState<'' | SubInvoiceStatus>('');\n\n const { data, isLoading, isError, refetch, isFetching } = useInvoices({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markPaid = useMarkInvoicePaid();\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const totals = useMemo(() => {\n const outstanding = items.filter((i) => PAYABLE.has(i.status));\n const currency = items[0]?.currency ?? 'USD';\n return {\n outstandingCount: outstanding.length,\n outstandingTotal: outstanding.reduce((sum, i) => sum + i.total, 0),\n currency,\n };\n }, [items]);\n\n return (\n <PageLayout title=\"Invoices\" description=\"Your billing ledger across every relationship\">\n <PagePurpose className=\"mb-4\">\n Every invoice from your recurring relationships in one ledger. Filter by status and settle\n issued or overdue invoices directly.\n </PagePurpose>\n\n {!isLoading && !isError && items.length > 0 && (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <MetricsCard\n featured\n icon={<Receipt />}\n value={formatCurrency(totals.outstandingTotal, totals.currency, {\n maximumFractionDigits: 0,\n })}\n label=\"Outstanding (this page)\"\n />\n <MetricsCard\n icon={<FileText />}\n value={totals.outstandingCount}\n label=\"Open invoices (this page)\"\n />\n </div>\n )}\n\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubInvoiceStatus);\n setPage(0);\n }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading invoices…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load invoices.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Receipt className=\"h-12 w-12\" />}\n title=\"No invoices\"\n description={\n status\n ? 'No invoices match this status filter.'\n : 'Invoices from your relationships will appear here.'\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"hidden grid-cols-[1.5fr_1fr_1fr_1fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid\">\n <span>Invoice</span>\n <span>Status</span>\n <span className=\"text-right\">Total</span>\n <span className=\"text-right\">Due</span>\n <span className=\"text-right\">Action</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((inv) => (\n <div\n key={inv.id}\n className=\"grid grid-cols-1 gap-2 px-4 py-3 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${inv.relationshipId}`)}\n className=\"min-w-0 text-left\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {inv.number || `Invoice ${inv.id.slice(0, 8)}`}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {inv.issueDate ? `Issued ${formatDate(inv.issueDate)}` : '—'}\n </p>\n </button>\n <span>\n <StatusBadge status={inv.status} />\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-text-primary sm:text-right\">\n {formatCurrency(inv.total, inv.currency)}\n </span>\n <span className=\"text-xs text-text-secondary sm:text-right\">\n {inv.dueDate ? formatDate(inv.dueDate) : '—'}\n </span>\n <span className=\"sm:text-right\">\n {PAYABLE.has(inv.status) ? (\n <button\n type=\"button\"\n disabled={markPaid.isPending}\n onClick={() => markPaid.mutate(inv.id)}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50\"\n >\n Mark paid\n </button>\n ) : inv.paidAt ? (\n <span className=\"text-xs text-text-secondary\">\n Paid {formatDate(inv.paidAt)}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/subscriberbot/pages/InvoicesPage.tsx"],"sourcesContent":["/**\n * Invoices\n * @module subscriberbot/pages\n *\n * The Universal Billing Layer's invoice ledger across every relationship, plus\n * the passive payment-method annotations that pay for them. Filter by status,\n * mark issued/overdue invoices paid, create invoices, and manage payment\n * methods. Reads `subListInvoices` / `subListPaymentInstruments`; writes\n * `subMarkInvoicePaid`, `subCreateInvoice`, `subCreatePaymentInstrument`,\n * `subRemovePaymentInstrument`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Receipt, FileText, Plus, CreditCard, Trash2, Star } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Modal } from '../components/Modal';\nimport { LoadingView, ErrorView, Section } from '../components/StateViews';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport {\n useInvoices,\n useMarkInvoicePaid,\n useCreateInvoice,\n useRelationships,\n usePaymentInstruments,\n useCreatePaymentInstrument,\n useRemovePaymentInstrument,\n} from '../hooks/useSubscriberBotApi';\nimport { cn } from '../utils/cn';\nimport { formatCurrency, formatDate } from '../utils/formatters';\nimport type { SubInvoiceStatus, SubPaymentInstrumentType } from '../types';\n\nconst PAGE_SIZE = 25;\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubInvoiceStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ISSUED', label: 'Issued' },\n { value: 'OVERDUE', label: 'Overdue' },\n { value: 'PAID', label: 'Paid' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'CANCELLED', label: 'Cancelled' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst INVOICE_STATUSES: SubInvoiceStatus[] = [\n 'ISSUED',\n 'DRAFT',\n 'OVERDUE',\n 'PAID',\n 'FAILED',\n 'CANCELLED',\n 'REFUNDED',\n];\n\nconst PAYMENT_TYPES: SubPaymentInstrumentType[] = [\n 'CARD',\n 'BANK_ACCOUNT',\n 'PAYPAL',\n 'UPI',\n 'WALLET',\n 'OTHER',\n];\n\nconst PAYABLE = new Set<SubInvoiceStatus>(['ISSUED', 'OVERDUE', 'FAILED']);\n\n// ---------------------------------------------------------------------------\n// Create invoice modal → subCreateInvoice\n// ---------------------------------------------------------------------------\n\nfunction CreateInvoiceModal({ onClose }: { onClose: () => void }) {\n const create = useCreateInvoice();\n const relationshipsQ = useRelationships({ pagination: { limit: 100, offset: 0 } });\n const relationships = relationshipsQ.data?.items ?? [];\n\n const [relationshipId, setRelationshipId] = useState('');\n const [number, setNumber] = useState('');\n const [status, setStatus] = useState<SubInvoiceStatus>('ISSUED');\n const [currency, setCurrency] = useState('USD');\n const [subtotal, setSubtotal] = useState('');\n const [taxAmount, setTaxAmount] = useState('');\n const [total, setTotal] = useState('');\n const [issueDate, setIssueDate] = useState('');\n const [dueDate, setDueDate] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!relationshipId) {\n setFormError('Select the relationship this invoice belongs to.');\n return;\n }\n create.mutate(\n {\n relationshipId,\n status,\n currency: currency.trim() || 'USD',\n ...(number.trim() ? { number: number.trim() } : {}),\n ...(subtotal.trim() ? { subtotal: Number(subtotal) } : {}),\n ...(taxAmount.trim() ? { taxAmount: Number(taxAmount) } : {}),\n ...(total.trim() ? { total: Number(total) } : {}),\n ...(issueDate ? { issueDate: new Date(issueDate).toISOString() } : {}),\n ...(dueDate ? { dueDate: new Date(dueDate).toISOString() } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create invoice.'),\n }\n );\n };\n\n return (\n <Modal title=\"Create invoice\" onClose={onClose} maxWidthClassName=\"max-w-xl\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"inv-relationship\" className={labelCls}>\n Relationship <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"inv-relationship\"\n value={relationshipId}\n onChange={(e) => setRelationshipId(e.target.value)}\n className={inputCls}\n required\n >\n <option value=\"\">— Select a relationship —</option>\n {relationships.map((r) => (\n <option key={r.id} value={r.id}>\n {r.label}\n </option>\n ))}\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-number\" className={labelCls}>\n Number\n </label>\n <input\n id=\"inv-number\"\n type=\"text\"\n value={number}\n onChange={(e) => setNumber(e.target.value)}\n placeholder=\"INV-0001\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-status\" className={labelCls}>\n Status\n </label>\n <select\n id=\"inv-status\"\n value={status}\n onChange={(e) => setStatus(e.target.value as SubInvoiceStatus)}\n className={inputCls}\n >\n {INVOICE_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"inv-currency\" className={labelCls}>\n Currency\n </label>\n <input\n id=\"inv-currency\"\n type=\"text\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"inv-subtotal\" className={labelCls}>\n Subtotal\n </label>\n <input\n id=\"inv-subtotal\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={subtotal}\n onChange={(e) => setSubtotal(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-tax\" className={labelCls}>\n Tax\n </label>\n <input\n id=\"inv-tax\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={taxAmount}\n onChange={(e) => setTaxAmount(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-total\" className={labelCls}>\n Total\n </label>\n <input\n id=\"inv-total\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={total}\n onChange={(e) => setTotal(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"inv-issue\" className={labelCls}>\n Issue date\n </label>\n <input\n id=\"inv-issue\"\n type=\"date\"\n value={issueDate}\n onChange={(e) => setIssueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"inv-due\" className={labelCls}>\n Due date\n </label>\n <input\n id=\"inv-due\"\n type=\"date\"\n value={dueDate}\n onChange={(e) => setDueDate(e.target.value)}\n className={inputCls}\n />\n </div>\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create invoice'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Add payment method modal → subCreatePaymentInstrument\n// ---------------------------------------------------------------------------\n\nfunction AddPaymentMethodModal({ onClose }: { onClose: () => void }) {\n const create = useCreatePaymentInstrument();\n const [label, setLabel] = useState('');\n const [type, setType] = useState<SubPaymentInstrumentType>('CARD');\n const [brand, setBrand] = useState('');\n const [last4, setLast4] = useState('');\n const [expiryMonth, setExpiryMonth] = useState('');\n const [expiryYear, setExpiryYear] = useState('');\n const [isDefault, setIsDefault] = useState(false);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!label.trim()) {\n setFormError('A label is required.');\n return;\n }\n create.mutate(\n {\n label: label.trim(),\n type,\n isDefault,\n ...(brand.trim() ? { brand: brand.trim() } : {}),\n ...(last4.trim() ? { last4: last4.trim() } : {}),\n ...(expiryMonth.trim() ? { expiryMonth: Number(expiryMonth) } : {}),\n ...(expiryYear.trim() ? { expiryYear: Number(expiryYear) } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add payment method.'),\n }\n );\n };\n\n return (\n <Modal\n title=\"Add payment method\"\n description=\"A passive annotation of which external card/account pays for a subscription — no full card numbers or CVV.\"\n onClose={onClose}\n maxWidthClassName=\"max-w-xl\"\n >\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"pm-label\" className={labelCls}>\n Label <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"pm-label\"\n type=\"text\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n placeholder=\"e.g. Personal Visa\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"pm-type\" className={labelCls}>\n Type\n </label>\n <select\n id=\"pm-type\"\n value={type}\n onChange={(e) => setType(e.target.value as SubPaymentInstrumentType)}\n className={inputCls}\n >\n {PAYMENT_TYPES.map((t) => (\n <option key={t} value={t}>\n {t.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"pm-brand\" className={labelCls}>\n Brand\n </label>\n <input\n id=\"pm-brand\"\n type=\"text\"\n value={brand}\n onChange={(e) => setBrand(e.target.value)}\n placeholder=\"Visa, Amex…\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-last4\" className={labelCls}>\n Last 4\n </label>\n <input\n id=\"pm-last4\"\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={4}\n value={last4}\n onChange={(e) => setLast4(e.target.value.replace(/\\D/g, ''))}\n placeholder=\"4242\"\n className={inputCls}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <label htmlFor=\"pm-exp-month\" className={labelCls}>\n Exp. mo.\n </label>\n <input\n id=\"pm-exp-month\"\n type=\"number\"\n min=\"1\"\n max=\"12\"\n value={expiryMonth}\n onChange={(e) => setExpiryMonth(e.target.value)}\n placeholder=\"12\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"pm-exp-year\" className={labelCls}>\n Exp. yr.\n </label>\n <input\n id=\"pm-exp-year\"\n type=\"number\"\n min=\"2024\"\n max=\"2099\"\n value={expiryYear}\n onChange={(e) => setExpiryYear(e.target.value)}\n placeholder=\"2028\"\n className={inputCls}\n />\n </div>\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isDefault}\n onChange={(e) => setIsDefault(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Set as default\n </label>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Adding…' : 'Add payment method'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Payment methods panel\n// ---------------------------------------------------------------------------\n\nfunction PaymentMethodsSection() {\n const { data, isLoading, isError, refetch } = usePaymentInstruments();\n const remove = useRemovePaymentInstrument();\n const [showAdd, setShowAdd] = useState(false);\n\n const instruments = data ?? [];\n\n return (\n <Section\n title=\"Payment methods\"\n description=\"Which external cards and accounts pay for your subscriptions\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAdd(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add payment method\n </button>\n }\n >\n {isLoading ? (\n <LoadingView label=\"Loading payment methods…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load payment methods.\" onRetry={() => void refetch()} />\n ) : instruments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<CreditCard className=\"h-12 w-12\" />}\n title=\"No payment methods\"\n description=\"Annotate which card or account pays for each subscription. No full card numbers are stored.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle rounded-lg border border-border-subtle bg-bg-surface\">\n {instruments.map((pi) => (\n <div key={pi.id} className=\"flex items-center justify-between gap-3 p-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <CreditCard className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-1.5 truncate text-sm font-medium text-text-primary\">\n {pi.label}\n {pi.isDefault && (\n <Star className=\"h-3 w-3 fill-current text-status-warning-text\" />\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {[\n pi.brand,\n pi.last4 ? `•••• ${pi.last4}` : null,\n pi.expiryMonth && pi.expiryYear ? `${pi.expiryMonth}/${pi.expiryYear}` : null,\n ]\n .filter(Boolean)\n .join(' · ') || pi.type.replace(/_/g, ' ')}\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusBadge status={pi.status} />\n <button\n type=\"button\"\n aria-label={`Remove ${pi.label}`}\n disabled={remove.isPending}\n onClick={() => remove.mutate(pi.id)}\n className=\"rounded-lg border border-border-subtle p-1.5 text-text-secondary hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n\n {showAdd && <AddPaymentMethodModal onClose={() => setShowAdd(false)} />}\n </Section>\n );\n}\n\nexport function InvoicesPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [status, setStatus] = useState<'' | SubInvoiceStatus>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch, isFetching } = useInvoices({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const markPaid = useMarkInvoicePaid();\n\n const items = useMemo(() => data?.items ?? [], [data]);\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const totals = useMemo(() => {\n const outstanding = items.filter((i) => PAYABLE.has(i.status));\n const currency = items[0]?.currency ?? 'USD';\n return {\n outstandingCount: outstanding.length,\n outstandingTotal: outstanding.reduce((sum, i) => sum + i.total, 0),\n currency,\n };\n }, [items]);\n\n return (\n <PageLayout\n title=\"Invoices\"\n description=\"Your billing ledger across every relationship\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> Create invoice\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Every invoice from your recurring relationships in one ledger. Filter by status and settle\n issued or overdue invoices directly.\n </PagePurpose>\n\n {!isLoading && !isError && items.length > 0 && (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <MetricsCard\n featured\n icon={<Receipt />}\n value={formatCurrency(totals.outstandingTotal, totals.currency, {\n maximumFractionDigits: 0,\n })}\n label=\"Outstanding (this page)\"\n />\n <MetricsCard\n icon={<FileText />}\n value={totals.outstandingCount}\n label=\"Open invoices (this page)\"\n />\n </div>\n )}\n\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by status\"\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubInvoiceStatus);\n setPage(0);\n }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading invoices…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load invoices.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Receipt className=\"h-12 w-12\" />}\n title=\"No invoices\"\n description={\n status\n ? 'No invoices match this status filter.'\n : 'Invoices from your relationships will appear here — or create one manually.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Create invoice\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 overflow-hidden rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"hidden grid-cols-[1.5fr_1fr_1fr_1fr_auto] gap-4 border-b border-border-subtle bg-bg-sunken px-4 py-2.5 text-xs font-medium text-text-secondary sm:grid\">\n <span>Invoice</span>\n <span>Status</span>\n <span className=\"text-right\">Total</span>\n <span className=\"text-right\">Due</span>\n <span className=\"text-right\">Action</span>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {items.map((inv) => (\n <div\n key={inv.id}\n className=\"grid grid-cols-1 gap-2 px-4 py-3 sm:grid-cols-[1.5fr_1fr_1fr_1fr_auto] sm:items-center sm:gap-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/relationships/${inv.relationshipId}`)}\n className=\"min-w-0 text-left\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {inv.number || `Invoice ${inv.id.slice(0, 8)}`}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {inv.issueDate ? `Issued ${formatDate(inv.issueDate)}` : '—'}\n </p>\n </button>\n <span>\n <StatusBadge status={inv.status} />\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-text-primary sm:text-right\">\n {formatCurrency(inv.total, inv.currency)}\n </span>\n <span className=\"text-xs text-text-secondary sm:text-right\">\n {inv.dueDate ? formatDate(inv.dueDate) : '—'}\n </span>\n <span className=\"sm:text-right\">\n {PAYABLE.has(inv.status) ? (\n <button\n type=\"button\"\n disabled={markPaid.isPending}\n onClick={() => markPaid.mutate(inv.id)}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50\"\n >\n Mark paid\n </button>\n ) : inv.paidAt ? (\n <span className=\"text-xs text-text-secondary\">\n Paid {formatDate(inv.paidAt)}\n </span>\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n\n <PaymentMethodsSection />\n\n {showCreate && <CreateInvoiceModal onClose={() => setShowCreate(false)} />}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,IAAM,IAAY,IAEZ,IACJ,wMACI,IAAW,sDAEX,IAAyE;CAC7E;EAAE,OAAO;EAAI,OAAO;EAAgB;CACpC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAY,OAAO;EAAY;CACzC,EAEK,IAAuC;CAC3C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAA4C;CAChD;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAU,IAAI,IAAsB;CAAC;CAAU;CAAW;CAAS,CAAC;AAM1E,SAAS,EAAmB,EAAE,cAAoC;CAChE,IAAM,IAAS,GAAkB,EAE3B,IADiB,EAAiB,EAAE,YAAY;EAAE,OAAO;EAAK,QAAQ;EAAG,EAAE,CAAC,CAC7C,MAAM,SAAS,EAAE,EAEhD,CAAC,GAAgB,KAAqB,EAAS,GAAG,EAClD,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAQ,KAAa,EAA2B,SAAS,EAC1D,CAAC,GAAU,KAAe,EAAS,MAAM,EACzC,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAW,KAAgB,EAAwB,KAAK;AA6B/D,QACE,kBAAC,GAAD;EAAO,OAAM;EAA0B;EAAS,mBAAkB;YAChE,kBAAC,QAAD;GAAM,WA7BY,MAAuB;AAG3C,QAFA,EAAE,gBAAgB,EAClB,EAAa,KAAK,EACd,CAAC,GAAgB;AACnB,OAAa,mDAAmD;AAChE;;AAEF,MAAO,OACL;KACE;KACA;KACA,UAAU,EAAS,MAAM,IAAI;KAC7B,GAAI,EAAO,MAAM,GAAG,EAAE,QAAQ,EAAO,MAAM,EAAE,GAAG,EAAE;KAClD,GAAI,EAAS,MAAM,GAAG,EAAE,UAAU,OAAO,EAAS,EAAE,GAAG,EAAE;KACzD,GAAI,EAAU,MAAM,GAAG,EAAE,WAAW,OAAO,EAAU,EAAE,GAAG,EAAE;KAC5D,GAAI,EAAM,MAAM,GAAG,EAAE,OAAO,OAAO,EAAM,EAAE,GAAG,EAAE;KAChD,GAAI,IAAY,EAAE,WAAW,IAAI,KAAK,EAAU,CAAC,aAAa,EAAE,GAAG,EAAE;KACrE,GAAI,IAAU,EAAE,SAAS,IAAI,KAAK,EAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;KAChE,EACD;KACE,iBAAiB,GAAS;KAC1B,UAAU,MACR,EAAa,aAAe,QAAQ,EAAI,UAAU,4BAA4B;KACjF,CACF;;GAK+B,WAAU;aAAxC;IACG,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAmB,WAAW;eAA7C,CAAuD,iBACxC,kBAAC,QAAD;MAAM,WAAU;gBAAyB;MAAQ,CAAA,CACxD;QACR,kBAAC,UAAD;KACE,IAAG;KACH,OAAO;KACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;KAClD,WAAW;KACX,UAAA;eALF,CAOE,kBAAC,UAAD;MAAQ,OAAM;gBAAG;MAAkC,CAAA,EAClD,EAAc,KAAK,MAClB,kBAAC,UAAD;MAAmB,OAAO,EAAE;gBACzB,EAAE;MACI,EAFI,EAAE,GAEN,CACT,CACK;OACL,EAAA,CAAA;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAa,WAAW;iBAAU;OAEzC,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;OAC1C,aAAY;OACZ,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAa,WAAW;iBAAU;OAEzC,CAAA,EACR,kBAAC,UAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAA0B;OAC9D,WAAW;iBAEV,EAAiB,KAAK,MACrB,kBAAC,UAAD;QAAgB,OAAO;kBACpB,EAAE,QAAQ,MAAM,IAAI;QACd,EAFI,EAEJ,CACT;OACK,CAAA,CACL,EAAA,CAAA;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAe,WAAW;iBAAU;OAE3C,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM,aAAa,CAAC;OAC1D,WAAW;OACX,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACF;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAe,WAAW;iBAAU;OAE3C,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,KAAI;OACJ,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;OAC5C,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAU,WAAW;iBAAU;OAEtC,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,KAAI;OACJ,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;OAC7C,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAY,WAAW;iBAAU;OAExC,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,KAAI;OACJ,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;OACzC,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACF;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAY,WAAW;gBAAU;MAExC,CAAA,EACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;MAC7C,WAAW;MACX,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAU,WAAW;gBAAU;MAEtC,CAAA,EACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAW,EAAE,OAAO,MAAM;MAC3C,WAAW;MACX,CAAA,CACE,EAAA,CAAA,CACF;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBACX;MAEQ,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,EAAO;MACjB,WAAU;gBAET,EAAO,YAAY,cAAc;MAC3B,CAAA,CACL;;IACD;;EACD,CAAA;;AAQZ,SAAS,EAAsB,EAAE,cAAoC;CACnE,IAAM,IAAS,GAA4B,EACrC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAM,KAAW,EAAmC,OAAO,EAC5D,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAW,KAAgB,EAAwB,KAAK;AA2B/D,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACH;EACT,mBAAkB;YAElB,kBAAC,QAAD;GAAM,WAhCY,MAAuB;AAG3C,QAFA,EAAE,gBAAgB,EAClB,EAAa,KAAK,EACd,CAAC,EAAM,MAAM,EAAE;AACjB,OAAa,uBAAuB;AACpC;;AAEF,MAAO,OACL;KACE,OAAO,EAAM,MAAM;KACnB;KACA;KACA,GAAI,EAAM,MAAM,GAAG,EAAE,OAAO,EAAM,MAAM,EAAE,GAAG,EAAE;KAC/C,GAAI,EAAM,MAAM,GAAG,EAAE,OAAO,EAAM,MAAM,EAAE,GAAG,EAAE;KAC/C,GAAI,EAAY,MAAM,GAAG,EAAE,aAAa,OAAO,EAAY,EAAE,GAAG,EAAE;KAClE,GAAI,EAAW,MAAM,GAAG,EAAE,YAAY,OAAO,EAAW,EAAE,GAAG,EAAE;KAChE,EACD;KACE,iBAAiB,GAAS;KAC1B,UAAU,MACR,EAAa,aAAe,QAAQ,EAAI,UAAU,gCAAgC;KACrF,CACF;;GAU+B,WAAU;aAAxC;IACG,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAW,WAAW;gBAArC,CAA+C,UACvC,kBAAC,QAAD;OAAM,WAAU;iBAAyB;OAAQ,CAAA,CACjD;SACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;MACzC,aAAY;MACZ,WAAW;MACX,UAAA;MACA,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAU,WAAW;gBAAU;MAEtC,CAAA,EACR,kBAAC,UAAD;MACE,IAAG;MACH,OAAO;MACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAkC;MACpE,WAAW;gBAEV,EAAc,KAAK,MAClB,kBAAC,UAAD;OAAgB,OAAO;iBACpB,EAAE,QAAQ,MAAM,IAAI;OACd,EAFI,EAEJ,CACT;MACK,CAAA,CACL,EAAA,CAAA,CACF;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,WAAW;iBAAU;OAEvC,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;OACzC,aAAY;OACZ,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,WAAW;iBAAU;OAEvC,CAAA,EACR,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,WAAU;OACV,WAAW;OACX,OAAO;OACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM,QAAQ,OAAO,GAAG,CAAC;OAC5D,aAAY;OACZ,WAAW;OACX,CAAA,CACE,EAAA,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAe,WAAW;kBAAU;QAE3C,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,KAAI;QACJ,KAAI;QACJ,OAAO;QACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;QAC/C,aAAY;QACZ,WAAW;QACX,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAc,WAAW;kBAAU;QAE1C,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,KAAI;QACJ,KAAI;QACJ,OAAO;QACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;QAC9C,aAAY;QACZ,WAAW;QACX,CAAA,CACE,EAAA,CAAA,CACF;;MACF;;IACN,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD;MACE,MAAK;MACL,SAAS;MACT,WAAW,MAAM,EAAa,EAAE,OAAO,QAAQ;MAC/C,WAAU;MACV,CAAA,EAAA,iBAEI;;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBACX;MAEQ,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,EAAO;MACjB,WAAU;gBAET,EAAO,YAAY,YAAY;MACzB,CAAA,CACL;;IACD;;EACD,CAAA;;AAQZ,SAAS,IAAwB;CAC/B,IAAM,EAAE,SAAM,cAAW,YAAS,eAAY,GAAuB,EAC/D,IAAS,GAA4B,EACrC,CAAC,GAAS,KAAc,EAAS,GAAM,EAEvC,IAAc,KAAQ,EAAE;AAE9B,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,QACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,GAAK;GAC/B,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,sBACzB;;YAVb,CAaG,IACC,kBAAC,GAAD,EAAa,OAAM,4BAA6B,CAAA,GAC9C,IACF,kBAAC,GAAD;GAAW,SAAQ;GAAkC,eAAe,KAAK,GAAS;GAAI,CAAA,GACpF,EAAY,WAAW,IACzB,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;GAC1C,OAAM;GACN,aAAY;GACZ,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAY,KAAK,MAChB,kBAAC,OAAD;IAAiB,WAAU;cAA3B,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAY,WAAU,wCAAyC,CAAA,EAC/D,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAG,OACH,EAAG,aACF,kBAAC,GAAD,EAAM,WAAU,iDAAkD,CAAA,CAElE;UACJ,kBAAC,KAAD;OAAG,WAAU;iBACV;QACC,EAAG;QACH,EAAG,QAAQ,QAAQ,EAAG,UAAU;QAChC,EAAG,eAAe,EAAG,aAAa,GAAG,EAAG,YAAY,GAAG,EAAG,eAAe;QAC1E,CACE,OAAO,QAAQ,CACf,KAAK,MAAM,IAAI,EAAG,KAAK,QAAQ,MAAM,IAAI;OAC1C,CAAA,CACA;QACF;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAa,QAAQ,EAAG,QAAU,CAAA,EAClC,kBAAC,UAAD;MACE,MAAK;MACL,cAAY,UAAU,EAAG;MACzB,UAAU,EAAO;MACjB,eAAe,EAAO,OAAO,EAAG,GAAG;MACnC,WAAU;gBAEV,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;MACvB,CAAA,CACL;OACF;MAjCI,EAAG,GAiCP,CACN;GACE,CAAA,EAGP,KAAW,kBAAC,GAAD,EAAuB,eAAe,EAAW,GAAM,EAAI,CAAA,CAC/D;;;AAId,SAAgB,IAAe;CAC7B,IAAM,EAAE,kBAAe,GAAkB,EACnC,CAAC,GAAM,KAAW,EAAS,EAAE,EAC7B,CAAC,GAAQ,KAAa,EAAgC,GAAG,EACzD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,EAAE,SAAM,cAAW,YAAS,YAAS,kBAAe,EAAY;EACpE,QAAQ,KAAU,KAAA;EAClB,YAAY;GAAE,OAAO;GAAW,QAAQ,IAAO;GAAW;EAC3D,CAAC,EACI,IAAW,GAAoB,EAE/B,IAAQ,QAAc,GAAM,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EAChD,IAAU,GAAM,SAAS,WAAW,IAEpC,IAAS,QAAc;EAC3B,IAAM,IAAc,EAAM,QAAQ,MAAM,EAAQ,IAAI,EAAE,OAAO,CAAC,EACxD,IAAW,EAAM,IAAI,YAAY;AACvC,SAAO;GACL,kBAAkB,EAAY;GAC9B,kBAAkB,EAAY,QAAQ,GAAK,MAAM,IAAM,EAAE,OAAO,EAAE;GAClE;GACD;IACA,CAAC,EAAM,CAAC;AAEX,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAc,GAAK;GAClC,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,kBACrB;;YAVb;GAaE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAGhB,CAAA;GAEb,CAAC,KAAa,CAAC,KAAW,EAAM,SAAS,KACxC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,UAAA;KACA,MAAM,kBAAC,GAAD,EAAW,CAAA;KACjB,OAAO,EAAe,EAAO,kBAAkB,EAAO,UAAU,EAC9D,uBAAuB,GACxB,CAAC;KACF,OAAM;KACN,CAAA,EACF,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAY,CAAA;KAClB,OAAO,EAAO;KACd,OAAM;KACN,CAAA,CACE;;GAGR,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,cAAW;KACX,OAAO;KACP,WAAW,MAAM;AAEf,MADA,EAAU,EAAE,OAAO,MAA+B,EAClD,EAAQ,EAAE;;KAEZ,WAAU;eAET,EAAe,KAAK,MACnB,kBAAC,UAAD;MAAsB,OAAO,EAAE;gBAC5B,EAAE;MACI,EAFI,EAAE,MAEN,CACT;KACK,CAAA;IACL,CAAA;GAEL,IACC,kBAAC,GAAD,EAAa,OAAM,qBAAsB,CAAA,GACvC,IACF,kBAAC,GAAD;IAAW,SAAQ;IAA2B,eAAe,KAAK,GAAS;IAAI,CAAA,GAC7E,EAAM,WAAW,IACnB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAS,WAAU,aAAc,CAAA;KACvC,OAAM;KACN,aACE,IACI,0CACA;KAEN,QACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAc,GAAK;MAClC,WAAU;gBACX;MAEQ,CAAA;KAEX,CAAA;IACE,CAAA,GAEN,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;MACpB,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;MACnB,kBAAC,QAAD;OAAM,WAAU;iBAAa;OAAY,CAAA;MACzC,kBAAC,QAAD;OAAM,WAAU;iBAAa;OAAU,CAAA;MACvC,kBAAC,QAAD;OAAM,WAAU;iBAAa;OAAa,CAAA;MACtC;QACN,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,MACV,kBAAC,OAAD;MAEE,WAAU;gBAFZ;OAIE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,kBAAkB,EAAI,iBAAiB;QACjE,WAAU;kBAHZ,CAKE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAI,UAAU,WAAW,EAAI,GAAG,MAAM,GAAG,EAAE;SAC1C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAI,YAAY,UAAU,EAAW,EAAI,UAAU,KAAK;SACvD,CAAA,CACG;;OACT,kBAAC,QAAD,EAAA,UACE,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA,EAC9B,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAe,EAAI,OAAO,EAAI,SAAS;QACnC,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAI,UAAU,EAAW,EAAI,QAAQ,GAAG;QACpC,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAQ,IAAI,EAAI,OAAO,GACtB,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,EAAS;SACnB,eAAe,EAAS,OAAO,EAAI,GAAG;SACtC,WAAU;mBACX;SAEQ,CAAA,GACP,EAAI,SACN,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA8C,SACtC,EAAW,EAAI,OAAO,CACvB;aAEP,kBAAC,QAAD;SAAM,WAAU;mBAA8B;SAAQ,CAAA;QAEnD,CAAA;OACH;QA1CC,EAAI,GA0CL,CACN;KACE,CAAA,CACF;OAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA2C;MACnC,IAAO;MACZ,IAAa,mBAAmB;MAC/B;QACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAS;MACnB,eAAe,GAAS,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;MACjD,WAAW,EACT,0EACA,MAAS,IACL,sDACA,uCACL;gBACF;MAEQ,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,CAAC;MACX,eAAe,GAAS,MAAM,IAAI,EAAE;MACpC,WAAW,EACT,0EACC,IAEG,yCADA,oDAEL;gBACF;MAEQ,CAAA,CACL;OACF;MACL,EAAA,CAAA;GAGL,kBAAC,GAAD,EAAyB,CAAA;GAExB,KAAc,kBAAC,GAAD,EAAoB,eAAe,EAAc,GAAM,EAAI,CAAA;GAC/D"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { PageLayout as e } from "../components/PageLayout.js";
|
|
2
|
+
import { formatRelativeTime as t } from "../utils/formatters.js";
|
|
3
|
+
import { StatusBadge as n } from "../components/StatusBadge.js";
|
|
4
|
+
import { EmptyState as r } from "../components/EmptyState.js";
|
|
5
|
+
import { ErrorView as i, LoadingView as a } from "../components/StateViews.js";
|
|
6
|
+
import { useActivateWorkflow as o, useCreateWorkflow as s, useWorkflows as c } from "../hooks/useSubscriberBotApi.js";
|
|
7
|
+
import { Modal as l } from "../components/Modal.js";
|
|
8
8
|
import { useState as u } from "react";
|
|
9
|
-
import { Play as d, Plus as f, Workflow as p
|
|
10
|
-
import { jsx as
|
|
11
|
-
import { PagePurpose as
|
|
9
|
+
import { Play as d, Plus as f, Workflow as p } from "lucide-react";
|
|
10
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
11
|
+
import { PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/subscriberbot/pages/WorkflowsPage.tsx
|
|
13
|
-
var
|
|
13
|
+
var _ = [
|
|
14
14
|
{
|
|
15
15
|
value: "",
|
|
16
16
|
label: "All statuses"
|
|
@@ -31,206 +31,192 @@ var v = [
|
|
|
31
31
|
value: "ARCHIVED",
|
|
32
32
|
label: "Archived"
|
|
33
33
|
}
|
|
34
|
-
],
|
|
35
|
-
function
|
|
36
|
-
let
|
|
37
|
-
|
|
34
|
+
], v = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", y = "mb-1 block text-xs font-medium text-text-secondary";
|
|
35
|
+
function b() {
|
|
36
|
+
let [b, x] = u(""), [S, C] = u(!1), { data: w, isLoading: T, isError: E, refetch: D } = c(b || void 0), O = s(), k = o(), [A, j] = u(""), [M, N] = u(""), [P, F] = u(""), [I, L] = u(null), R = w ?? [], z = () => {
|
|
37
|
+
j(""), N(""), F(""), L(null);
|
|
38
|
+
}, B = () => {
|
|
39
|
+
C(!1), z();
|
|
38
40
|
};
|
|
39
|
-
return /* @__PURE__ */
|
|
41
|
+
return /* @__PURE__ */ h(e, {
|
|
40
42
|
title: "Workflows",
|
|
41
43
|
description: "Event-driven automation across your portfolio",
|
|
42
|
-
actions: /* @__PURE__ */
|
|
44
|
+
actions: /* @__PURE__ */ h("button", {
|
|
43
45
|
type: "button",
|
|
44
|
-
onClick: () =>
|
|
46
|
+
onClick: () => C(!0),
|
|
45
47
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
48
|
+
children: [/* @__PURE__ */ m(f, { className: "h-4 w-4" }), " New workflow"]
|
|
47
49
|
}),
|
|
48
50
|
children: [
|
|
49
|
-
/* @__PURE__ */
|
|
51
|
+
/* @__PURE__ */ m(g, {
|
|
50
52
|
className: "mb-4",
|
|
51
53
|
children: "Workflows react to domain events — a new invoice, a detected renewal, a cancelled relationship — and run multi-step automations. Draft a workflow, then activate it when you're ready."
|
|
52
54
|
}),
|
|
53
|
-
/* @__PURE__ */
|
|
55
|
+
/* @__PURE__ */ m("div", {
|
|
54
56
|
className: "flex flex-wrap items-center gap-3",
|
|
55
|
-
children: /* @__PURE__ */
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
children: /* @__PURE__ */ m("select", {
|
|
58
|
+
"aria-label": "Filter by status",
|
|
59
|
+
value: b,
|
|
60
|
+
onChange: (e) => x(e.target.value),
|
|
58
61
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
59
|
-
children:
|
|
62
|
+
children: _.map((e) => /* @__PURE__ */ m("option", {
|
|
60
63
|
value: e.value,
|
|
61
64
|
children: e.label
|
|
62
65
|
}, e.value))
|
|
63
66
|
})
|
|
64
67
|
}),
|
|
65
|
-
|
|
68
|
+
T ? /* @__PURE__ */ m(a, { label: "Loading workflows…" }) : E ? /* @__PURE__ */ m(i, {
|
|
66
69
|
message: "Could not load workflows.",
|
|
67
|
-
onRetry: () => void
|
|
68
|
-
}) :
|
|
70
|
+
onRetry: () => void D()
|
|
71
|
+
}) : R.length === 0 ? /* @__PURE__ */ m("div", {
|
|
69
72
|
className: "mt-6",
|
|
70
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ m(r, {
|
|
71
74
|
illustration: "empty-data",
|
|
72
|
-
icon: /* @__PURE__ */
|
|
75
|
+
icon: /* @__PURE__ */ m(p, { className: "h-12 w-12" }),
|
|
73
76
|
title: "No workflows yet",
|
|
74
77
|
description: "Create your first event-driven workflow to automate your recurring portfolio.",
|
|
75
|
-
action: /* @__PURE__ */
|
|
78
|
+
action: /* @__PURE__ */ m("button", {
|
|
76
79
|
type: "button",
|
|
77
|
-
onClick: () =>
|
|
80
|
+
onClick: () => C(!0),
|
|
78
81
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
79
82
|
children: "New workflow"
|
|
80
83
|
})
|
|
81
84
|
})
|
|
82
|
-
}) : /* @__PURE__ */
|
|
85
|
+
}) : /* @__PURE__ */ m("div", {
|
|
83
86
|
className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
84
|
-
children:
|
|
87
|
+
children: R.map((e) => /* @__PURE__ */ h("div", {
|
|
85
88
|
className: "flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
86
89
|
children: [
|
|
87
|
-
/* @__PURE__ */
|
|
90
|
+
/* @__PURE__ */ h("div", {
|
|
88
91
|
className: "flex items-start justify-between gap-2",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ m("span", {
|
|
90
93
|
className: "flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
91
|
-
children: /* @__PURE__ */
|
|
92
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ m(p, { className: "h-4 w-4" })
|
|
95
|
+
}), /* @__PURE__ */ m(n, { status: e.status })]
|
|
93
96
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ m("p", {
|
|
95
98
|
className: "mt-3 truncate text-sm font-semibold text-text-primary",
|
|
96
99
|
children: e.name
|
|
97
100
|
}),
|
|
98
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ h("p", {
|
|
99
102
|
className: "truncate text-xs text-text-secondary",
|
|
100
103
|
children: ["on ", e.triggerEvent]
|
|
101
104
|
}),
|
|
102
|
-
e.description && /* @__PURE__ */
|
|
105
|
+
e.description && /* @__PURE__ */ m("p", {
|
|
103
106
|
className: "mt-2 line-clamp-2 text-xs text-text-secondary",
|
|
104
107
|
children: e.description
|
|
105
108
|
}),
|
|
106
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ h("p", {
|
|
107
110
|
className: "mt-2 text-xs text-text-secondary",
|
|
108
111
|
children: [
|
|
109
112
|
"Ran ",
|
|
110
113
|
e.runCount,
|
|
111
114
|
"×",
|
|
112
|
-
e.lastRunAt ? ` · last ${
|
|
115
|
+
e.lastRunAt ? ` · last ${t(e.lastRunAt)}` : ""
|
|
113
116
|
]
|
|
114
117
|
}),
|
|
115
|
-
e.status !== "ACTIVE" && /* @__PURE__ */
|
|
118
|
+
e.status !== "ACTIVE" && /* @__PURE__ */ h("button", {
|
|
116
119
|
type: "button",
|
|
117
|
-
disabled:
|
|
118
|
-
onClick: () =>
|
|
120
|
+
disabled: k.isPending,
|
|
121
|
+
onClick: () => k.mutate(e.id),
|
|
119
122
|
className: "mt-3 inline-flex items-center justify-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
123
|
+
children: [/* @__PURE__ */ m(d, { className: "h-3.5 w-3.5" }), " Activate"]
|
|
121
124
|
})
|
|
122
125
|
]
|
|
123
126
|
}, e.id))
|
|
124
127
|
}),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
128
|
+
S && /* @__PURE__ */ m(l, {
|
|
129
|
+
title: "New workflow",
|
|
130
|
+
onClose: B,
|
|
131
|
+
children: /* @__PURE__ */ h("form", {
|
|
132
|
+
onSubmit: (e) => {
|
|
133
|
+
if (e.preventDefault(), L(null), !A.trim() || !M.trim()) {
|
|
134
|
+
L("Name and trigger event are required.");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
let t = {
|
|
138
|
+
name: A.trim(),
|
|
139
|
+
triggerEvent: M.trim(),
|
|
140
|
+
...P.trim() ? { description: P.trim() } : {}
|
|
141
|
+
};
|
|
142
|
+
O.mutate(t, {
|
|
143
|
+
onSuccess: () => {
|
|
144
|
+
z(), C(!1);
|
|
138
145
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
className:
|
|
169
|
-
children:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
placeholder: "What should this workflow do when the event fires?",
|
|
208
|
-
className: y
|
|
209
|
-
})] }),
|
|
210
|
-
/* @__PURE__ */ g("div", {
|
|
211
|
-
className: "flex justify-end gap-3",
|
|
212
|
-
children: [/* @__PURE__ */ h("button", {
|
|
213
|
-
type: "button",
|
|
214
|
-
onClick: () => {
|
|
215
|
-
T(!1), V();
|
|
216
|
-
},
|
|
217
|
-
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
218
|
-
children: "Cancel"
|
|
219
|
-
}), /* @__PURE__ */ h("button", {
|
|
220
|
-
type: "submit",
|
|
221
|
-
disabled: A.isPending,
|
|
222
|
-
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50",
|
|
223
|
-
children: A.isPending ? "Creating…" : "Create workflow"
|
|
224
|
-
})]
|
|
225
|
-
})
|
|
226
|
-
]
|
|
227
|
-
})]
|
|
146
|
+
onError: (e) => L(e instanceof Error ? e.message : "Could not create workflow.")
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
className: "space-y-4",
|
|
150
|
+
children: [
|
|
151
|
+
I && /* @__PURE__ */ m("div", {
|
|
152
|
+
className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
153
|
+
children: I
|
|
154
|
+
}),
|
|
155
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("label", {
|
|
156
|
+
htmlFor: "wf-name",
|
|
157
|
+
className: y,
|
|
158
|
+
children: ["Name ", /* @__PURE__ */ m("span", {
|
|
159
|
+
className: "text-status-error-text",
|
|
160
|
+
children: "*"
|
|
161
|
+
})]
|
|
162
|
+
}), /* @__PURE__ */ m("input", {
|
|
163
|
+
id: "wf-name",
|
|
164
|
+
type: "text",
|
|
165
|
+
value: A,
|
|
166
|
+
onChange: (e) => j(e.target.value),
|
|
167
|
+
placeholder: "e.g. Invoice → notify finance",
|
|
168
|
+
className: v,
|
|
169
|
+
required: !0
|
|
170
|
+
})] }),
|
|
171
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("label", {
|
|
172
|
+
htmlFor: "wf-trigger",
|
|
173
|
+
className: y,
|
|
174
|
+
children: ["Trigger event ", /* @__PURE__ */ m("span", {
|
|
175
|
+
className: "text-status-error-text",
|
|
176
|
+
children: "*"
|
|
177
|
+
})]
|
|
178
|
+
}), /* @__PURE__ */ m("input", {
|
|
179
|
+
id: "wf-trigger",
|
|
180
|
+
type: "text",
|
|
181
|
+
value: M,
|
|
182
|
+
onChange: (e) => N(e.target.value),
|
|
183
|
+
placeholder: "e.g. InvoiceReceived, RenewalDetected",
|
|
184
|
+
className: v,
|
|
185
|
+
required: !0
|
|
186
|
+
})] }),
|
|
187
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
|
|
188
|
+
htmlFor: "wf-desc",
|
|
189
|
+
className: y,
|
|
190
|
+
children: "Description"
|
|
191
|
+
}), /* @__PURE__ */ m("textarea", {
|
|
192
|
+
id: "wf-desc",
|
|
193
|
+
value: P,
|
|
194
|
+
onChange: (e) => F(e.target.value),
|
|
195
|
+
rows: 3,
|
|
196
|
+
placeholder: "What should this workflow do when the event fires?",
|
|
197
|
+
className: v
|
|
198
|
+
})] }),
|
|
199
|
+
/* @__PURE__ */ h("div", {
|
|
200
|
+
className: "flex justify-end gap-3",
|
|
201
|
+
children: [/* @__PURE__ */ m("button", {
|
|
202
|
+
type: "button",
|
|
203
|
+
onClick: B,
|
|
204
|
+
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
205
|
+
children: "Cancel"
|
|
206
|
+
}), /* @__PURE__ */ m("button", {
|
|
207
|
+
type: "submit",
|
|
208
|
+
disabled: O.isPending,
|
|
209
|
+
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50",
|
|
210
|
+
children: O.isPending ? "Creating…" : "Create workflow"
|
|
211
|
+
})]
|
|
212
|
+
})
|
|
213
|
+
]
|
|
228
214
|
})
|
|
229
215
|
})
|
|
230
216
|
]
|
|
231
217
|
});
|
|
232
218
|
}
|
|
233
219
|
//#endregion
|
|
234
|
-
export {
|
|
220
|
+
export { b as WorkflowsPage };
|
|
235
221
|
|
|
236
222
|
//# sourceMappingURL=WorkflowsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/subscriberbot/pages/WorkflowsPage.tsx"],"sourcesContent":["/**\n * Workflows (Automation)\n * @module subscriberbot/pages\n *\n * The Workflow Engine — event-driven automation (\"when invoice arrives →\n * classify → update relationship → notify finance\"). List + create + activate.\n * Reads `subListWorkflows`; writes `subCreateWorkflow` / `subActivateWorkflow`.\n */\n\nimport { useState } from 'react';\nimport { Plus, Workflow as WorkflowIcon, Play, X } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { useSubscriberBot } from '../context/SubscriberBotContext';\nimport { useWorkflows, useCreateWorkflow, useActivateWorkflow } from '../hooks/useSubscriberBotApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { SubCreateWorkflowInput, SubWorkflowStatus } from '../types';\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubWorkflowStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PAUSED', label: 'Paused' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nexport function WorkflowsPage() {\n const { navigate } = useSubscriberBot();\n const [statusFilter, setStatusFilter] = useState<'' | SubWorkflowStatus>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useWorkflows(statusFilter || undefined);\n const create = useCreateWorkflow();\n const activate = useActivateWorkflow();\n\n const [name, setName] = useState('');\n const [triggerEvent, setTriggerEvent] = useState('');\n const [description, setDescription] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const workflows = data ?? [];\n // `navigate` is only consumed via the back-compat context; reference it so\n // its presence keeps integrated routing wired (no-op otherwise).\n void navigate;\n\n const resetForm = () => {\n setName('');\n setTriggerEvent('');\n setDescription('');\n setFormError(null);\n };\n\n const handleCreate = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim() || !triggerEvent.trim()) {\n setFormError('Name and trigger event are required.');\n return;\n }\n const input: SubCreateWorkflowInput = {\n name: name.trim(),\n triggerEvent: triggerEvent.trim(),\n ...(description.trim() ? { description: description.trim() } : {}),\n };\n create.mutate(input, {\n onSuccess: () => {\n resetForm();\n setShowCreate(false);\n },\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create workflow.'),\n });\n };\n\n return (\n <PageLayout\n title=\"Workflows\"\n description=\"Event-driven automation across your portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New workflow\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Workflows react to domain events — a new invoice, a detected renewal, a cancelled\n relationship — and run multi-step automations. Draft a workflow, then activate it when\n you're ready.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as '' | SubWorkflowStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading workflows…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load workflows.\" onRetry={() => void refetch()} />\n ) : workflows.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<WorkflowIcon className=\"h-12 w-12\" />}\n title=\"No workflows yet\"\n description=\"Create your first event-driven workflow to automate your recurring portfolio.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New workflow\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {workflows.map((wf) => (\n <div\n key={wf.id}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <WorkflowIcon className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={wf.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{wf.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">on {wf.triggerEvent}</p>\n {wf.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{wf.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n Ran {wf.runCount}×\n {wf.lastRunAt ? ` · last ${formatRelativeTime(wf.lastRunAt)}` : ''}\n </p>\n {wf.status !== 'ACTIVE' && (\n <button\n type=\"button\"\n disabled={activate.isPending}\n onClick={() => activate.mutate(wf.id)}\n className=\"mt-3 inline-flex items-center justify-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Play className=\"h-3.5 w-3.5\" /> Activate\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* Create modal */}\n {showCreate && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-lg rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"text-lg font-semibold text-text-primary\">New workflow</h3>\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n resetForm();\n }}\n className=\"rounded-md p-1 text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n <form onSubmit={handleCreate} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"wf-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"wf-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Invoice → notify finance\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"wf-trigger\" className={labelCls}>\n Trigger event <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"wf-trigger\"\n type=\"text\"\n value={triggerEvent}\n onChange={(e) => setTriggerEvent(e.target.value)}\n placeholder=\"e.g. InvoiceReceived, RenewalDetected\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"wf-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"wf-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"What should this workflow do when the event fires?\"\n className={inputCls}\n />\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setShowCreate(false);\n resetForm();\n }}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create workflow'}\n </button>\n </div>\n </form>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/subscriberbot/pages/WorkflowsPage.tsx"],"sourcesContent":["/**\n * Workflows (Automation)\n * @module subscriberbot/pages\n *\n * The Workflow Engine — event-driven automation (\"when invoice arrives →\n * classify → update relationship → notify finance\"). List + create + activate.\n * Reads `subListWorkflows`; writes `subCreateWorkflow` / `subActivateWorkflow`.\n */\n\nimport { useState } from 'react';\nimport { Plus, Workflow as WorkflowIcon, Play } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { EmptyState } from '../components/EmptyState';\nimport { Modal } from '../components/Modal';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { useWorkflows, useCreateWorkflow, useActivateWorkflow } from '../hooks/useSubscriberBotApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport type { SubCreateWorkflowInput, SubWorkflowStatus } from '../types';\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubWorkflowStatus; label: string }> = [\n { value: '', label: 'All statuses' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PAUSED', label: 'Paused' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nexport function WorkflowsPage() {\n const [statusFilter, setStatusFilter] = useState<'' | SubWorkflowStatus>('');\n const [showCreate, setShowCreate] = useState(false);\n\n const { data, isLoading, isError, refetch } = useWorkflows(statusFilter || undefined);\n const create = useCreateWorkflow();\n const activate = useActivateWorkflow();\n\n const [name, setName] = useState('');\n const [triggerEvent, setTriggerEvent] = useState('');\n const [description, setDescription] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const workflows = data ?? [];\n\n const resetForm = () => {\n setName('');\n setTriggerEvent('');\n setDescription('');\n setFormError(null);\n };\n\n const closeCreate = () => {\n setShowCreate(false);\n resetForm();\n };\n\n const handleCreate = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim() || !triggerEvent.trim()) {\n setFormError('Name and trigger event are required.');\n return;\n }\n const input: SubCreateWorkflowInput = {\n name: name.trim(),\n triggerEvent: triggerEvent.trim(),\n ...(description.trim() ? { description: description.trim() } : {}),\n };\n create.mutate(input, {\n onSuccess: () => {\n resetForm();\n setShowCreate(false);\n },\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create workflow.'),\n });\n };\n\n return (\n <PageLayout\n title=\"Workflows\"\n description=\"Event-driven automation across your portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New workflow\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Workflows react to domain events — a new invoice, a detected renewal, a cancelled\n relationship — and run multi-step automations. Draft a workflow, then activate it when\n you're ready.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as '' | SubWorkflowStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading workflows…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load workflows.\" onRetry={() => void refetch()} />\n ) : workflows.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<WorkflowIcon className=\"h-12 w-12\" />}\n title=\"No workflows yet\"\n description=\"Create your first event-driven workflow to automate your recurring portfolio.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New workflow\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {workflows.map((wf) => (\n <div\n key={wf.id}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <WorkflowIcon className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={wf.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{wf.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">on {wf.triggerEvent}</p>\n {wf.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{wf.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n Ran {wf.runCount}×\n {wf.lastRunAt ? ` · last ${formatRelativeTime(wf.lastRunAt)}` : ''}\n </p>\n {wf.status !== 'ACTIVE' && (\n <button\n type=\"button\"\n disabled={activate.isPending}\n onClick={() => activate.mutate(wf.id)}\n className=\"mt-3 inline-flex items-center justify-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Play className=\"h-3.5 w-3.5\" /> Activate\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* Create modal */}\n {showCreate && (\n <Modal title=\"New workflow\" onClose={closeCreate}>\n <form onSubmit={handleCreate} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"wf-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"wf-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Invoice → notify finance\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"wf-trigger\" className={labelCls}>\n Trigger event <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"wf-trigger\"\n type=\"text\"\n value={triggerEvent}\n onChange={(e) => setTriggerEvent(e.target.value)}\n placeholder=\"e.g. InvoiceReceived, RenewalDetected\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"wf-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"wf-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"What should this workflow do when the event fires?\"\n className={inputCls}\n />\n </div>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={closeCreate}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create workflow'}\n </button>\n </div>\n </form>\n </Modal>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|