@cosmoops/connect-sdk 0.45.0 → 0.47.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/dist/cjs/client/components/access-gate.js +1 -1
  3. package/dist/cjs/client/components/account-sessions.js +1 -1
  4. package/dist/cjs/client/components/ai-format.js +3 -3
  5. package/dist/cjs/client/components/ai-widgets.js +5 -5
  6. package/dist/cjs/client/components/apps-grid.js +2 -2
  7. package/dist/cjs/client/components/ask-ai-history.js +2 -2
  8. package/dist/cjs/client/components/ask-ai.js +1 -1
  9. package/dist/cjs/client/components/availability-form.js +1 -1
  10. package/dist/cjs/client/components/booking-calendar.js +4 -4
  11. package/dist/cjs/client/components/booking-dialogs.js +2 -2
  12. package/dist/cjs/client/components/call-button.js +6 -6
  13. package/dist/cjs/client/components/create-workspace-form.js +1 -1
  14. package/dist/cjs/client/components/event-type-form.js +1 -1
  15. package/dist/cjs/client/components/filter-tabs.js +1 -1
  16. package/dist/cjs/client/components/grievance-notice.js +1 -1
  17. package/dist/cjs/client/components/integrations-modal.js +2 -2
  18. package/dist/cjs/client/components/plans.js +5 -5
  19. package/dist/cjs/client/components/readiness.js +1 -1
  20. package/dist/cjs/client/components/subscribe-dialog.js +1 -1
  21. package/dist/cjs/client/components/subscription.js +5 -5
  22. package/dist/cjs/client/components/usage-meter.js +2 -2
  23. package/dist/cjs/client/components/user-menu.js +1 -1
  24. package/dist/cjs/client/components/workspace-switcher.js +1 -1
  25. package/dist/cjs/client/portal-links.js +1 -1
  26. package/dist/cjs/errors.js +9 -0
  27. package/dist/cjs/server/client.js +1 -1
  28. package/dist/cjs/server/http.js +4 -1
  29. package/dist/cjs/server/resources/drive.js +11 -0
  30. package/dist/cjs/server/resources/integrations.js +16 -0
  31. package/dist/cjs/server/resources/workspaces.js +9 -0
  32. package/dist/cjs/ui/page-shell.js +64 -2
  33. package/dist/esm/client/components/access-gate.js +1 -1
  34. package/dist/esm/client/components/account-sessions.js +1 -1
  35. package/dist/esm/client/components/ai-format.js +3 -3
  36. package/dist/esm/client/components/ai-widgets.js +5 -5
  37. package/dist/esm/client/components/apps-grid.js +2 -2
  38. package/dist/esm/client/components/ask-ai-history.js +2 -2
  39. package/dist/esm/client/components/ask-ai.js +1 -1
  40. package/dist/esm/client/components/availability-form.js +1 -1
  41. package/dist/esm/client/components/booking-calendar.js +4 -4
  42. package/dist/esm/client/components/booking-dialogs.js +2 -2
  43. package/dist/esm/client/components/call-button.js +6 -6
  44. package/dist/esm/client/components/create-workspace-form.js +1 -1
  45. package/dist/esm/client/components/event-type-form.js +1 -1
  46. package/dist/esm/client/components/filter-tabs.js +1 -1
  47. package/dist/esm/client/components/grievance-notice.js +1 -1
  48. package/dist/esm/client/components/integrations-modal.js +2 -2
  49. package/dist/esm/client/components/plans.js +5 -5
  50. package/dist/esm/client/components/readiness.js +1 -1
  51. package/dist/esm/client/components/subscribe-dialog.js +1 -1
  52. package/dist/esm/client/components/subscription.js +5 -5
  53. package/dist/esm/client/components/usage-meter.js +2 -2
  54. package/dist/esm/client/components/user-menu.js +1 -1
  55. package/dist/esm/client/components/workspace-switcher.js +1 -1
  56. package/dist/esm/client/portal-links.js +1 -1
  57. package/dist/esm/errors.js +9 -0
  58. package/dist/esm/server/client.js +1 -1
  59. package/dist/esm/server/http.js +4 -1
  60. package/dist/esm/server/resources/drive.js +11 -0
  61. package/dist/esm/server/resources/integrations.js +16 -0
  62. package/dist/esm/server/resources/workspaces.js +9 -0
  63. package/dist/esm/ui/page-shell.js +31 -2
  64. package/dist/theme.css +78 -2
  65. package/dist/types/client/components/call-button.d.ts +3 -1
  66. package/dist/types/client/portal-links.d.ts +1 -1
  67. package/dist/types/errors.d.ts +7 -0
  68. package/dist/types/server/client.d.ts +1 -1
  69. package/dist/types/server/index.d.ts +1 -1
  70. package/dist/types/server/resources/drive.d.ts +8 -1
  71. package/dist/types/server/resources/integrations.d.ts +33 -1
  72. package/dist/types/server/resources/workspaces.d.ts +9 -0
  73. package/dist/types/types.d.ts +21 -0
  74. package/dist/types/ui/page-shell.d.ts +20 -2
  75. package/package.json +1 -1
