@burdenoff/microfe-timecampus 2026.719.4 → 2026.731.1

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.
Files changed (33) hide show
  1. package/dist/generated/wspace-operations.d.ts +527 -0
  2. package/dist/generated/wspace-operations.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.js +1112 -880
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/generated/wspace-types.d.ts +175 -0
  6. package/dist/generated/wspace-types.d.ts.map +1 -1
  7. package/dist/generated/wspace-types.js +5 -3
  8. package/dist/generated/wspace-types.js.map +1 -1
  9. package/dist/index.js +5 -5
  10. package/dist/timecampus/TimeCampusRoot.d.ts.map +1 -1
  11. package/dist/timecampus/TimeCampusRoot.js +81 -73
  12. package/dist/timecampus/TimeCampusRoot.js.map +1 -1
  13. package/dist/timecampus/TimeCampusRoutes.d.ts.map +1 -1
  14. package/dist/timecampus/TimeCampusRoutes.js +17 -13
  15. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  16. package/dist/timecampus/components/StatusBadge.d.ts.map +1 -1
  17. package/dist/timecampus/components/StatusBadge.js.map +1 -1
  18. package/dist/timecampus/pages/invoicing/InvoicingPage.d.ts +2 -0
  19. package/dist/timecampus/pages/invoicing/InvoicingPage.d.ts.map +1 -0
  20. package/dist/timecampus/pages/invoicing/InvoicingPage.js +595 -0
  21. package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -0
  22. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.d.ts.map +1 -1
  23. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js +88 -59
  24. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
  25. package/dist/timecampus/types/index.d.ts.map +1 -1
  26. package/dist/timecampus/utils/helpers.d.ts.map +1 -1
  27. package/dist/timecampus/utils/helpers.js +3 -1
  28. package/dist/timecampus/utils/helpers.js.map +1 -1
  29. package/dist/types/assistant.d.ts.map +1 -1
  30. package/dist/utils/pageContext.d.ts.map +1 -1
  31. package/dist/utils/pageContext.js +1 -0
  32. package/dist/utils/pageContext.js.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoicingPage.js","names":[],"sources":["../../../../src/timecampus/pages/invoicing/InvoicingPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n FileText,\n Plus,\n DollarSign,\n Send,\n CheckCircle2,\n Trash2,\n Eye,\n Download,\n AlertTriangle,\n} from \"lucide-react\";\nimport { gql } from \"@apollo/client\";\nimport { useApolloClient } from \"@apollo/client/react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { Modal } from \"../../components/Modal\";\nimport { ToastContainer, useToast } from \"../../components/Toast\";\nimport { formatCurrency, formatDate } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListInvoicesQuery,\n useGetInvoiceStatsQuery,\n useGenerateInvoiceDraftMutation,\n useUpdateInvoiceStatusMutation,\n useDeleteInvoiceDraftMutation,\n type InvoiceFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { TimeCampusInvoiceStatus } from \"@/generated/wspace-types\";\n\nconst PAGE_SIZE = 25;\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\n\n// Fetched on-demand only (view / download), never on page load — same\n// apollo.query() pattern as the expense-tracking receipt-preview link, so we\n// don't need a generated lazy-query hook for a rarely-used, heavy payload.\nconst GET_INVOICE_HTML = gql`\n query TimeCampusInvoicing_GetInvoiceHtml($id: ID!, $workspaceId: ID!) {\n timeCampusInvoiceHtml(id: $id, workspaceId: $workspaceId)\n }\n`;\n\nconst GET_INVOICE_PDF = gql`\n query TimeCampusInvoicing_GetInvoicePdf($id: ID!, $workspaceId: ID!) {\n timeCampusInvoicePdf(id: $id, workspaceId: $workspaceId)\n }\n`;\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nfunction firstOfMonthIso(): string {\n const d = new Date();\n return new Date(d.getFullYear(), d.getMonth(), 1).toISOString().slice(0, 10);\n}\n\ninterface GenerateFormState {\n clientName: string;\n clientEmail: string;\n clientAddress: string;\n periodStart: string;\n periodEnd: string;\n taxRatePercent: string;\n currency: string;\n dueDate: string;\n notes: string;\n projectIds: string;\n}\n\nconst emptyForm: GenerateFormState = {\n clientName: \"\",\n clientEmail: \"\",\n clientAddress: \"\",\n periodStart: firstOfMonthIso(),\n periodEnd: todayIso(),\n taxRatePercent: \"0\",\n currency: \"USD\",\n dueDate: \"\",\n notes: \"\",\n projectIds: \"\",\n};\n\n/** Downloads a base64-encoded PDF payload as a file in the browser. */\nfunction downloadBase64Pdf(base64: string, filename: string): void {\n const bytes = atob(base64);\n const buffer = new Uint8Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) buffer[i] = bytes.charCodeAt(i);\n const blob = new Blob([buffer], { type: \"application/pdf\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n link.remove();\n URL.revokeObjectURL(url);\n}\n\nexport function InvoicingPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const { toasts, addToast, dismissToast } = useToast();\n const apollo = useApolloClient();\n\n const {\n data: invoicesData,\n loading: invoicesLoading,\n error: invoicesError,\n refetch,\n fetchMore,\n } = useListInvoicesQuery({\n variables: { workspaceId, pagination: { limit: PAGE_SIZE, offset: 0 } },\n notifyOnNetworkStatusChange: true,\n });\n const {\n data: statsData,\n loading: statsLoading,\n refetch: refetchStats,\n } = useGetInvoiceStatsQuery({ variables: { workspaceId } });\n\n const [generateDraft, { loading: generating }] =\n useGenerateInvoiceDraftMutation();\n const [updateStatus] = useUpdateInvoiceStatusMutation();\n const [deleteDraft, { loading: deleting }] = useDeleteInvoiceDraftMutation();\n\n const invoices = invoicesData?.timeCampusInvoices?.items ?? [];\n const total = invoicesData?.timeCampusInvoices?.total ?? 0;\n const stats = statsData?.timeCampusInvoiceStats;\n\n const [loadingMore, setLoadingMore] = useState(false);\n const [canLoadMore, setCanLoadMore] = useState(true);\n\n // Generate modal\n const [formOpen, setFormOpen] = useState(false);\n const [form, setForm] = useState<GenerateFormState>(emptyForm);\n\n // View/preview modal\n const [viewTarget, setViewTarget] = useState<InvoiceFieldsFragment | null>(\n null,\n );\n const [viewHtml, setViewHtml] = useState<string | null>(null);\n const [viewLoading, setViewLoading] = useState(false);\n const [downloadingId, setDownloadingId] = useState<string | null>(null);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] =\n useState<InvoiceFieldsFragment | null>(null);\n\n // Per-row busy state for status transitions\n const [busyId, setBusyId] = useState<string | null>(null);\n\n const refreshAll = async () => {\n await Promise.all([refetch(), refetchStats()]);\n };\n\n const openGenerate = () => {\n setForm({\n ...emptyForm,\n periodStart: firstOfMonthIso(),\n periodEnd: todayIso(),\n });\n setFormOpen(true);\n };\n const closeGenerate = () => setFormOpen(false);\n\n const handleGenerate = async () => {\n if (!form.clientName.trim() || !form.periodStart || !form.periodEnd) return;\n const taxRate = Number(form.taxRatePercent || \"0\") / 100;\n if (Number.isNaN(taxRate) || taxRate < 0 || taxRate > 1) {\n addToast({\n title: \"Tax rate must be between 0 and 100%\",\n variant: \"error\",\n });\n return;\n }\n try {\n await generateDraft({\n variables: {\n input: {\n workspaceId,\n clientName: form.clientName.trim(),\n clientEmail: form.clientEmail.trim() || undefined,\n clientAddress: form.clientAddress.trim() || undefined,\n periodStart: new Date(form.periodStart).toISOString(),\n periodEnd: new Date(\n new Date(form.periodEnd).getTime() + 86_400_000 - 1,\n ).toISOString(),\n taxRate,\n currency: form.currency.trim() || \"USD\",\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n notes: form.notes.trim() || undefined,\n projectIds: form.projectIds.trim()\n ? form.projectIds\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n });\n addToast({ title: \"Draft invoice generated\", variant: \"success\" });\n closeGenerate();\n await refreshAll();\n } catch (err) {\n addToast({\n title:\n err instanceof Error\n ? err.message\n : \"Failed to generate invoice — check the billing period has un-invoiced billable time\",\n variant: \"error\",\n });\n }\n };\n\n const handleMarkSent = async (invoice: InvoiceFieldsFragment) => {\n setBusyId(invoice.id);\n try {\n await updateStatus({\n variables: {\n input: {\n id: invoice.id,\n workspaceId,\n status: TimeCampusInvoiceStatus.Sent,\n },\n },\n });\n addToast({ title: \"Invoice marked as sent\", variant: \"success\" });\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to update invoice status\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleMarkPaid = async (invoice: InvoiceFieldsFragment) => {\n setBusyId(invoice.id);\n try {\n await updateStatus({\n variables: {\n input: {\n id: invoice.id,\n workspaceId,\n status: TimeCampusInvoiceStatus.Paid,\n },\n },\n });\n addToast({ title: \"Invoice marked as paid\", variant: \"success\" });\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to update invoice status\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n try {\n await deleteDraft({\n variables: { id: deleteTarget.id, workspaceId },\n });\n addToast({ title: \"Draft invoice deleted\", variant: \"success\" });\n setDeleteTarget(null);\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to delete invoice\", variant: \"error\" });\n }\n };\n\n const openView = async (invoice: InvoiceFieldsFragment) => {\n setViewTarget(invoice);\n setViewHtml(null);\n setViewLoading(true);\n try {\n const { data } = await apollo.query({\n query: GET_INVOICE_HTML,\n variables: { id: invoice.id, workspaceId },\n fetchPolicy: \"no-cache\",\n });\n const html = (data as { timeCampusInvoiceHtml?: string } | null)\n ?.timeCampusInvoiceHtml;\n setViewHtml(html ?? null);\n } catch {\n addToast({ title: \"Failed to load invoice preview\", variant: \"error\" });\n } finally {\n setViewLoading(false);\n }\n };\n\n const handleDownloadPdf = async (invoice: InvoiceFieldsFragment) => {\n setDownloadingId(invoice.id);\n try {\n const { data } = await apollo.query({\n query: GET_INVOICE_PDF,\n variables: { id: invoice.id, workspaceId },\n fetchPolicy: \"no-cache\",\n });\n const base64 = (data as { timeCampusInvoicePdf?: string } | null)\n ?.timeCampusInvoicePdf;\n if (!base64) throw new Error(\"Empty PDF payload\");\n downloadBase64Pdf(base64, `${invoice.invoiceNumber}.pdf`);\n } catch {\n addToast({ title: \"Failed to download PDF\", variant: \"error\" });\n } finally {\n setDownloadingId(null);\n }\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n const res = await fetchMore({\n variables: {\n workspaceId,\n pagination: { limit: PAGE_SIZE, offset: invoices.length },\n },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n timeCampusInvoices: {\n ...fetchMoreResult.timeCampusInvoices,\n items: [\n ...prev.timeCampusInvoices.items,\n ...fetchMoreResult.timeCampusInvoices.items,\n ],\n },\n };\n },\n });\n if ((res.data?.timeCampusInvoices?.items?.length ?? 0) < PAGE_SIZE) {\n setCanLoadMore(false);\n }\n } finally {\n setLoadingMore(false);\n }\n };\n\n if (invoicesLoading && statsLoading && invoices.length === 0) {\n return (\n <PageLayout\n title=\"Invoicing\"\n description=\"Bill clients for billable time\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (invoicesError) {\n return (\n <PageLayout\n title=\"Invoicing\"\n description=\"Bill clients for billable time\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load invoices. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const generateButton = (\n <button\n onClick={openGenerate}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Generate Invoice\n </button>\n );\n\n return (\n <PageLayout\n title=\"Invoicing\"\n description=\"Bill clients for billable time\"\n purpose=\"Turn logged, billable hours into a real invoice — pick a client and a billing period, and TimeCampus aggregates every un-invoiced billable time entry by project and task, applying each entry's own rate, into a draft you can review, send, and mark paid.\"\n actions={generateButton}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Total Invoiced\"\n value={formatCurrency(stats?.totalInvoiced ?? 0)}\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Outstanding\"\n value={formatCurrency(stats?.totalOutstanding ?? 0)}\n description={`${stats?.sentCount ?? 0} sent, awaiting payment`}\n />\n <StatCard\n title=\"Paid\"\n value={formatCurrency(stats?.totalPaid ?? 0)}\n description={`${stats?.paidCount ?? 0} invoices paid`}\n />\n <StatCard\n title=\"Drafts\"\n value={String(stats?.draftCount ?? 0)}\n icon={<FileText className=\"h-5 w-5\" />}\n description=\"not yet sent\"\n />\n </div>\n\n {invoices.length === 0 && !invoicesLoading ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No invoices yet\"\n description=\"Generate a draft invoice from real billable time entries to bill a client.\"\n action={generateButton}\n />\n ) : (\n <>\n <div className=\"mt-4 rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {invoices.map((invoice: InvoiceFieldsFragment) => {\n const rowBusy = busyId === invoice.id;\n const isDraft =\n invoice.status === TimeCampusInvoiceStatus.Draft;\n const isSent = invoice.status === TimeCampusInvoiceStatus.Sent;\n return (\n <div\n key={invoice.id}\n className=\"flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <FileText className=\"h-5 w-5 shrink-0 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {invoice.invoiceNumber} &middot; {invoice.clientName}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatDate(invoice.periodStart)} &ndash;{\" \"}\n {formatDate(invoice.periodEnd)}\n {invoice.dueDate && (\n <> &middot; Due {formatDate(invoice.dueDate)}</>\n )}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatCurrency(invoice.total, invoice.currency)}\n </p>\n <StatusBadge\n label={invoice.status.toLowerCase()}\n status={invoice.status.toLowerCase()}\n />\n {invoice.isOverdue && (\n <span\n title=\"Past due date\"\n className=\"inline-flex items-center gap-1 text-xs font-medium text-status-error-text\"\n >\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n Overdue\n </span>\n )}\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => void openView(invoice)}\n title=\"View invoice\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken\"\n >\n <Eye className=\"h-4 w-4\" />\n </button>\n <button\n type=\"button\"\n disabled={downloadingId === invoice.id}\n onClick={() => void handleDownloadPdf(invoice)}\n title=\"Download PDF\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n {isDraft && (\n <button\n type=\"button\"\n disabled={rowBusy}\n onClick={() => handleMarkSent(invoice)}\n title=\"Mark as sent\"\n className=\"rounded-md p-1.5 text-status-info-text hover:bg-status-info-bg disabled:opacity-50\"\n >\n <Send className=\"h-4 w-4\" />\n </button>\n )}\n {isSent && (\n <button\n type=\"button\"\n disabled={rowBusy}\n onClick={() => handleMarkPaid(invoice)}\n title=\"Mark as paid\"\n className=\"rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50\"\n >\n <CheckCircle2 className=\"h-4 w-4\" />\n </button>\n )}\n {isDraft && (\n <button\n type=\"button\"\n onClick={() => setDeleteTarget(invoice)}\n title=\"Delete draft\"\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n {canLoadMore && invoices.length < total && (\n <div className=\"mt-4 flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Generate draft invoice */}\n <Modal\n open={formOpen}\n onClose={closeGenerate}\n title=\"Generate Invoice\"\n description=\"Aggregates every billable, un-invoiced time entry in the selected period into a draft invoice.\"\n footer={\n <>\n <button\n onClick={closeGenerate}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleGenerate}\n disabled={\n generating ||\n !form.clientName.trim() ||\n !form.periodStart ||\n !form.periodEnd\n }\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {generating ? \"Generating...\" : \"Generate Draft\"}\n </button>\n </>\n }\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Client name\n </label>\n <input\n type=\"text\"\n placeholder=\"Acme Corp\"\n value={form.clientName}\n onChange={(e) => setForm({ ...form, clientName: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Client email\n </label>\n <input\n type=\"email\"\n placeholder=\"billing@acme.com\"\n value={form.clientEmail}\n onChange={(e) =>\n setForm({ ...form, clientEmail: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Currency\n </label>\n <input\n type=\"text\"\n placeholder=\"USD\"\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Client address\n </label>\n <input\n type=\"text\"\n placeholder=\"Optional billing address\"\n value={form.clientAddress}\n onChange={(e) =>\n setForm({ ...form, clientAddress: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Billing period start\n </label>\n <input\n type=\"date\"\n value={form.periodStart}\n onChange={(e) =>\n setForm({ ...form, periodStart: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Billing period end\n </label>\n <input\n type=\"date\"\n value={form.periodEnd}\n onChange={(e) =>\n setForm({ ...form, periodEnd: e.target.value })\n }\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Tax rate (%)\n </label>\n <input\n type=\"number\"\n min={0}\n max={100}\n step=\"0.1\"\n value={form.taxRatePercent}\n onChange={(e) =>\n setForm({ ...form, taxRatePercent: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Due date\n </label>\n <input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Project IDs\n </label>\n <input\n type=\"text\"\n placeholder=\"Optional, comma-separated. Leave blank for all projects.\"\n value={form.projectIds}\n onChange={(e) => setForm({ ...form, projectIds: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Notes\n </label>\n <textarea\n rows={2}\n placeholder=\"Optional notes printed on the invoice\"\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </Modal>\n\n {/* View invoice */}\n <Modal\n open={viewTarget !== null}\n onClose={() => setViewTarget(null)}\n title={viewTarget ? `Invoice ${viewTarget.invoiceNumber}` : \"Invoice\"}\n description={viewTarget?.clientName}\n size=\"xl\"\n footer={\n viewTarget && (\n <>\n <button\n onClick={() => setViewTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Close\n </button>\n <button\n onClick={() => void handleDownloadPdf(viewTarget)}\n disabled={downloadingId === viewTarget.id}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n {downloadingId === viewTarget.id\n ? \"Downloading...\"\n : \"Download PDF\"}\n </button>\n </>\n )\n }\n >\n {viewLoading ? (\n <LoadingSkeleton variant=\"detail\" count={1} />\n ) : viewHtml ? (\n <iframe\n title=\"Invoice preview\"\n srcDoc={viewHtml}\n sandbox=\"\"\n className=\"h-[60vh] w-full rounded-md border border-border-subtle bg-white\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Failed to load invoice preview.\n </p>\n )}\n </Modal>\n\n {/* Delete confirm */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title=\"Delete draft invoice?\"\n description={\n deleteTarget\n ? `${deleteTarget.invoiceNumber} · ${formatCurrency(deleteTarget.total, deleteTarget.currency)} will be removed and its time entries released back to the un-invoiced pool.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {deleting ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n This permanently removes the draft. This action cannot be undone.\n </p>\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PersonalCorporatePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"names":[],"mappings":"AA8HA,wBAAgB,qBAAqB,gCA4epC"}
