@protobase/ui 0.0.0-stage → 0.1.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.
- package/LICENSE +202 -0
- package/dist/activity-chart/activity-chart.js +125 -0
- package/dist/activity-chart/activity-math.js +11 -0
- package/dist/activity-chart/activity-tooltip.js +24 -0
- package/dist/app/action-icon.js +10 -0
- package/dist/app/app.d.ts +24 -0
- package/dist/app/app.js +123 -0
- package/dist/app/batch-failure.js +21 -0
- package/dist/app/breadcrumb-for.js +27 -0
- package/dist/app/create/create-page.js +118 -0
- package/dist/app/create/create-record-form.js +68 -0
- package/dist/app/create/create-values.js +52 -0
- package/dist/app/delete/dialogs.js +109 -0
- package/dist/app/delete/record-diff.js +17 -0
- package/dist/app/delete/use-delete-flow.js +193 -0
- package/dist/app/delete/use-restore.js +9 -0
- package/dist/app/draft-storage.js +18 -0
- package/dist/app/error-banner.js +28 -0
- package/dist/app/field-editor.js +88 -0
- package/dist/app/icons.js +27 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.html +12 -0
- package/dist/app/list-chart.js +36 -0
- package/dist/app/list-order.js +15 -0
- package/dist/app/list-page.js +213 -0
- package/dist/app/list-search.js +30 -0
- package/dist/app/main.d.ts +1 -0
- package/dist/app/main.js +14 -0
- package/dist/app/menu-icons.js +21 -0
- package/dist/app/meta-gate.js +46 -0
- package/dist/app/nav-from-meta.js +65 -0
- package/dist/app/nav-recent.js +33 -0
- package/dist/app/pages/action-href.js +7 -0
- package/dist/app/pages/blocks/action.js +38 -0
- package/dist/app/pages/blocks/block-props.js +5 -0
- package/dist/app/pages/blocks/card-frame.js +34 -0
- package/dist/app/pages/blocks/card-row.js +55 -0
- package/dist/app/pages/blocks/card.js +19 -0
- package/dist/app/pages/blocks/field.js +45 -0
- package/dist/app/pages/blocks/grid.js +34 -0
- package/dist/app/pages/blocks/link.js +27 -0
- package/dist/app/pages/blocks/modal-form.js +149 -0
- package/dist/app/pages/blocks/page-block.js +20 -0
- package/dist/app/pages/blocks/progress.js +54 -0
- package/dist/app/pages/blocks/record-card.js +56 -0
- package/dist/app/pages/blocks/show.js +43 -0
- package/dist/app/pages/blocks/stat.js +78 -0
- package/dist/app/pages/blocks/table.js +65 -0
- package/dist/app/pages/composed-page.js +19 -0
- package/dist/app/pages/confirm-action.js +20 -0
- package/dist/app/pages/field-value.js +58 -0
- package/dist/app/pages/project-ui.d.ts +30 -0
- package/dist/app/pages/project-ui.js +13 -0
- package/dist/app/pages/record-actions.js +40 -0
- package/dist/app/pages/record-scope.d.ts +16 -0
- package/dist/app/pages/record-scope.js +12 -0
- package/dist/app/pages/record-table.js +110 -0
- package/dist/app/pages/render-node.js +84 -0
- package/dist/app/pages/use-layout-data.js +73 -0
- package/dist/app/pages/use-path-value.js +65 -0
- package/dist/app/pages/use-relation-labels.js +30 -0
- package/dist/app/pages/use-run-action.js +88 -0
- package/dist/app/permissions.js +15 -0
- package/dist/app/record-lines.js +39 -0
- package/dist/app/record-page.js +318 -0
- package/dist/app/related-section.js +126 -0
- package/dist/app/relation-picker.js +69 -0
- package/dist/app/router.js +155 -0
- package/dist/app/search/search-model.js +70 -0
- package/dist/app/search/use-global-search.js +68 -0
- package/dist/app/sensitive-field.js +65 -0
- package/dist/app/use-lines-draft.js +45 -0
- package/dist/app/use-lines.js +19 -0
- package/dist/app/use-live-save.js +54 -0
- package/dist/app/use-nav-recent.js +41 -0
- package/dist/app/use-record-editor.js +154 -0
- package/dist/app/use-related-records.js +31 -0
- package/dist/app/user-menu-from-meta.js +36 -0
- package/dist/app-shell/app-shell.d.ts +4 -0
- package/dist/app-shell/app-shell.js +57 -0
- package/dist/app-shell/breadcrumb.js +37 -0
- package/dist/app-shell/drawer.js +30 -0
- package/dist/app-shell/global-search.d.ts +1 -0
- package/dist/app-shell/global-search.js +145 -0
- package/dist/app-shell/index.d.ts +9 -0
- package/dist/app-shell/logo.js +46 -0
- package/dist/app-shell/nav-open-storage.js +10 -0
- package/dist/app-shell/nav.d.ts +2 -0
- package/dist/app-shell/nav.js +85 -0
- package/dist/app-shell/page-header.d.ts +1 -0
- package/dist/app-shell/page-header.js +17 -0
- package/dist/app-shell/profile-menu.d.ts +2 -0
- package/dist/app-shell/profile-menu.js +115 -0
- package/dist/app-shell/sidebar-group.js +94 -0
- package/dist/app-shell/sidebar-item.js +46 -0
- package/dist/app-shell/sidebar-mode.d.ts +4 -0
- package/dist/app-shell/sidebar-mode.js +13 -0
- package/dist/app-shell/sidebar.d.ts +2 -0
- package/dist/app-shell/sidebar.js +64 -0
- package/dist/app-shell/status-dot.d.ts +1 -0
- package/dist/app-shell/status-dot.js +20 -0
- package/dist/app-shell/top-bar.js +82 -0
- package/dist/assistant-dock/assistant-dock.d.ts +15 -0
- package/dist/assistant-dock/assistant-dock.js +81 -0
- package/dist/assistant-dock/chat-message.js +9 -0
- package/dist/assistant-dock/composer.js +39 -0
- package/dist/assistant-dock/dock-item.d.ts +4 -0
- package/dist/assistant-dock/dock-item.js +37 -0
- package/dist/assistant-dock/model.d.ts +84 -0
- package/dist/assistant-dock/model.js +26 -0
- package/dist/assistant-dock/plan-card.d.ts +13 -0
- package/dist/assistant-dock/plan-card.js +133 -0
- package/dist/assistant-dock/progress-card.js +18 -0
- package/dist/assistant-dock/query-card.js +52 -0
- package/dist/assistant-dock/queue-card.js +31 -0
- package/dist/assistant-dock/result-card.js +77 -0
- package/dist/assistant-panel/content.d.ts +7 -0
- package/dist/assistant-panel/step-list.js +30 -0
- package/dist/auth/auth-gate.js +70 -0
- package/dist/auth/auth-layout.js +29 -0
- package/dist/auth/auth-provider.js +71 -0
- package/dist/auth/first-run-page.js +51 -0
- package/dist/auth/forgot-password-form.js +75 -0
- package/dist/auth/forgot-password-page.js +35 -0
- package/dist/auth/reset-link.js +29 -0
- package/dist/auth/reset-password-form.js +88 -0
- package/dist/auth/reset-password-page.js +43 -0
- package/dist/auth/sign-in-form.js +90 -0
- package/dist/auth/sign-in-page.js +39 -0
- package/dist/auth/user-to-shell.js +18 -0
- package/dist/data/api-provider.d.ts +54 -0
- package/dist/data/api-provider.js +53 -0
- package/dist/data/meta-data.d.ts +14 -0
- package/dist/data/meta-data.js +9 -0
- package/dist/data/query-keys.js +95 -0
- package/dist/data/use-aggregates.d.ts +8 -0
- package/dist/data/use-aggregates.js +43 -0
- package/dist/data/use-list.d.ts +231 -0
- package/dist/data/use-list.js +31 -0
- package/dist/data/use-meta.d.ts +8 -0
- package/dist/data/use-meta.js +35 -0
- package/dist/data/use-record.d.ts +6 -0
- package/dist/data/use-record.js +16 -0
- package/dist/data/use-update-record.d.ts +15 -0
- package/dist/data/use-update-record.js +26 -0
- package/dist/data-grid/cells.js +85 -0
- package/dist/data-grid/column-spec.js +4 -0
- package/dist/data-grid/columns.js +16 -0
- package/dist/data-grid/data-grid.js +238 -0
- package/dist/data-grid/geometry.js +32 -0
- package/dist/data-grid/grid-footer.js +33 -0
- package/dist/data-grid/grid-header.js +69 -0
- package/dist/data-grid/grid-row.js +83 -0
- package/dist/data-grid/grid-template.js +12 -0
- package/dist/data-grid/header-menu.js +52 -0
- package/dist/data-grid/page-cache.js +27 -0
- package/dist/data-grid/pinned-storage.js +13 -0
- package/dist/data-grid/row-menu.js +88 -0
- package/dist/data-grid/use-grid-scroll.js +80 -0
- package/dist/data-grid/use-horizontal-scroll.js +38 -0
- package/dist/data-grid/use-paged-rows.js +50 -0
- package/dist/data-grid/use-pinned-columns.js +16 -0
- package/dist/filter-panel/active-filter-chips.js +34 -0
- package/dist/filter-panel/date-presets.js +20 -0
- package/dist/filter-panel/facet-group.js +48 -0
- package/dist/filter-panel/filter-config.js +18 -0
- package/dist/filter-panel/filter-menu.js +41 -0
- package/dist/filter-panel/filter-panel.js +112 -0
- package/dist/filter-panel/filter-section.js +11 -0
- package/dist/filter-panel/filter-state.js +63 -0
- package/dist/filter-panel/range-filter.js +54 -0
- package/dist/filter-panel/range-math.js +11 -0
- package/dist/filter-panel/range-slider.css +32 -0
- package/dist/filter-panel/toggle-row.js +13 -0
- package/dist/filter-panel/use-filter-state.js +15 -0
- package/dist/format/date.js +37 -0
- package/dist/format/money.js +15 -0
- package/dist/format/number.js +12 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +11 -0
- package/dist/inline-child-table/columns.js +4 -0
- package/dist/inline-child-table/inline-child-table.js +120 -0
- package/dist/inline-child-table/line-menu.js +54 -0
- package/dist/inline-child-table/line-row.js +91 -0
- package/dist/inline-child-table/number-cell.js +25 -0
- package/dist/inline-child-table/reorder.js +10 -0
- package/dist/inline-child-table/totals-footer.js +42 -0
- package/dist/lib/cn.js +4 -0
- package/dist/lib/guarded.js +12 -0
- package/dist/lib/use-debounced.js +13 -0
- package/dist/lib/use-focus-trap.js +46 -0
- package/dist/lib/use-media-query.js +12 -0
- package/dist/live/columns-from-model.js +93 -0
- package/dist/live/display-value.js +24 -0
- package/dist/live/field-values.js +32 -0
- package/dist/live/filter-string.js +135 -0
- package/dist/live/grid-loader.js +63 -0
- package/dist/live/model-helpers.js +30 -0
- package/dist/live/naming.js +9 -0
- package/dist/live/relation-labels.js +53 -0
- package/dist/live/use-filter-config.js +88 -0
- package/dist/live/use-live-rows.js +45 -0
- package/dist/live/view-fields.js +35 -0
- package/dist/mocks/activity.js +24 -0
- package/dist/mocks/invoices.js +13 -0
- package/dist/mocks/products.js +167 -0
- package/dist/mocks/rng.js +20 -0
- package/dist/mocks/users.js +63 -0
- package/dist/primitives/avatar.js +22 -0
- package/dist/primitives/badge.d.ts +1 -0
- package/dist/primitives/badge.js +25 -0
- package/dist/primitives/button.js +23 -0
- package/dist/primitives/checkbox.js +21 -0
- package/dist/primitives/dialog.js +41 -0
- package/dist/primitives/input.js +16 -0
- package/dist/primitives/kbd.js +8 -0
- package/dist/primitives/segmented.js +14 -0
- package/dist/primitives/sheet.js +44 -0
- package/dist/primitives/skeleton.js +6 -0
- package/dist/primitives/spinner.js +10 -0
- package/dist/primitives/switch.js +14 -0
- package/dist/primitives/tooltip.js +13 -0
- package/dist/record-view/action-rail.js +28 -0
- package/dist/record-view/field.js +62 -0
- package/dist/record-view/record-header.js +50 -0
- package/dist/record-view/record-sidebar.js +58 -0
- package/dist/record-view/section.js +21 -0
- package/dist/styles.css +132 -0
- package/dist/toasts/toast-provider.js +69 -0
- package/dist/toasts/toast.js +45 -0
- package/package.json +45 -3
- package/README.md +0 -3
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Badge } from "../primitives/badge.js";
|
|
2
|
+
import { Button } from "../primitives/button.js";
|
|
3
|
+
import { approval, approveLabel, formatCredits, waitingText } from "./model.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { AlertTriangle, GitBranch, ListChecks } from "lucide-react";
|
|
6
|
+
//#region src/assistant-dock/plan-card.tsx
|
|
7
|
+
const statusNote = {
|
|
8
|
+
awaiting: void 0,
|
|
9
|
+
approved: "Approved. The credits are held until the change is live.",
|
|
10
|
+
canceled: "Canceled. The 1-credit deposit was kept.",
|
|
11
|
+
superseded: "Replaced by a newer plan.",
|
|
12
|
+
dropped: "The repository changed, so this plan was dropped and the deposit refunded."
|
|
13
|
+
};
|
|
14
|
+
const PlanCard = ({ plan, balance, onApprove, onRequestChanges, onCancel, onUpdatePlan }) => {
|
|
15
|
+
const approve = approval(plan, balance);
|
|
16
|
+
const awaiting = plan.status === "awaiting";
|
|
17
|
+
const waiting = awaiting ? waitingText(plan.waiting) : void 0;
|
|
18
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
19
|
+
"aria-label": `Plan, revision ${plan.revision}`,
|
|
20
|
+
className: "shrink-0 overflow-hidden rounded-lg border border-primary-border bg-background shadow-sm",
|
|
21
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
22
|
+
className: "flex items-center gap-2 bg-primary-soft px-3 py-2",
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx(ListChecks, { className: "size-3.5 text-primary-text" }),
|
|
25
|
+
/* @__PURE__ */ jsxs("h3", {
|
|
26
|
+
className: "flex-1 text-[13px] font-semibold text-primary-text",
|
|
27
|
+
children: ["Plan", plan.revision > 1 && `, revision ${plan.revision}`]
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ jsxs(Badge, {
|
|
30
|
+
tone: "blue",
|
|
31
|
+
dot: false,
|
|
32
|
+
children: ["Size ", plan.size]
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ jsx("span", {
|
|
35
|
+
className: "text-xs font-medium text-primary-text",
|
|
36
|
+
children: formatCredits(plan.credits)
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: "space-y-2.5 px-3 py-2.5",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("p", {
|
|
43
|
+
className: "text-[13px]",
|
|
44
|
+
children: plan.summary
|
|
45
|
+
}),
|
|
46
|
+
plan.steps.length > 0 && /* @__PURE__ */ jsx("ol", {
|
|
47
|
+
className: "list-decimal space-y-1 pl-5 text-xs text-muted-foreground",
|
|
48
|
+
children: plan.steps.map((step) => /* @__PURE__ */ jsx("li", { children: step.title }, step.title))
|
|
49
|
+
}),
|
|
50
|
+
plan.migrations.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
51
|
+
className: "space-y-1",
|
|
52
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
53
|
+
className: "text-xs font-semibold",
|
|
54
|
+
children: "Database changes"
|
|
55
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
56
|
+
className: "space-y-1",
|
|
57
|
+
children: plan.migrations.map((migration) => /* @__PURE__ */ jsxs("li", {
|
|
58
|
+
className: "flex items-start gap-2 text-xs",
|
|
59
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
60
|
+
className: "min-w-0 flex-1",
|
|
61
|
+
children: migration.description
|
|
62
|
+
}), migration.destructive && /* @__PURE__ */ jsx(Badge, {
|
|
63
|
+
tone: "red",
|
|
64
|
+
children: "Destructive"
|
|
65
|
+
})]
|
|
66
|
+
}, migration.description))
|
|
67
|
+
})]
|
|
68
|
+
}),
|
|
69
|
+
plan.risks.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
70
|
+
className: "space-y-1",
|
|
71
|
+
children: plan.risks.map((risk) => /* @__PURE__ */ jsxs("li", {
|
|
72
|
+
className: "flex items-start gap-2 text-xs text-warning-text",
|
|
73
|
+
children: [/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 size-3 shrink-0" }), risk]
|
|
74
|
+
}, risk))
|
|
75
|
+
}),
|
|
76
|
+
awaiting && plan.outdated && /* @__PURE__ */ jsxs("p", {
|
|
77
|
+
role: "status",
|
|
78
|
+
className: "flex items-start gap-2 rounded-md bg-warning-soft px-2.5 py-2 text-xs text-warning-text",
|
|
79
|
+
children: [/* @__PURE__ */ jsx(GitBranch, { className: "mt-0.5 size-3 shrink-0" }), "The branch moved on GitHub since this plan was made. Approving runs it on the new code."]
|
|
80
|
+
}),
|
|
81
|
+
waiting && /* @__PURE__ */ jsxs("p", {
|
|
82
|
+
className: "text-xs text-muted-foreground",
|
|
83
|
+
children: [waiting, " Cancel frees the queue."]
|
|
84
|
+
}),
|
|
85
|
+
statusNote[plan.status] && /* @__PURE__ */ jsx("p", {
|
|
86
|
+
className: "text-xs text-muted-foreground",
|
|
87
|
+
children: statusNote[plan.status]
|
|
88
|
+
}),
|
|
89
|
+
awaiting && /* @__PURE__ */ jsxs("div", {
|
|
90
|
+
className: "space-y-2 pt-0.5",
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx(Button, {
|
|
93
|
+
variant: "primary",
|
|
94
|
+
className: "w-full",
|
|
95
|
+
disabled: !approve.enabled,
|
|
96
|
+
onClick: () => onApprove?.(plan.taskId),
|
|
97
|
+
children: approveLabel(plan)
|
|
98
|
+
}),
|
|
99
|
+
approve.reason && /* @__PURE__ */ jsx("p", {
|
|
100
|
+
className: "text-xs text-danger-text",
|
|
101
|
+
children: approve.reason
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsxs("div", {
|
|
104
|
+
className: "flex gap-2",
|
|
105
|
+
children: [
|
|
106
|
+
plan.outdated && /* @__PURE__ */ jsx(Button, {
|
|
107
|
+
size: "sm",
|
|
108
|
+
className: "flex-1",
|
|
109
|
+
onClick: () => onUpdatePlan?.(plan.taskId),
|
|
110
|
+
children: "Update plan"
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ jsx(Button, {
|
|
113
|
+
size: "sm",
|
|
114
|
+
className: "flex-1",
|
|
115
|
+
onClick: () => onRequestChanges?.(plan.taskId),
|
|
116
|
+
children: "Ask for changes"
|
|
117
|
+
}),
|
|
118
|
+
/* @__PURE__ */ jsx(Button, {
|
|
119
|
+
size: "sm",
|
|
120
|
+
variant: "ghost",
|
|
121
|
+
onClick: () => onCancel?.(plan.taskId),
|
|
122
|
+
children: "Cancel"
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
})
|
|
128
|
+
]
|
|
129
|
+
})]
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
//#endregion
|
|
133
|
+
export { PlanCard };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StepList } from "../assistant-panel/step-list.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/assistant-dock/progress-card.tsx
|
|
4
|
+
const titles = {
|
|
5
|
+
planning: "Planning",
|
|
6
|
+
running: "Making the change"
|
|
7
|
+
};
|
|
8
|
+
/** What the planning run or the job is doing, step by step. */
|
|
9
|
+
const ProgressCard = ({ phase, steps }) => /* @__PURE__ */ jsxs("section", {
|
|
10
|
+
"aria-label": titles[phase],
|
|
11
|
+
className: "shrink-0 space-y-2 rounded-lg border border-border-strong bg-background px-3 py-2.5 shadow-sm",
|
|
12
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
13
|
+
className: "text-xs font-semibold",
|
|
14
|
+
children: titles[phase]
|
|
15
|
+
}), /* @__PURE__ */ jsx(StepList, { steps })]
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ProgressCard };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { formatCell } from "./model.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Database } from "lucide-react";
|
|
4
|
+
//#region src/assistant-dock/query-card.tsx
|
|
5
|
+
/** A read-only query the chat ran, with its rows as a table. */
|
|
6
|
+
const QueryCard = ({ query }) => /* @__PURE__ */ jsxs("section", {
|
|
7
|
+
"aria-label": "Query result",
|
|
8
|
+
className: "shrink-0 overflow-hidden rounded-lg border border-border-strong bg-background shadow-sm",
|
|
9
|
+
children: [
|
|
10
|
+
/* @__PURE__ */ jsxs("header", {
|
|
11
|
+
className: "flex items-start gap-2 bg-surface px-3 py-2",
|
|
12
|
+
children: [/* @__PURE__ */ jsx(Database, { className: "mt-0.5 size-3.5 shrink-0 text-muted-foreground" }), /* @__PURE__ */ jsx("code", {
|
|
13
|
+
className: "min-w-0 flex-1 whitespace-pre-wrap break-words font-mono text-[11px] text-foreground",
|
|
14
|
+
children: query.sql
|
|
15
|
+
})]
|
|
16
|
+
}),
|
|
17
|
+
query.rows.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
18
|
+
className: "px-3 py-2 text-xs text-muted-foreground",
|
|
19
|
+
children: "No rows."
|
|
20
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
21
|
+
className: "max-h-64 overflow-auto",
|
|
22
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
23
|
+
className: "w-full border-collapse text-left text-[11px]",
|
|
24
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
25
|
+
className: "sticky top-0 bg-background",
|
|
26
|
+
children: /* @__PURE__ */ jsx("tr", { children: query.columns.map((column) => /* @__PURE__ */ jsx("th", {
|
|
27
|
+
scope: "col",
|
|
28
|
+
className: "border-b border-border px-2 py-1 font-semibold whitespace-nowrap",
|
|
29
|
+
children: column
|
|
30
|
+
}, column)) })
|
|
31
|
+
}), /* @__PURE__ */ jsx("tbody", { children: query.rows.map((row, index) => /* @__PURE__ */ jsx("tr", {
|
|
32
|
+
className: "border-b border-border last:border-b-0",
|
|
33
|
+
children: row.map((value, column) => /* @__PURE__ */ jsx("td", {
|
|
34
|
+
className: value === null ? "px-2 py-1 whitespace-nowrap text-faint-foreground" : "px-2 py-1 whitespace-nowrap",
|
|
35
|
+
children: formatCell(value)
|
|
36
|
+
}, column))
|
|
37
|
+
}, index)) })]
|
|
38
|
+
})
|
|
39
|
+
}),
|
|
40
|
+
/* @__PURE__ */ jsxs("footer", {
|
|
41
|
+
className: "border-t border-border px-3 py-1.5 text-[11px] text-muted-foreground",
|
|
42
|
+
children: [
|
|
43
|
+
query.rows.length,
|
|
44
|
+
" ",
|
|
45
|
+
query.rows.length === 1 ? "row" : "rows",
|
|
46
|
+
query.truncated && ", cut off at the limit"
|
|
47
|
+
]
|
|
48
|
+
})
|
|
49
|
+
]
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { QueryCard };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Button } from "../primitives/button.js";
|
|
2
|
+
import { queueText } from "./model.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Clock } from "lucide-react";
|
|
5
|
+
//#region src/assistant-dock/queue-card.tsx
|
|
6
|
+
/** A task waiting in the app's queue, with its deposit billed. */
|
|
7
|
+
const QueueCard = ({ taskId, request, ahead, waitingOnPlan, onCancel }) => /* @__PURE__ */ jsxs("section", {
|
|
8
|
+
"aria-label": "Queued change",
|
|
9
|
+
className: "flex shrink-0 items-start gap-2.5 rounded-lg border border-border-strong bg-background px-3 py-2.5 shadow-sm",
|
|
10
|
+
children: [
|
|
11
|
+
/* @__PURE__ */ jsx(Clock, { className: "mt-0.5 size-3.5 shrink-0 text-muted-foreground" }),
|
|
12
|
+
/* @__PURE__ */ jsxs("div", {
|
|
13
|
+
className: "min-w-0 flex-1 space-y-0.5",
|
|
14
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
15
|
+
className: "truncate text-[13px] font-medium",
|
|
16
|
+
children: request
|
|
17
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
18
|
+
className: "text-xs text-muted-foreground",
|
|
19
|
+
children: queueText(ahead, waitingOnPlan)
|
|
20
|
+
})]
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsx(Button, {
|
|
23
|
+
variant: "ghost",
|
|
24
|
+
size: "sm",
|
|
25
|
+
onClick: () => onCancel?.(taskId),
|
|
26
|
+
children: "Cancel"
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
//#endregion
|
|
31
|
+
export { QueueCard };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { formatCredits } from "./model.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { CircleCheck, CircleX, GitCommitHorizontal } from "lucide-react";
|
|
5
|
+
//#region src/assistant-dock/result-card.tsx
|
|
6
|
+
const headings = {
|
|
7
|
+
live: "Live now",
|
|
8
|
+
failed: "The change failed",
|
|
9
|
+
capped: "The change hit its limit"
|
|
10
|
+
};
|
|
11
|
+
/** How a job ended: live with its commit, or failed or capped at no cost, with the database changes that stay live. */
|
|
12
|
+
const ResultCard = ({ result }) => {
|
|
13
|
+
const live = result.outcome === "live";
|
|
14
|
+
const Icon = live ? CircleCheck : CircleX;
|
|
15
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
16
|
+
"aria-label": headings[result.outcome],
|
|
17
|
+
className: cn("shrink-0 overflow-hidden rounded-lg border bg-background shadow-sm", live ? "border-success/40" : "border-danger/40"),
|
|
18
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
19
|
+
className: cn("flex items-center gap-2 px-3 py-2", live ? "bg-success-soft text-success-text" : "bg-danger-soft text-danger-text"),
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-3.5" }),
|
|
22
|
+
/* @__PURE__ */ jsx("h3", {
|
|
23
|
+
className: "flex-1 text-[13px] font-semibold",
|
|
24
|
+
children: headings[result.outcome]
|
|
25
|
+
}),
|
|
26
|
+
/* @__PURE__ */ jsx("span", {
|
|
27
|
+
className: "text-xs font-medium",
|
|
28
|
+
children: live ? formatCredits(result.credits) : "No charge"
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: "space-y-2.5 px-3 py-2.5",
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsx("p", {
|
|
35
|
+
className: "text-[13px]",
|
|
36
|
+
children: result.summary
|
|
37
|
+
}),
|
|
38
|
+
result.commit && /* @__PURE__ */ jsxs("div", {
|
|
39
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
40
|
+
children: [/* @__PURE__ */ jsx(GitCommitHorizontal, { className: "size-3.5" }), result.commit.url ? /* @__PURE__ */ jsx("a", {
|
|
41
|
+
className: "font-mono text-primary-text hover:underline",
|
|
42
|
+
href: result.commit.url,
|
|
43
|
+
target: "_blank",
|
|
44
|
+
rel: "noreferrer",
|
|
45
|
+
children: result.commit.sha.slice(0, 7)
|
|
46
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
47
|
+
className: "font-mono",
|
|
48
|
+
children: result.commit.sha.slice(0, 7)
|
|
49
|
+
})]
|
|
50
|
+
}),
|
|
51
|
+
result.keptOnBranch && /* @__PURE__ */ jsxs("p", {
|
|
52
|
+
className: "text-xs text-muted-foreground",
|
|
53
|
+
children: [
|
|
54
|
+
"GitHub did not accept the push. The code is kept on the branch ",
|
|
55
|
+
/* @__PURE__ */ jsx("code", {
|
|
56
|
+
className: "font-mono",
|
|
57
|
+
children: result.keptOnBranch
|
|
58
|
+
}),
|
|
59
|
+
"."
|
|
60
|
+
]
|
|
61
|
+
}),
|
|
62
|
+
result.dbChanges.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "space-y-1",
|
|
64
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
65
|
+
className: "text-xs font-semibold",
|
|
66
|
+
children: live ? "Database changes" : "Database changes that stay live"
|
|
67
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
68
|
+
className: "space-y-0.5 font-mono text-[11px] text-muted-foreground",
|
|
69
|
+
children: result.dbChanges.map((change) => /* @__PURE__ */ jsx("li", { children: change }, change))
|
|
70
|
+
})]
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
})]
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { ResultCard };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Spinner } from "../primitives/spinner.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Check, X } from "lucide-react";
|
|
5
|
+
//#region src/assistant-panel/step-list.tsx
|
|
6
|
+
const StepList = ({ steps }) => /* @__PURE__ */ jsx("ul", {
|
|
7
|
+
className: "shrink-0 space-y-2",
|
|
8
|
+
children: steps.map((step) => /* @__PURE__ */ jsxs("li", {
|
|
9
|
+
className: "flex items-start gap-2.5 text-[13px]",
|
|
10
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
11
|
+
className: "mt-0.5 flex size-4 shrink-0 items-center justify-center",
|
|
12
|
+
children: [
|
|
13
|
+
step.state === "running" && /* @__PURE__ */ jsx(Spinner, { className: "size-3.5 text-primary" }),
|
|
14
|
+
step.state === "done" && /* @__PURE__ */ jsx(Check, {
|
|
15
|
+
className: "size-3.5 text-success",
|
|
16
|
+
strokeWidth: 2.5
|
|
17
|
+
}),
|
|
18
|
+
step.state === "failed" && /* @__PURE__ */ jsx(X, {
|
|
19
|
+
className: "size-3.5 text-danger",
|
|
20
|
+
strokeWidth: 2.5
|
|
21
|
+
})
|
|
22
|
+
]
|
|
23
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
24
|
+
className: cn(step.state === "running" ? "text-foreground" : "text-muted-foreground", step.state === "failed" && "text-danger-text"),
|
|
25
|
+
children: step.label
|
|
26
|
+
})]
|
|
27
|
+
}, step.label))
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { StepList };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Spinner } from "../primitives/spinner.js";
|
|
2
|
+
import { useAuth } from "./auth-provider.js";
|
|
3
|
+
import { Button } from "../primitives/button.js";
|
|
4
|
+
import { FirstRunPage } from "./first-run-page.js";
|
|
5
|
+
import { readResetLink, withoutResetLink } from "./reset-link.js";
|
|
6
|
+
import { ForgotPasswordPage } from "./forgot-password-page.js";
|
|
7
|
+
import { ResetPasswordPage } from "./reset-password-page.js";
|
|
8
|
+
import { SignInPage } from "./sign-in-page.js";
|
|
9
|
+
import { useEffect, useState } from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/auth/auth-gate.tsx
|
|
12
|
+
const firstPage = () => {
|
|
13
|
+
const link = readResetLink(window.location.href);
|
|
14
|
+
return link ? {
|
|
15
|
+
kind: "reset-password",
|
|
16
|
+
token: link.token
|
|
17
|
+
} : { kind: "sign-in" };
|
|
18
|
+
};
|
|
19
|
+
const forgetResetLink = () => window.history.replaceState(window.history.state, "", withoutResetLink(window.location.href));
|
|
20
|
+
/** Shows the app when the user is signed in (or the server has no login), and the right page otherwise. */
|
|
21
|
+
const AuthGate = ({ workspace, children }) => {
|
|
22
|
+
const { state, recheck, passwordReset, signOut } = useAuth();
|
|
23
|
+
const [page, setPage] = useState(firstPage);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (state.kind === "signed-in") setPage((current) => current.kind === "sign-in" && current.notice ? { kind: "sign-in" } : current);
|
|
26
|
+
}, [state.kind]);
|
|
27
|
+
const leaveResetPage = (next) => {
|
|
28
|
+
forgetResetLink();
|
|
29
|
+
setPage(next);
|
|
30
|
+
};
|
|
31
|
+
const passwordChanged = async () => {
|
|
32
|
+
if (state.kind === "signed-in") await signOut();
|
|
33
|
+
leaveResetPage({
|
|
34
|
+
kind: "sign-in",
|
|
35
|
+
notice: "Your password is changed. Sign in with the new one."
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
switch (state.kind) {
|
|
39
|
+
case "loading": return /* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: "flex h-full min-h-48 items-center justify-center gap-2 text-[13px] text-muted-foreground",
|
|
41
|
+
children: [/* @__PURE__ */ jsx(Spinner, {}), " Loading"]
|
|
42
|
+
});
|
|
43
|
+
case "error": return /* @__PURE__ */ jsxs("div", {
|
|
44
|
+
className: "flex h-full min-h-48 flex-col items-center justify-center gap-3 p-8 text-center",
|
|
45
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
46
|
+
className: "text-[13px] text-muted-foreground",
|
|
47
|
+
children: ["Cannot reach the server: ", state.message]
|
|
48
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
49
|
+
onClick: recheck,
|
|
50
|
+
children: "Try again"
|
|
51
|
+
})]
|
|
52
|
+
});
|
|
53
|
+
case "needs-admin": return /* @__PURE__ */ jsx(FirstRunPage, { onCheckAgain: recheck });
|
|
54
|
+
}
|
|
55
|
+
if (page.kind === "reset-password") return /* @__PURE__ */ jsx(ResetPasswordPage, {
|
|
56
|
+
token: page.token,
|
|
57
|
+
onDone: () => void passwordChanged(),
|
|
58
|
+
onRequestNewLink: passwordReset ? () => leaveResetPage({ kind: "forgot-password" }) : void 0,
|
|
59
|
+
onBack: () => leaveResetPage({ kind: "sign-in" })
|
|
60
|
+
});
|
|
61
|
+
if (state.kind === "signed-in") return children;
|
|
62
|
+
if (page.kind === "forgot-password") return /* @__PURE__ */ jsx(ForgotPasswordPage, { onBack: () => setPage({ kind: "sign-in" }) });
|
|
63
|
+
return /* @__PURE__ */ jsx(SignInPage, {
|
|
64
|
+
workspace,
|
|
65
|
+
notice: page.notice,
|
|
66
|
+
onForgotPassword: passwordReset ? () => setPage({ kind: "forgot-password" }) : void 0
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
//#endregion
|
|
70
|
+
export { AuthGate };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LogoMark } from "../app-shell/logo.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/auth/auth-layout.tsx
|
|
4
|
+
/** The frame of the sign-in and first-run pages: logo, title and a card. */
|
|
5
|
+
const AuthLayout = ({ title, description, children }) => /* @__PURE__ */ jsx("div", {
|
|
6
|
+
className: "flex min-h-full w-full items-center justify-center bg-surface px-4 py-10",
|
|
7
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: "w-full max-w-sm",
|
|
9
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
10
|
+
className: "mb-6 flex flex-col items-center text-center",
|
|
11
|
+
children: [
|
|
12
|
+
/* @__PURE__ */ jsx(LogoMark, { className: "size-10" }),
|
|
13
|
+
/* @__PURE__ */ jsx("h1", {
|
|
14
|
+
className: "mt-4 text-xl font-semibold tracking-tight",
|
|
15
|
+
children: title
|
|
16
|
+
}),
|
|
17
|
+
description && /* @__PURE__ */ jsx("p", {
|
|
18
|
+
className: "mt-1 text-[13px] text-muted-foreground",
|
|
19
|
+
children: description
|
|
20
|
+
})
|
|
21
|
+
]
|
|
22
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
23
|
+
className: "rounded-lg border bg-background p-5 shadow-sm",
|
|
24
|
+
children
|
|
25
|
+
})]
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
//#endregion
|
|
29
|
+
export { AuthLayout };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createAuthSession } from "@protobase/client";
|
|
4
|
+
//#region src/auth/auth-provider.tsx
|
|
5
|
+
const AuthContext = createContext(null);
|
|
6
|
+
const useAuth = () => {
|
|
7
|
+
const api = useContext(AuthContext);
|
|
8
|
+
if (!api) throw new Error("useAuth must be used inside AuthProvider");
|
|
9
|
+
return api;
|
|
10
|
+
};
|
|
11
|
+
/** Finds out whether the server has a login and who is signed in, and keeps that state for the app. */
|
|
12
|
+
const AuthProvider = ({ session: given, children }) => {
|
|
13
|
+
const [state, setState] = useState({ kind: "loading" });
|
|
14
|
+
const [passwordReset, setPasswordReset] = useState(false);
|
|
15
|
+
const [socialProviders, setSocialProviders] = useState([]);
|
|
16
|
+
const markSignedOut = useCallback(() => setState((current) => current.kind === "signed-in" ? { kind: "signed-out" } : current), []);
|
|
17
|
+
const signedOut = useRef(markSignedOut);
|
|
18
|
+
signedOut.current = markSignedOut;
|
|
19
|
+
const session = useMemo(() => given ?? createAuthSession({ onSignedOut: () => signedOut.current() }), [given]);
|
|
20
|
+
const recheck = useCallback(() => {
|
|
21
|
+
setState({ kind: "loading" });
|
|
22
|
+
session.status().then(async (status) => {
|
|
23
|
+
setPasswordReset(status.passwordReset);
|
|
24
|
+
setSocialProviders(status.socialProviders);
|
|
25
|
+
if (status.needsAdmin) return setState({ kind: "needs-admin" });
|
|
26
|
+
const user = await session.session();
|
|
27
|
+
setState(user ? {
|
|
28
|
+
kind: "signed-in",
|
|
29
|
+
user
|
|
30
|
+
} : { kind: "signed-out" });
|
|
31
|
+
}, (error) => setState({
|
|
32
|
+
kind: "error",
|
|
33
|
+
message: error instanceof Error ? error.message : "The server did not answer"
|
|
34
|
+
}));
|
|
35
|
+
}, [session]);
|
|
36
|
+
useEffect(recheck, [recheck]);
|
|
37
|
+
const api = useMemo(() => ({
|
|
38
|
+
state,
|
|
39
|
+
session,
|
|
40
|
+
passwordReset,
|
|
41
|
+
socialProviders,
|
|
42
|
+
markSignedOut,
|
|
43
|
+
recheck,
|
|
44
|
+
signIn: async (email, password) => {
|
|
45
|
+
setState({
|
|
46
|
+
kind: "signed-in",
|
|
47
|
+
user: await session.signIn(email, password)
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
signInSocial: async (provider) => {
|
|
51
|
+
window.location.assign(await session.signInSocial(provider, window.location.href));
|
|
52
|
+
},
|
|
53
|
+
signOut: async () => {
|
|
54
|
+
await session.signOut();
|
|
55
|
+
setState({ kind: "signed-out" });
|
|
56
|
+
}
|
|
57
|
+
}), [
|
|
58
|
+
state,
|
|
59
|
+
session,
|
|
60
|
+
passwordReset,
|
|
61
|
+
socialProviders,
|
|
62
|
+
markSignedOut,
|
|
63
|
+
recheck
|
|
64
|
+
]);
|
|
65
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, {
|
|
66
|
+
value: api,
|
|
67
|
+
children
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
export { AuthProvider, useAuth };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Button } from "../primitives/button.js";
|
|
2
|
+
import { AuthLayout } from "./auth-layout.js";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Check, Copy } from "lucide-react";
|
|
6
|
+
//#region src/auth/first-run-page.tsx
|
|
7
|
+
const createAdminCommand = "protobase users create you@example.com";
|
|
8
|
+
/**
|
|
9
|
+
* Shown while the admin store has no user. Accounts are not created over the web (a fresh URL must not be claimable
|
|
10
|
+
* by whoever reaches it first), so this says how to create the first admin on the host.
|
|
11
|
+
*/
|
|
12
|
+
const FirstRunPage = ({ command = createAdminCommand, onCheckAgain, busy }) => {
|
|
13
|
+
const [copied, setCopied] = useState(false);
|
|
14
|
+
return /* @__PURE__ */ jsxs(AuthLayout, {
|
|
15
|
+
title: "No admin yet",
|
|
16
|
+
description: "This Protobase has no users to sign in with.",
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsx("p", {
|
|
19
|
+
className: "text-[13px] text-muted-foreground",
|
|
20
|
+
children: "Run this in the terminal where Protobase runs, then reload. The first admin is created on the server, not in the browser, so nobody else can claim a fresh URL."
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", {
|
|
23
|
+
className: "mt-4 flex items-center gap-2 rounded-md border bg-surface py-1.5 pl-3 pr-1.5",
|
|
24
|
+
children: [/* @__PURE__ */ jsx("code", {
|
|
25
|
+
className: "min-w-0 flex-1 break-all font-mono text-xs",
|
|
26
|
+
children: command
|
|
27
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
28
|
+
type: "button",
|
|
29
|
+
"aria-label": "Copy command",
|
|
30
|
+
onClick: () => {
|
|
31
|
+
navigator.clipboard.writeText(command).then(() => {
|
|
32
|
+
setCopied(true);
|
|
33
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
className: "inline-flex size-8 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted pointer-coarse:size-11",
|
|
37
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-4 text-success" }) : /* @__PURE__ */ jsx(Copy, { className: "size-4" })
|
|
38
|
+
})]
|
|
39
|
+
}),
|
|
40
|
+
/* @__PURE__ */ jsx(Button, {
|
|
41
|
+
variant: "primary",
|
|
42
|
+
className: "mt-4 min-h-10 w-full",
|
|
43
|
+
loading: busy,
|
|
44
|
+
onClick: onCheckAgain,
|
|
45
|
+
children: "Reload"
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { FirstRunPage, createAdminCommand };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Input } from "../primitives/input.js";
|
|
2
|
+
import { Button } from "../primitives/button.js";
|
|
3
|
+
import { AuthLayout } from "./auth-layout.js";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/auth/forgot-password-form.tsx
|
|
7
|
+
const ForgotPasswordForm = ({ onSubmit, onBack, busy, error, sentTo }) => {
|
|
8
|
+
const [email, setEmail] = useState("");
|
|
9
|
+
if (sentTo) return /* @__PURE__ */ jsxs(AuthLayout, {
|
|
10
|
+
title: "Check your email",
|
|
11
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
12
|
+
className: "text-[13px] text-muted-foreground",
|
|
13
|
+
children: [
|
|
14
|
+
"If ",
|
|
15
|
+
/* @__PURE__ */ jsx("span", {
|
|
16
|
+
className: "font-medium text-foreground",
|
|
17
|
+
children: sentTo
|
|
18
|
+
}),
|
|
19
|
+
" has an account, an email with a link to choose a new password is on its way. The link works once, for a limited time."
|
|
20
|
+
]
|
|
21
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
22
|
+
variant: "primary",
|
|
23
|
+
className: "mt-4 min-h-10 w-full",
|
|
24
|
+
onClick: onBack,
|
|
25
|
+
children: "Back to sign in"
|
|
26
|
+
})]
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ jsx(AuthLayout, {
|
|
29
|
+
title: "Reset your password",
|
|
30
|
+
description: "We email you a link to choose a new one.",
|
|
31
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
32
|
+
className: "flex flex-col gap-4",
|
|
33
|
+
onSubmit: (event) => {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
onSubmit(email.trim());
|
|
36
|
+
},
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsxs("label", {
|
|
39
|
+
className: "flex flex-col gap-1.5 text-[13px] font-medium",
|
|
40
|
+
children: ["Email", /* @__PURE__ */ jsx(Input, {
|
|
41
|
+
type: "email",
|
|
42
|
+
name: "email",
|
|
43
|
+
autoComplete: "username",
|
|
44
|
+
required: true,
|
|
45
|
+
autoFocus: true,
|
|
46
|
+
value: email,
|
|
47
|
+
invalid: Boolean(error),
|
|
48
|
+
onChange: (event) => setEmail(event.target.value),
|
|
49
|
+
className: "min-h-6"
|
|
50
|
+
})]
|
|
51
|
+
}),
|
|
52
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
53
|
+
role: "alert",
|
|
54
|
+
className: "text-xs font-medium text-danger-text",
|
|
55
|
+
children: error
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsx(Button, {
|
|
58
|
+
variant: "primary",
|
|
59
|
+
type: "submit",
|
|
60
|
+
loading: busy,
|
|
61
|
+
className: "min-h-10",
|
|
62
|
+
children: "Send reset link"
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ jsx(Button, {
|
|
65
|
+
variant: "ghost",
|
|
66
|
+
onClick: onBack,
|
|
67
|
+
className: "min-h-10",
|
|
68
|
+
children: "Back to sign in"
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ForgotPasswordForm };
|