@@ -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,
@@ -320,7 +320,7 @@ function unavailableReason(availability) {
320
320
  function ConnectCallUnavailable({ availability, className, }) {
321
321
  const { text, action } = unavailableReason(availability);
322
322
  const href = useConnectPortalUrl(action?.path ?? "/calling");
323
- return (_jsxs("p", { className: cn("text-xs text-muted", className), children: [text, " ", action && (_jsx("a", { href: href, className: "font-medium text-accent hover:underline", children: action.label }))] }));
323
+ return (_jsxs("p", { className: cn("text-xs text-muted", className), children: [text, " ", action && (_jsx("a", { href: href, className: "font-medium text-link hover:underline", children: action.label }))] }));
324
324
  }
325
325
  function formatRupees(paise) {
326
326
  return `₹${(paise / 100).toLocaleString("en-IN", {
@@ -342,13 +342,13 @@ export function ConnectCallingStatus({ className }) {
342
342
  return _jsx("div", { className: cn("h-24 animate-pulse rounded-lg bg-surface-secondary", className) });
343
343
  }
344
344
  const rate = formatRupees(availability.ratePaisePerMinute);
345
- return (_jsxs("div", { className: cn("space-y-3 rounded-lg border border-separator bg-surface p-4", className), children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs text-muted", children: "Business number" }), _jsx("p", { className: "text-sm font-medium tabular-nums", children: availability.number ?? (availability.status === "number_pending" ? "Being set up…" : "None") })] }), availability.number && (_jsxs("div", { className: "text-right", children: [_jsx("p", { className: "text-xs text-muted", children: "Calling minutes" }), _jsxs("p", { className: cn("text-sm font-medium tabular-nums", availability.lowBalance && "text-danger"), children: [formatRupees(availability.available), " \u00B7 about ", availability.availableMinutes, " min"] })] }))] }), availability.status === "no_permission" ? (_jsx("p", { className: "text-xs text-muted", children: "You don't have permission to make calls here." })) : !availability.number ? (_jsxs("p", { className: "text-xs text-muted", children: ["Call customers from their record, from your own business number. Calls are not recorded, and minutes are prepaid at ", rate, " + GST a minute.", " ", availability.status === "number_pending"
346
- ? availability.canManageNumber && (_jsx("a", { href: callingHref, className: "font-medium text-accent hover:underline", children: "Check on your number" }))
345
+ return (_jsxs("div", { className: cn("space-y-3 rounded-lg border border-separator bg-surface p-4", className), children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-xs text-muted", children: "Business number" }), _jsx("p", { className: "text-sm font-medium tabular-nums", children: availability.number ?? (availability.status === "number_pending" ? "Being set up…" : "None") })] }), availability.number && (_jsxs("div", { className: "text-right", children: [_jsx("p", { className: "text-xs text-muted", children: "Calling minutes" }), _jsxs("p", { className: cn("text-sm font-medium tabular-nums", availability.lowBalance && "text-danger-soft-foreground"), children: [formatRupees(availability.available), " \u00B7 about ", availability.availableMinutes, " min"] })] }))] }), availability.status === "no_permission" ? (_jsx("p", { className: "text-xs text-muted", children: "You don't have permission to make calls here." })) : !availability.number ? (_jsxs("p", { className: "text-xs text-muted", children: ["Call customers from their record, from your own business number. Calls are not recorded, and minutes are prepaid at ", rate, " + GST a minute.", " ", availability.status === "number_pending"
346
+ ? availability.canManageNumber && (_jsx("a", { href: callingHref, className: "font-medium text-link hover:underline", children: "Check on your number" }))
347
347
  : availability.canManageNumber && availability.offer
348
- ? (_jsxs("a", { href: offerHref, className: "font-medium text-accent hover:underline", children: ["Add ", availability.offer.addonName, " (", formatRupees(availability.offer.price), " + GST)"] }))
348
+ ? (_jsxs("a", { href: offerHref, className: "font-medium text-link hover:underline", children: ["Add ", availability.offer.addonName, " (", formatRupees(availability.offer.price), " + GST)"] }))
349
349
  : "Ask a workspace admin to add a business number."] })) : (_jsxs("p", { className: "text-xs text-muted", children: [availability.lowBalance
350
350
  ? "Calling minutes are running low. "
351
- : `Calls cost ${rate} + GST a minute, from prepaid minutes. `, availability.canTopUp ? (_jsx("a", { href: callingHref, className: "font-medium text-accent hover:underline", children: "Top up minutes" })) : ("A workspace admin can top up.")] }))] }));
351
+ : `Calls cost ${rate} + GST a minute, from prepaid minutes. `, availability.canTopUp ? (_jsx("a", { href: callingHref, className: "font-medium text-link hover:underline", children: "Top up minutes" })) : ("A workspace admin can top up.")] }))] }));
352
352
  }
353
353
  const PHASE_LABEL = {
354
354
  connecting: "Connecting…",
@@ -370,7 +370,7 @@ export function ConnectActiveCallBar({ call, onHangUp, onToggleMute, className,
370
370
  ? `Call ended · ${formatSeconds(call.endedAfterSeconds)}`
371
371
  : PHASE_LABEL[call.phase];
372
372
  const remaining = call.phase === "ended" ? minutesLeftLabel(call.minutesLeft) : null;
373
- return (_jsx("div", { role: "status", "aria-live": "polite", className: cn("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: _jsxs("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent", children: _jsx(PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: call.label }), _jsxs("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && _jsx("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && (_jsxs(_Fragment, { children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? _jsx(MicOff, { className: "size-4" }) : _jsx(Mic, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: _jsx(PhoneOff, { className: "size-4" }) })] }))] }) }));
373
+ return (_jsx("div", { role: "status", "aria-live": "polite", className: cn("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: _jsxs("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent-soft-foreground", children: _jsx(PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: call.label }), _jsxs("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && _jsx("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && (_jsxs(_Fragment, { children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? _jsx(MicOff, { className: "size-4" }) : _jsx(Mic, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: _jsx(PhoneOff, { className: "size-4" }) })] }))] }) }));
374
374
  }
375
375
  /** "42 min of calling left", once the post-call balance read has landed. */
376
376
  function minutesLeftLabel(minutes) {
@@ -59,5 +59,5 @@ function CreateWorkspaceFields({ onCancel, onCreated, }) {
59
59
  if (created)
60
60
  onCreated(created);
61
61
  };
62
- return (_jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { placeholder: "Acme Logistics", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), _jsxs(RadioGroup, { variant: "secondary", value: type, onChange: (value) => setType(value), isDisabled: isPending, children: [_jsx(Label, { children: "Type" }), _jsx("div", { className: "mt-1.5 flex flex-col gap-2", children: WORKSPACE_TYPES.map((option) => (_jsx(Radio, { value: option.id, children: _jsxs(Radio.Content, { children: [_jsx(Radio.Control, { children: _jsx(Radio.Indicator, {}) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: option.label }), _jsx("p", { className: "text-xs text-muted", children: option.hint })] })] }) }, option.id))) })] }), 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 ? "Creating…" : "Create" })] })] }));
62
+ return (_jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { placeholder: "Acme Logistics", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), _jsxs(RadioGroup, { variant: "secondary", value: type, onChange: (value) => setType(value), isDisabled: isPending, children: [_jsx(Label, { children: "Type" }), _jsx("div", { className: "mt-1.5 flex flex-col gap-2", children: WORKSPACE_TYPES.map((option) => (_jsx(Radio, { value: option.id, children: _jsxs(Radio.Content, { children: [_jsx(Radio.Control, { children: _jsx(Radio.Indicator, {}) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: option.label }), _jsx("p", { className: "text-xs text-muted", children: option.hint })] })] }) }, option.id))) })] }), 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 ? "Creating…" : "Create" })] })] }));
63
63
  }
@@ -149,5 +149,5 @@ function EventTypeFields({ editing, schedules, members, onCancel, onSaved, }) {
149
149
  if (saved)
150
150
  onSaved(saved);
151
151
  };
152
- return (_jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { placeholder: "Consultation", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Description" }), _jsx(TextArea, { placeholder: "What this appointment is for", value: description, onChange: (e) => setDescription(e.currentTarget.value), disabled: isPending, rows: 2 })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Length (min)" }), _jsx(Input, { type: "number", value: String(durationMinutes), onChange: (e) => setDurationMinutes(Number(e.currentTarget.value)), disabled: isPending })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Gap before" }), _jsx(Input, { type: "number", value: String(bufferBefore), onChange: (e) => setBufferBefore(Number(e.currentTarget.value)), disabled: isPending })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Gap after" }), _jsx(Input, { type: "number", value: String(bufferAfter), onChange: (e) => setBufferAfter(Number(e.currentTarget.value)), disabled: isPending })] })] }), schedules.length > 1 && (_jsxs(Select, { variant: "secondary", value: effectiveScheduleId, onChange: (value) => setScheduleId(String(value)), isDisabled: isPending, children: [_jsx(Label, { children: "Availability" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: schedules.map((schedule) => (_jsxs(ListBox.Item, { id: schedule.id, textValue: schedule.name, children: [schedule.name, _jsx(ListBox.ItemIndicator, {})] }, schedule.id))) }) })] })), members.length > 1 && (_jsxs(Select, { variant: "secondary", value: effectiveHostMemberId, onChange: (value) => setHostMemberId(String(value)), isDisabled: isPending, children: [_jsx(Label, { children: "Who hosts it" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: members.map((member) => (_jsxs(ListBox.Item, { id: member.memberId, textValue: member.displayName ?? member.email, children: [member.displayName ?? member.email, _jsx(ListBox.ItemIndicator, {})] }, member.memberId))) }) })] })), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs(Select, { variant: "secondary", value: locationKind, onChange: (value) => setLocationKind(value), isDisabled: isPending, children: [_jsx(Label, { children: "Where" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: LOCATION_KINDS.map((kind) => (_jsxs(ListBox.Item, { id: kind, textValue: LOCATION_LABELS[kind], children: [LOCATION_LABELS[kind], _jsx(ListBox.ItemIndicator, {})] }, kind))) }) })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Details" }), _jsx(TextArea, { placeholder: locationKind === "address" ? "Where to come" : "Left blank is fine", value: locationValue, onChange: (e) => setLocationValue(e.currentTarget.value), disabled: isPending, rows: 2 })] })] }), 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" : "Create" })] })] }));
152
+ return (_jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { placeholder: "Consultation", value: name, onChange: (e) => setName(e.currentTarget.value), disabled: isPending, autoFocus: true })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Description" }), _jsx(TextArea, { placeholder: "What this appointment is for", value: description, onChange: (e) => setDescription(e.currentTarget.value), disabled: isPending, rows: 2 })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(TextField, { isRequired: true, variant: "secondary", children: [_jsx(Label, { children: "Length (min)" }), _jsx(Input, { type: "number", value: String(durationMinutes), onChange: (e) => setDurationMinutes(Number(e.currentTarget.value)), disabled: isPending })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Gap before" }), _jsx(Input, { type: "number", value: String(bufferBefore), onChange: (e) => setBufferBefore(Number(e.currentTarget.value)), disabled: isPending })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Gap after" }), _jsx(Input, { type: "number", value: String(bufferAfter), onChange: (e) => setBufferAfter(Number(e.currentTarget.value)), disabled: isPending })] })] }), schedules.length > 1 && (_jsxs(Select, { variant: "secondary", value: effectiveScheduleId, onChange: (value) => setScheduleId(String(value)), isDisabled: isPending, children: [_jsx(Label, { children: "Availability" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: schedules.map((schedule) => (_jsxs(ListBox.Item, { id: schedule.id, textValue: schedule.name, children: [schedule.name, _jsx(ListBox.ItemIndicator, {})] }, schedule.id))) }) })] })), members.length > 1 && (_jsxs(Select, { variant: "secondary", value: effectiveHostMemberId, onChange: (value) => setHostMemberId(String(value)), isDisabled: isPending, children: [_jsx(Label, { children: "Who hosts it" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: members.map((member) => (_jsxs(ListBox.Item, { id: member.memberId, textValue: member.displayName ?? member.email, children: [member.displayName ?? member.email, _jsx(ListBox.ItemIndicator, {})] }, member.memberId))) }) })] })), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs(Select, { variant: "secondary", value: locationKind, onChange: (value) => setLocationKind(value), isDisabled: isPending, children: [_jsx(Label, { children: "Where" }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: LOCATION_KINDS.map((kind) => (_jsxs(ListBox.Item, { id: kind, textValue: LOCATION_LABELS[kind], children: [LOCATION_LABELS[kind], _jsx(ListBox.ItemIndicator, {})] }, kind))) }) })] }), _jsxs(TextField, { variant: "secondary", children: [_jsx(Label, { children: "Details" }), _jsx(TextArea, { placeholder: locationKind === "address" ? "Where to come" : "Left blank is fine", value: locationValue, onChange: (e) => setLocationValue(e.currentTarget.value), disabled: isPending, rows: 2 })] })] }), 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" : "Create" })] })] }));
153
153
  }
@@ -34,6 +34,6 @@ export function ConnectFilterTabs({ options, value, onChange, label, className,
34
34
  */
35
35
  _jsx("div", { role: "radiogroup", "aria-label": label, className: cn("-mx-1 flex flex-wrap gap-2 px-1", className), children: options.map((option) => {
36
36
  const isSelected = option.value === value;
37
- return (_jsx("button", { type: "button", role: "radio", "aria-checked": isSelected, onClick: () => onChange(option.value), className: "shrink-0 rounded-full focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent", children: _jsxs(Chip, { variant: isSelected ? "primary" : "soft", color: isSelected ? "accent" : "default", className: "min-h-8 px-3.5 py-1.5 text-sm", children: [option.label, option.count !== undefined && _jsx("span", { className: "ml-1 opacity-70", children: option.count })] }) }, option.value ?? "__all"));
37
+ return (_jsx("button", { type: "button", role: "radio", "aria-checked": isSelected, onClick: () => onChange(option.value), className: "shrink-0 rounded-full focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus", children: _jsxs(Chip, { variant: isSelected ? "primary" : "soft", color: isSelected ? "accent" : "default", className: "min-h-8 px-3.5 py-1.5 text-sm", children: [option.label, option.count !== undefined && _jsx("span", { className: "ml-1 opacity-70", children: option.count })] }) }, option.value ?? "__all"));
38
38
  }) }));
39
39
  }
@@ -24,5 +24,5 @@ export function ConnectGrievanceNotice({ title = "Grievance redressal", applicat
24
24
  if (!profile)
25
25
  return null;
26
26
  const { grievance } = profile;
27
- return (_jsxs("section", { className: cn("space-y-3", className), children: [title !== null && _jsx("h3", { className: "text-base font-semibold", children: title }), _jsxs("p", { className: "text-sm text-muted", children: [applicationName ? `${applicationName} is operated on ` : "This service runs on ", "the CosmoOps Connect platform. ", _jsx("strong", { className: "font-medium text-foreground", children: profile.legalName }), " ", "is the Data Fiduciary for personal data processed here, under the Digital Personal Data Protection Act, 2023."] }), _jsxs("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-[auto_1fr]", children: [_jsx("dt", { className: "text-muted", children: "Grievance Officer" }), _jsx("dd", { className: "text-foreground", children: grievance.officerName }), _jsx("dt", { className: "text-muted", children: "Email" }), _jsx("dd", { children: _jsx("a", { className: "text-accent underline underline-offset-2", href: `mailto:${grievance.officerEmail}`, children: grievance.officerEmail }) }), _jsx("dt", { className: "text-muted", children: "Contactable" }), _jsx("dd", { className: "text-foreground", children: grievance.hours }), _jsx("dt", { className: "text-muted", children: "Registered address" }), _jsx("dd", { className: "text-foreground", children: profile.registeredAddress })] }), _jsxs("p", { className: "text-sm text-muted", children: ["Complaints are acknowledged and resolved within ", grievance.responseDays, " days of receipt."] })] }));
27
+ return (_jsxs("section", { className: cn("space-y-3", className), children: [title !== null && _jsx("h3", { className: "text-base font-semibold", children: title }), _jsxs("p", { className: "text-sm text-muted", children: [applicationName ? `${applicationName} is operated on ` : "This service runs on ", "the CosmoOps Connect platform. ", _jsx("strong", { className: "font-medium text-foreground", children: profile.legalName }), " ", "is the Data Fiduciary for personal data processed here, under the Digital Personal Data Protection Act, 2023."] }), _jsxs("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-[auto_1fr]", children: [_jsx("dt", { className: "text-muted", children: "Grievance Officer" }), _jsx("dd", { className: "text-foreground", children: grievance.officerName }), _jsx("dt", { className: "text-muted", children: "Email" }), _jsx("dd", { children: _jsx("a", { className: "text-link underline underline-offset-2", href: `mailto:${grievance.officerEmail}`, children: grievance.officerEmail }) }), _jsx("dt", { className: "text-muted", children: "Contactable" }), _jsx("dd", { className: "text-foreground", children: grievance.hours }), _jsx("dt", { className: "text-muted", children: "Registered address" }), _jsx("dd", { className: "text-foreground", children: profile.registeredAddress })] }), _jsxs("p", { className: "text-sm text-muted", children: ["Complaints are acknowledged and resolved within ", grievance.responseDays, " days of receipt."] })] }));
28
28
  }
@@ -39,7 +39,7 @@ function ToolkitTile({ toolkit, isPending, onConnect, onDisconnect }) {
39
39
  // Deliberately a plain `<img>`: this package ships into any React app
40
40
  // and cannot assume `next/image` or any other framework loader.
41
41
  // eslint-disable-next-line @next/next/no-img-element
42
- _jsx("img", { src: toolkit.logoUrl, alt: "", className: "size-full object-cover" })) : (_jsx(Puzzle, { className: "size-6 text-muted", "aria-hidden": "true" })), toolkit.status === "failed" && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-danger text-danger-foreground", children: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-center text-xs font-medium text-foreground", children: toolkit.name }), _jsx(Chip, { size: "sm", variant: "soft", color: STATUS_COLOR[toolkit.status], children: STATUS_LABEL[toolkit.status] }), _jsxs("div", { className: "flex w-full flex-col gap-1.5 pt-1", children: [CAN_CONNECT.has(toolkit.status) && (_jsx(Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Connect" })), toolkit.status === "active" && (_jsxs(_Fragment, { children: [_jsx(Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Reconnect" }), _jsxs(AlertDialog, { isOpen: confirmingDisconnect, onOpenChange: setConfirmingDisconnect, children: [_jsx(Button, { size: "sm", variant: "tertiary", className: "w-full text-xs text-danger", isDisabled: isPending, children: "Disconnect" }), _jsx(AlertDialog.Backdrop, { children: _jsx(AlertDialog.Container, { children: _jsxs(AlertDialog.Dialog, { className: "sm:max-w-100", children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "danger" }), _jsxs(AlertDialog.Heading, { children: ["Disconnect ", toolkit.name, "?"] })] }), _jsx(AlertDialog.Body, { children: _jsx("p", { className: "text-sm text-muted", children: "Every member loses this connection immediately - it can be reconnected later." }) }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "secondary", className: "text-xs", children: "Cancel" }), _jsx(Button, { slot: "close", variant: "danger", className: "text-xs", onPress: onDisconnect, children: "Disconnect" })] })] }) }) })] })] })), toolkit.status === "pending" && (_jsx(Button, { size: "sm", variant: "tertiary", className: "w-full text-xs text-danger", isDisabled: isPending, onPress: onDisconnect, children: "Cancel" }))] })] }));
42
+ _jsx("img", { src: toolkit.logoUrl, alt: "", className: "size-full object-cover" })) : (_jsx(Puzzle, { className: "size-6 text-muted", "aria-hidden": "true" })), toolkit.status === "failed" && (_jsx("span", { className: "absolute -right-0.5 -top-0.5 flex size-4 items-center justify-center rounded-full bg-danger text-danger-foreground", children: _jsx(AlertCircle, { className: "size-3", "aria-hidden": "true" }) }))] }), _jsx("span", { className: "line-clamp-2 text-center text-xs font-medium text-foreground", children: toolkit.name }), _jsx(Chip, { size: "sm", variant: "soft", color: STATUS_COLOR[toolkit.status], children: STATUS_LABEL[toolkit.status] }), _jsxs("div", { className: "flex w-full flex-col gap-1.5 pt-1", children: [CAN_CONNECT.has(toolkit.status) && (_jsx(Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Connect" })), toolkit.status === "active" && (_jsxs(_Fragment, { children: [_jsx(Button, { size: "sm", variant: "secondary", className: "w-full text-xs", isPending: isPending, isDisabled: isPending, onPress: onConnect, children: "Reconnect" }), _jsxs(AlertDialog, { isOpen: confirmingDisconnect, onOpenChange: setConfirmingDisconnect, children: [_jsx(Button, { size: "sm", variant: "danger-soft", className: "w-full text-xs", isDisabled: isPending, children: "Disconnect" }), _jsx(AlertDialog.Backdrop, { children: _jsx(AlertDialog.Container, { children: _jsxs(AlertDialog.Dialog, { className: "sm:max-w-100", children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "danger" }), _jsxs(AlertDialog.Heading, { children: ["Disconnect ", toolkit.name, "?"] })] }), _jsx(AlertDialog.Body, { children: _jsx("p", { className: "text-sm text-muted", children: "Every member loses this connection immediately - it can be reconnected later." }) }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "secondary", className: "text-xs", children: "Cancel" }), _jsx(Button, { slot: "close", variant: "danger", className: "text-xs", onPress: onDisconnect, children: "Disconnect" })] })] }) }) })] })] })), toolkit.status === "pending" && (_jsx(Button, { size: "sm", variant: "danger-soft", className: "w-full text-xs", isDisabled: isPending, onPress: onDisconnect, children: "Cancel" }))] })] }));
43
43
  }
44
44
  /**
45
45
  * A grid of every Composio-hosted toolkit this workspace can connect -
@@ -88,5 +88,5 @@ export function ConnectIntegrationsModal({ className, label = "Integrations" })
88
88
  })
89
89
  .finally(() => setPendingSlug(null));
90
90
  };
91
- return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [_jsxs(Button, { variant: "secondary", className: className, children: [_jsx(Puzzle, { className: "size-4" }), label] }), _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: "Integrations" }) }), _jsxs(Modal.Body, { children: [isLoading && (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: Array.from({ length: 6 }, (_, index) => (_jsxs("div", { className: "flex flex-col items-center gap-2 rounded-xl border border-separator p-3", children: [_jsx(Skeleton, { className: "size-12 rounded-xl" }), _jsx(Skeleton, { className: "h-3 w-16" }), _jsx(Skeleton, { className: "h-7 w-full rounded-lg" })] }, index))) })), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger", children: "Could not load integrations." })), !isLoading && !error && toolkits.length === 0 && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: "No integrations are enabled on this platform yet." })), !isLoading && !error && toolkits.length > 0 && (_jsx(m.div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.04 } } }, initial: "hidden", animate: "show", children: toolkits.map((toolkit) => (_jsx(m.div, { variants: { hidden: { opacity: 0, y: 8 }, show: { opacity: 1, y: 0, transition: ENTER } }, children: _jsx(ToolkitTile, { toolkit: toolkit, isPending: pendingSlug === toolkit.slug, onConnect: () => connect(toolkit.slug), onDisconnect: () => disconnect(toolkit.slug) }) }, toolkit.slug))) }))] })] }) }) })] }) }));
91
+ return (_jsx(MotionProvider, { children: _jsxs(Modal, { children: [_jsxs(Button, { variant: "secondary", className: className, children: [_jsx(Puzzle, { className: "size-4" }), label] }), _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: "Integrations" }) }), _jsxs(Modal.Body, { children: [isLoading && (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: Array.from({ length: 6 }, (_, index) => (_jsxs("div", { className: "flex flex-col items-center gap-2 rounded-xl border border-separator p-3", children: [_jsx(Skeleton, { className: "size-12 rounded-xl" }), _jsx(Skeleton, { className: "h-3 w-16" }), _jsx(Skeleton, { className: "h-7 w-full rounded-lg" })] }, index))) })), !isLoading && error && (_jsx("p", { className: "py-6 text-center text-sm text-danger-soft-foreground", children: "Could not load integrations." })), !isLoading && !error && toolkits.length === 0 && (_jsx("p", { className: "py-6 text-center text-sm text-muted", children: "No integrations are enabled on this platform yet." })), !isLoading && !error && toolkits.length > 0 && (_jsx(m.div, { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", variants: { show: { transition: { staggerChildren: 0.04 } } }, initial: "hidden", animate: "show", children: toolkits.map((toolkit) => (_jsx(m.div, { variants: { hidden: { opacity: 0, y: 8 }, show: { opacity: 1, y: 0, transition: ENTER } }, children: _jsx(ToolkitTile, { toolkit: toolkit, isPending: pendingSlug === toolkit.slug, onConnect: () => connect(toolkit.slug), onDisconnect: () => disconnect(toolkit.slug) }) }, toolkit.slug))) }))] })] }) }) })] }) }));
92
92
  }
@@ -109,9 +109,9 @@ export function ConnectPlans({ planIds, hideCoupon = false, className }) {
109
109
  return _jsx("p", { className: "text-sm text-muted", children: "Sign in to see your plan options." });
110
110
  }
111
111
  if (error) {
112
- return _jsx("p", { className: "text-sm text-danger", children: "Could not load plans." });
112
+ return _jsx("p", { className: "text-sm text-danger-soft-foreground", children: "Could not load plans." });
113
113
  }
114
- return (_jsx(MotionProvider, { children: _jsxs("div", { className: cn("flex flex-col gap-6", className), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), _jsx(Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: _jsx(Switch.Content, { children: _jsx(Switch.Control, { children: _jsx(Switch.Thumb, {}) }) }) }), _jsx("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && (_jsxs(Chip, { className: "bg-accent-soft text-accent", children: ["Save up to ", bestSaving, "%"] }))] })), !hideCoupon && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "size-4 text-muted", "aria-hidden": "true" }), _jsx(Input, { "aria-label": "Coupon code", placeholder: "Coupon code", value: coupon, onChange: (event) => setCoupon(event.target.value.toUpperCase()), className: "w-40" })] }))] }), subscription?.subscription?.appliedDiscount && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", children: [_jsx(Sparkles, { className: "size-4 shrink-0", "aria-hidden": "true" }), _jsxs("span", { children: [discountLabel(subscription.subscription.appliedDiscount), subscription.subscription.appliedDiscount.code
114
+ return (_jsx(MotionProvider, { children: _jsxs("div", { className: cn("flex flex-col gap-6", className), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [hasAnnual && hasMonthly && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: showAnnual ? "text-sm text-muted" : "text-sm font-medium text-foreground", children: "Monthly" }), _jsx(Switch, { "aria-label": "Bill annually", isSelected: showAnnual, onChange: setAnnual, children: _jsx(Switch.Content, { children: _jsx(Switch.Control, { children: _jsx(Switch.Thumb, {}) }) }) }), _jsx("span", { className: showAnnual ? "text-sm font-medium text-foreground" : "text-sm text-muted", children: "Annual" }), bestSaving !== null && (_jsxs(Chip, { color: "accent", variant: "soft", children: ["Save up to ", bestSaving, "%"] }))] })), !hideCoupon && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "size-4 text-muted", "aria-hidden": "true" }), _jsx(Input, { "aria-label": "Coupon code", placeholder: "Coupon code", value: coupon, onChange: (event) => setCoupon(event.target.value.toUpperCase()), className: "w-40" })] }))] }), subscription?.subscription?.appliedDiscount && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent-soft-foreground", children: [_jsx(Sparkles, { className: "size-4 shrink-0", "aria-hidden": "true" }), _jsxs("span", { children: [discountLabel(subscription.subscription.appliedDiscount), subscription.subscription.appliedDiscount.code
115
115
  ? ` - code ${subscription.subscription.appliedDiscount.code}`
116
116
  : ""] })] })), _jsx(ConnectCheckoutStatusRow, { checkout: checkout }), _jsx(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) => {
117
117
  // The price point this card is showing. A tier missing the toggled
@@ -131,7 +131,7 @@ export function ConnectPlans({ planIds, hideCoupon = false, className }) {
131
131
  tier.oneTime?.id === subscription.plan.id);
132
132
  const isCurrent = action === "current";
133
133
  const cadenceUnavailable = showAnnual ? tier.annual === null : tier.monthly === null;
134
- return (_jsx(m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: ENTER } }, children: _jsxs(Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [_jsxs(Card.Header, { children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx(Card.Title, { children: tier.name }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && (_jsxs(Chip, { className: "bg-accent-soft text-accent", children: ["Save ", tier.annualSavingPercent, "%"] })), isCurrentTier && _jsx(Chip, { className: "bg-accent-soft text-accent", children: "Current" })] })] }), tier.description && _jsx(Card.Description, { children: tier.description })] }), _jsxs(Card.Content, { className: "flex flex-col gap-4", children: [_jsxs("div", { children: [_jsx(AnimatePresence, { mode: "popLayout", initial: false, children: _jsx(m.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.15 }, children: isFreePlan(plan.price) ? (_jsx("span", { className: "text-2xl font-semibold text-foreground", children: "Free" })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-2xl font-semibold text-foreground", children: formatPrice(plan.price) }), _jsx("span", { className: "text-sm text-muted", children: pricePerLabel(plan.billingInterval) })] })) }, plan.billingInterval) }), !isFreePlan(plan.price) && (_jsxs("p", { className: "mt-1 text-xs text-muted", children: ["+", plan.gstRate, "% GST"] })), cadenceUnavailable && (_jsxs("p", { className: "mt-1 text-xs text-muted", children: ["Only sold ", intervalLabel(plan.billingInterval).adverb, "."] }))] }), plan.trialDays && !isFreePlan(plan.price) ? (_jsxs("p", { className: "text-xs font-medium text-accent", children: [plan.trialDays, "-day free trial"] })) : null, plan.features.length > 0 && (_jsx("ul", { className: "flex flex-col gap-1.5", children: plan.features.map((feature) => (
134
+ return (_jsx(m.div, { variants: { hidden: { opacity: 0, y: 12 }, show: { opacity: 1, y: 0, transition: ENTER } }, children: _jsxs(Card, { className: isCurrentTier ? "h-full border-accent" : "h-full", children: [_jsxs(Card.Header, { children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsx(Card.Title, { children: tier.name }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [tier.annualSavingPercent !== null && showAnnual && (_jsxs(Chip, { color: "accent", variant: "soft", children: ["Save ", tier.annualSavingPercent, "%"] })), isCurrentTier && _jsx(Chip, { color: "accent", variant: "soft", children: "Current" })] })] }), tier.description && _jsx(Card.Description, { children: tier.description })] }), _jsxs(Card.Content, { className: "flex flex-col gap-4", children: [_jsxs("div", { children: [_jsx(AnimatePresence, { mode: "popLayout", initial: false, children: _jsx(m.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, transition: { duration: 0.15 }, children: isFreePlan(plan.price) ? (_jsx("span", { className: "text-2xl font-semibold text-foreground", children: "Free" })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-2xl font-semibold text-foreground", children: formatPrice(plan.price) }), _jsx("span", { className: "text-sm text-muted", children: pricePerLabel(plan.billingInterval) })] })) }, plan.billingInterval) }), !isFreePlan(plan.price) && (_jsxs("p", { className: "mt-1 text-xs text-muted", children: ["+", plan.gstRate, "% GST"] })), cadenceUnavailable && (_jsxs("p", { className: "mt-1 text-xs text-muted", children: ["Only sold ", intervalLabel(plan.billingInterval).adverb, "."] }))] }), plan.trialDays && !isFreePlan(plan.price) ? (_jsxs("p", { className: "text-xs font-medium text-accent-soft-foreground", children: [plan.trialDays, "-day free trial"] })) : null, plan.features.length > 0 && (_jsx("ul", { className: "flex flex-col gap-1.5", children: plan.features.map((feature) => (
135
135
  // `name`, not `key` - the key is an identifier
136
136
  // ("campaign-module"), and printing it here is what this
137
137
  // card used to do. The description rides along as a
@@ -167,7 +167,7 @@ function CheckoutStatusRowContent({ checkout, }) {
167
167
  return (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-separator px-3 py-2 text-sm text-muted", children: [_jsx(Loader2, { className: "size-4 shrink-0 animate-spin", "aria-hidden": "true" }), _jsx("span", { children: "Finish up in the Connect window. This page updates on its own." })] }));
168
168
  }
169
169
  if (checkout.status === "completed") {
170
- return (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg bg-accent-soft px-3 py-2 text-sm text-accent", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), _jsx(Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
170
+ return (_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: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Your plan is updated."] }), _jsx(Button, { variant: "tertiary", onPress: checkout.reset, children: "Dismiss" })] }));
171
171
  }
172
172
  // `blocked` and `abandoned` share a row: both mean nothing changed, and both
173
173
  // are fixed by going to the same URL - so offer it rather than explaining.
@@ -202,7 +202,7 @@ function ConnectUnsubscribeRow({ subscription, appUrl, checkout, }) {
202
202
  if (subscription.cancelAtPeriodEnd) {
203
203
  return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [_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."] }), _jsxs(Button, { variant: "tertiary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(subscription.id)}`), children: ["View on Connect", _jsx(ExternalLink, { className: "size-3.5" })] })] }));