1
+ {"version":3,"file":"PersonalCorporatePage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/lifetime/PersonalCorporatePage.tsx"],"names":[],"mappings":"AA8HA,wBAAgB,qBAAqB,gCAijBpC"}
@@ -2,15 +2,15 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { cn as t } from "../../utils/helpers.js";
3
3
  import { StatCard as n } from "../../components/StatCard.js";
4
4
  import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
5
- import { useGrantSourceConsentMutation as i, useMyContextSharingPolicyQuery as ee, useMyPersonalDataSummaryQuery as te, useMySourceConsentsQuery as ne, useRevokeSourceConsentMutation as re, useUpdateContextSharingPolicyMutation as a } from "../../../generated/wspace-operations.js";
6
- import { PageLayout as o } from "../../components/PageLayout.js";
7
- import { StatusBadge as s } from "../../components/StatusBadge.js";
8
- import { EmptyState as c } from "../../components/EmptyState.js";
9
- import { ToastContainer as ie, useToast as ae } from "../../components/Toast.js";
5
+ import { useGrantSourceConsentMutation as i, useMyContextSharingPolicyQuery as a, useMyPersonalDataSummaryQuery as ee, useMySourceConsentsQuery as o, useRevokeSourceConsentMutation as s, useUpdateContextSharingPolicyMutation as c } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as l } from "../../components/PageLayout.js";
7
+ import { StatusBadge as u } from "../../components/StatusBadge.js";
8
+ import { EmptyState as d } from "../../components/EmptyState.js";
9
+ import { ToastContainer as te, useToast as ne } from "../../components/Toast.js";
10
10
  import "../../components/index.js";
