@cosmoops/connect-sdk 0.44.0 → 0.46.0
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/CHANGELOG.md +78 -0
- package/dist/cjs/client/components/access-gate.js +1 -1
- package/dist/cjs/client/components/account-sessions.js +1 -1
- package/dist/cjs/client/components/ai-format.js +3 -3
- package/dist/cjs/client/components/ai-widgets.js +5 -5
- package/dist/cjs/client/components/apps-grid.js +2 -2
- package/dist/cjs/client/components/ask-ai-history.js +2 -2
- package/dist/cjs/client/components/ask-ai.js +1 -1
- package/dist/cjs/client/components/availability-form.js +1 -1
- package/dist/cjs/client/components/booking-calendar.js +4 -4
- package/dist/cjs/client/components/booking-dialogs.js +2 -2
- package/dist/cjs/client/components/call-button.js +6 -6
- package/dist/cjs/client/components/create-workspace-form.js +1 -1
- package/dist/cjs/client/components/event-type-form.js +1 -1
- package/dist/cjs/client/components/filter-tabs.js +1 -1
- package/dist/cjs/client/components/grievance-notice.js +1 -1
- package/dist/cjs/client/components/integrations-modal.js +2 -2
- package/dist/cjs/client/components/plans.js +5 -5
- package/dist/cjs/client/components/readiness.js +1 -1
- package/dist/cjs/client/components/subscribe-dialog.js +1 -1
- package/dist/cjs/client/components/subscription.js +5 -5
- package/dist/cjs/client/components/usage-meter.js +2 -2
- package/dist/cjs/client/components/user-menu.js +1 -1
- package/dist/cjs/client/components/workspace-switcher.js +1 -1
- package/dist/cjs/server/client.js +1 -1
- package/dist/cjs/server/resources/drive.js +11 -0
- package/dist/cjs/ui/page-shell.js +64 -2
- package/dist/esm/client/components/access-gate.js +1 -1
- package/dist/esm/client/components/account-sessions.js +1 -1
- package/dist/esm/client/components/ai-format.js +3 -3
- package/dist/esm/client/components/ai-widgets.js +5 -5
- package/dist/esm/client/components/apps-grid.js +2 -2
- package/dist/esm/client/components/ask-ai-history.js +2 -2
- package/dist/esm/client/components/ask-ai.js +1 -1
- package/dist/esm/client/components/availability-form.js +1 -1
- package/dist/esm/client/components/booking-calendar.js +4 -4
- package/dist/esm/client/components/booking-dialogs.js +2 -2
- package/dist/esm/client/components/call-button.js +6 -6
- package/dist/esm/client/components/create-workspace-form.js +1 -1
- package/dist/esm/client/components/event-type-form.js +1 -1
- package/dist/esm/client/components/filter-tabs.js +1 -1
- package/dist/esm/client/components/grievance-notice.js +1 -1
- package/dist/esm/client/components/integrations-modal.js +2 -2
- package/dist/esm/client/components/plans.js +5 -5
- package/dist/esm/client/components/readiness.js +1 -1
- package/dist/esm/client/components/subscribe-dialog.js +1 -1
- package/dist/esm/client/components/subscription.js +5 -5
- package/dist/esm/client/components/usage-meter.js +2 -2
- package/dist/esm/client/components/user-menu.js +1 -1
- package/dist/esm/client/components/workspace-switcher.js +1 -1
- package/dist/esm/server/client.js +1 -1
- package/dist/esm/server/resources/drive.js +11 -0
- package/dist/esm/ui/page-shell.js +31 -2
- package/dist/theme.css +126 -2
- package/dist/types/server/client.d.ts +1 -1
- package/dist/types/server/resources/drive.d.ts +8 -1
- package/dist/types/types.d.ts +9 -0
- package/dist/types/ui/page-shell.d.ts +20 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,84 @@
|
|
|
3
3
|
`@cosmoops/connect-sdk` follows semver, with the 0.x convention that **minor
|
|
4
4
|
versions may carry breaking changes** and patch versions never do.
|
|
5
5
|
|
|
6
|
+
## 0.46.0
|
|
7
|
+
|
|
8
|
+
### `secondary` buttons are soft indigo
|
|
9
|
+
|
|
10
|
+
`<Button variant="secondary">` and `<ConnectLinkButton variant="secondary">`
|
|
11
|
+
now paint an indigo tint with indigo text instead of the neutral grey,
|
|
12
|
+
so a view reads as one solid green primary action beside indigo secondary
|
|
13
|
+
ones. `tertiary` stays neutral, for quieter in-row controls. New tokens
|
|
14
|
+
`--secondary-soft`, `--secondary-soft-hover` and
|
|
15
|
+
`--secondary-soft-foreground`, in both themes. To keep the old look,
|
|
16
|
+
redefine `.button--secondary` after importing `theme.css`.
|
|
17
|
+
|
|
18
|
+
### `ConnectPageShell` plays a route transition
|
|
19
|
+
|
|
20
|
+
Every page drawn in `ConnectPageShell` now fades the outgoing page out and
|
|
21
|
+
the arriving one in with a slight rise, using React's `<ViewTransition>`.
|
|
22
|
+
The animations ship in `theme.css` (`connect-page-enter`/`connect-page-exit`)
|
|
23
|
+
and respect `prefers-reduced-motion`. Nothing to wire.
|
|
24
|
+
|
|
25
|
+
- **Next.js App Router only, in practice.** `<ViewTransition>` exists in
|
|
26
|
+
the React canary Next bundles and in no stable React yet. The shell looks
|
|
27
|
+
it up at runtime, so on any other host the page renders exactly as before,
|
|
28
|
+
with no motion and no error.
|
|
29
|
+
- **New prop `transition`** (default `true`). Pass `transition={false}` if
|
|
30
|
+
your app already animates its routes, or the two transitions play over each
|
|
31
|
+
other.
|
|
32
|
+
- Where a *layout* draws the shell, such as a record page with tabs, entering
|
|
33
|
+
and leaving the record animates and switching tabs does not.
|
|
34
|
+
|
|
35
|
+
### `connect.drive.getUsage()`
|
|
36
|
+
|
|
37
|
+
`connect.drive.getUsage({ workspaceId })` returns `{ usedBytes, limitBytes }`
|
|
38
|
+
for the workspace's Drive - the same limit an upload is refused against with
|
|
39
|
+
`quota_exceeded`, so a usage meter agrees with the refusal. `limitBytes: null`
|
|
40
|
+
is unlimited. Needs `drive:read` and an active subscription to your
|
|
41
|
+
application. New type: `DriveUsage`.
|
|
42
|
+
|
|
43
|
+
### Components follow HeroUI's colour roles
|
|
44
|
+
|
|
45
|
+
No API changes; these are colour fixes inside the components.
|
|
46
|
+
|
|
47
|
+
- **Links are links.** The calling notices and the grievance contact draw in
|
|
48
|
+
`--link` (the indigo) instead of the green, which is for actions.
|
|
49
|
+
- **State colours as text are legible.** Error messages, the usage meter's
|
|
50
|
+
overdraw, banner text, the trial note and the AI widgets' tone text used the
|
|
51
|
+
fill colours (`text-danger`, `text-accent`, `text-success`, `text-warning`)
|
|
52
|
+
as text, which measures under 3:1 on a light surface. They now use HeroUI's
|
|
53
|
+
`*-soft-foreground` tokens, which HeroUI derives for exactly that and which
|
|
54
|
+
follow a host's re-themed `--accent`/`--danger`.
|
|
55
|
+
- **Chips and destructive buttons use HeroUI's own variants.** Status and
|
|
56
|
+
saving chips are `<Chip color variant="soft">` instead of hand-painted
|
|
57
|
+
classes; Cancel subscription, Sign out and Disconnect are
|
|
58
|
+
`variant="danger-soft"` instead of a tertiary button painted red.
|
|
59
|
+
- The filter tabs' keyboard ring uses `--focus`, like every HeroUI component.
|
|
60
|
+
|
|
61
|
+
## 0.45.0
|
|
62
|
+
|
|
63
|
+
### `theme.css`: a secondary colour
|
|
64
|
+
|
|
65
|
+
The shared theme gains a second brand colour, a deep indigo, beside the
|
|
66
|
+
green. HeroUI v3 has a single brand role (`--accent`), so the indigo adds no
|
|
67
|
+
classes. It fills two roles HeroUI components already read:
|
|
68
|
+
|
|
69
|
+
- **`--link`** - `<Link>`, prose anchors and `text-link` now draw in indigo
|
|
70
|
+
rather than in body-text colour.
|
|
71
|
+
- **`--focus`** - the keyboard focus ring on every component is indigo rather
|
|
72
|
+
than green, so a focused primary button is no longer a green ring around a
|
|
73
|
+
green button.
|
|
74
|
+
|
|
75
|
+
`--secondary` holds the value both read from, so a platform restyling them
|
|
76
|
+
redefines one token after the import. Green stays the colour of action;
|
|
77
|
+
indigo never paints a fill. The current breadcrumb item, which HeroUI also
|
|
78
|
+
draws with `--link`, is pinned to `--foreground` so the page you are on does
|
|
79
|
+
not look like a link.
|
|
80
|
+
|
|
81
|
+
**What moves on upgrade:** links and focus rings change colour. Nothing else
|
|
82
|
+
does.
|
|
83
|
+
|
|
6
84
|
## 0.44.0
|
|
7
85
|
|
|
8
86
|
### `ConnectCall.dialledAt`: was the person's phone rung?
|
|
@@ -308,7 +308,7 @@ function PaymentRetryBanner({ blocker, className, }) {
|
|
|
308
308
|
const [dismissed, setDismissed] = (0, react_1.useState)(() => dismissKey !== null && typeof window !== "undefined" && window.localStorage.getItem(dismissKey) === "1");
|
|
309
309
|
if (dismissed)
|
|
310
310
|
return null;
|
|
311
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(motion_js_1.m.div, { className: (0, cn_js_1.cn)("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", className), role: "status", initial: { opacity: 0, y: -6 }, animate: { opacity: 1, y: 0 }, transition: motion_js_1.ENTER, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })), (0, jsx_runtime_1.jsx)(react_2.Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
|
|
311
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(motion_js_1.m.div, { className: (0, cn_js_1.cn)("flex flex-wrap items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", className), role: "status", initial: { opacity: 0, y: -6 }, animate: { opacity: 1, y: 0 }, transition: motion_js_1.ENTER, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })), (0, jsx_runtime_1.jsx)(react_2.Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
|
|
312
312
|
setDismissed(true);
|
|
313
313
|
if (dismissKey && typeof window !== "undefined")
|
|
314
314
|
window.localStorage.setItem(dismissKey, "1");
|
|
@@ -95,7 +95,7 @@ function ConnectAccountSessions({ className, maxVisible = 4 }) {
|
|
|
95
95
|
const hasOtherSessions = sessions.some((session) => !session.isCurrent);
|
|
96
96
|
const isCollapsible = maxVisible > 0 && sessions.length > maxVisible;
|
|
97
97
|
const visible = isCollapsible && !showAll ? sessions.slice(0, maxVisible) : sessions;
|
|
98
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("section", { className: (0, cn_js_1.cn)("flex flex-col gap-3", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", (0, jsx_runtime_1.jsx)(react_2.Chip, { size: "sm", variant: "soft", children: sessions.length })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
|
|
98
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("section", { className: (0, cn_js_1.cn)("flex flex-col gap-3", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", (0, jsx_runtime_1.jsx)(react_2.Chip, { size: "sm", variant: "soft", children: sessions.length })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger-soft-foreground", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
|
|
99
99
|
setIsRevokingOthers(true);
|
|
100
100
|
void mutateResource("/sessions/revoke-others")
|
|
101
101
|
.then(() => {
|
|
@@ -123,9 +123,9 @@ function aiStatusTone(value) {
|
|
|
123
123
|
/** HeroUI semantic tokens only, so these inherit whatever theme the host defined. */
|
|
124
124
|
exports.AI_TONE_CHIP = {
|
|
125
125
|
neutral: "bg-surface-tertiary text-foreground",
|
|
126
|
-
positive: "bg-success
|
|
127
|
-
warning: "bg-warning
|
|
128
|
-
danger: "bg-danger
|
|
126
|
+
positive: "bg-success-soft text-success-soft-foreground",
|
|
127
|
+
warning: "bg-warning-soft text-warning-soft-foreground",
|
|
128
|
+
danger: "bg-danger-soft text-danger-soft-foreground",
|
|
129
129
|
};
|
|
130
130
|
exports.AI_TONE_DOT = {
|
|
131
131
|
neutral: "bg-muted",
|
|
@@ -34,9 +34,9 @@ function Panel({ title, children }) {
|
|
|
34
34
|
}
|
|
35
35
|
const TONE_TEXT = {
|
|
36
36
|
neutral: "text-muted",
|
|
37
|
-
positive: "text-success",
|
|
38
|
-
warning: "text-warning",
|
|
39
|
-
danger: "text-danger",
|
|
37
|
+
positive: "text-success-soft-foreground",
|
|
38
|
+
warning: "text-warning-soft-foreground",
|
|
39
|
+
danger: "text-danger-soft-foreground",
|
|
40
40
|
};
|
|
41
41
|
function StatusChip({ value }) {
|
|
42
42
|
return ((0, jsx_runtime_1.jsx)("span", { className: `inline-block rounded-full px-1.5 py-0.5 text-[10px] font-medium ${ai_format_js_1.AI_TONE_CHIP[(0, ai_format_js_1.aiStatusTone)(value)]}`, children: String(value) }));
|
|
@@ -206,7 +206,7 @@ function AiMetrics({ title, metrics }) {
|
|
|
206
206
|
: metric.deltaMeaning === "up-is-good"
|
|
207
207
|
? rising
|
|
208
208
|
: !rising;
|
|
209
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-[11px] text-muted", children: metric.label }), (0, jsx_runtime_1.jsx)("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: (0, ai_format_js_1.formatAiValue)(metric.value, metric.format) }), delta !== null && ((0, jsx_runtime_1.jsxs)("p", { className: `mt-0.5 flex items-center gap-0.5 text-[11px] tabular-nums ${good === null ? "text-muted" : good ? "text-success" : "text-danger"}`, children: [(0, jsx_runtime_1.jsx)(Arrow, { className: "size-3", "aria-hidden": true }), (0, ai_format_js_1.formatAiValue)(Math.abs(delta), "percent")] })), metric.hint && (0, jsx_runtime_1.jsx)("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
|
|
209
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-[11px] text-muted", children: metric.label }), (0, jsx_runtime_1.jsx)("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: (0, ai_format_js_1.formatAiValue)(metric.value, metric.format) }), delta !== null && ((0, jsx_runtime_1.jsxs)("p", { className: `mt-0.5 flex items-center gap-0.5 text-[11px] tabular-nums ${good === null ? "text-muted" : good ? "text-success-soft-foreground" : "text-danger-soft-foreground"}`, children: [(0, jsx_runtime_1.jsx)(Arrow, { className: "size-3", "aria-hidden": true }), (0, ai_format_js_1.formatAiValue)(Math.abs(delta), "percent")] })), metric.hint && (0, jsx_runtime_1.jsx)("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
|
|
210
210
|
}) }) }));
|
|
211
211
|
}
|
|
212
212
|
/** `showTimeline`: dated events, oldest first, on a single rail. */
|
|
@@ -219,7 +219,7 @@ function AiDetails({ title, fields }) {
|
|
|
219
219
|
}
|
|
220
220
|
/** `showSteps`: an ordered procedure. `renderDetail` is how the panel feeds each step back through its markdown renderer. */
|
|
221
221
|
function AiSteps({ title, steps, renderDetail, }) {
|
|
222
|
-
return ((0, jsx_runtime_1.jsx)(Panel, { title: title, children: (0, jsx_runtime_1.jsx)("ol", { className: "space-y-2.5", children: steps.map((step, index) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex gap-2.5", children: [(0, jsx_runtime_1.jsx)("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent
|
|
222
|
+
return ((0, jsx_runtime_1.jsx)(Panel, { title: title, children: (0, jsx_runtime_1.jsx)("ol", { className: "space-y-2.5", children: steps.map((step, index) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex gap-2.5", children: [(0, jsx_runtime_1.jsx)("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent-soft text-[10px] font-semibold text-accent-soft-foreground", children: index + 1 }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs font-medium", children: step.title }), step.detail && ((0, jsx_runtime_1.jsx)("div", { className: "mt-0.5 text-xs text-muted", children: renderDetail ? renderDetail(step.detail) : (0, jsx_runtime_1.jsx)("span", { className: "whitespace-pre-wrap", children: step.detail }) }))] })] }, step.title))) }) }));
|
|
223
223
|
}
|
|
224
224
|
/**
|
|
225
225
|
* `showSources`: what a grounded answer actually rested on. The point of a
|
|
@@ -58,7 +58,7 @@ function AppTile({ app, onOpen }) {
|
|
|
58
58
|
// there is nothing to open and nothing to buy.
|
|
59
59
|
const disabled = app.subscribed && app.launchUrl === null;
|
|
60
60
|
const shared = "group/tile flex w-full flex-col items-center gap-2 rounded-2xl p-3 text-center outline-offset-2 transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-focus";
|
|
61
|
-
const body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "relative flex size-14 items-center justify-center overflow-hidden rounded-[0.8125rem] bg-surface-secondary text-muted transition-colors duration-200 group-hover/tile:bg-default group-hover/tile:text-foreground", children: [(0, jsx_runtime_1.jsx)(AppGlyph, { app: app }), app.needsAttention && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-warning text-warning-foreground", children: (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-default text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? ((0, jsx_runtime_1.jsx)("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent", children: "Current" })) : (!app.subscribed && ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-0.5 text-[0.625rem] uppercase tracking-wide text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: ["Subscribe", (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
|
|
61
|
+
const body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "relative flex size-14 items-center justify-center overflow-hidden rounded-[0.8125rem] bg-surface-secondary text-muted transition-colors duration-200 group-hover/tile:bg-default group-hover/tile:text-foreground", children: [(0, jsx_runtime_1.jsx)(AppGlyph, { app: app }), app.needsAttention && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-warning text-warning-foreground", children: (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-default text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? ((0, jsx_runtime_1.jsx)("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent-soft-foreground", children: "Current" })) : (!app.subscribed && ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-0.5 text-[0.625rem] uppercase tracking-wide text-muted opacity-0 transition-opacity duration-200 group-hover/tile:opacity-100", children: ["Subscribe", (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
|
|
62
62
|
if (disabled) {
|
|
63
63
|
return ((0, jsx_runtime_1.jsx)("div", { className: `${shared} cursor-default opacity-60`, title: `${app.name} has no launch URL configured`, children: body }));
|
|
64
64
|
}
|
|
@@ -139,7 +139,7 @@ function ConnectAppsGrid({ className, label = "Switch app", variant = "row" }) {
|
|
|
139
139
|
};
|
|
140
140
|
const showSearch = apps.length > SEARCH_THRESHOLD;
|
|
141
141
|
const isEmpty = !isLoading && !error && mine.length === 0 && explore.length === 0;
|
|
142
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal, { children: [variant === "row" ? ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": label, className: (0, shell_context_js_1.sidebarRowClass)({ className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-5 shrink-0 items-center justify-center", children: (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutGrid, { className: "size-5" }) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { children: label })] })) : ((0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutGrid, { className: "size-5" }) })), (0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { className: "sm:max-w-lg", children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: "Your apps" }) }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "space-y-4", children: [showSearch && ((0, jsx_runtime_1.jsxs)(react_1.TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [(0, jsx_runtime_1.jsx)(react_1.Label, { className: "sr-only", children: "Filter apps" }), (0, jsx_runtime_1.jsx)(react_1.Input, { className: "pr-9", placeholder: "Filter apps" }), (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && (0, jsx_runtime_1.jsx)(GridSkeleton, {}), !isLoading && error && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-danger", children: "Could not load your apps." })), isEmpty && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-muted", children: query
|
|
142
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal, { children: [variant === "row" ? ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", "aria-label": label, className: (0, shell_context_js_1.sidebarRowClass)({ className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-5 shrink-0 items-center justify-center", children: (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutGrid, { className: "size-5" }) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { children: label })] })) : ((0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutGrid, { className: "size-5" }) })), (0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { className: "sm:max-w-lg", children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: "Your apps" }) }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "space-y-4", children: [showSearch && ((0, jsx_runtime_1.jsxs)(react_1.TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [(0, jsx_runtime_1.jsx)(react_1.Label, { className: "sr-only", children: "Filter apps" }), (0, jsx_runtime_1.jsx)(react_1.Input, { className: "pr-9", placeholder: "Filter apps" }), (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && (0, jsx_runtime_1.jsx)(GridSkeleton, {}), !isLoading && error && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-danger-soft-foreground", children: "Could not load your apps." })), isEmpty && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-muted", children: query
|
|
143
143
|
? `Nothing matches “${query}”.`
|
|
144
144
|
: "No apps yet. Anything this workspace can open shows up here." })), !isLoading && !error && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(AppsSection, { title: "Your apps", hint: "Open", apps: mine, offset: 0, onOpen: openApp }), (0, jsx_runtime_1.jsx)(AppsSection, { title: "Explore", hint: "Subscribe on Connect", apps: explore, offset: mine.length, onOpen: openApp })] }))] })] }) }) })] }) }));
|
|
145
145
|
}
|
|
@@ -96,9 +96,9 @@ function AskAiHistory({ refreshKey, activeConversationId, onOpenConversation, })
|
|
|
96
96
|
return ((0, jsx_runtime_1.jsx)("div", { className: "space-y-2 p-4", "aria-busy": "true", children: [0, 1, 2].map((row) => ((0, jsx_runtime_1.jsx)("div", { className: "h-9 animate-pulse rounded-md bg-surface-secondary" }, row))) }));
|
|
97
97
|
}
|
|
98
98
|
if (conversations.length === 0) {
|
|
99
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: error ? ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-danger", children: error })) : ((0, jsx_runtime_1.jsx)("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
|
|
99
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "p-4", children: error ? ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-danger-soft-foreground", children: error })) : ((0, jsx_runtime_1.jsx)("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
|
|
100
100
|
}
|
|
101
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-2", children: [error && (0, jsx_runtime_1.jsx)("p", { className: "px-2 pb-2 text-xs text-danger", children: error }), (0, jsx_runtime_1.jsx)("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
|
|
101
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-2", children: [error && (0, jsx_runtime_1.jsx)("p", { className: "px-2 pb-2 text-xs text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsx)("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
|
|
102
102
|
const isConfirming = confirmingId === conversation.id;
|
|
103
103
|
const isActive = conversation.id === activeConversationId;
|
|
104
104
|
return ((0, jsx_runtime_1.jsx)("li", { children: isConfirming ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-md border border-border px-2 py-1.5", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex-1 truncate text-xs text-muted", children: "Delete this conversation?" }), (0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "danger", className: "h-7 px-2 text-xs", isDisabled: busyId === conversation.id, onPress: () => void remove(conversation.id), children: "Delete" }), (0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "tertiary", className: "h-7 px-2 text-xs", onPress: () => setConfirmingId(null), children: "Cancel" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: `group flex items-center gap-1 rounded-md ${isActive ? "bg-surface-secondary" : ""}`, children: [(0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "ghost", className: "h-auto min-w-0 flex-1 justify-start gap-2 px-2 py-1.5 text-start", isDisabled: busyId === conversation.id, onPress: () => void open(conversation.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.MessageSquare, { className: "size-3.5 shrink-0 text-muted" }), (0, jsx_runtime_1.jsx)("span", { className: "min-w-0 flex-1 truncate text-xs font-normal", children: conversation.title })] }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "ghost", className: "shrink-0", "aria-label": `Delete "${conversation.title}"`, onPress: () => setConfirmingId(conversation.id), children: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: "size-3.5 text-muted" }) })] })) }, conversation.id));
|
|
@@ -547,7 +547,7 @@ function ConnectAskAi({ title = "Ask AI", assistantName = "Assistant", placehold
|
|
|
547
547
|
// Disabled on an empty one rather than hidden: a control that
|
|
548
548
|
// appears and disappears as you use the panel is harder to find
|
|
549
549
|
// than one that is always in the same place.
|
|
550
|
-
isDisabled: turns.length === 0, onPress: startNewConversation, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: (0, jsx_runtime_1.jsx)(lucide_react_1.History, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "size-4" }) })] }), showHistory ? ((0, jsx_runtime_1.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: (0, jsx_runtime_1.jsx)(ask_ai_history_js_1.AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "h-auto rounded-full px-2.5 py-1 text-start text-xs whitespace-normal", isDisabled: isBusy, onPress: () => void send(suggestion), children: suggestion }, suggestion))) })) : null] })), rendered, isBusy && lastTurn?.role === "user" && ((0, jsx_runtime_1.jsxs)("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [(0, jsx_runtime_1.jsx)("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), (0, jsx_runtime_1.jsx)(ThinkingShimmer, {})] })), error && ((0, jsx_runtime_1.jsxs)("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-danger/30 bg-danger
|
|
550
|
+
isDisabled: turns.length === 0, onPress: startNewConversation, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: (0, jsx_runtime_1.jsx)(lucide_react_1.History, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "size-4" }) })] }), showHistory ? ((0, jsx_runtime_1.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: (0, jsx_runtime_1.jsx)(ask_ai_history_js_1.AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "h-auto rounded-full px-2.5 py-1 text-start text-xs whitespace-normal", isDisabled: isBusy, onPress: () => void send(suggestion), children: suggestion }, suggestion))) })) : null] })), rendered, isBusy && lastTurn?.role === "user" && ((0, jsx_runtime_1.jsxs)("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [(0, jsx_runtime_1.jsx)("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), (0, jsx_runtime_1.jsx)(ThinkingShimmer, {})] })), error && ((0, jsx_runtime_1.jsxs)("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-danger/30 bg-danger-soft p-2.5 text-xs text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.TriangleAlert, { className: "mt-px size-3.5 shrink-0", "aria-hidden": true }), (0, jsx_runtime_1.jsx)("p", { className: "flex-1", children: error.message }), error.retry && ((0, jsx_runtime_1.jsxs)(react_2.Button, { size: "sm", variant: "tertiary", className: "h-6 shrink-0 px-2 text-xs", onPress: retry, isDisabled: isBusy, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.RotateCcw, { className: "size-3", "aria-hidden": true }), "Try again"] }))] })), (0, jsx_runtime_1.jsx)("div", { ref: bottomRef })] })), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex gap-2 border-t border-border p-3", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", className: "flex-1", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { className: "sr-only", children: placeholder }), (0, jsx_runtime_1.jsx)(react_2.Input, { placeholder: placeholder, value: input, onChange: (event) => setInput(event.currentTarget.value), disabled: isBusy })] }), isBusy ? ((0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", isIconOnly: true, variant: "secondary", "aria-label": "Stop generating", onPress: stop, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Square, { className: "size-3.5" }) })) : ((0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", isIconOnly: true, variant: "primary", "aria-label": "Send", isDisabled: !input.trim(), children: (0, jsx_runtime_1.jsx)(lucide_react_1.Send, { className: "size-3.5" }) }))] })] }) })] }));
|
|
551
551
|
}
|
|
552
552
|
/** Hoisted for the same reference-equality reason as `ANIMATED` - Streamdown compares it by identity. */
|
|
553
553
|
const LINK_SAFETY = { enabled: false };
|
|
@@ -99,5 +99,5 @@ function ScheduleFields({ editing, onCancel, onSaved, }) {
|
|
|
99
99
|
const interval = weeklyHours[day][0];
|
|
100
100
|
const isOpenDay = interval !== undefined;
|
|
101
101
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(react_2.Switch, { isSelected: isOpenDay, onChange: (open) => toggleDay(day, open), isDisabled: isPending, children: (0, jsx_runtime_1.jsxs)(react_2.Switch.Content, { children: [(0, jsx_runtime_1.jsx)(react_2.Switch.Control, { children: (0, jsx_runtime_1.jsx)(react_2.Switch.Thumb, {}) }), (0, jsx_runtime_1.jsx)("span", { className: "w-24 shrink-0 text-sm", children: WEEKDAY_LABELS[day] })] }) }), isOpenDay && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Input, { type: "time", value: minutesToTimeInput(interval.startMinute), onChange: (e) => setDayTime(day, "startMinute", e.currentTarget.value), disabled: isPending }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-muted", children: "to" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "time", value: minutesToTimeInput(interval.endMinute), onChange: (e) => setDayTime(day, "endMinute", e.currentTarget.value), disabled: isPending })] }))] }, day));
|
|
102
|
-
}) }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
|
|
102
|
+
}) }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
|
|
103
103
|
}
|
|
@@ -69,7 +69,7 @@ function ConnectBookingCalendar({ timeZone, hostMemberId, window: visibleWindow
|
|
|
69
69
|
const marks = (0, react_2.useMemo)(() => (0, booking_grid_js_1.hourMarks)(visibleWindow), [visibleWindow]);
|
|
70
70
|
const shiftWeek = (weeks) => setWeekAnchor((anchor) => new Date(anchor.getTime() + weeks * 7 * 24 * 60 * 60_000));
|
|
71
71
|
const eventTypeById = (0, react_2.useMemo)(() => new Map(eventTypes.map((eventType) => [eventType.id, eventType])), [eventTypes]);
|
|
72
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-4", className), children: [(0, jsx_runtime_1.jsxs)("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Previous week", onPress: () => shiftWeek(-1), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)(react_1.Button, { size: "sm", variant: "secondary", onPress: () => setWeekAnchor(startOfWeek(new Date(), zone)), children: "Today" }), (0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Next week", onPress: () => shiftWeek(1), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)("p", { className: "ml-2 text-sm font-medium", children: weekLabel(columns, zone) })] }), canBook && eventTypes.length > 0 && ((0, jsx_runtime_1.jsx)(react_1.Button, { size: "sm", variant: "primary", onPress: () => setBooking({ startAt: nextHour(now) }), children: "New appointment" }))] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), isLoading ? ((0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-96 w-full rounded-lg" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Card, { className: "hidden overflow-hidden sm:block", children: (0, jsx_runtime_1.jsx)(WeekGrid, { columns: columns, marks: marks, window: visibleWindow, positioned: positioned, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected, onPickEmpty: canBook ? (startAt) => setBooking({ startAt }) : undefined }) }), (0, jsx_runtime_1.jsx)("div", { className: "sm:hidden", children: (0, jsx_runtime_1.jsx)(AgendaView, { columns: columns, bookings: bookings, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected }) })] })), booking && ((0, jsx_runtime_1.jsx)(booking_dialogs_js_1.ConnectBookingDialog, { isOpen: true, onOpenChange: (open) => !open && setBooking(null), eventTypes: eventTypes, initialStartAt: booking.startAt, timeZone: zone, onBooked: () => {
|
|
72
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-4", className), children: [(0, jsx_runtime_1.jsxs)("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Previous week", onPress: () => shiftWeek(-1), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)(react_1.Button, { size: "sm", variant: "secondary", onPress: () => setWeekAnchor(startOfWeek(new Date(), zone)), children: "Today" }), (0, jsx_runtime_1.jsx)(react_1.Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Next week", onPress: () => shiftWeek(1), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)("p", { className: "ml-2 text-sm font-medium", children: weekLabel(columns, zone) })] }), canBook && eventTypes.length > 0 && ((0, jsx_runtime_1.jsx)(react_1.Button, { size: "sm", variant: "primary", onPress: () => setBooking({ startAt: nextHour(now) }), children: "New appointment" }))] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), isLoading ? ((0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-96 w-full rounded-lg" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Card, { className: "hidden overflow-hidden sm:block", children: (0, jsx_runtime_1.jsx)(WeekGrid, { columns: columns, marks: marks, window: visibleWindow, positioned: positioned, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected, onPickEmpty: canBook ? (startAt) => setBooking({ startAt }) : undefined }) }), (0, jsx_runtime_1.jsx)("div", { className: "sm:hidden", children: (0, jsx_runtime_1.jsx)(AgendaView, { columns: columns, bookings: bookings, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected }) })] })), booking && ((0, jsx_runtime_1.jsx)(booking_dialogs_js_1.ConnectBookingDialog, { isOpen: true, onOpenChange: (open) => !open && setBooking(null), eventTypes: eventTypes, initialStartAt: booking.startAt, timeZone: zone, onBooked: () => {
|
|
73
73
|
setBooking(null);
|
|
74
74
|
refetch();
|
|
75
75
|
} })), selected && ((0, jsx_runtime_1.jsx)(booking_dialogs_js_1.ConnectBookingDetail, { booking: selected, eventType: eventTypeById.get(selected.eventTypeId) ?? null, timeZone: zone, onOpenChange: (open) => !open && setSelected(null), onChanged: () => {
|
|
@@ -79,7 +79,7 @@ function ConnectBookingCalendar({ timeZone, hostMemberId, window: visibleWindow
|
|
|
79
79
|
}
|
|
80
80
|
function WeekGrid({ columns, marks, window: visibleWindow, positioned, eventTypeById, zone, onSelect, onPickEmpty, }) {
|
|
81
81
|
const span = visibleWindow.endMinute - visibleWindow.startMinute;
|
|
82
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-[3rem_repeat(7,minmax(0,1fr))]", children: [(0, jsx_runtime_1.jsx)("div", { className: "border-b border-separator" }), columns.map((column) => ((0, jsx_runtime_1.jsx)("div", { className: `border-b border-l border-separator px-2 py-2 text-center ${column.isToday ? "bg-accent/5" : ""}`, children: (0, jsx_runtime_1.jsx)("p", { className: `text-xs font-medium ${column.isToday ? "text-accent" : ""}`, children: column.label }) }, column.dateKey))), (0, jsx_runtime_1.jsx)("div", { className: "relative", style: { height: `${(span / 60) * 3}rem` }, children: marks.map((mark) => ((0, jsx_runtime_1.jsx)("span", { className: "absolute right-1 -translate-y-1/2 text-[0.625rem] text-muted", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` }, children: mark.label }, mark.minute))) }), columns.map((column) => ((0, jsx_runtime_1.jsxs)("div", { className: `relative border-l border-separator ${column.isToday ? "bg-accent/5" : ""}`, style: { height: `${(span / 60) * 3}rem` }, children: [marks.map((mark) => ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-x-0 border-t border-separator/60", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` } }, mark.minute))), onPickEmpty &&
|
|
82
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-[3rem_repeat(7,minmax(0,1fr))]", children: [(0, jsx_runtime_1.jsx)("div", { className: "border-b border-separator" }), columns.map((column) => ((0, jsx_runtime_1.jsx)("div", { className: `border-b border-l border-separator px-2 py-2 text-center ${column.isToday ? "bg-accent/5" : ""}`, children: (0, jsx_runtime_1.jsx)("p", { className: `text-xs font-medium ${column.isToday ? "text-accent-soft-foreground" : ""}`, children: column.label }) }, column.dateKey))), (0, jsx_runtime_1.jsx)("div", { className: "relative", style: { height: `${(span / 60) * 3}rem` }, children: marks.map((mark) => ((0, jsx_runtime_1.jsx)("span", { className: "absolute right-1 -translate-y-1/2 text-[0.625rem] text-muted", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` }, children: mark.label }, mark.minute))) }), columns.map((column) => ((0, jsx_runtime_1.jsxs)("div", { className: `relative border-l border-separator ${column.isToday ? "bg-accent/5" : ""}`, style: { height: `${(span / 60) * 3}rem` }, children: [marks.map((mark) => ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-x-0 border-t border-separator/60", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` } }, mark.minute))), onPickEmpty &&
|
|
83
83
|
marks.map((mark) => ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": `Book ${column.label} at ${mark.label}`, className: "absolute inset-x-0 cursor-pointer transition-colors hover:bg-accent/10", style: {
|
|
84
84
|
top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%`,
|
|
85
85
|
height: `${(60 / span) * 100}%`,
|
|
@@ -94,7 +94,7 @@ function WeekGrid({ columns, marks, window: visibleWindow, positioned, eventType
|
|
|
94
94
|
// gap so two adjacent blocks read as two things.
|
|
95
95
|
left: `${(entry.column / entry.columns) * 100}%`,
|
|
96
96
|
width: `calc(${(1 / entry.columns) * 100}% - 2px)`,
|
|
97
|
-
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem] font-medium text-accent", children: timeLabel(new Date(entry.booking.startAt), zone) }), (0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem]", children: entry.booking.attendee.name }), eventType && ((0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem] text-muted", children: eventType.name }))] }, entry.booking.id));
|
|
97
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem] font-medium text-accent-soft-foreground", children: timeLabel(new Date(entry.booking.startAt), zone) }), (0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem]", children: entry.booking.attendee.name }), eventType && ((0, jsx_runtime_1.jsx)("span", { className: "block truncate text-[0.625rem] text-muted", children: eventType.name }))] }, entry.booking.id));
|
|
98
98
|
})] }, column.dateKey)))] }));
|
|
99
99
|
}
|
|
100
100
|
function AgendaView({ columns, bookings, eventTypeById, zone, onSelect, }) {
|
|
@@ -113,7 +113,7 @@ function AgendaView({ columns, bookings, eventTypeById, zone, onSelect, }) {
|
|
|
113
113
|
if (populated.length === 0) {
|
|
114
114
|
return ((0, jsx_runtime_1.jsx)(empty_state_js_1.ConnectEmptyState, { variant: "dashed", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.CalendarDays, { "aria-hidden": true }), title: "Nothing booked this week", description: "Appointments booked here, on your public link, or by a voice agent all show up together." }));
|
|
115
115
|
}
|
|
116
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-4", children: populated.map((column) => ((0, jsx_runtime_1.jsxs)("section", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("h3", { className: `text-sm font-medium ${column.isToday ? "text-accent" : ""}`, children: column.label }), (byDay.get(column.dateKey) ?? []).map((booking) => {
|
|
116
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-4", children: populated.map((column) => ((0, jsx_runtime_1.jsxs)("section", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("h3", { className: `text-sm font-medium ${column.isToday ? "text-accent-soft-foreground" : ""}`, children: column.label }), (byDay.get(column.dateKey) ?? []).map((booking) => {
|
|
117
117
|
const eventType = eventTypeById.get(booking.eventTypeId);
|
|
118
118
|
const Icon = eventType ? LOCATION_ICON[eventType.locationKind] : lucide_react_1.CalendarDays;
|
|
119
119
|
return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => onSelect(booking), children: (0, jsx_runtime_1.jsx)(react_1.Card, { className: "transition-colors hover:bg-surface", children: (0, jsx_runtime_1.jsxs)(react_1.Card.Content, { className: "flex items-center gap-3 py-3 text-left", children: [(0, jsx_runtime_1.jsx)(Icon, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-medium", children: booking.attendee.name }), (0, jsx_runtime_1.jsxs)("p", { className: "truncate text-xs text-muted", children: [timeLabel(new Date(booking.startAt), zone), eventType ? ` · ${eventType.name}` : ""] })] }), booking.source === "agent-call" && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { size: "sm", variant: "secondary", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Phone, { className: "size-3", "aria-hidden": true }), "Agent"] }))] }) }) }, booking.id));
|
|
@@ -38,7 +38,7 @@ function ConnectBookingDialog({ isOpen, onOpenChange, eventTypes, initialStartAt
|
|
|
38
38
|
// The old pick belongs to the old type's grid and is
|
|
39
39
|
// almost never a legal slot on the new one.
|
|
40
40
|
setChosen(null);
|
|
41
|
-
}, children: eventType.name }, eventType.id))) })), (0, jsx_runtime_1.jsx)(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone }), (0, jsx_runtime_1.jsxs)(react_1.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_1.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_1.Input, { value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_1.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_1.Label, { children: "Phone" }), (0, jsx_runtime_1.jsx)(react_1.Input, { type: "tel", value: phone, onChange: (e) => setPhone(e.currentTarget.value), disabled: isPending })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { variant: "secondary", onPress: () => onOpenChange(false), isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "primary", isDisabled: !canSubmit, onPress: async () => {
|
|
41
|
+
}, children: eventType.name }, eventType.id))) })), (0, jsx_runtime_1.jsx)(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone }), (0, jsx_runtime_1.jsxs)(react_1.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_1.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_1.Input, { value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_1.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_1.Label, { children: "Phone" }), (0, jsx_runtime_1.jsx)(react_1.Input, { type: "tel", value: phone, onChange: (e) => setPhone(e.currentTarget.value), disabled: isPending })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Button, { variant: "secondary", onPress: () => onOpenChange(false), isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "primary", isDisabled: !canSubmit, onPress: async () => {
|
|
42
42
|
clearError();
|
|
43
43
|
const booked = await createBooking({
|
|
44
44
|
eventTypeId,
|
|
@@ -66,7 +66,7 @@ function ConnectBookingDetail({ booking, eventType, timeZone, onOpenChange, onCh
|
|
|
66
66
|
to: new Date(start.getTime() + SEARCH_DAYS * 24 * 60 * 60_000),
|
|
67
67
|
}), [start]);
|
|
68
68
|
const { slots, isLoading } = (0, use_scheduling_js_1.useAvailableSlots)(isMoving ? { eventTypeId: booking.eventTypeId, ...window, excludeBookingId: booking.id } : null);
|
|
69
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Modal, { isOpen: true, onOpenChange: onOpenChange, children: (0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: booking.attendee.name }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [(0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 text-muted", "aria-hidden": true }), longLabel(start, timeZone)] }), eventType && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: eventType.name }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: booking.attendee.phone }), booking.notes && (0, jsx_runtime_1.jsx)("p", { className: "pt-1 text-sm", children: booking.notes })] }), isMoving && ((0, jsx_runtime_1.jsx)(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone })), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap justify-end gap-2", children: isMoving ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Button, { variant: "secondary", onPress: () => setMoving(false), isDisabled: isPending, children: "Back" }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "primary", isDisabled: !chosen || isPending, onPress: async () => {
|
|
69
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Modal, { isOpen: true, onOpenChange: onOpenChange, children: (0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: booking.attendee.name }), (0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [(0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 text-muted", "aria-hidden": true }), longLabel(start, timeZone)] }), eventType && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: eventType.name }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: booking.attendee.phone }), booking.notes && (0, jsx_runtime_1.jsx)("p", { className: "pt-1 text-sm", children: booking.notes })] }), isMoving && ((0, jsx_runtime_1.jsx)(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone })), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap justify-end gap-2", children: isMoving ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Button, { variant: "secondary", onPress: () => setMoving(false), isDisabled: isPending, children: "Back" }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "primary", isDisabled: !chosen || isPending, onPress: async () => {
|
|
70
70
|
clearError();
|
|
71
71
|
const moved = await rescheduleBooking({
|
|
72
72
|
bookingId: booking.id,
|
|
@@ -360,7 +360,7 @@ function unavailableReason(availability) {
|
|
|
360
360
|
function ConnectCallUnavailable({ availability, className, }) {
|
|
361
361
|
const { text, action } = unavailableReason(availability);
|
|
362
362
|
const href = (0, portal_links_js_1.useConnectPortalUrl)(action?.path ?? "/calling");
|
|
363
|
-
return ((0, jsx_runtime_1.jsxs)("p", { className: (0, cn_js_1.cn)("text-xs text-muted", className), children: [text, " ", action && ((0, jsx_runtime_1.jsx)("a", { href: href, className: "font-medium text-
|
|
363
|
+
return ((0, jsx_runtime_1.jsxs)("p", { className: (0, cn_js_1.cn)("text-xs text-muted", className), children: [text, " ", action && ((0, jsx_runtime_1.jsx)("a", { href: href, className: "font-medium text-link hover:underline", children: action.label }))] }));
|
|
364
364
|
}
|
|
365
365
|
function formatRupees(paise) {
|
|
366
366
|
return `₹${(paise / 100).toLocaleString("en-IN", {
|
|
@@ -382,13 +382,13 @@ function ConnectCallingStatus({ className }) {
|
|
|
382
382
|
return (0, jsx_runtime_1.jsx)("div", { className: (0, cn_js_1.cn)("h-24 animate-pulse rounded-lg bg-surface-secondary", className) });
|
|
383
383
|
}
|
|
384
384
|
const rate = formatRupees(availability.ratePaisePerMinute);
|
|
385
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("space-y-3 rounded-lg border border-separator bg-surface p-4", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Business number" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium tabular-nums", children: availability.number ?? (availability.status === "number_pending" ? "Being set up…" : "None") })] }), availability.number && ((0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Calling minutes" }), (0, jsx_runtime_1.jsxs)("p", { className: (0, cn_js_1.cn)("text-sm font-medium tabular-nums", availability.lowBalance && "text-danger"), children: [formatRupees(availability.available), " \u00B7 about ", availability.availableMinutes, " min"] })] }))] }), availability.status === "no_permission" ? ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "You don't have permission to make calls here." })) : !availability.number ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Call customers from their record, from your own business number. Calls are not recorded, and minutes are prepaid at ", rate, " + GST a minute.", " ", availability.status === "number_pending"
|
|
386
|
-
? availability.canManageNumber && ((0, jsx_runtime_1.jsx)("a", { href: callingHref, className: "font-medium text-
|
|
385
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("space-y-3 rounded-lg border border-separator bg-surface p-4", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Business number" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium tabular-nums", children: availability.number ?? (availability.status === "number_pending" ? "Being set up…" : "None") })] }), availability.number && ((0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "Calling minutes" }), (0, jsx_runtime_1.jsxs)("p", { className: (0, cn_js_1.cn)("text-sm font-medium tabular-nums", availability.lowBalance && "text-danger-soft-foreground"), children: [formatRupees(availability.available), " \u00B7 about ", availability.availableMinutes, " min"] })] }))] }), availability.status === "no_permission" ? ((0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: "You don't have permission to make calls here." })) : !availability.number ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Call customers from their record, from your own business number. Calls are not recorded, and minutes are prepaid at ", rate, " + GST a minute.", " ", availability.status === "number_pending"
|
|
386
|
+
? availability.canManageNumber && ((0, jsx_runtime_1.jsx)("a", { href: callingHref, className: "font-medium text-link hover:underline", children: "Check on your number" }))
|
|
387
387
|
: availability.canManageNumber && availability.offer
|
|
388
|
-
? ((0, jsx_runtime_1.jsxs)("a", { href: offerHref, className: "font-medium text-
|
|
388
|
+
? ((0, jsx_runtime_1.jsxs)("a", { href: offerHref, className: "font-medium text-link hover:underline", children: ["Add ", availability.offer.addonName, " (", formatRupees(availability.offer.price), " + GST)"] }))
|
|
389
389
|
: "Ask a workspace admin to add a business number."] })) : ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [availability.lowBalance
|
|
390
390
|
? "Calling minutes are running low. "
|
|
391
|
-
: `Calls cost ${rate} + GST a minute, from prepaid minutes. `, availability.canTopUp ? ((0, jsx_runtime_1.jsx)("a", { href: callingHref, className: "font-medium text-
|
|
391
|
+
: `Calls cost ${rate} + GST a minute, from prepaid minutes. `, availability.canTopUp ? ((0, jsx_runtime_1.jsx)("a", { href: callingHref, className: "font-medium text-link hover:underline", children: "Top up minutes" })) : ("A workspace admin can top up.")] }))] }));
|
|
392
392
|
}
|
|
393
393
|
const PHASE_LABEL = {
|
|
394
394
|
connecting: "Connecting…",
|
|
@@ -410,7 +410,7 @@ function ConnectActiveCallBar({ call, onHangUp, onToggleMute, className, }) {
|
|
|
410
410
|
? `Call ended · ${formatSeconds(call.endedAfterSeconds)}`
|
|
411
411
|
: PHASE_LABEL[call.phase];
|
|
412
412
|
const remaining = call.phase === "ended" ? minutesLeftLabel(call.minutesLeft) : null;
|
|
413
|
-
return ((0, jsx_runtime_1.jsx)("div", { role: "status", "aria-live": "polite", className: (0, cn_js_1.cn)("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent", children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-medium", children: call.label }), (0, jsx_runtime_1.jsxs)("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? (0, jsx_runtime_1.jsx)(lucide_react_1.MicOff, { className: "size-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Mic, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOff, { className: "size-4" }) })] }))] }) }));
|
|
413
|
+
return ((0, jsx_runtime_1.jsx)("div", { role: "status", "aria-live": "polite", className: (0, cn_js_1.cn)("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent-soft-foreground", children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-medium", children: call.label }), (0, jsx_runtime_1.jsxs)("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? (0, jsx_runtime_1.jsx)(lucide_react_1.MicOff, { className: "size-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Mic, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOff, { className: "size-4" }) })] }))] }) }));
|
|
414
414
|
}
|
|
415
415
|
/** "42 min of calling left", once the post-call balance read has landed. */
|
|
416
416
|
function minutesLeftLabel(minutes) {
|
|
@@ -62,5 +62,5 @@ function CreateWorkspaceFields({ onCancel, onCreated, }) {
|
|
|
62
62
|
if (created)
|
|
63
63
|
onCreated(created);
|
|
64
64
|
};
|
|
65
|
-
return ((0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_2.Input, { placeholder: "Acme Logistics", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), (0, jsx_runtime_1.jsxs)(react_2.RadioGroup, { variant: "secondary", value: type, onChange: (value) => setType(value), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Type" }), (0, jsx_runtime_1.jsx)("div", { className: "mt-1.5 flex flex-col gap-2", children: WORKSPACE_TYPES.map((option) => ((0, jsx_runtime_1.jsx)(react_2.Radio, { value: option.id, children: (0, jsx_runtime_1.jsxs)(react_2.Radio.Content, { children: [(0, jsx_runtime_1.jsx)(react_2.Radio.Control, { children: (0, jsx_runtime_1.jsx)(react_2.Radio.Indicator, {}) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium", children: option.label }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: option.hint })] })] }) }, option.id))) })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Creating…" : "Create" })] })] }));
|
|
65
|
+
return ((0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_2.Input, { placeholder: "Acme Logistics", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), (0, jsx_runtime_1.jsxs)(react_2.RadioGroup, { variant: "secondary", value: type, onChange: (value) => setType(value), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Type" }), (0, jsx_runtime_1.jsx)("div", { className: "mt-1.5 flex flex-col gap-2", children: WORKSPACE_TYPES.map((option) => ((0, jsx_runtime_1.jsx)(react_2.Radio, { value: option.id, children: (0, jsx_runtime_1.jsxs)(react_2.Radio.Content, { children: [(0, jsx_runtime_1.jsx)(react_2.Radio.Control, { children: (0, jsx_runtime_1.jsx)(react_2.Radio.Indicator, {}) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium", children: option.label }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: option.hint })] })] }) }, option.id))) })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Creating…" : "Create" })] })] }));
|
|
66
66
|
}
|
|
@@ -152,5 +152,5 @@ function EventTypeFields({ editing, schedules, members, onCancel, onSaved, }) {
|
|
|
152
152
|
if (saved)
|
|
153
153
|
onSaved(saved);
|
|
154
154
|
};
|
|
155
|
-
return ((0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_2.Input, { placeholder: "Consultation", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Description" }), (0, jsx_runtime_1.jsx)(react_2.TextArea, { placeholder: "What this appointment is for", value: description, onChange: (e) => setDescription(e.currentTarget.value), disabled: isPending, rows: 2 })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid gap-4 sm:grid-cols-3", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Length (min)" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(durationMinutes), onChange: (e) => setDurationMinutes(Number(e.currentTarget.value)), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Gap before" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(bufferBefore), onChange: (e) => setBufferBefore(Number(e.currentTarget.value)), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Gap after" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(bufferAfter), onChange: (e) => setBufferAfter(Number(e.currentTarget.value)), disabled: isPending })] })] }), schedules.length > 1 && ((0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: effectiveScheduleId, onChange: (value) => setScheduleId(String(value)), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Availability" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: schedules.map((schedule) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: schedule.id, textValue: schedule.name, children: [schedule.name, (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, schedule.id))) }) })] })), members.length > 1 && ((0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: effectiveHostMemberId, onChange: (value) => setHostMemberId(String(value)), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Who hosts it" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: members.map((member) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: member.memberId, textValue: member.displayName ?? member.email, children: [member.displayName ?? member.email, (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, member.memberId))) }) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "grid gap-4 sm:grid-cols-2", children: [(0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: locationKind, onChange: (value) => setLocationKind(value), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Where" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: LOCATION_KINDS.map((kind) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: kind, textValue: LOCATION_LABELS[kind], children: [LOCATION_LABELS[kind], (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, kind))) }) })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Details" }), (0, jsx_runtime_1.jsx)(react_2.TextArea, { placeholder: locationKind === "address" ? "Where to come" : "Left blank is fine", value: locationValue, onChange: (e) => setLocationValue(e.currentTarget.value), disabled: isPending, rows: 2 })] })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Create" })] })] }));
|
|
155
|
+
return ((0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Name" }), (0, jsx_runtime_1.jsx)(react_2.Input, { placeholder: "Consultation", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Description" }), (0, jsx_runtime_1.jsx)(react_2.TextArea, { placeholder: "What this appointment is for", value: description, onChange: (e) => setDescription(e.currentTarget.value), disabled: isPending, rows: 2 })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid gap-4 sm:grid-cols-3", children: [(0, jsx_runtime_1.jsxs)(react_2.TextField, { isRequired: true, variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Length (min)" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(durationMinutes), onChange: (e) => setDurationMinutes(Number(e.currentTarget.value)), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Gap before" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(bufferBefore), onChange: (e) => setBufferBefore(Number(e.currentTarget.value)), disabled: isPending })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Gap after" }), (0, jsx_runtime_1.jsx)(react_2.Input, { type: "number", value: String(bufferAfter), onChange: (e) => setBufferAfter(Number(e.currentTarget.value)), disabled: isPending })] })] }), schedules.length > 1 && ((0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: effectiveScheduleId, onChange: (value) => setScheduleId(String(value)), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Availability" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: schedules.map((schedule) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: schedule.id, textValue: schedule.name, children: [schedule.name, (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, schedule.id))) }) })] })), members.length > 1 && ((0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: effectiveHostMemberId, onChange: (value) => setHostMemberId(String(value)), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Who hosts it" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: members.map((member) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: member.memberId, textValue: member.displayName ?? member.email, children: [member.displayName ?? member.email, (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, member.memberId))) }) })] })), (0, jsx_runtime_1.jsxs)("div", { className: "grid gap-4 sm:grid-cols-2", children: [(0, jsx_runtime_1.jsxs)(react_2.Select, { variant: "secondary", value: locationKind, onChange: (value) => setLocationKind(value), isDisabled: isPending, children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Where" }), (0, jsx_runtime_1.jsxs)(react_2.Select.Trigger, { children: [(0, jsx_runtime_1.jsx)(react_2.Select.Value, {}), (0, jsx_runtime_1.jsx)(react_2.Select.Indicator, {})] }), (0, jsx_runtime_1.jsx)(react_2.Select.Popover, { children: (0, jsx_runtime_1.jsx)(react_2.ListBox, { children: LOCATION_KINDS.map((kind) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: kind, textValue: LOCATION_LABELS[kind], children: [LOCATION_LABELS[kind], (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, kind))) }) })] }), (0, jsx_runtime_1.jsxs)(react_2.TextField, { variant: "secondary", children: [(0, jsx_runtime_1.jsx)(react_2.Label, { children: "Details" }), (0, jsx_runtime_1.jsx)(react_2.TextArea, { placeholder: locationKind === "address" ? "Where to come" : "Left blank is fine", value: locationValue, onChange: (e) => setLocationValue(e.currentTarget.value), disabled: isPending, rows: 2 })] })] }), error && (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: error }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-2", children: [(0, jsx_runtime_1.jsx)(react_2.Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Create" })] })] }));
|
|
156
156
|
}
|
|
@@ -37,6 +37,6 @@ function ConnectFilterTabs({ options, value, onChange, label, className, }) {
|
|
|
37
37
|
*/
|
|
38
38
|
(0, jsx_runtime_1.jsx)("div", { role: "radiogroup", "aria-label": label, className: (0, cn_js_1.cn)("-mx-1 flex flex-wrap gap-2 px-1", className), children: options.map((option) => {
|
|
39
39
|
const isSelected = option.value === value;
|
|
40
|
-
return ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": isSelected, onClick: () => onChange(option.value), className: "shrink-0 rounded-full focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": isSelected, onClick: () => onChange(option.value), className: "shrink-0 rounded-full focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus", children: (0, jsx_runtime_1.jsxs)(react_1.Chip, { variant: isSelected ? "primary" : "soft", color: isSelected ? "accent" : "default", className: "min-h-8 px-3.5 py-1.5 text-sm", children: [option.label, option.count !== undefined && (0, jsx_runtime_1.jsx)("span", { className: "ml-1 opacity-70", children: option.count })] }) }, option.value ?? "__all"));
|
|
41
41
|
}) }));
|
|
42
42
|
}
|
|
@@ -27,5 +27,5 @@ function ConnectGrievanceNotice({ title = "Grievance redressal", applicationName
|
|
|
27
27
|
if (!profile)
|
|
28
28
|
return null;
|
|
29
29
|
const { grievance } = profile;
|
|
30
|
-
return ((0, jsx_runtime_1.jsxs)("section", { className: (0, cn_js_1.cn)("space-y-3", className), children: [title !== null && (0, jsx_runtime_1.jsx)("h3", { className: "text-base font-semibold", children: title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-muted", children: [applicationName ? `${applicationName} is operated on ` : "This service runs on ", "the CosmoOps Connect platform. ", (0, jsx_runtime_1.jsx)("strong", { className: "font-medium text-foreground", children: profile.legalName }), " ", "is the Data Fiduciary for personal data processed here, under the Digital Personal Data Protection Act, 2023."] }), (0, jsx_runtime_1.jsxs)("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-[auto_1fr]", children: [(0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Grievance Officer" }), (0, jsx_runtime_1.jsx)("dd", { className: "text-foreground", children: grievance.officerName }), (0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Email" }), (0, jsx_runtime_1.jsx)("dd", { children: (0, jsx_runtime_1.jsx)("a", { className: "text-
|
|
30
|
+
return ((0, jsx_runtime_1.jsxs)("section", { className: (0, cn_js_1.cn)("space-y-3", className), children: [title !== null && (0, jsx_runtime_1.jsx)("h3", { className: "text-base font-semibold", children: title }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-muted", children: [applicationName ? `${applicationName} is operated on ` : "This service runs on ", "the CosmoOps Connect platform. ", (0, jsx_runtime_1.jsx)("strong", { className: "font-medium text-foreground", children: profile.legalName }), " ", "is the Data Fiduciary for personal data processed here, under the Digital Personal Data Protection Act, 2023."] }), (0, jsx_runtime_1.jsxs)("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-[auto_1fr]", children: [(0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Grievance Officer" }), (0, jsx_runtime_1.jsx)("dd", { className: "text-foreground", children: grievance.officerName }), (0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Email" }), (0, jsx_runtime_1.jsx)("dd", { children: (0, jsx_runtime_1.jsx)("a", { className: "text-link underline underline-offset-2", href: `mailto:${grievance.officerEmail}`, children: grievance.officerEmail }) }), (0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Contactable" }), (0, jsx_runtime_1.jsx)("dd", { className: "text-foreground", children: grievance.hours }), (0, jsx_runtime_1.jsx)("dt", { className: "text-muted", children: "Registered address" }), (0, jsx_runtime_1.jsx)("dd", { className: "text-foreground", children: profile.registeredAddress })] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-muted", children: ["Complaints are acknowledged and resolved within ", grievance.responseDays, " days of receipt."] })] }));
|
|
31
31
|
}
|
|
@@ -42,7 +42,7 @@ function ToolkitTile({ toolkit, isPending, onConnect, onDisconnect }) {
|
|
|
42
42
|
// Deliberately a plain `<img>`: this package ships into any React app
|
|
43
43
|
// and cannot assume `next/image` or any other framework loader.
|
|
44
44
|
// eslint-disable-next-line @next/next/no-img-element
|
|
45
|
-
(0, jsx_runtime_1.jsx)("img", { src: toolkit.logoUrl, alt: "", className: "size-full object-cover" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Puzzle, { className: "size-6 text-muted", "aria-hidden": "true" })), toolkit.status === "failed" && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-danger text-danger-foreground", children: (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "size-3", "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("span", { className: "line-clamp-2 text-center text-xs font-medium text-foreground", children: toolkit.name }), (0, jsx_runtime_1.jsx)(react_2.Chip, { size: "sm", variant: "soft", color: STATUS_COLOR[toolkit.status], children: STATUS_LABEL[toolkit.status] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full flex-col gap-1.5 pt-1", children: [CAN_CONNECT.has(toolkit.status) && ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Connect" })), toolkit.status === "active" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Reconnect" }), (0, jsx_runtime_1.jsxs)(react_2.AlertDialog, { isOpen: confirmingDisconnect, onOpenChange: setConfirmingDisconnect, children: [(0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "
|
|
45
|
+
(0, jsx_runtime_1.jsx)("img", { src: toolkit.logoUrl, alt: "", className: "size-full object-cover" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Puzzle, { className: "size-6 text-muted", "aria-hidden": "true" })), toolkit.status === "failed" && ((0, jsx_runtime_1.jsx)("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-danger text-danger-foreground", children: (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "size-3", "aria-hidden": "true" }) }))] }), (0, jsx_runtime_1.jsx)("span", { className: "line-clamp-2 text-center text-xs font-medium text-foreground", children: toolkit.name }), (0, jsx_runtime_1.jsx)(react_2.Chip, { size: "sm", variant: "soft", color: STATUS_COLOR[toolkit.status], children: STATUS_LABEL[toolkit.status] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full flex-col gap-1.5 pt-1", children: [CAN_CONNECT.has(toolkit.status) && ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Connect" })), toolkit.status === "active" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Reconnect" }), (0, jsx_runtime_1.jsxs)(react_2.AlertDialog, { isOpen: confirmingDisconnect, onOpenChange: setConfirmingDisconnect, children: [(0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "danger-soft", className: "w-full text-xs", isDisabled: isPending, children: "Disconnect" }), (0, jsx_runtime_1.jsx)(react_2.AlertDialog.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_2.AlertDialog.Container, { children: (0, jsx_runtime_1.jsxs)(react_2.AlertDialog.Dialog, { className: "sm:max-w-100", children: [(0, jsx_runtime_1.jsx)(react_2.AlertDialog.CloseTrigger, {}), (0, jsx_runtime_1.jsxs)(react_2.AlertDialog.Header, { children: [(0, jsx_runtime_1.jsx)(react_2.AlertDialog.Icon, { status: "danger" }), (0, jsx_runtime_1.jsxs)(react_2.AlertDialog.Heading, { children: ["Disconnect ", toolkit.name, "?"] })] }), (0, jsx_runtime_1.jsx)(react_2.AlertDialog.Body, { children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Every member loses this connection immediately - it can be reconnected later." }) }), (0, jsx_runtime_1.jsxs)(react_2.AlertDialog.Footer, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { slot: "close", variant: "secondary", className: "text-xs", children: "Cancel" }), (0, jsx_runtime_1.jsx)(react_2.Button, { slot: "close", variant: "danger", className: "text-xs", onPress: onDisconnect, children: "Disconnect" })] })] }) }) })] })] })), toolkit.status === "pending" && ((0, jsx_runtime_1.jsx)(react_2.Button, { size: "sm", variant: "danger-soft", className: "w-full text-xs", isDisabled: isPending, onPress: onDisconnect, children: "Cancel" }))] })] }));
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* A grid of every Composio-hosted toolkit this workspace can connect -
|
|
@@ -91,5 +91,5 @@ function ConnectIntegrationsModal({ className, label = "Integrations" }) {
|
|
|
91
91
|
})
|
|
92
92
|
.finally(() => setPendingSlug(null));
|
|
93
93
|
};
|
|
94
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(react_2.Modal, { children: [(0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", className: className, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Puzzle, { className: "size-4" }), label] }), (0, jsx_runtime_1.jsx)(react_2.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_2.Modal.Dialog, { className: "sm:max-w-lg", children: [(0, jsx_runtime_1.jsx)(react_2.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_2.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Heading, { children: "Integrations" }) }), (0, jsx_runtime_1.jsxs)(react_2.Modal.Body, { children: [isLoading && ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: Array.from({ length: 6 }, (_, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center gap-2 rounded-xl border border-separator p-3", children: [(0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "size-12 rounded-xl" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "h-3 w-16" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "h-7 w-full rounded-lg" })] }, index))) })), !isLoading && error && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-danger", children: "Could not load integrations." })), !isLoading && !error && toolkits.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-muted", children: "No integrations are enabled on this platform yet." })), !isLoading && !error && toolkits.length > 0 && ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.04 } } }, initial: "hidden", animate: "show", children: toolkits.map((toolkit) => ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 8 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsx)(ToolkitTile, { toolkit: toolkit, isPending: pendingSlug === toolkit.slug, onConnect: () => connect(toolkit.slug), onDisconnect: () => disconnect(toolkit.slug) }) }, toolkit.slug))) }))] })] }) }) })] }) }));
|
|
94
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)(react_2.Modal, { children: [(0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", className: className, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Puzzle, { className: "size-4" }), label] }), (0, jsx_runtime_1.jsx)(react_2.Modal.Backdrop, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Container, { children: (0, jsx_runtime_1.jsxs)(react_2.Modal.Dialog, { className: "sm:max-w-lg", children: [(0, jsx_runtime_1.jsx)(react_2.Modal.CloseTrigger, {}), (0, jsx_runtime_1.jsx)(react_2.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_2.Modal.Heading, { children: "Integrations" }) }), (0, jsx_runtime_1.jsxs)(react_2.Modal.Body, { children: [isLoading && ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: Array.from({ length: 6 }, (_, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center gap-2 rounded-xl border border-separator p-3", children: [(0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "size-12 rounded-xl" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "h-3 w-16" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { className: "h-7 w-full rounded-lg" })] }, index))) })), !isLoading && error && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-danger-soft-foreground", children: "Could not load integrations." })), !isLoading && !error && toolkits.length === 0 && ((0, jsx_runtime_1.jsx)("p", { className: "py-6 text-center text-sm text-muted", children: "No integrations are enabled on this platform yet." })), !isLoading && !error && toolkits.length > 0 && ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.04 } } }, initial: "hidden", animate: "show", children: toolkits.map((toolkit) => ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 8 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsx)(ToolkitTile, { toolkit: toolkit, isPending: pendingSlug === toolkit.slug, onConnect: () => connect(toolkit.slug), onDisconnect: () => disconnect(toolkit.slug) }) }, toolkit.slug))) }))] })] }) }) })] }) }));
|
|
95
95
|
}
|