204
204
  }
205
- return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [_jsx("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), _jsxs(Button, { variant: "tertiary", className: "text-danger", isDisabled: checkout.status === "waiting", onPress: () => checkout.start({
205
+ return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t border-separator pt-4", children: [_jsx("p", { className: "text-sm text-muted", children: "Cancel any time. Access continues to the end of the paid cycle." }), _jsxs(Button, { variant: "danger-soft", isDisabled: checkout.status === "waiting", onPress: () => checkout.start({
206
206
  url: buildCancelUrl({ appUrl, subscriptionId: subscription.id }),
207
207
  // A paused subscription cancels outright (`status` goes to
208
208
  // `"cancelled"`); an active one is deferred to the cycle end
@@ -71,7 +71,7 @@ function BlockerRow({ blocker }) {
71
71
  const { openConnect } = useConnect();
72
72
  const inProgress = IN_PROGRESS_CODES.has(blocker.code);
73
73
  const isBlocking = blocker.severity === "blocking";
74
- return (_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: [_jsxs("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? (_jsx(Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : (_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), _jsx(ExternalLink, { className: "size-3.5" })] }))] }));
74
+ return (_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: [_jsxs("span", { className: "flex items-center gap-2", children: [inProgress || !isBlocking ? (_jsx(Info, { className: "size-4 shrink-0", "aria-hidden": "true" })) : (_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" })), blocker.message] }), blocker.resolveUrl && !inProgress && (_jsxs(Button, { variant: "secondary", onPress: () => openConnect(blocker.resolveUrl), children: [actionLabel(blocker), _jsx(ExternalLink, { className: "size-3.5" })] }))] }));
75
75
  }
76
76
  function ReadyRow({ className }) {
77
77
  return (_jsx("p", { className: cn("text-sm text-muted", className), children: "This workspace is cleared to use every part of the application." }));
@@ -47,7 +47,7 @@ export function ConnectSubscribeDialog({ plan, action, current, coupon, onClose,
47
47
  // member and the one most in need of it: the two prices differ by an order
48
48
  // of magnitude, and showing only the new one reads as a price rise.
49
49
  const isChange = action === "upgrade" || action === "downgrade" || isIntervalSwitch;
50
- return (_jsx(Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: _jsx(Modal.Container, { size: "sm", children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.CloseTrigger, { onPress: onClose }), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: HEADING[action] }) }), plan && (_jsxs(_Fragment, { children: [_jsxs(Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? (_jsxs("div", { className: "flex items-center gap-3 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-muted", children: currentPlan.name }), _jsxs("p", { className: "text-xs text-muted", children: [formatPrice(currentPlan.price), "/", intervalLabel(currentPlan.billingInterval).short] })] }), _jsx(ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate font-medium text-foreground", children: plan.name }), _jsxs("p", { className: "text-xs text-muted", children: [formatPrice(plan.price), "/", intervalLabel(plan.billingInterval).short] })] })] })) : (_jsxs("div", { children: [_jsx("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && _jsx("p", { className: "text-xs text-muted", children: plan.description })] })), isFreePlan(plan.price) ? (_jsxs("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [_jsx("span", { children: plan.name }), _jsx("span", { children: "Free" })] })) : (_jsxs("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [_jsxs("div", { className: "flex justify-between text-muted", children: [_jsx("span", { children: plan.name }), _jsx("span", { children: formatPrice(plan.price) })] }), _jsxs("div", { className: "flex justify-between text-muted", children: [_jsxs("span", { children: ["GST (", plan.gstRate, "%)"] }), _jsx("span", { children: formatPrice(withGst(plan.price, plan.gstRate) - plan.price) })] }), _jsxs("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [_jsxs("span", { children: ["Per ", cadenceOf(plan)] }), _jsx("span", { children: formatPrice(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && (_jsxs("p", { className: "text-xs text-muted", children: ["Works out to ", formatPrice(monthlyOf(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !isFreePlan(plan.price) ? (_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() ? (_jsxs("p", { className: "text-xs text-muted", children: ["Coupon ", _jsx("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, _jsxs("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [_jsx(Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), _jsxs("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
50
+ return (_jsx(Modal.Backdrop, { isOpen: plan !== null, onOpenChange: (open) => !open && onClose(), children: _jsx(Modal.Container, { size: "sm", children: _jsxs(Modal.Dialog, { children: [_jsx(Modal.CloseTrigger, { onPress: onClose }), _jsx(Modal.Header, { children: _jsx(Modal.Heading, { children: HEADING[action] }) }), plan && (_jsxs(_Fragment, { children: [_jsxs(Modal.Body, { className: "flex flex-col gap-4", children: [isChange && currentPlan ? (_jsxs("div", { className: "flex items-center gap-3 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-muted", children: currentPlan.name }), _jsxs("p", { className: "text-xs text-muted", children: [formatPrice(currentPlan.price), "/", intervalLabel(currentPlan.billingInterval).short] })] }), _jsx(ArrowRight, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate font-medium text-foreground", children: plan.name }), _jsxs("p", { className: "text-xs text-muted", children: [formatPrice(plan.price), "/", intervalLabel(plan.billingInterval).short] })] })] })) : (_jsxs("div", { children: [_jsx("p", { className: "font-medium text-foreground", children: plan.name }), plan.description && _jsx("p", { className: "text-xs text-muted", children: plan.description })] })), isFreePlan(plan.price) ? (_jsxs("div", { className: "flex justify-between border-t border-separator pt-3 text-sm font-semibold text-foreground", children: [_jsx("span", { children: plan.name }), _jsx("span", { children: "Free" })] })) : (_jsxs("div", { className: "flex flex-col gap-1 border-t border-separator pt-3 text-sm", children: [_jsxs("div", { className: "flex justify-between text-muted", children: [_jsx("span", { children: plan.name }), _jsx("span", { children: formatPrice(plan.price) })] }), _jsxs("div", { className: "flex justify-between text-muted", children: [_jsxs("span", { children: ["GST (", plan.gstRate, "%)"] }), _jsx("span", { children: formatPrice(withGst(plan.price, plan.gstRate) - plan.price) })] }), _jsxs("div", { className: "flex justify-between border-t border-separator pt-1 font-semibold text-foreground", children: [_jsxs("span", { children: ["Per ", cadenceOf(plan)] }), _jsx("span", { children: formatPrice(withGst(plan.price, plan.gstRate)) })] }), plan.billingInterval === "annual" && (_jsxs("p", { className: "text-xs text-muted", children: ["Works out to ", formatPrice(monthlyOf(withGst(plan.price, plan.gstRate), "annual")), " a month, billed yearly."] }))] })), plan.trialDays && !isFreePlan(plan.price) ? (_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() ? (_jsxs("p", { className: "text-xs text-muted", children: ["Coupon ", _jsx("span", { className: "font-mono text-foreground", children: coupon.trim() }), " is carried over and validated on Connect."] })) : null, _jsxs("div", { className: "flex gap-2 rounded-lg bg-surface p-3 text-xs text-muted", children: [_jsx(Info, { className: "mt-0.5 size-4 shrink-0", "aria-hidden": "true" }), _jsxs("div", { className: "flex flex-col gap-1", children: [action === "subscribe" &&
51
51
  (isFreePlan(plan.price) ? (_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." })) : (_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" && (_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" && (_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 && (
52
52
  // Not a proration. The cycle you are on ends, so this is
53
53
  // the one action where the amount collected can exceed a
@@ -83,15 +83,15 @@ export function ConnectSubscriptionCard({ plansHref, className }) {
83
83
  function SubscriptionChip({ state }) {
84
84
  switch (state) {
85
85
  case "trialing":
86
- return _jsx(Chip, { className: "bg-accent-soft text-accent", children: "Free trial" });
86
+ return _jsx(Chip, { color: "accent", variant: "soft", children: "Free trial" });
87
87
  case "cancelling":
88
88
  return _jsx(Chip, { className: "bg-surface text-muted", children: "Ending soon" });
89
89
  case "pending":
90
90
  return _jsx(Chip, { className: "bg-surface text-muted", children: "Confirming payment" });
91
91
  case "problem":
92
- return _jsx(Chip, { className: "bg-danger-soft text-danger", children: "Needs attention" });
92
+ return _jsx(Chip, { color: "danger", variant: "soft", children: "Needs attention" });
93
93
  default:
94
- return _jsx(Chip, { className: "bg-accent-soft text-accent", children: "Active" });
94
+ return _jsx(Chip, { color: "accent", variant: "soft", children: "Active" });
95
95
  }
96
96
  }
97
97
  /** The one sentence that matters most: what happens next, and when. */
@@ -104,7 +104,7 @@ function NextEvent({ detail, state, }) {
104
104
  return (_jsxs("span", { className: "flex items-center gap-2 text-muted", children: [_jsx(CreditCard, { className: "size-4 shrink-0", "aria-hidden": "true" }), "Waiting for your first payment to confirm."] }));
105
105
  }
106
106
  if (state === "problem") {
107
- return (_jsxs("span", { className: "flex items-center gap-2 text-danger", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
107
+ return (_jsxs("span", { className: "flex items-center gap-2 text-danger-soft-foreground", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0", "aria-hidden": "true" }), "This subscription isn't active. Settle it on Connect to restore access."] }));
108
108
  }
109
109
  if (!detail.nextBillingDate)
110
110
  return null;
@@ -153,7 +153,7 @@ export function ConnectTrialBanner({ plansHref, className }) {
153
153
  const { detail, state } = useSubscriptionState();
154
154
  if (state !== "trialing" || !detail)
155
155
  return null;
156
- return (_jsxs("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), children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(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)}.` : "."] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), _jsxs(Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", _jsx(ExternalLink, { className: "size-3.5" })] })] })] }));
156
+ return (_jsxs("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), children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(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)}.` : "."] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ConnectLinkButton, { href: plansHref ?? DEFAULT_PLANS_HREF, variant: "tertiary", children: "Compare plans" }), _jsxs(Button, { variant: "secondary", onPress: () => openConnect(`/billing/subscriptions/${encodeURIComponent(detail.id)}`), children: ["Manage", _jsx(ExternalLink, { className: "size-3.5" })] })] })] }));
157
157
  }
158
158
  /**
159
159
  * Renders `children` for a subscribed workspace, and the "pick a plan" card for
@@ -61,7 +61,7 @@ function AddonRow({ addon }) {
61
61
  // unclamped width renders as a bar overflowing its own track.
62
62
  const fraction = addon.includedPerCycle > 0 ? Math.min(1, Math.max(0, addon.balance / addon.includedPerCycle)) : 0;
63
63
  const carried = addon.balance > addon.includedPerCycle ? addon.balance - addon.includedPerCycle : 0;
64
- return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [_jsx("span", { className: "font-medium text-foreground", children: addon.name }), _jsxs("span", { className: overdrawn ? "text-danger" : "text-muted", children: [overdrawn
64
+ return (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-2 text-sm", children: [_jsx("span", { className: "font-medium text-foreground", children: addon.name }), _jsxs("span", { className: overdrawn ? "text-danger-soft-foreground" : "text-muted", children: [overdrawn
65
65
  ? `${formatUnits(Math.abs(addon.balance), addon.unit)} over`
66
- : `${formatUnits(addon.balance, addon.unit)} left`, _jsxs("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), _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: _jsx(m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: METER_SPRING }) }), carried > 0 && (_jsxs("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [_jsx(AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", formatPrice(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-danger", children: [_jsx(AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
66
+ : `${formatUnits(addon.balance, addon.unit)} left`, _jsxs("span", { className: "text-muted", children: [" of ", formatUnits(addon.includedPerCycle, addon.unit)] })] })] }), _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: _jsx(m.div, { className: `h-full rounded-full ${overdrawn ? "bg-danger" : "bg-accent"}`, initial: { width: 0 }, animate: { width: `${overdrawn ? 100 : fraction * 100}%` }, transition: METER_SPRING }) }), carried > 0 && (_jsxs("p", { className: "text-xs text-muted", children: ["Includes ", formatUnits(carried, addon.unit), " carried over from last cycle."] })), overdrawn && addon.overagePricePerUnit !== null && (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [_jsx(AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Billed at ", formatPrice(addon.overagePricePerUnit), " per ", addon.unit, " beyond your allowance."] })), !overdrawn && addon.overagePolicy === "reject" && addon.balance === 0 && (_jsxs("p", { className: "flex items-center gap-1.5 text-xs text-danger-soft-foreground", children: [_jsx(AlertTriangle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }), "Nothing left this cycle - further use is blocked until it resets."] }))] }));
67
67
  }
@@ -128,7 +128,7 @@ function ProfileSurface({ onSignOut, theme, onThemeChange, }) {
128
128
  const [next] = [...keys];
129
129
  if (typeof next === "string")
130
130
  onThemeChange(next);
131
- }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => (_jsxs(ToggleButton, { id: value, children: [index > 0 && _jsx(ToggleButtonGroup.Separator, {}), _jsx(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), _jsx(ConnectAccountSessions, {})] }), _jsx(Modal.Footer, { children: _jsxs(Button, { slot: "close", fullWidth: true, variant: "tertiary", className: "text-danger", onPress: () => {
131
+ }, children: THEME_OPTIONS.map(({ value, label: optionLabel, icon: Icon }, index) => (_jsxs(ToggleButton, { id: value, children: [index > 0 && _jsx(ToggleButtonGroup.Separator, {}), _jsx(Icon, { className: "size-4", "aria-hidden": "true" }), optionLabel] }, value))) })] })), _jsx(ConnectAccountSessions, {})] }), _jsx(Modal.Footer, { children: _jsxs(Button, { slot: "close", fullWidth: true, variant: "danger-soft", onPress: () => {
132
132
  void signOut().then(onSignOut);
133
133
  }, children: [_jsx(LogOut, { className: "size-4 shrink-0" }), "Sign out"] }) })] }) }) }));
134
134
  }
@@ -127,7 +127,7 @@ export function ConnectWorkspaceSwitcher({ onSwitched, row = true, className, al
127
127
  // A 32px tile rather than a 20px glyph, so the initials land on the
128
128
  // same optical centre the avatar below them uses - see
129
129
  // `SIDEBAR_ROW_TILE`. Collapsed, this tile *is* the switcher.
130
- _jsxs(Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: sidebarRowClass({ tile: true, className }), children: [_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) }), _jsx(SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), _jsx(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: _jsx(ChevronsUpDown, { className: "size-4" }) })] })) : (_jsxs(Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [_jsx("span", { className: "truncate", children: label }), _jsx(ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), _jsx(Dropdown.Popover, { children: _jsxs(ListBox, { children: [workspaces.map((workspace) => (_jsxs(ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : (_jsx("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), _jsx(ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? (_jsxs(_Fragment, { children: [workspaces.length > 0 && _jsx(Separator, {}), _jsxs(ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [_jsx(Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), _jsx("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? _jsx("p", { className: "mt-1 px-2 text-xs text-danger", children: error }) : null, alwaysShow ? (_jsx(ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
130
+ _jsxs(Button, { variant: "tertiary", "aria-label": `Workspace: ${label}. Switch workspace`, className: sidebarRowClass({ tile: true, className }), children: [_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) }), _jsx(SidebarLabel, { className: "text-sm font-medium text-foreground", children: label }), _jsx(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: _jsx(ChevronsUpDown, { className: "size-4" }) })] })) : (_jsxs(Button, { variant: "secondary", size: "sm", className: "w-full justify-between", children: [_jsx("span", { className: "truncate", children: label }), _jsx(ChevronsUpDown, { className: "size-4 shrink-0 text-muted", "aria-hidden": "true" })] })), _jsx(Dropdown.Popover, { children: _jsxs(ListBox, { children: [workspaces.map((workspace) => (_jsxs(ListBox.Item, { id: workspace.workspaceId, textValue: workspace.name, isDisabled: !selectable(workspace) || switching !== null, onAction: () => void handleSwitch(workspace), children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm", children: workspace.name }), selectable(workspace) ? null : (_jsx("p", { className: "truncate text-xs text-muted", children: reasonUnavailable(workspace) }))] }), _jsx(ListBox.ItemIndicator, {})] }, workspace.workspaceId))), alwaysShow ? (_jsxs(_Fragment, { children: [workspaces.length > 0 && _jsx(Separator, {}), _jsxs(ListBox.Item, { id: "__create_workspace", textValue: "Create workspace", onAction: () => setIsCreateOpen(true), children: [_jsx(Plus, { className: "size-4 shrink-0 text-muted", "aria-hidden": true }), _jsx("p", { className: "truncate text-sm", children: "Create workspace" })] })] })) : null] }) })] }), error && !collapsed ? _jsx("p", { className: "mt-1 px-2 text-xs text-danger-soft-foreground", children: error }) : null, alwaysShow ? (_jsx(ConnectCreateWorkspaceModal, { isOpen: isCreateOpen, onOpenChange: setIsCreateOpen, onCreated: (workspace) => void handleCreated(workspace) })) : null] }));
131
131
  }
132
132
  /** Belongs to the user, and usable here. The same two facts `switch` enforces. */
133
133
  function selectable(workspace) {
@@ -7,7 +7,7 @@ import { useConnect } from "./provider.js";
7
7
  *
8
8
  * Connect answers any of these paths for a signed-in user on the bare URL -
9
9
  * these two params only add the `"Back to <your app>"` banner
10
- * (`ApplicationReturnBar` on Connect's side). It validates `return_to`
10
+ * (the hand-off strip in `ShellAnnouncements` on Connect's side). It validates `return_to`
11
11
  * against this `client_id`'s registered redirect origins and silently drops
12
12
  * the banner if they don't match, so a wrong or stale `appUrl`/`clientId`
13
13
  * pairing degrades to what linking there did before rather than erroring.
@@ -33,8 +33,17 @@ export class ConnectError extends Error {
33
33
  * already retries these internally (see `ConnectClientOptions.maxRetries`);
34
34
  * this is for callers deciding whether to queue their own retry after the
35
35
  * SDK has given up.
36
+ *
37
+ * **A 503 is retryable only when it carries `Retry-After`.** Connect sends
38
+ * that header on every 503 it means to be retried - a dependency blip, a
39
+ * contended transaction - and leaves it off when the dependency is
40
+ * *misconfigured* (a revoked provider key, say), where every retry gets the
41
+ * identical answer. Retrying those anyway cost a page three round trips and
42
+ * two sleeps per read to learn nothing.
36
43
  */
37
44
  get isRetryable() {
45
+ if (this.status === 503)
46
+ return this.retryAfterMs !== null;
38
47
  return this.status === 0 || this.status === 429 || this.status >= 500;
39
48
  }
40
49
  /**
@@ -179,4 +179,4 @@ export class ConnectClient {
179
179
  }
180
180
  }
181
181
  /** Kept in step with `package.json` by `sdk.test.ts`. */
182
- export const SDK_VERSION = "0.45.0";
182
+ export const SDK_VERSION = "0.47.0";
@@ -184,7 +184,10 @@ export async function request(options, spec) {
184
184
  return (await readBody(response));
185
185
  }
186
186
  lastError = toConnectError(response, await readBody(response), endpoint);
187
- if (!RETRYABLE_STATUS.has(response.status))
187
+ // The status decides, and then the error's own verdict - which is what
188
+ // keeps a 503 sent without `Retry-After` from being replayed. See
189
+ // `ConnectError.isRetryable`.
190
+ if (!RETRYABLE_STATUS.has(response.status) || !lastError.isRetryable)
188
191
  throw lastError;
189
192
  }
190
193
  finally {
@@ -32,4 +32,15 @@ export class DriveResource {
32
32
  });
33
33
  return result.url;
34
34
  }
35
+ /**
36
+ * How much of the workspace's Drive is used, against its limit. Needs
37
+ * `drive:read` and an active subscription to your application.
38
+ */
39
+ getUsage(input) {
40
+ return request(this.transport, {
41
+ method: "GET",
42
+ path: "/drive/usage",
43
+ query: { workspaceId: input.workspaceId },
44
+ });
45
+ }
35
46
  }
@@ -53,6 +53,22 @@ export class IntegrationsResource {
53
53
  json: { workspaceId, toolkitSlug },
54
54
  });
55
55
  }
56
+ /**
57
+ * One raw request to a connected provider's own API, for an endpoint the
58
+ * toolkit has no action for (Zoho Books ships no "create sales order").
59
+ *
60
+ * **Resolves whenever Connect relayed the request**, whatever the provider
61
+ * answered: read `status` and `data` for that. A provider-side 4xx is a
62
+ * business answer ("that GSTIN is invalid") rather than a transport
63
+ * failure, so it is not thrown as one.
64
+ */
65
+ async request(workspaceId, input) {
66
+ return request(this.transport, {
67
+ method: "POST",
68
+ path: "/integrations/proxy",
69
+ json: { workspaceId, ...input },
70
+ });
71
+ }
56
72
  /** Calls a Composio action against this workspace's connected account for the named toolkit. */
57
73
  async execute(workspaceId, input) {
58
74
  return request(this.transport, {
@@ -110,11 +110,20 @@ export class WorkspacesResource {
110
110
  * Connect roles - so you never reimplement Connect's role resolution, and
111
111
  * you are told nothing about the customer's Connect account beyond what your
112
112
  * application needs.
113
+ *
114
+ * **Seat holders only, by default.** A workspace member with no seat on your
115
+ * application cannot sign in to it, so they are not part of your team and
116
+ * do not belong in an owner picker or an assignee list. Pass
117
+ * `includeUnassigned: true` from the screen where an admin *gives* a seat -
118
+ * it needs the people who do not have one yet. That form needs the signed-in
119
+ * member to be able to change access (a 403 otherwise), and every row
120
+ * carries `assigned` so the screen can tell the two apart.
113
121
  */
114
122
  async members(options) {
115
123
  const result = await request(this.transport, {
116
124
  method: "GET",
117
125
  path: "/workspaces/members",
126
+ query: { include: options.includeUnassigned ? "unassigned" : undefined },
118
127
  headers: actingAs(options.asUser),
119
128
  });
120
129
  return result.members;