11
- import { formatSecondsHuman as l, isoDaysAgo as u, isoNow as d, lifeDomainLabel as f } from "./domains.js";
12
- import { useMemo as p, useState as m } from "react";
13
- import { ArrowUpRight as oe, Building2 as se, Info as h, Lock as g, Plug as _, Share2 as v, ShieldCheck as ce } from "lucide-react";
11
+ import { formatSecondsHuman as re, isoDaysAgo as ie, isoNow as ae, lifeDomainLabel as oe } from "./domains.js";
12
+ import { useMemo as f, useState as p } from "react";
13
+ import { ArrowUpRight as m, Building2 as h, Info as g, Lock as _, Plug as v, Share2 as se, ShieldCheck as ce } from "lucide-react";
14
14
  import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
15
  import { GlassCard as x } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/timecampus/pages/lifetime/PersonalCorporatePage.tsx
@@ -48,21 +48,21 @@ function w({ checked: e, onChange: n, disabled: r = !1, ariaLabel: i }) {
48
48
  });
49
49
  }
50
50
  function T() {
51
- let { workspaceId: T = "" } = e(), { toasts: E, addToast: D, dismissToast: O } = ae(), k = p(() => ({
52
- from: u(30),
53
- to: d()
54
- }), []), { data: A, loading: j, error: M } = ee({ skip: !T }), { data: N, loading: P, error: F, refetch: I } = te({
51
+ let { workspaceId: T = "" } = e(), { toasts: le, addToast: E, dismissToast: ue } = ne(), D = f(() => ({
52
+ from: ie(30),
53
+ to: ae()
54
+ }), []), { data: O, loading: k, error: A } = a({ skip: !T }), { data: j, loading: M, error: N, refetch: P } = ee({
55
55
  variables: {
56
- from: k.from,
57
- to: k.to
56
+ from: D.from,
57
+ to: D.to
58
58
  },
59
59
  skip: !T
60
- }), { data: L, loading: R, error: z, refetch: B } = ne({ skip: !T }), [V] = a(), [H] = i(), [U] = re(), [W, G] = m(null), [le, K] = m(null), q = A?.myContextSharingPolicy, J = N?.myPersonalDataSummary, Y = L?.mySourceConsents ?? [], X = p(() => [...J?.byDomain ?? []].sort((e, t) => t.totalSeconds - e.totalSeconds), [J]), Z = X[0]?.totalSeconds ?? 0, Q = J?.sharedAggregates ?? [], $ = {
60
+ }), { data: F, loading: I, error: L, refetch: R } = o({ skip: !T }), [z] = c(), [B] = i(), [V] = s(), [H, U] = p(null), [de, W] = p(null), [G, K] = p(null), q = O?.myContextSharingPolicy, J = j?.myPersonalDataSummary, Y = F?.mySourceConsents ?? [], X = f(() => [...J?.byDomain ?? []].sort((e, t) => t.totalSeconds - e.totalSeconds), [J]), Z = X[0]?.totalSeconds ?? 0, Q = J?.sharedAggregates ?? [], $ = {
61
61
  title: "Personal & Corporate",
62
62
  description: "Two contexts, one boundary you control",
63
63
  purpose: "TimeCampus keeps two worlds apart: your private life record, which stays yours, and the workspace you work in. This is where you decide exactly which aggregate work signals — never your personal context — travel up to your employer."
64
64
  };
65
- if (!T || j && !A || P && !N) return /* @__PURE__ */ y(o, {
65
+ if (!T || k && !O || M && !j) return /* @__PURE__ */ y(l, {
66
66
  ...$,
67
67
  children: /* @__PURE__ */ b("div", {
68
68
  className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
@@ -82,65 +82,89 @@ function T() {
82
82
  ]
83
83
  })
84
84
  });
85
- if (M && F && !q && !J) return /* @__PURE__ */ y(o, {
85
+ if (A && N && !q && !J) return /* @__PURE__ */ y(l, {
86
86
  ...$,
87
87
  children: /* @__PURE__ */ y("div", {
88
88
  className: "rounded-xl border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
89
89
  children: "We couldn't load your privacy settings right now. Please try again in a moment."
90
90
  })
91
91
  });
92
- let ue = async (e, t) => {
92
+ let fe = async (e, t) => {
93
93
  if (!q) return;
94
94
  let n = S.find((t) => t.key === e)?.label ?? "signal";
95
- G(e);
95
+ U(e);
96
96
  try {
97
- let r = await V({ variables: { input: {
97
+ let r = await z({ variables: { input: {
98
98
  sustainableHours: q.sustainableHours,
99
99
  recoveryBand: q.recoveryBand,
100
100
  workLifeBoundary: q.workLifeBoundary,
101
101
  [e]: t
102
102
  } } });
103
103
  if (r.error) throw Error(r.error.message);
104
- await I(), D({
104
+ await P(), E({
105
105
  title: t ? `Now sharing "${n}"` : `Stopped sharing "${n}"`,
106
106
  description: "Your workspace only ever sees this as an aggregate signal.",
107
107
  variant: "success"
108
108
  });
109
109
  } catch {
110
- D({
110
+ E({
111
111
  title: "Couldn't update what you share",
112
112
  description: "Nothing was changed. Please try again.",
113
113
  variant: "error"
114
114
  });
115
115
  } finally {
116
- G(null);
116
+ U(null);
117
117
  }
118
- }, de = async (e, t) => {
118
+ }, pe = async (e, t) => {
119
119
  let n = C(e.source);
120
120
  K(e.source);
121
+ try {
122
+ let r = await B({ variables: {
123
+ source: e.source,
124
+ mode: e.mode,
125
+ onDeviceOnly: t
126
+ } });
127
+ if (r.error) throw Error(r.error.message);
128
+ await R(), E({
129
+ title: t ? `${n} now on-device only` : `${n} on-device-only turned off`,
130
+ description: t ? "Only aggregated app/category/timing data reaches your workspace — no titles, URLs, or other details." : void 0,
131
+ variant: "success"
132
+ });
133
+ } catch {
134
+ E({
135
+ title: "Couldn't update on-device-only",
136
+ description: "Nothing was changed. Please try again.",
137
+ variant: "error"
138
+ });
139
+ } finally {
140
+ K(null);
141
+ }
142
+ }, me = async (e, t) => {
143
+ let n = C(e.source);
144
+ W(e.source);
121
145
  try {
122
146
  if (t) {
123
- let t = await H({ variables: { source: e.source } });
147
+ let t = await B({ variables: { source: e.source } });
124
148
  if (t.error) throw Error(t.error.message);
125
149
  } else {
126
- let t = await U({ variables: { source: e.source } });
150
+ let t = await V({ variables: { source: e.source } });
127
151
  if (t.error) throw Error(t.error.message);
128
152
  }
129
- await B(), D({
153
+ await R(), E({
130
154
  title: t ? `${n} connected` : `${n} disconnected`,
131
155
  variant: "success"
132
156
  });
133
157
  } catch {
134
- D({
158
+ E({
135
159
  title: "Couldn't update this source",
136
160
  description: "Nothing was changed. Please try again.",
137
161
  variant: "error"
138
162
  });
139
163
  } finally {
140
- K(null);
164
+ W(null);
141
165
  }
142
166
  };
143
- return /* @__PURE__ */ b(o, {
167
+ return /* @__PURE__ */ b(l, {
144
168
  ...$,
145
169
  children: [
146
170
  /* @__PURE__ */ b("div", {
@@ -153,7 +177,7 @@ function T() {
153
177
  className: "mb-4 flex items-start gap-3",
154
178
  children: [/* @__PURE__ */ y("div", {
155
179
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
156
- children: /* @__PURE__ */ y(g, { className: "h-5 w-5" })
180
+ children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
157
181
  }), /* @__PURE__ */ b("div", {
158
182
  className: "min-w-0",
159
183
  children: [/* @__PURE__ */ b("h2", {
@@ -171,10 +195,10 @@ function T() {
171
195
  children: "Everything you capture about your life. Locked to your account — it never leaves this side."
172
196
  })]
173
197
  })]
174
- }), F && !J ? /* @__PURE__ */ y("div", {
198
+ }), N && !J ? /* @__PURE__ */ y("div", {
175
199
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
176
200
  children: "Couldn't load your private data summary right now."
177
- }) : !J || J.totalPersonalEvents === 0 ? /* @__PURE__ */ y(c, {
201
+ }) : !J || J.totalPersonalEvents === 0 ? /* @__PURE__ */ y(d, {
178
202
  illustration: "empty-data",
179
203
  title: "Nothing private captured yet",
180
204
  description: "Log your first life entry and it will live here — always private to you."
@@ -183,7 +207,7 @@ function T() {
183
207
  children: [/* @__PURE__ */ y(n, {
184
208
  title: "Private life entries",
185
209
  value: J.totalPersonalEvents.toLocaleString(),
186
- icon: /* @__PURE__ */ y(g, { className: "h-5 w-5" }),
210
+ icon: /* @__PURE__ */ y(_, { className: "h-5 w-5" }),
187
211
  description: "captured in the last 30 days"
188
212
  }), X.length === 0 ? /* @__PURE__ */ y("p", {
189
213
  className: "text-xs text-text-secondary",
@@ -200,11 +224,11 @@ function T() {
200
224
  className: "min-w-0",
201
225
  children: [/* @__PURE__ */ y("p", {
202
226
  className: "truncate text-sm font-medium text-text-primary",
203
- children: f(e.domain)
227
+ children: oe(e.domain)
204
228
  }), /* @__PURE__ */ b("p", {
205
229
  className: "mt-0.5 text-xs text-text-secondary",
206
230
  children: [
207
- l(e.totalSeconds),
231
+ re(e.totalSeconds),
208
232
  " ·",
209
233
  " ",
210
234
  e.eventCount.toLocaleString(),
@@ -212,7 +236,7 @@ function T() {
212
236
  e.eventCount === 1 ? "entry" : "entries"
213
237
  ]
214
238
  })]
215
- }), /* @__PURE__ */ y(g, {
239
+ }), /* @__PURE__ */ y(_, {
216
240
  className: "h-3.5 w-3.5 shrink-0 text-text-muted",
217
241
  "aria-label": "Private to you"
218
242
  })]
@@ -238,7 +262,7 @@ function T() {
238
262
  className: "mb-4 flex items-start gap-3",
239
263
  children: [/* @__PURE__ */ y("div", {
240
264
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-status-info-bg/40 text-status-info-text",
241
- children: /* @__PURE__ */ y(v, { className: "h-5 w-5" })
265
+ children: /* @__PURE__ */ y(se, { className: "h-5 w-5" })
242
266
  }), /* @__PURE__ */ b("div", {
243
267
  className: "min-w-0",
244
268
  children: [/* @__PURE__ */ y("h2", {
@@ -249,13 +273,13 @@ function T() {
249
273
  children: "Aggregate signals only. Every switch is off until you turn it on."
250
274
  })]
251
275
  })]
252
- }), M && !q ? /* @__PURE__ */ y("div", {
276
+ }), A && !q ? /* @__PURE__ */ y("div", {
253
277
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
254
278
  children: "Couldn't load your sharing settings right now."
255
279
  }) : /* @__PURE__ */ y("div", {
256
280
  className: "space-y-3",
257
281
  children: S.map((e) => {
258
- let n = q ? q[e.key] : !1, r = W === e.key;
282
+ let n = q ? q[e.key] : !1, r = H === e.key;
259
283
  return /* @__PURE__ */ y("div", {
260
284
  className: "rounded-lg border border-border-subtle bg-bg-sunken/40 p-3",
261
285
  children: /* @__PURE__ */ b("div", {
@@ -275,7 +299,7 @@ function T() {
275
299
  checked: n,
276
300
  disabled: r || !q,
277
301
  ariaLabel: `Share "${e.label}" with your workspace`,
278
- onChange: (t) => ue(e.key, t)
302
+ onChange: (t) => fe(e.key, t)
279
303
  }), /* @__PURE__ */ y("span", {
280
304
  className: t("text-[0.65rem] font-medium uppercase tracking-wide", n ? "text-status-success-text" : "text-text-muted"),
281
305
  children: r ? "Saving…" : n ? "On" : "Off"
@@ -307,7 +331,7 @@ function T() {
307
331
  className: "mb-4 flex items-start gap-3",
308
332
  children: [/* @__PURE__ */ y("div", {
309
333
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
310
- children: /* @__PURE__ */ y(se, { className: "h-5 w-5" })
334
+ children: /* @__PURE__ */ y(h, { className: "h-5 w-5" })
311
335
  }), /* @__PURE__ */ b("div", {
312
336
  className: "min-w-0",
313
337
  children: [/* @__PURE__ */ b("h2", {
@@ -330,7 +354,7 @@ function T() {
330
354
  children: [
331
355
  /* @__PURE__ */ b("div", {
332
356
  className: "flex items-start gap-2 rounded-lg border border-status-info-border/40 bg-status-info-bg/10 px-3 py-2.5 text-xs text-status-info-text",
333
- children: [/* @__PURE__ */ y(h, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ y("span", { children: "Your workspace only ever receives aggregate work signals — coarse yes/no bands, never entries, locations, health or timestamps." })]
357
+ children: [/* @__PURE__ */ y(g, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), /* @__PURE__ */ y("span", { children: "Your workspace only ever receives aggregate work signals — coarse yes/no bands, never entries, locations, health or timestamps." })]
334
358
  }),
335
359
  /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
336
360
  className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-muted",
@@ -350,11 +374,11 @@ function T() {
350
374
  className: "flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-bg-sunken/40 px-3 py-2.5",
351
375
  children: [/* @__PURE__ */ b("span", {
352
376
  className: "flex min-w-0 items-center gap-2 text-sm text-text-primary",
353
- children: [/* @__PURE__ */ y(oe, { className: "h-4 w-4 shrink-0 text-status-success-text" }), /* @__PURE__ */ y("span", {
377
+ children: [/* @__PURE__ */ y(m, { className: "h-4 w-4 shrink-0 text-status-success-text" }), /* @__PURE__ */ y("span", {
354
378
  className: "truncate",
355
379
  children: C(e)
356
380
  })]
357
- }), /* @__PURE__ */ y(s, {
381
+ }), /* @__PURE__ */ y(u, {
358
382
  variant: "success",
359
383
  label: "Shared",
360
384
  dot: !0
@@ -377,7 +401,7 @@ function T() {
377
401
  className: "mb-4 flex items-start gap-3",
378
402
  children: [/* @__PURE__ */ y("div", {
379
403
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
380
- children: /* @__PURE__ */ y(_, { className: "h-5 w-5" })
404
+ children: /* @__PURE__ */ y(v, { className: "h-5 w-5" })
381
405
  }), /* @__PURE__ */ b("div", {
382
406
  className: "min-w-0",
383
407
  children: [/* @__PURE__ */ y("h3", {
@@ -388,20 +412,20 @@ function T() {
388
412
  children: "Each capture source only feeds your private record once you consent — and you can withdraw at any moment."
389
413
  })]
390
414
  })]
391
- }), z && Y.length === 0 ? /* @__PURE__ */ y("div", {
415
+ }), L && Y.length === 0 ? /* @__PURE__ */ y("div", {
392
416
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-3 text-xs text-status-error-text",
393
417
  children: "Couldn't load your connected sources right now."
394
- }) : R && Y.length === 0 ? /* @__PURE__ */ y(r, {
418
+ }) : I && Y.length === 0 ? /* @__PURE__ */ y(r, {
395
419
  variant: "list",
396
420
  count: 3
397
- }) : Y.length === 0 ? /* @__PURE__ */ y(c, {
421
+ }) : Y.length === 0 ? /* @__PURE__ */ y(d, {
398
422
  illustration: "empty-data",
399
423
  title: "No sources connected yet",
400
424
  description: "When you connect a capture source — a calendar, location or health signal — you'll grant and revoke exactly what it may feed here."
401
425
  }) : /* @__PURE__ */ y("div", {
402
426
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
403
427
  children: Y.map((e) => {
404
- let n = le === e.source;
428
+ let n = de === e.source;
405
429
  return /* @__PURE__ */ y(x, {
406
430
  treatment: "metric",
407
431
  className: "p-4",
@@ -415,15 +439,20 @@ function T() {
415
439
  }), /* @__PURE__ */ b("div", {
416
440
  className: "mt-1.5 flex flex-wrap items-center gap-1.5",
417
441
  children: [
418
- /* @__PURE__ */ y(s, {
442
+ /* @__PURE__ */ y(u, {
419
443
  variant: "muted",
420
444
  label: C(e.mode)
421
445
  }),
422
- e.onDeviceOnly && /* @__PURE__ */ y(s, {
423
- variant: "info",
424
- label: "On-device only"
446
+ e.granted && /* @__PURE__ */ y("button", {
447
+ type: "button",
448
+ role: "switch",
449
+ "aria-checked": e.onDeviceOnly,
450
+ disabled: G === e.source,
451
+ onClick: () => pe(e, !e.onDeviceOnly),
452
+ className: t("rounded-full border px-2 py-0.5 text-[0.65rem] font-medium uppercase tracking-wide transition-colors", "focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg/50", e.onDeviceOnly ? "border-status-info-border/60 bg-status-info-bg/40 text-status-info-text" : "border-border-subtle bg-bg-sunken/40 text-text-muted hover:text-text-secondary", G === e.source && "cursor-not-allowed opacity-50"),
453
+ children: G === e.source ? "Saving…" : e.onDeviceOnly ? "On-device only" : "Turn on on-device only"
425
454
  }),
426
- e.retentionDays != null && /* @__PURE__ */ y(s, {
455
+ e.retentionDays != null && /* @__PURE__ */ y(u, {
427
456
  variant: "muted",
428
457
  label: `Kept ${e.retentionDays}d`
429
458
  })
@@ -435,7 +464,7 @@ function T() {
435
464
  checked: e.granted,
436
465
  disabled: n,
437
466
  ariaLabel: `Consent for ${C(e.source)}`,
438
- onChange: (t) => de(e, t)
467
+ onChange: (t) => me(e, t)
439
468
  }), /* @__PURE__ */ y("span", {
440
469
  className: t("text-[0.65rem] font-medium uppercase tracking-wide", e.granted ? "text-status-success-text" : "text-text-muted"),
441
470
  children: n ? "Saving…" : e.granted ? "Granted" : "Revoked"
@@ -446,9 +475,9 @@ function T() {
446
475
  })
447
476
  })]
448
477
  }),
449
- /* @__PURE__ */ y(ie, {
450
- toasts: E,
451
- onDismiss: O
478
+ /* @__PURE__ */ y(te, {
479
+ toasts: le,
480
+ onDismiss: ue
452
481
  })
453
482
  ]
454
483
  });