@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
|
@@ -112,9 +112,9 @@ function ConnectPlans({ planIds, hideCoupon = false, className }) {
|
|
|
112
112
|
return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Sign in to see your plan options." });
|
|
113
113
|
}
|
|
114
114
|
if (error) {
|
|
115
|
-
return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger", children: "Could not load plans." });
|
|
115
|
+
return (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-danger-soft-foreground", children: "Could not load plans." });
|
|
116
116
|
}
|
|
117
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-6", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), (0, jsx_runtime_1.jsx)(react_1.Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: (0, jsx_runtime_1.jsx)(react_1.Switch.Content, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Control, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Thumb, {}) }) }) }), (0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && ((0, jsx_runtime_1.jsxs)(react_1.Chip, {
|
|
117
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.MotionProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("flex flex-col gap-6", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), (0, jsx_runtime_1.jsx)(react_1.Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: (0, jsx_runtime_1.jsx)(react_1.Switch.Content, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Control, { children: (0, jsx_runtime_1.jsx)(react_1.Switch.Thumb, {}) }) }) }), (0, jsx_runtime_1.jsx)("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { color: "accent", variant: "soft", children: ["Save up to ", bestSaving, "%"] }))] })), !hideCoupon && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Tag, { className: "size-4 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)(react_1.Input, { "aria-label": "Coupon code", placeholder: "Coupon code", value: coupon, onChange: (event) => setCoupon(event.target.value.toUpperCase()), className: "w-40" })] }))] }), subscription?.subscription?.appliedDiscount && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { className: "size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("span", { children: [(0, plan_pricing_js_1.discountLabel)(subscription.subscription.appliedDiscount), subscription.subscription.appliedDiscount.code
|
|
118
118
|
? ` - code ${subscription.subscription.appliedDiscount.code}`
|
|
119
119
|
: ""] })] })), (0, jsx_runtime_1.jsx)(ConnectCheckoutStatusRow, { checkout: checkout }), (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.06 } } }, initial: "hidden", animate: "show", children: tiers.map((tier) => {
|
|
120
120
|
// The price point this card is showing. A tier missing the toggled
|
|
@@ -134,7 +134,7 @@ function ConnectPlans({ planIds, hideCoupon = false, className }) {
|
|
|
134
134
|
tier.oneTime?.id === subscription.plan.id);
|
|
135
135
|
const isCurrent = action === "current";
|
|
136
136
|
const cadenceUnavailable = showAnnual ? tier.annual === null : tier.monthly === null;
|
|
137
|
-
return ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsxs)(react_1.Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [(0, jsx_runtime_1.jsxs)(react_1.Card.Header, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Card.Title, { children: tier.name }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && ((0, jsx_runtime_1.jsxs)(react_1.Chip, {
|
|
137
|
+
return ((0, jsx_runtime_1.jsx)(motion_js_1.m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: motion_js_1.ENTER } }, children: (0, jsx_runtime_1.jsxs)(react_1.Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [(0, jsx_runtime_1.jsxs)(react_1.Card.Header, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.Card.Title, { children: tier.name }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && ((0, jsx_runtime_1.jsxs)(react_1.Chip, { color: "accent", variant: "soft", children: ["Save ", tier.annualSavingPercent, "%"] })), isCurrentTier && (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Current" })] })] }), tier.description && (0, jsx_runtime_1.jsx)(react_1.Card.Description, { children: tier.description })] }), (0, jsx_runtime_1.jsxs)(react_1.Card.Content, { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(motion_js_1.AnimatePresence, { mode: "popLayout", initial: false, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.15 }, children: (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: "Free" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: (0, plan_pricing_js_1.formatPrice)(plan.price) }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-muted", children: (0, plan_pricing_js_1.pricePerLabel)(plan.billingInterval) })] })) }, plan.billingInterval) }), !(0, plan_pricing_js_1.isFreePlan)(plan.price) && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["+", plan.gstRate, "% GST"] })), cadenceUnavailable && ((0, jsx_runtime_1.jsxs)("p", { className: "mt-1 text-xs text-muted", children: ["Only sold ", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).adverb, "."] }))] }), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs font-medium text-accent-soft-foreground", children: [plan.trialDays, "-day free trial"] })) : null, plan.features.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: "flex flex-col gap-1.5", children: plan.features.map((feature) => (
|
|
138
138
|
// `name`, not `key` - the key is an identifier
|
|
139
139
|
// ("campaign-module"), and printing it here is what this
|
|
140
140
|
// card used to do. The description rides along as a
|
|
@@ -170,7 +170,7 @@ function CheckoutStatusRowContent({ checkout, }) {
|
|
|
170
170
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 rounded-lg border border-separator px-3 py-2 text-sm text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "size-4 shrink-0 animate-spin", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: "Finish up in the Connect window. This page updates on its own." })] }));
|
|
171
171
|
}
|
|
172
172
|
if (checkout.status === "completed") {
|
|
173
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
|
|
173
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
|
|
174
174
|
}
|
|
175
175
|
// `blocked` and `abandoned` share a row: both mean nothing changed, and both
|
|
176
176
|
// are fixed by going to the same URL - so offer it rather than explaining.
|
|
@@ -205,7 +205,7 @@ function ConnectUnsubscribeRow({ subscription, appUrl, checkout, }) {
|
|
|
205
205
|
if (subscription.cancelAtPeriodEnd) {
|
|
206
206
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-muted", children: ["Cancels", endsOn ? ` on ${new Date(endsOn).toLocaleDateString()}` : " at the end of this cycle", ". You keep access until then."] }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "tertiary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(subscription.id)}`), children: ["View on Connect", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] }));
|
|
207
207
|
}
|
|
208
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "
|
|
208
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "danger-soft", isDisabled: checkout.status === "waiting", onPress: () => checkout.start({
|
|
209
209
|
url: (0, use_checkout_js_1.buildCancelUrl)({ appUrl, subscriptionId: subscription.id }),
|
|
210
210
|
// A paused subscription cancels outright (`status` goes to
|
|
211
211
|
// `"cancelled"`); an active one is deferred to the cycle end
|
|
@@ -75,7 +75,7 @@ function BlockerRow({ blocker }) {
|
|
|
75
75
|
const { openConnect } = (0, provider_js_1.useConnect)();
|
|
76
76
|
const inProgress = IN_PROGRESS_CODES.has(blocker.code);
|
|
77
77
|
const isBlocking = blocker.severity === "blocking";
|
|
78
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-wrap items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${inProgress || !isBlocking ? "bg-surface text-muted" : "bg-danger-soft text-danger"}`, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] }))] }));
|
|
78
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `flex flex-wrap items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${inProgress || !isBlocking ? "bg-surface text-muted" : "bg-danger-soft text-danger-soft-foreground"}`, children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] }))] }));
|
|
79
79
|
}
|
|
80
80
|
function ReadyRow({ className }) {
|
|
81
81
|
return ((0, jsx_runtime_1.jsx)("p", { className: (0, cn_js_1.cn)("text-sm text-muted", className), children: "This workspace is cleared to use every part of the application." }));
|
|
@@ -50,7 +50,7 @@ function ConnectSubscribeDialog({ plan, action, current, coupon, onClose, onConf
|
|
|
50
50
|
// member and the one most in need of it: the two prices differ by an order
|
|
51
51
|
// of magnitude, and showing only the new one reads as a price rise.
|
|
52
52
|
const isChange = action === "upgrade" || action === "downgrade" || isIntervalSwitch;
|
|
53
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, { onPress: onClose }), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: HEADING[action] }) }), plan && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-muted", children: currentPlan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(currentPlan.price), "/", (0, plan_pricing_js_1.intervalLabel)(currentPlan.billingInterval).short] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate font-medium text-foreground", children: plan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(plan.price), "/", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).short] })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: plan.description })] })), (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: "Free" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["GST (", plan.gstRate, "%)"] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate) - plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["Per ", cadenceOf(plan)] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Works out to ", (0, plan_pricing_js_1.formatPrice)((0, plan_pricing_js_1.monthlyOf)(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-accent", children: [plan.trialDays, "-day free trial, if this workspace has not subscribed to this application before."] })) : null, coupon?.trim() ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Coupon ", (0, jsx_runtime_1.jsx)("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
|
|
53
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: (0, jsx_runtime_1.jsx)(react_1.Modal.Container, { size: "sm", children: (0, jsx_runtime_1.jsxs)(react_1.Modal.Dialog, { children: [(0, jsx_runtime_1.jsx)(react_1.Modal.CloseTrigger, { onPress: onClose }), (0, jsx_runtime_1.jsx)(react_1.Modal.Header, { children: (0, jsx_runtime_1.jsx)(react_1.Modal.Heading, { children: HEADING[action] }) }), plan && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-muted", children: currentPlan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(currentPlan.price), "/", (0, plan_pricing_js_1.intervalLabel)(currentPlan.billingInterval).short] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate font-medium text-foreground", children: plan.name }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: [(0, plan_pricing_js_1.formatPrice)(plan.price), "/", (0, plan_pricing_js_1.intervalLabel)(plan.billingInterval).short] })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-muted", children: plan.description })] })), (0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: "Free" })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsx)("span", { children: plan.name }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-muted", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["GST (", plan.gstRate, "%)"] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate) - plan.price) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [(0, jsx_runtime_1.jsxs)("span", { children: ["Per ", cadenceOf(plan)] }), (0, jsx_runtime_1.jsx)("span", { children: (0, plan_pricing_js_1.formatPrice)(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Works out to ", (0, plan_pricing_js_1.formatPrice)((0, plan_pricing_js_1.monthlyOf)(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !(0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-accent-soft-foreground", children: [plan.trialDays, "-day free trial, if this workspace has not subscribed to this application before."] })) : null, coupon?.trim() ? ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Coupon ", (0, jsx_runtime_1.jsx)("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
|
|
54
54
|
((0, plan_pricing_js_1.isFreePlan)(plan.price) ? ((0, jsx_runtime_1.jsx)("p", { children: "This plan is free. Connect activates it straight away - there is no payment to make, no card or UPI mandate to set up, and no invoice to expect. This page updates by itself once it is done." })) : ((0, jsx_runtime_1.jsx)("p", { children: "Payment is completed on Connect, where your card or UPI mandate is set up and the tax invoice is issued. This page updates by itself once it is done." }))), action === "upgrade" && ((0, jsx_runtime_1.jsxs)("p", { children: ["Connect prices the difference for the days left in your current cycle and collects just that amount now. From the next cycle you are billed the ", plan.name, " rate."] })), action === "downgrade" && ((0, jsx_runtime_1.jsx)("p", { children: "You keep what you have paid for. Connect works out the unused part of this cycle and applies it as credit or a refund, depending on how this application is configured." })), isIntervalSwitch && (
|
|
55
55
|
// Not a proration. The cycle you are on ends, so this is
|
|
56
56
|
// the one action where the amount collected can exceed a
|
|
@@ -91,15 +91,15 @@ function ConnectSubscriptionCard({ plansHref, className }) {
|
|
|
91
91
|
function SubscriptionChip({ state }) {
|
|
92
92
|
switch (state) {
|
|
93
93
|
case "trialing":
|
|
94
|
-
return (0, jsx_runtime_1.jsx)(react_1.Chip, {
|
|
94
|
+
return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Free trial" });
|
|
95
95
|
case "cancelling":
|
|
96
96
|
return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-surface text-muted", children: "Ending soon" });
|
|
97
97
|
case "pending":
|
|
98
98
|
return (0, jsx_runtime_1.jsx)(react_1.Chip, { className: "bg-surface text-muted", children: "Confirming payment" });
|
|
99
99
|
case "problem":
|
|
100
|
-
return (0, jsx_runtime_1.jsx)(react_1.Chip, {
|
|
100
|
+
return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "danger", variant: "soft", children: "Needs attention" });
|
|
101
101
|
default:
|
|
102
|
-
return (0, jsx_runtime_1.jsx)(react_1.Chip, {
|
|
102
|
+
return (0, jsx_runtime_1.jsx)(react_1.Chip, { color: "accent", variant: "soft", children: "Active" });
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
/** The one sentence that matters most: what happens next, and when. */
|
|
@@ -112,7 +112,7 @@ function NextEvent({ detail, state, }) {
|
|
|
112
112
|
return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-muted", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Waiting for your first payment to confirm."] }));
|
|
113
113
|
}
|
|
114
114
|
if (state === "problem") {
|
|
115
|
-
return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
|
|
115
|
+
return ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2 text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
|
|
116
116
|
}
|
|
117
117
|
if (!detail.nextBillingDate)
|
|
118
118
|
return null;
|
|
@@ -161,7 +161,7 @@ function ConnectTrialBanner({ plansHref, className }) {
|
|
|
161
161
|
const { detail, state } = useSubscriptionState();
|
|
162
162
|
if (state !== "trialing" || !detail)
|
|
163
163
|
return null;
|
|
164
|
-
return ((0, jsx_runtime_1.jsxs)("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), children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 shrink-0", "aria-hidden": "true" }), "You're on a free trial", detail.nextBillingDate ? `. Your first charge is on ${formatDate(detail.nextBillingDate)}.` : "."] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(link_button_js_1.ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] })] }));
|
|
164
|
+
return ((0, jsx_runtime_1.jsxs)("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), children: [(0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "size-4 shrink-0", "aria-hidden": "true" }), "You're on a free trial", detail.nextBillingDate ? `. Your first charge is on ${formatDate(detail.nextBillingDate)}.` : "."] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(link_button_js_1.ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "size-3.5" })] })] })] }));
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* Renders `children` for a subscribed workspace, and the "pick a plan" card for
|
|
@@ -64,7 +64,7 @@ function AddonRow({ addon }) {
|
|
|
64
64
|
// unclamped width renders as a bar overflowing its own track.
|
|
65
65
|
const fraction = addon.includedPerCycle > 0 ? Math.min(1, Math.max(0, addon.balance / addon.includedPerCycle)) : 0;
|
|
66
66
|
const carried = addon.balance > addon.includedPerCycle ? addon.balance - addon.includedPerCycle : 0;
|
|
67
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "font-medium text-foreground", children: addon.name }), (0, jsx_runtime_1.jsxs)("span", { className: overdrawn ? "text-danger" : "text-muted", children: [overdrawn
|
|
67
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "font-medium text-foreground", children: addon.name }), (0, jsx_runtime_1.jsxs)("span", { className: overdrawn ? "text-danger-soft-foreground" : "text-muted", children: [overdrawn
|
|
68
68
|
? `${formatUnits(Math.abs(addon.balance), addon.unit)} over`
|
|
69
|
-
: `${formatUnits(addon.balance, addon.unit)} left`, (0, jsx_runtime_1.jsxs)("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-surface", role: "meter", "aria-label": `${addon.name} remaining`, "aria-valuenow": addon.balance, "aria-valuemin": 0, "aria-valuemax": addon.includedPerCycle, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: motion_js_1.METER_SPRING }) }), carried > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", (0, plan_pricing_js_1.formatPrice)(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
|
|
69
|
+
: `${formatUnits(addon.balance, addon.unit)} left`, (0, jsx_runtime_1.jsxs)("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-surface", role: "meter", "aria-label": `${addon.name} remaining`, "aria-valuenow": addon.balance, "aria-valuemin": 0, "aria-valuemax": addon.includedPerCycle, children: (0, jsx_runtime_1.jsx)(motion_js_1.m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: motion_js_1.METER_SPRING }) }), carried > 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", (0, plan_pricing_js_1.formatPrice)(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && ((0, jsx_runtime_1.jsxs)("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
|
|
70
70
|
}
|
|
@@ -138,7 +138,7 @@ function ProfileSurface({ onSignOut, theme, onThemeChange, }) {
|
|
|
138
138
|
const [next] = [...keys];
|
|
139
139
|
if (typeof next === "string")
|
|
140
140
|
onThemeChange(next);
|
|
141
|
-
}, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "
|
|
141
|
+
}, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => ((0, jsx_runtime_1.jsxs)(react_1.ToggleButton, { id: value, children: [index > 0 && (0, jsx_runtime_1.jsx)(react_1.ToggleButtonGroup.Separator, {}), (0, jsx_runtime_1.jsx)(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), (0, jsx_runtime_1.jsx)(account_sessions_js_1.ConnectAccountSessions, {})] }), (0, jsx_runtime_1.jsx)(react_1.Modal.Footer, { children: (0, jsx_runtime_1.jsxs)(react_1.Button, { slot: "close", fullWidth: true, variant: "danger-soft", onPress: () => {
|
|
142
142
|
void signOut().then(onSignOut);
|
|
143
143
|
}, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LogOut, { className: "size-4 shrink-0" }), "Sign out"] }) })] }) }) }));
|
|
144
144
|
}
|
|
@@ -130,7 +130,7 @@ function ConnectWorkspaceSwitcher({ onSwitched, row = true, className, alwaysSho
|
|
|
130
130
|
// A 32px tile rather than a 20px glyph, so the initials land on the
|
|
131
131
|
// same optical centre the avatar below them uses - see
|
|
132
132
|
// `SIDEBAR_ROW_TILE`. Collapsed, this tile *is* the switcher.
|
|
133
|
-
(0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-xs font-semibold text-accent", children: workspaceInitials(label) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate", children: label }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), (0, jsx_runtime_1.jsx)(react_2.Dropdown.Popover, { children: (0, jsx_runtime_1.jsxs)(react_2.ListBox, { children: [workspaces.map((workspace) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : ((0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [workspaces.length > 0 && (0, jsx_runtime_1.jsx)(react_2.Separator, {}), (0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? (0, jsx_runtime_1.jsx)("p", { className: "mt-1 px-2 text-xs text-danger", children: error }) : null, alwaysShow ? ((0, jsx_runtime_1.jsx)(create_workspace_form_js_1.ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
|
|
133
|
+
(0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: (0, shell_context_js_1.sidebarRowClass)({ tile: true, className }), children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-accent-soft text-xs font-semibold text-accent-soft-foreground", children: workspaceInitials(label) }), (0, jsx_runtime_1.jsx)(shell_context_js_1.SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), (0, jsx_runtime_1.jsx)(motion_js_1.m.span, { className: "shrink-0 text-muted", animate: { opacity: collapsed ? 0 : 1 }, transition: collapsed ? { duration: 0.1 } : { duration: 0.18, delay: 0.1 }, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4" }) })] })) : ((0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "truncate", children: label }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), (0, jsx_runtime_1.jsx)(react_2.Dropdown.Popover, { children: (0, jsx_runtime_1.jsxs)(react_2.ListBox, { children: [workspaces.map((workspace) => ((0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [(0, jsx_runtime_1.jsxs)("div", { className: "min-w-0", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : ((0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), (0, jsx_runtime_1.jsx)(react_2.ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [workspaces.length > 0 && (0, jsx_runtime_1.jsx)(react_2.Separator, {}), (0, jsx_runtime_1.jsxs)(react_2.ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? (0, jsx_runtime_1.jsx)("p", { className: "mt-1 px-2 text-xs text-danger-soft-foreground", children: error }) : null, alwaysShow ? ((0, jsx_runtime_1.jsx)(create_workspace_form_js_1.ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
|
|
134
134
|
}
|
|
135
135
|
/** Belongs to the user, and usable here. The same two facts `switch` enforces. */
|
|
136
136
|
function selectable(workspace) {
|
|
@@ -35,5 +35,16 @@ class DriveResource {
|
|
|
35
35
|
});
|
|
36
36
|
return result.url;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* How much of the workspace's Drive is used, against its limit. Needs
|
|
40
|
+
* `drive:read` and an active subscription to your application.
|
|
41
|
+
*/
|
|
42
|
+
getUsage(input) {
|
|
43
|
+
return (0, http_js_1.request)(this.transport, {
|
|
44
|
+
method: "GET",
|
|
45
|
+
path: "/drive/usage",
|
|
46
|
+
query: { workspaceId: input.workspaceId },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
38
49
|
}
|
|
39
50
|
exports.DriveResource = DriveResource;
|
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.ConnectPageShell = ConnectPageShell;
|
|
4
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
const React = __importStar(require("react"));
|
|
5
39
|
const react_1 = require("react");
|
|
6
40
|
const page_header_js_1 = require("./page-header.js");
|
|
7
41
|
const cn_js_1 = require("../cn.js");
|
|
42
|
+
/**
|
|
43
|
+
* React's `<ViewTransition>`, when the host's React has one.
|
|
44
|
+
*
|
|
45
|
+
* It ships in the React canary that Next's App Router bundles and in no stable
|
|
46
|
+
* React yet - 19.2 exports nothing by that name. So it is looked up rather
|
|
47
|
+
* than imported: a static `import { ViewTransition }` resolves to `undefined`
|
|
48
|
+
* on a Vite or Storybook host and renders as an invalid element type, which
|
|
49
|
+
* would take down every page drawn in this shell to animate it. Absent, the
|
|
50
|
+
* page renders exactly as it did before, with no motion.
|
|
51
|
+
*
|
|
52
|
+
* Read by a computed key so a bundler doing strict export checks does not
|
|
53
|
+
* warn that `react` has no such export - on those hosts it genuinely has not.
|
|
54
|
+
*/
|
|
55
|
+
const RouteTransition = React["ViewTransition"];
|
|
8
56
|
/**
|
|
9
57
|
* The frame a page opens with: its vertical rhythm, its header, and the
|
|
10
58
|
* suspended regions beneath.
|
|
@@ -23,9 +71,23 @@ const cn_js_1 = require("../cn.js");
|
|
|
23
71
|
* never be made to wait, not that every title can be static.
|
|
24
72
|
*
|
|
25
73
|
* `space-y-5` is the page gap, owned here so it has exactly one definition.
|
|
74
|
+
*
|
|
75
|
+
* **It is also where the route transition lives**, because it is the one
|
|
76
|
+
* thing every page opens with. A transition has to wrap the *page*: a layout
|
|
77
|
+
* persists across navigation, so its enter and exit never fire, and a
|
|
78
|
+
* `template.tsx` remounts its subtree on every navigation, discarding the
|
|
79
|
+
* form drafts and scroll positions Next's `<Activity>` keeps. Where a layout
|
|
80
|
+
* draws the shell instead - a record with tabs - entering and leaving the
|
|
81
|
+
* record animates and switching tabs does not, which is right: a tab is the
|
|
82
|
+
* same place. `default="none"` keeps it still during anything else - a
|
|
83
|
+
* filter written to the URL, a `router.refresh()` after a save. The
|
|
84
|
+
* animations are `connect-page-enter`/`connect-page-exit` in `theme.css`.
|
|
26
85
|
*/
|
|
27
|
-
function ConnectPageShell({ title, description, action, clamp, above, children, className, }) {
|
|
28
|
-
|
|
86
|
+
function ConnectPageShell({ title, description, action, clamp, above, children, className, transition = true, }) {
|
|
87
|
+
const page = ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)("space-y-5", className), children: [above, title !== undefined && ((0, jsx_runtime_1.jsx)(page_header_js_1.ConnectPageHeader, { title: title, description: description, action: action, clamp: clamp })), children] }));
|
|
88
|
+
if (!transition || !RouteTransition)
|
|
89
|
+
return page;
|
|
90
|
+
return ((0, jsx_runtime_1.jsx)(RouteTransition, { enter: "connect-page-enter", exit: "connect-page-exit", default: "none", children: page }));
|
|
29
91
|
}
|
|
30
92
|
/**
|
|
31
93
|
* The page header, for a detail route that can only name itself once its data
|
|
@@ -304,7 +304,7 @@ function PaymentRetryBanner({ blocker, className, }) {
|
|
|
304
304
|
const [dismissed, setDismissed] = useState(() => dismissKey !== null && typeof window !== "undefined" && window.localStorage.getItem(dismissKey) === "1");
|
|
305
305
|
if (dismissed)
|
|
306
306
|
return null;
|
|
307
|
-
return (_jsx(MotionProvider, { children: _jsxs(m.div, { className: 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: ENTER, children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), _jsxs("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", _jsx(ExternalLink, { className: "size-3.5" })] })), _jsx(Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
|
|
307
|
+
return (_jsx(MotionProvider, { children: _jsxs(m.div, { className: 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: ENTER, children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), blocker.message, blocker.graceEndsAt && ` Resolve by ${formatDate(blocker.graceEndsAt)} to avoid losing access.`] }), _jsxs("div", { className: "flex items-center gap-2", children: [blocker.resolveUrl && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: ["Resolve payment", _jsx(ExternalLink, { className: "size-3.5" })] })), _jsx(Button, { variant: "tertiary", isIconOnly: true, "aria-label": "Dismiss", onPress: () => {
|
|
308
308
|
setDismissed(true);
|
|
309
309
|
if (dismissKey && typeof window !== "undefined")
|
|
310
310
|
window.localStorage.setItem(dismissKey, "1");
|
|
@@ -92,7 +92,7 @@ export function ConnectAccountSessions({ className, maxVisible = 4 }) {
|
|
|
92
92
|
const hasOtherSessions = sessions.some((session) => !session.isCurrent);
|
|
93
93
|
const isCollapsible = maxVisible > 0 && sessions.length > maxVisible;
|
|
94
94
|
const visible = isCollapsible && !showAll ? sessions.slice(0, maxVisible) : sessions;
|
|
95
|
-
return (_jsx(MotionProvider, { children: _jsxs("section", { className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", _jsx(Chip, { size: "sm", variant: "soft", children: sessions.length })] }), _jsx("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && (_jsx(Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
|
|
95
|
+
return (_jsx(MotionProvider, { children: _jsxs("section", { className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: ["Active sessions", _jsx(Chip, { size: "sm", variant: "soft", children: sessions.length })] }), _jsx("p", { className: "text-xs text-muted", children: "Devices signed in to your Connect account." })] }), hasOtherSessions && (_jsx(Button, { size: "sm", variant: "tertiary", className: "shrink-0 text-xs text-danger-soft-foreground", isPending: isRevokingOthers, isDisabled: isRevokingOthers, onPress: () => {
|
|
96
96
|
setIsRevokingOthers(true);
|
|
97
97
|
void mutateResource("/sessions/revoke-others")
|
|
98
98
|
.then(() => {
|
|
@@ -117,9 +117,9 @@ export function aiStatusTone(value) {
|
|
|
117
117
|
/** HeroUI semantic tokens only, so these inherit whatever theme the host defined. */
|
|
118
118
|
export const AI_TONE_CHIP = {
|
|
119
119
|
neutral: "bg-surface-tertiary text-foreground",
|
|
120
|
-
positive: "bg-success
|
|
121
|
-
warning: "bg-warning
|
|
122
|
-
danger: "bg-danger
|
|
120
|
+
positive: "bg-success-soft text-success-soft-foreground",
|
|
121
|
+
warning: "bg-warning-soft text-warning-soft-foreground",
|
|
122
|
+
danger: "bg-danger-soft text-danger-soft-foreground",
|
|
123
123
|
};
|
|
124
124
|
export const AI_TONE_DOT = {
|
|
125
125
|
neutral: "bg-muted",
|
|
@@ -24,9 +24,9 @@ function Panel({ title, children }) {
|
|
|
24
24
|
}
|
|
25
25
|
const TONE_TEXT = {
|
|
26
26
|
neutral: "text-muted",
|
|
27
|
-
positive: "text-success",
|
|
28
|
-
warning: "text-warning",
|
|
29
|
-
danger: "text-danger",
|
|
27
|
+
positive: "text-success-soft-foreground",
|
|
28
|
+
warning: "text-warning-soft-foreground",
|
|
29
|
+
danger: "text-danger-soft-foreground",
|
|
30
30
|
};
|
|
31
31
|
function StatusChip({ value }) {
|
|
32
32
|
return (_jsx("span", { className: `inline-block rounded-full px-1.5 py-0.5 text-[10px] font-medium ${AI_TONE_CHIP[aiStatusTone(value)]}`, children: String(value) }));
|
|
@@ -196,7 +196,7 @@ export function AiMetrics({ title, metrics }) {
|
|
|
196
196
|
: metric.deltaMeaning === "up-is-good"
|
|
197
197
|
? rising
|
|
198
198
|
: !rising;
|
|
199
|
-
return (_jsxs("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [_jsx("p", { className: "truncate text-[11px] text-muted", children: metric.label }), _jsx("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: formatAiValue(metric.value, metric.format) }), delta !== null && (_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: [_jsx(Arrow, { className: "size-3", "aria-hidden": true }), formatAiValue(Math.abs(delta), "percent")] })), metric.hint && _jsx("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
|
|
199
|
+
return (_jsxs("div", { className: "rounded-md bg-surface-secondary p-2.5", children: [_jsx("p", { className: "truncate text-[11px] text-muted", children: metric.label }), _jsx("p", { className: "mt-0.5 text-base font-semibold tabular-nums", children: formatAiValue(metric.value, metric.format) }), delta !== null && (_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: [_jsx(Arrow, { className: "size-3", "aria-hidden": true }), formatAiValue(Math.abs(delta), "percent")] })), metric.hint && _jsx("p", { className: "mt-0.5 text-[10px] text-muted", children: metric.hint })] }, metric.label));
|
|
200
200
|
}) }) }));
|
|
201
201
|
}
|
|
202
202
|
/** `showTimeline`: dated events, oldest first, on a single rail. */
|
|
@@ -209,7 +209,7 @@ export function AiDetails({ title, fields }) {
|
|
|
209
209
|
}
|
|
210
210
|
/** `showSteps`: an ordered procedure. `renderDetail` is how the panel feeds each step back through its markdown renderer. */
|
|
211
211
|
export function AiSteps({ title, steps, renderDetail, }) {
|
|
212
|
-
return (_jsx(Panel, { title: title, children: _jsx("ol", { className: "space-y-2.5", children: steps.map((step, index) => (_jsxs("li", { className: "flex gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-accent
|
|
212
|
+
return (_jsx(Panel, { title: title, children: _jsx("ol", { className: "space-y-2.5", children: steps.map((step, index) => (_jsxs("li", { className: "flex gap-2.5", children: [_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 }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-xs font-medium", children: step.title }), step.detail && (_jsx("div", { className: "mt-0.5 text-xs text-muted", children: renderDetail ? renderDetail(step.detail) : _jsx("span", { className: "whitespace-pre-wrap", children: step.detail }) }))] })] }, step.title))) }) }));
|
|
213
213
|
}
|
|
214
214
|
/**
|
|
215
215
|
* `showSources`: what a grounded answer actually rested on. The point of a
|
|
@@ -55,7 +55,7 @@ function AppTile({ app, onOpen }) {
|
|
|
55
55
|
// there is nothing to open and nothing to buy.
|
|
56
56
|
const disabled = app.subscribed && app.launchUrl === null;
|
|
57
57
|
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";
|
|
58
|
-
const body = (_jsxs(_Fragment, { children: [_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: [_jsx(AppGlyph, { app: app }), app.needsAttention && (_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: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && (_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: _jsx(Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? (_jsx("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent", children: "Current" })) : (!app.subscribed && (_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", _jsx(ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
|
|
58
|
+
const body = (_jsxs(_Fragment, { children: [_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: [_jsx(AppGlyph, { app: app }), app.needsAttention && (_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: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) })), !app.subscribed && (_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: _jsx(Plus, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-xs font-medium leading-tight text-foreground", children: app.name }), app.isCurrent ? (_jsx("span", { className: "text-[0.625rem] uppercase tracking-wide text-accent-soft-foreground", children: "Current" })) : (!app.subscribed && (_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", _jsx(ArrowUpRight, { className: "size-2.5", "aria-hidden": "true" })] })))] }));
|
|
59
59
|
if (disabled) {
|
|
60
60
|
return (_jsx("div", { className: `${shared} cursor-default opacity-60`, title: `${app.name} has no launch URL configured`, children: body }));
|
|
61
61
|
}
|
|
@@ -136,7 +136,7 @@ export function ConnectAppsGrid({ className, label = "Switch app", variant = "ro
|
|
|
136
136
|
};
|
|
137
137
|
const showSearch = apps.length > SEARCH_THRESHOLD;
|
|
138
138
|
const isEmpty = !isLoading && !error && mine.length === 0 && explore.length === 0;
|
|
139
|
-
return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [variant === "row" ? (_jsxs(Button, { variant: "tertiary", "aria-label": label, className: sidebarRowClass({ className }), children: [_jsx("span", { className: "flex size-5 shrink-0 items-center justify-center", children: _jsx(LayoutGrid, { className: "size-5" }) }), _jsx(SidebarLabel, { children: label })] })) : (_jsx(Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: _jsx(LayoutGrid, { className: "size-5" }) })), _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { className: "sm:max-w-lg", children: [_jsx(Modal.CloseTrigger, {}), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: "Your apps" }) }), _jsxs(Modal.Body, { className: "space-y-4", children: [showSearch && (_jsxs(TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [_jsx(Label, { className: "sr-only", children: "Filter apps" }), _jsx(Input, { className: "pr-9", placeholder: "Filter apps" }), _jsx(Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && _jsx(GridSkeleton, {}), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger", children: "Could not load your apps." })), isEmpty && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: query
|
|
139
|
+
return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [variant === "row" ? (_jsxs(Button, { variant: "tertiary", "aria-label": label, className: sidebarRowClass({ className }), children: [_jsx("span", { className: "flex size-5 shrink-0 items-center justify-center", children: _jsx(LayoutGrid, { className: "size-5" }) }), _jsx(SidebarLabel, { children: label })] })) : (_jsx(Button, { isIconOnly: true, variant: "primary", "aria-label": label, className: className, children: _jsx(LayoutGrid, { className: "size-5" }) })), _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { className: "sm:max-w-lg", children: [_jsx(Modal.CloseTrigger, {}), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: "Your apps" }) }), _jsxs(Modal.Body, { className: "space-y-4", children: [showSearch && (_jsxs(TextField, { className: "relative", variant: "secondary", value: query, onChange: setQuery, children: [_jsx(Label, { className: "sr-only", children: "Filter apps" }), _jsx(Input, { className: "pr-9", placeholder: "Filter apps" }), _jsx(Search, { className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted", "aria-hidden": "true" })] })), isLoading && _jsx(GridSkeleton, {}), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger-soft-foreground", children: "Could not load your apps." })), isEmpty && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: query
|
|
140
140
|
? `Nothing matches “${query}”.`
|
|
141
141
|
: "No apps yet. Anything this workspace can open shows up here." })), !isLoading && !error && (_jsxs(_Fragment, { children: [_jsx(AppsSection, { title: "Your apps", hint: "Open", apps: mine, offset: 0, onOpen: openApp }), _jsx(AppsSection, { title: "Explore", hint: "Subscribe on Connect", apps: explore, offset: mine.length, onOpen: openApp })] }))] })] }) }) })] }) }));
|
|
142
142
|
}
|
|
@@ -93,9 +93,9 @@ export function AskAiHistory({ refreshKey, activeConversationId, onOpenConversat
|
|
|
93
93
|
return (_jsx("div", { className: "space-y-2 p-4", "aria-busy": "true", children: [0, 1, 2].map((row) => (_jsx("div", { className: "h-9 animate-pulse rounded-md bg-surface-secondary" }, row))) }));
|
|
94
94
|
}
|
|
95
95
|
if (conversations.length === 0) {
|
|
96
|
-
return (_jsx("div", { className: "p-4", children: error ? (_jsx("p", { className: "text-xs text-danger", children: error })) : (_jsx("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
|
|
96
|
+
return (_jsx("div", { className: "p-4", children: error ? (_jsx("p", { className: "text-xs text-danger-soft-foreground", children: error })) : (_jsx("p", { className: "text-center text-xs text-muted", children: "Nothing here yet. Conversations you have are saved automatically." })) }));
|
|
97
97
|
}
|
|
98
|
-
return (_jsxs("div", { className: "p-2", children: [error && _jsx("p", { className: "px-2 pb-2 text-xs text-danger", children: error }), _jsx("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
|
|
98
|
+
return (_jsxs("div", { className: "p-2", children: [error && _jsx("p", { className: "px-2 pb-2 text-xs text-danger-soft-foreground", children: error }), _jsx("ul", { className: "space-y-0.5", children: conversations.map((conversation) => {
|
|
99
99
|
const isConfirming = confirmingId === conversation.id;
|
|
100
100
|
const isActive = conversation.id === activeConversationId;
|
|
101
101
|
return (_jsx("li", { children: isConfirming ? (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-border px-2 py-1.5", children: [_jsx("span", { className: "flex-1 truncate text-xs text-muted", children: "Delete this conversation?" }), _jsx(Button, { size: "sm", variant: "danger", className: "h-7 px-2 text-xs", isDisabled: busyId === conversation.id, onPress: () => void remove(conversation.id), children: "Delete" }), _jsx(Button, { size: "sm", variant: "tertiary", className: "h-7 px-2 text-xs", onPress: () => setConfirmingId(null), children: "Cancel" })] })) : (_jsxs("div", { className: `group flex items-center gap-1 rounded-md ${isActive ? "bg-surface-secondary" : ""}`, children: [_jsxs(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: [_jsx(MessageSquare, { className: "size-3.5 shrink-0 text-muted" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-normal", children: conversation.title })] }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "ghost", className: "shrink-0", "aria-label": `Delete "${conversation.title}"`, onPress: () => setConfirmingId(conversation.id), children: _jsx(Trash2, { className: "size-3.5 text-muted" }) })] })) }, conversation.id));
|
|
@@ -511,7 +511,7 @@ export function ConnectAskAi({ title = "Ask AI", assistantName = "Assistant", pl
|
|
|
511
511
|
// Disabled on an empty one rather than hidden: a control that
|
|
512
512
|
// appears and disappears as you use the panel is harder to find
|
|
513
513
|
// than one that is always in the same place.
|
|
514
|
-
isDisabled: turns.length === 0, onPress: startNewConversation, children: _jsx(Plus, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: _jsx(History, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: _jsx(X, { className: "size-4" }) })] }), showHistory ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : (_jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => (_jsx(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" && (_jsxs("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [_jsx("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), _jsx(ThinkingShimmer, {})] })), error && (_jsxs("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-danger/30 bg-danger
|
|
514
|
+
isDisabled: turns.length === 0, onPress: startNewConversation, children: _jsx(Plus, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": showHistory ? "Hide past conversations" : "Past conversations", "aria-pressed": showHistory, onPress: () => setShowHistory((open) => !open), children: _jsx(History, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Close", onPress: () => setIsOpen(false), children: _jsx(X, { className: "size-4" }) })] }), showHistory ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(AskAiHistory, { refreshKey: writeCount, activeConversationId: conversationId, onOpenConversation: openConversation }) })) : (_jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [turns.length === 0 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted", children: hint }), currentScreen?.suggestions?.length ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: currentScreen.suggestions.map((suggestion) => (_jsx(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" && (_jsxs("div", { className: "rounded-md border border-border bg-accent/5 p-2.5 text-sm", children: [_jsx("p", { className: "mb-1 text-[11px] font-semibold tracking-wide text-muted uppercase", children: assistantName }), _jsx(ThinkingShimmer, {})] })), error && (_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: [_jsx(TriangleAlert, { className: "mt-px size-3.5 shrink-0", "aria-hidden": true }), _jsx("p", { className: "flex-1", children: error.message }), error.retry && (_jsxs(Button, { size: "sm", variant: "tertiary", className: "h-6 shrink-0 px-2 text-xs", onPress: retry, isDisabled: isBusy, children: [_jsx(RotateCcw, { className: "size-3", "aria-hidden": true }), "Try again"] }))] })), _jsx("div", { ref: bottomRef })] })), _jsxs("form", { onSubmit: handleSubmit, className: "flex gap-2 border-t border-border p-3", children: [_jsxs(TextField, { variant: "secondary", className: "flex-1", children: [_jsx(Label, { className: "sr-only", children: placeholder }), _jsx(Input, { placeholder: placeholder, value: input, onChange: (event) => setInput(event.currentTarget.value), disabled: isBusy })] }), isBusy ? (_jsx(Button, { type: "button", isIconOnly: true, variant: "secondary", "aria-label": "Stop generating", onPress: stop, children: _jsx(Square, { className: "size-3.5" }) })) : (_jsx(Button, { type: "submit", isIconOnly: true, variant: "primary", "aria-label": "Send", isDisabled: !input.trim(), children: _jsx(Send, { className: "size-3.5" }) }))] })] }) })] }));
|
|
515
515
|
}
|
|
516
516
|
/** Hoisted for the same reference-equality reason as `ANIMATED` - Streamdown compares it by identity. */
|
|
517
517
|
const LINK_SAFETY = { enabled: false };
|
|
@@ -96,5 +96,5 @@ function ScheduleFields({ editing, onCancel, onSaved, }) {
|
|
|
96
96
|
const interval = weeklyHours[day][0];
|
|
97
97
|
const isOpenDay = interval !== undefined;
|
|
98
98
|
return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { isSelected: isOpenDay, onChange: (open) => toggleDay(day, open), isDisabled: isPending, children: _jsxs(Switch.Content, { children: [_jsx(Switch.Control, { children: _jsx(Switch.Thumb, {}) }), _jsx("span", { className: "w-24 shrink-0 text-sm", children: WEEKDAY_LABELS[day] })] }) }), isOpenDay && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { type: "time", value: minutesToTimeInput(interval.startMinute), onChange: (e) => setDayTime(day, "startMinute", e.currentTarget.value), disabled: isPending }), _jsx("span", { className: "text-xs text-muted", children: "to" }), _jsx(Input, { type: "time", value: minutesToTimeInput(interval.endMinute), onChange: (e) => setDayTime(day, "endMinute", e.currentTarget.value), disabled: isPending })] }))] }, day));
|
|
99
|
-
}) }), error && _jsx("p", { className: "text-sm text-danger", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
|
|
99
|
+
}) }), error && _jsx("p", { className: "text-sm text-danger-soft-foreground", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", onPress: onCancel, isDisabled: isPending, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", isDisabled: !isFormValid, children: isPending ? "Saving..." : isEdit ? "Save changes" : "Save" })] })] }));
|
|
100
100
|
}
|
|
@@ -65,7 +65,7 @@ export function ConnectBookingCalendar({ timeZone, hostMemberId, window: visible
|
|
|
65
65
|
const marks = useMemo(() => hourMarks(visibleWindow), [visibleWindow]);
|
|
66
66
|
const shiftWeek = (weeks) => setWeekAnchor((anchor) => new Date(anchor.getTime() + weeks * 7 * 24 * 60 * 60_000));
|
|
67
67
|
const eventTypeById = useMemo(() => new Map(eventTypes.map((eventType) => [eventType.id, eventType])), [eventTypes]);
|
|
68
|
-
return (_jsxs("div", { className: cn("flex flex-col gap-4", className), children: [_jsxs("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Previous week", onPress: () => shiftWeek(-1), children: _jsx(ChevronLeft, { className: "size-4", "aria-hidden": true }) }), _jsx(Button, { size: "sm", variant: "secondary", onPress: () => setWeekAnchor(startOfWeek(new Date(), zone)), children: "Today" }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Next week", onPress: () => shiftWeek(1), children: _jsx(ChevronRight, { className: "size-4", "aria-hidden": true }) }), _jsx("p", { className: "ml-2 text-sm font-medium", children: weekLabel(columns, zone) })] }), canBook && eventTypes.length > 0 && (_jsx(Button, { size: "sm", variant: "primary", onPress: () => setBooking({ startAt: nextHour(now) }), children: "New appointment" }))] }), error && _jsx("p", { className: "text-sm text-danger", children: error }), isLoading ? (_jsx(Skeleton, { className: "h-96 w-full rounded-lg" })) : (_jsxs(_Fragment, { children: [_jsx(Card, { className: "hidden overflow-hidden sm:block", children: _jsx(WeekGrid, { columns: columns, marks: marks, window: visibleWindow, positioned: positioned, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected, onPickEmpty: canBook ? (startAt) => setBooking({ startAt }) : undefined }) }), _jsx("div", { className: "sm:hidden", children: _jsx(AgendaView, { columns: columns, bookings: bookings, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected }) })] })), booking && (_jsx(ConnectBookingDialog, { isOpen: true, onOpenChange: (open) => !open && setBooking(null), eventTypes: eventTypes, initialStartAt: booking.startAt, timeZone: zone, onBooked: () => {
|
|
68
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-4", className), children: [_jsxs("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Previous week", onPress: () => shiftWeek(-1), children: _jsx(ChevronLeft, { className: "size-4", "aria-hidden": true }) }), _jsx(Button, { size: "sm", variant: "secondary", onPress: () => setWeekAnchor(startOfWeek(new Date(), zone)), children: "Today" }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", "aria-label": "Next week", onPress: () => shiftWeek(1), children: _jsx(ChevronRight, { className: "size-4", "aria-hidden": true }) }), _jsx("p", { className: "ml-2 text-sm font-medium", children: weekLabel(columns, zone) })] }), canBook && eventTypes.length > 0 && (_jsx(Button, { size: "sm", variant: "primary", onPress: () => setBooking({ startAt: nextHour(now) }), children: "New appointment" }))] }), error && _jsx("p", { className: "text-sm text-danger-soft-foreground", children: error }), isLoading ? (_jsx(Skeleton, { className: "h-96 w-full rounded-lg" })) : (_jsxs(_Fragment, { children: [_jsx(Card, { className: "hidden overflow-hidden sm:block", children: _jsx(WeekGrid, { columns: columns, marks: marks, window: visibleWindow, positioned: positioned, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected, onPickEmpty: canBook ? (startAt) => setBooking({ startAt }) : undefined }) }), _jsx("div", { className: "sm:hidden", children: _jsx(AgendaView, { columns: columns, bookings: bookings, eventTypeById: eventTypeById, zone: zone, onSelect: setSelected }) })] })), booking && (_jsx(ConnectBookingDialog, { isOpen: true, onOpenChange: (open) => !open && setBooking(null), eventTypes: eventTypes, initialStartAt: booking.startAt, timeZone: zone, onBooked: () => {
|
|
69
69
|
setBooking(null);
|
|
70
70
|
refetch();
|
|
71
71
|
} })), selected && (_jsx(ConnectBookingDetail, { booking: selected, eventType: eventTypeById.get(selected.eventTypeId) ?? null, timeZone: zone, onOpenChange: (open) => !open && setSelected(null), onChanged: () => {
|
|
@@ -75,7 +75,7 @@ export function ConnectBookingCalendar({ timeZone, hostMemberId, window: visible
|
|
|
75
75
|
}
|
|
76
76
|
function WeekGrid({ columns, marks, window: visibleWindow, positioned, eventTypeById, zone, onSelect, onPickEmpty, }) {
|
|
77
77
|
const span = visibleWindow.endMinute - visibleWindow.startMinute;
|
|
78
|
-
return (_jsxs("div", { className: "grid grid-cols-[3rem_repeat(7,minmax(0,1fr))]", children: [_jsx("div", { className: "border-b border-separator" }), columns.map((column) => (_jsx("div", { className: `border-b border-l border-separator px-2 py-2 text-center ${column.isToday ? "bg-accent/5" : ""}`, children: _jsx("p", { className: `text-xs font-medium ${column.isToday ? "text-accent" : ""}`, children: column.label }) }, column.dateKey))), _jsx("div", { className: "relative", style: { height: `${(span / 60) * 3}rem` }, children: marks.map((mark) => (_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) => (_jsxs("div", { className: `relative border-l border-separator ${column.isToday ? "bg-accent/5" : ""}`, style: { height: `${(span / 60) * 3}rem` }, children: [marks.map((mark) => (_jsx("div", { className: "absolute inset-x-0 border-t border-separator/60", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` } }, mark.minute))), onPickEmpty &&
|
|
78
|
+
return (_jsxs("div", { className: "grid grid-cols-[3rem_repeat(7,minmax(0,1fr))]", children: [_jsx("div", { className: "border-b border-separator" }), columns.map((column) => (_jsx("div", { className: `border-b border-l border-separator px-2 py-2 text-center ${column.isToday ? "bg-accent/5" : ""}`, children: _jsx("p", { className: `text-xs font-medium ${column.isToday ? "text-accent-soft-foreground" : ""}`, children: column.label }) }, column.dateKey))), _jsx("div", { className: "relative", style: { height: `${(span / 60) * 3}rem` }, children: marks.map((mark) => (_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) => (_jsxs("div", { className: `relative border-l border-separator ${column.isToday ? "bg-accent/5" : ""}`, style: { height: `${(span / 60) * 3}rem` }, children: [marks.map((mark) => (_jsx("div", { className: "absolute inset-x-0 border-t border-separator/60", style: { top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%` } }, mark.minute))), onPickEmpty &&
|
|
79
79
|
marks.map((mark) => (_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: {
|
|
80
80
|
top: `${((mark.minute - visibleWindow.startMinute) / span) * 100}%`,
|
|
81
81
|
height: `${(60 / span) * 100}%`,
|
|
@@ -90,7 +90,7 @@ function WeekGrid({ columns, marks, window: visibleWindow, positioned, eventType
|
|
|
90
90
|
// gap so two adjacent blocks read as two things.
|
|
91
91
|
left: `${(entry.column / entry.columns) * 100}%`,
|
|
92
92
|
width: `calc(${(1 / entry.columns) * 100}% - 2px)`,
|
|
93
|
-
}, children: [_jsx("span", { className: "block truncate text-[0.625rem] font-medium text-accent", children: timeLabel(new Date(entry.booking.startAt), zone) }), _jsx("span", { className: "block truncate text-[0.625rem]", children: entry.booking.attendee.name }), eventType && (_jsx("span", { className: "block truncate text-[0.625rem] text-muted", children: eventType.name }))] }, entry.booking.id));
|
|
93
|
+
}, children: [_jsx("span", { className: "block truncate text-[0.625rem] font-medium text-accent-soft-foreground", children: timeLabel(new Date(entry.booking.startAt), zone) }), _jsx("span", { className: "block truncate text-[0.625rem]", children: entry.booking.attendee.name }), eventType && (_jsx("span", { className: "block truncate text-[0.625rem] text-muted", children: eventType.name }))] }, entry.booking.id));
|
|
94
94
|
})] }, column.dateKey)))] }));
|
|
95
95
|
}
|
|
96
96
|
function AgendaView({ columns, bookings, eventTypeById, zone, onSelect, }) {
|
|
@@ -109,7 +109,7 @@ function AgendaView({ columns, bookings, eventTypeById, zone, onSelect, }) {
|
|
|
109
109
|
if (populated.length === 0) {
|
|
110
110
|
return (_jsx(ConnectEmptyState, { variant: "dashed", icon: _jsx(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." }));
|
|
111
111
|
}
|
|
112
|
-
return (_jsx("div", { className: "flex flex-col gap-4", children: populated.map((column) => (_jsxs("section", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: `text-sm font-medium ${column.isToday ? "text-accent" : ""}`, children: column.label }), (byDay.get(column.dateKey) ?? []).map((booking) => {
|
|
112
|
+
return (_jsx("div", { className: "flex flex-col gap-4", children: populated.map((column) => (_jsxs("section", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: `text-sm font-medium ${column.isToday ? "text-accent-soft-foreground" : ""}`, children: column.label }), (byDay.get(column.dateKey) ?? []).map((booking) => {
|
|
113
113
|
const eventType = eventTypeById.get(booking.eventTypeId);
|
|
114
114
|
const Icon = eventType ? LOCATION_ICON[eventType.locationKind] : CalendarDays;
|
|
115
115
|
return (_jsx("button", { type: "button", onClick: () => onSelect(booking), children: _jsx(Card, { className: "transition-colors hover:bg-surface", children: _jsxs(Card.Content, { className: "flex items-center gap-3 py-3 text-left", children: [_jsx(Icon, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: booking.attendee.name }), _jsxs("p", { className: "truncate text-xs text-muted", children: [timeLabel(new Date(booking.startAt), zone), eventType ? ` · ${eventType.name}` : ""] })] }), booking.source === "agent-call" && (_jsxs(Chip, { size: "sm", variant: "secondary", children: [_jsx(Phone, { className: "size-3", "aria-hidden": true }), "Agent"] }))] }) }) }, booking.id));
|
|
@@ -34,7 +34,7 @@ export function ConnectBookingDialog({ isOpen, onOpenChange, eventTypes, initial
|
|
|
34
34
|
// The old pick belongs to the old type's grid and is
|
|
35
35
|
// almost never a legal slot on the new one.
|
|
36
36
|
setChosen(null);
|
|
37
|
-
}, children: eventType.name }, eventType.id))) })), _jsx(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone }), _jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending })] }), _jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Phone" }), _jsx(Input, { type: "tel", value: phone, onChange: (e) => setPhone(e.currentTarget.value), disabled: isPending })] }), error && _jsx("p", { className: "text-sm text-danger", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "secondary", onPress: () => onOpenChange(false), isDisabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", isDisabled: !canSubmit, onPress: async () => {
|
|
37
|
+
}, children: eventType.name }, eventType.id))) })), _jsx(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone }), _jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending })] }), _jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Phone" }), _jsx(Input, { type: "tel", value: phone, onChange: (e) => setPhone(e.currentTarget.value), disabled: isPending })] }), error && _jsx("p", { className: "text-sm text-danger-soft-foreground", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "secondary", onPress: () => onOpenChange(false), isDisabled: isPending, children: "Cancel" }), _jsx(Button, { variant: "primary", isDisabled: !canSubmit, onPress: async () => {
|
|
38
38
|
clearError();
|
|
39
39
|
const booked = await createBooking({
|
|
40
40
|
eventTypeId,
|
|
@@ -62,7 +62,7 @@ export function ConnectBookingDetail({ booking, eventType, timeZone, onOpenChang
|
|
|
62
62
|
to: new Date(start.getTime() + SEARCH_DAYS * 24 * 60 * 60_000),
|
|
63
63
|
}), [start]);
|
|
64
64
|
const { slots, isLoading } = useAvailableSlots(isMoving ? { eventTypeId: booking.eventTypeId, ...window, excludeBookingId: booking.id } : null);
|
|
65
|
-
return (_jsx(Modal, { isOpen: true, onOpenChange: onOpenChange, children: _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.Header, { children: booking.attendee.name }), _jsxs(Modal.Body, { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm", children: [_jsx(Clock, { className: "size-4 text-muted", "aria-hidden": true }), longLabel(start, timeZone)] }), eventType && _jsx("p", { className: "text-sm text-muted", children: eventType.name }), _jsx("p", { className: "text-sm text-muted", children: booking.attendee.phone }), booking.notes && _jsx("p", { className: "pt-1 text-sm", children: booking.notes })] }), isMoving && (_jsx(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone })), error && _jsx("p", { className: "text-sm text-danger", children: error }), _jsx("div", { className: "flex flex-wrap justify-end gap-2", children: isMoving ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "secondary", onPress: () => setMoving(false), isDisabled: isPending, children: "Back" }), _jsx(Button, { variant: "primary", isDisabled: !chosen || isPending, onPress: async () => {
|
|
65
|
+
return (_jsx(Modal, { isOpen: true, onOpenChange: onOpenChange, children: _jsx(Modal.Backdrop, { children: _jsx(Modal.Container, { children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.Header, { children: booking.attendee.name }), _jsxs(Modal.Body, { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm", children: [_jsx(Clock, { className: "size-4 text-muted", "aria-hidden": true }), longLabel(start, timeZone)] }), eventType && _jsx("p", { className: "text-sm text-muted", children: eventType.name }), _jsx("p", { className: "text-sm text-muted", children: booking.attendee.phone }), booking.notes && _jsx("p", { className: "pt-1 text-sm", children: booking.notes })] }), isMoving && (_jsx(SlotPicker, { slots: slots, isLoading: isLoading, chosen: chosen, onChoose: setChosen, timeZone: timeZone })), error && _jsx("p", { className: "text-sm text-danger-soft-foreground", children: error }), _jsx("div", { className: "flex flex-wrap justify-end gap-2", children: isMoving ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "secondary", onPress: () => setMoving(false), isDisabled: isPending, children: "Back" }), _jsx(Button, { variant: "primary", isDisabled: !chosen || isPending, onPress: async () => {
|
|
66
66
|
clearError();
|
|
67
67
|
const moved = await rescheduleBooking({
|
|
68
68
|
bookingId: booking.id,
|