@wealthx/shadcn 1.5.21 → 1.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8384,6 +8384,15 @@ function AppointmentConfirmDialog({
8384
8384
  ] })
8385
8385
  ] }) });
8386
8386
  }
8387
+ var DAY_MAP = {
8388
+ Sun: 0,
8389
+ Mon: 1,
8390
+ Tue: 2,
8391
+ Wed: 3,
8392
+ Thu: 4,
8393
+ Fri: 5,
8394
+ Sat: 6
8395
+ };
8387
8396
  function AppointmentRescheduleDialog({
8388
8397
  open,
8389
8398
  onOpenChange,
@@ -8392,11 +8401,19 @@ function AppointmentRescheduleDialog({
8392
8401
  currentDate,
8393
8402
  currentTimeStart,
8394
8403
  currentTimeEnd,
8404
+ onDateChange,
8405
+ schedule,
8406
+ isLoadingSlots,
8395
8407
  onReschedule
8396
8408
  }) {
8397
8409
  const [date, setDate] = import_react13.default.useState(/* @__PURE__ */ new Date());
8398
8410
  const [slot, setSlot] = import_react13.default.useState();
8399
8411
  const [note, setNote] = import_react13.default.useState("");
8412
+ const today = import_react13.default.useMemo(() => /* @__PURE__ */ new Date(), []);
8413
+ const disabledDayOfWeek = import_react13.default.useMemo(
8414
+ () => schedule == null ? void 0 : schedule.filter((d) => !d.enabled).map((d) => DAY_MAP[d.day]).filter((n) => n !== void 0),
8415
+ [schedule]
8416
+ );
8400
8417
  const handleOpenChange = (next) => {
8401
8418
  if (!next) {
8402
8419
  setDate(/* @__PURE__ */ new Date());
@@ -8414,87 +8431,93 @@ function AppointmentRescheduleDialog({
8414
8431
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(DialogDescription, { children: "Select a new date and time slot. The client will be notified by email." })
8415
8432
  ] }),
8416
8433
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Separator, {}),
8417
- (currentDate || currentTimeStart) && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8418
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Current booking" }),
8419
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-4 border border-border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground", children: [
8420
- currentDate && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "flex items-center gap-1.5", children: [
8421
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react23.Calendar, { className: "h-3.5 w-3.5 shrink-0" }),
8422
- currentDate
8423
- ] }),
8424
- currentTimeStart && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "flex items-center gap-1.5", children: [
8425
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react23.Clock, { className: "h-3.5 w-3.5 shrink-0" }),
8426
- currentTimeStart,
8427
- currentTimeEnd ? ` \u2013 ${currentTimeEnd}` : ""
8434
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-4 overflow-y-auto max-h-[calc(90vh-200px)]", children: [
8435
+ (currentDate || currentTimeStart) && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8436
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Current booking" }),
8437
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-4 border border-border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground", children: [
8438
+ currentDate && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "flex items-center gap-1.5", children: [
8439
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react23.Calendar, { className: "h-3.5 w-3.5 shrink-0" }),
8440
+ currentDate
8441
+ ] }),
8442
+ currentTimeStart && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "flex items-center gap-1.5", children: [
8443
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react23.Clock, { className: "h-3.5 w-3.5 shrink-0" }),
8444
+ currentTimeStart,
8445
+ currentTimeEnd ? ` \u2013 ${currentTimeEnd}` : ""
8446
+ ] })
8428
8447
  ] })
8429
- ] })
8430
- ] }),
8431
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "grid grid-cols-[auto_1fr] items-start gap-5", children: [
8432
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8433
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Label, { children: "New date" }),
8434
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8435
- Calendar3,
8436
- {
8437
- mode: "single",
8438
- selected: date,
8439
- onSelect: (d) => {
8440
- setDate(d);
8441
- setSlot(void 0);
8442
- },
8443
- captionLayout: "label",
8444
- fromDate: /* @__PURE__ */ new Date(),
8445
- disabled: { before: /* @__PURE__ */ new Date() },
8446
- className: "border border-border"
8447
- }
8448
- )
8449
8448
  ] }),
8450
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex flex-col gap-5", children: date ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
8451
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center justify-between", children: [
8449
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "grid grid-cols-[auto_1fr] items-start gap-5", children: [
8450
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8451
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Label, { children: "New date" }),
8452
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8453
+ Calendar3,
8454
+ {
8455
+ mode: "single",
8456
+ selected: date,
8457
+ onSelect: (d) => {
8458
+ setDate(d);
8459
+ setSlot(void 0);
8460
+ if (d) onDateChange == null ? void 0 : onDateChange(d);
8461
+ },
8462
+ captionLayout: "label",
8463
+ fromDate: today,
8464
+ disabled: disabledDayOfWeek && disabledDayOfWeek.length > 0 ? [{ before: today }, { dayOfWeek: disabledDayOfWeek }] : { before: today },
8465
+ className: "border border-border"
8466
+ }
8467
+ )
8468
+ ] }),
8469
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex flex-col gap-5", children: date ? isLoadingSlots ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex h-full flex-col items-center justify-center gap-2 py-8 text-center", children: [
8470
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm font-semibold", children: "Loading slots\u2026" }),
8471
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs text-muted-foreground", children: "Fetching available times for the selected date." })
8472
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
8473
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center justify-between", children: [
8474
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm font-semibold", children: "Select a time slot" }),
8475
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
8476
+ totalAvailable,
8477
+ " available"
8478
+ ] })
8479
+ ] }),
8480
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8481
+ AppointmentSlotSection,
8482
+ {
8483
+ label: "Morning",
8484
+ slots: amSlots,
8485
+ selectedSlotId: slot == null ? void 0 : slot.id,
8486
+ onSelect: setSlot
8487
+ }
8488
+ ),
8489
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8490
+ AppointmentSlotSection,
8491
+ {
8492
+ label: "Afternoon",
8493
+ slots: pmSlots,
8494
+ selectedSlotId: slot == null ? void 0 : slot.id,
8495
+ onSelect: setSlot
8496
+ }
8497
+ )
8498
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex h-full flex-col items-center justify-center gap-2 py-8 text-center", children: [
8452
8499
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm font-semibold", children: "Select a time slot" }),
8453
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
8454
- totalAvailable,
8455
- " available"
8456
- ] })
8500
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs text-muted-foreground", children: "Pick a date on the left to see available slots." })
8501
+ ] }) })
8502
+ ] }),
8503
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8504
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(Label, { htmlFor: "reschedule-note", children: [
8505
+ "Note",
8506
+ " ",
8507
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
8457
8508
  ] }),
8458
8509
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8459
- AppointmentSlotSection,
8460
- {
8461
- label: "Morning",
8462
- slots: amSlots,
8463
- selectedSlotId: slot == null ? void 0 : slot.id,
8464
- onSelect: setSlot
8465
- }
8466
- ),
8467
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8468
- AppointmentSlotSection,
8510
+ Textarea,
8469
8511
  {
8470
- label: "Afternoon",
8471
- slots: pmSlots,
8472
- selectedSlotId: slot == null ? void 0 : slot.id,
8473
- onSelect: setSlot
8512
+ id: "reschedule-note",
8513
+ placeholder: "e.g. Rescheduling due to an internal conflict \u2014 apologies for the inconvenience\u2026",
8514
+ value: note,
8515
+ onChange: (e) => setNote(e.target.value),
8516
+ className: "w-full resize-none",
8517
+ rows: 2
8474
8518
  }
8475
8519
  )
8476
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex h-full flex-col items-center justify-center gap-2 py-8 text-center", children: [
8477
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-sm font-semibold", children: "Select a time slot" }),
8478
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs text-muted-foreground", children: "Pick a date on the left to see available slots." })
8479
- ] }) })
8480
- ] }),
8481
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
8482
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(Label, { htmlFor: "reschedule-note", children: [
8483
- "Note",
8484
- " ",
8485
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
8486
- ] }),
8487
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
8488
- Textarea,
8489
- {
8490
- id: "reschedule-note",
8491
- placeholder: "e.g. Rescheduling due to an internal conflict \u2014 apologies for the inconvenience\u2026",
8492
- value: note,
8493
- onChange: (e) => setNote(e.target.value),
8494
- className: "w-full resize-none",
8495
- rows: 2
8496
- }
8497
- )
8520
+ ] })
8498
8521
  ] }),
8499
8522
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(DialogFooter, { children: [
8500
8523
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(DialogClose, { render: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Button, { variant: "outline" }), children: "Cancel" }),
@@ -9058,12 +9081,17 @@ var import_jsx_runtime46 = require("react/jsx-runtime");
9058
9081
  function ClientSearch({
9059
9082
  clients,
9060
9083
  value,
9061
- onValueChange
9084
+ onValueChange,
9085
+ onSearch,
9086
+ isSearching,
9087
+ hasMore,
9088
+ onLoadMore,
9089
+ isLoadingMore
9062
9090
  }) {
9063
9091
  const [query, setQuery] = import_react14.default.useState("");
9064
9092
  const [open, setOpen] = import_react14.default.useState(false);
9065
9093
  const selected = clients.find((c) => c.id === value);
9066
- const filtered = clients.filter((c) => {
9094
+ const filtered = onSearch ? clients : clients.filter((c) => {
9067
9095
  const q = query.toLowerCase();
9068
9096
  return c.name.toLowerCase().includes(q) || c.email.toLowerCase().includes(q);
9069
9097
  });
@@ -9073,67 +9101,79 @@ function ClientSearch({
9073
9101
  {
9074
9102
  value: selected ? selected.name : query,
9075
9103
  onChange: (e) => {
9076
- setQuery(e.target.value);
9104
+ const v = e.target.value;
9105
+ setQuery(v);
9077
9106
  if (selected) onValueChange(void 0);
9078
- setOpen(e.target.value.length > 0);
9107
+ setOpen(v.length > 0);
9108
+ onSearch == null ? void 0 : onSearch(v);
9079
9109
  },
9080
9110
  onBlur: () => setTimeout(() => setOpen(false), 150),
9081
9111
  placeholder: "Search by name or email\u2026",
9082
9112
  autoComplete: "off"
9083
9113
  }
9084
9114
  ),
9085
- open && (filtered.length > 0 || query.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute z-50 mt-1 w-full border border-border bg-popover shadow-md", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "No clients found." }) : filtered.map((c) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
9086
- Button,
9087
- {
9088
- type: "button",
9089
- variant: "ghost",
9090
- className: "h-auto w-full flex-col items-start gap-0.5 px-3 py-2 text-left hover:bg-primary/5",
9091
- onMouseDown: (e) => e.preventDefault(),
9092
- onClick: () => {
9093
- onValueChange(c.id);
9094
- setQuery("");
9095
- setOpen(false);
9115
+ open && (filtered.length > 0 || query.length > 0 || isSearching) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute z-50 mt-1 max-h-64 w-full overflow-y-auto border border-border bg-popover shadow-md", children: isSearching ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "Searching..." }) : filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "No clients found." }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
9116
+ filtered.map((c) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
9117
+ Button,
9118
+ {
9119
+ type: "button",
9120
+ variant: "ghost",
9121
+ className: "h-auto w-full flex-col items-start gap-0.5 px-3 py-2 text-left hover:bg-primary/5",
9122
+ onMouseDown: (e) => e.preventDefault(),
9123
+ onClick: () => {
9124
+ onValueChange(c.id);
9125
+ setQuery("");
9126
+ setOpen(false);
9127
+ },
9128
+ children: [
9129
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-sm font-medium", children: c.name }),
9130
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-muted-foreground", children: c.email })
9131
+ ]
9096
9132
  },
9097
- children: [
9098
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-sm font-medium", children: c.name }),
9099
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-muted-foreground", children: c.email })
9100
- ]
9101
- },
9102
- c.id
9103
- )) })
9133
+ c.id
9134
+ )),
9135
+ hasMore && onLoadMore && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
9136
+ Button,
9137
+ {
9138
+ type: "button",
9139
+ variant: "ghost",
9140
+ size: "sm",
9141
+ className: "w-full text-sm text-primary",
9142
+ disabled: isLoadingMore,
9143
+ onMouseDown: (e) => e.preventDefault(),
9144
+ onClick: onLoadMore,
9145
+ children: isLoadingMore ? "Loading..." : "Load more"
9146
+ }
9147
+ )
9148
+ ] }) })
9104
9149
  ] });
9105
9150
  }
9106
- var FORMAT_OPTIONS = [
9107
- { value: "call", label: "Call", icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Phone, { className: "h-4 w-4" }) },
9108
- {
9151
+ function getFormatOptions(platform) {
9152
+ const call = {
9153
+ value: "call",
9154
+ label: "Call",
9155
+ icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Phone, { className: "h-4 w-4" })
9156
+ };
9157
+ const googleMeet = {
9109
9158
  value: "google-meet",
9110
9159
  label: "Google Meet",
9111
9160
  icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Video, { className: "h-4 w-4" })
9112
- },
9113
- {
9161
+ };
9162
+ const msTeams = {
9114
9163
  value: "microsoft-teams",
9115
9164
  label: "MS Teams",
9116
9165
  icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Users, { className: "h-4 w-4" })
9117
- },
9118
- {
9119
- value: "offline",
9120
- label: "Offline",
9121
- icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.MapPin, { className: "h-4 w-4" })
9122
- }
9123
- ];
9124
- var CLIENT_FORMAT_OPTIONS = [
9125
- { value: "call", label: "Call", icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Phone, { className: "h-4 w-4" }) },
9126
- {
9127
- value: "online",
9128
- label: "Online Meeting",
9129
- icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.Video, { className: "h-4 w-4" })
9130
- },
9131
- {
9166
+ };
9167
+ const offline = {
9132
9168
  value: "offline",
9133
9169
  label: "Offline Meeting",
9134
9170
  icon: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react26.MapPin, { className: "h-4 w-4" })
9135
- }
9136
- ];
9171
+ };
9172
+ if (platform === "google-meet") return [call, googleMeet, offline];
9173
+ if (platform === "microsoft-teams") return [call, msTeams, offline];
9174
+ if (platform === "any") return [call, googleMeet, msTeams, offline];
9175
+ return [call, offline];
9176
+ }
9137
9177
  function MeetingFormatSection({
9138
9178
  format: format5,
9139
9179
  onFormatChange,
@@ -9250,7 +9290,7 @@ function BookingConfirmationScreen({
9250
9290
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Button, { className: "w-full", onClick: onDone, children: "Done" })
9251
9291
  ] });
9252
9292
  }
9253
- var DAY_MAP = {
9293
+ var DAY_MAP2 = {
9254
9294
  Sun: 0,
9255
9295
  Mon: 1,
9256
9296
  Tue: 2,
@@ -9263,6 +9303,11 @@ function AppointmentBookDialog({
9263
9303
  open,
9264
9304
  onOpenChange,
9265
9305
  clients = [],
9306
+ onSearchClients,
9307
+ isSearchingClients,
9308
+ hasMoreClients,
9309
+ onLoadMoreClients,
9310
+ isLoadingMoreClients,
9266
9311
  meetingTypes = [],
9267
9312
  amSlots,
9268
9313
  pmSlots,
@@ -9273,14 +9318,15 @@ function AppointmentBookDialog({
9273
9318
  advisorInfo,
9274
9319
  initialClientId,
9275
9320
  defaultMeetingFormat,
9321
+ onlinePlatform,
9276
9322
  guestInfo,
9277
9323
  onBook
9278
9324
  }) {
9279
9325
  var _a, _b, _c;
9280
- const isClientMode = clients.length === 0;
9326
+ const isClientMode = clients.length === 0 && !onSearchClients;
9281
9327
  const showGuestForm = isClientMode && !((guestInfo == null ? void 0 : guestInfo.name) && (guestInfo == null ? void 0 : guestInfo.email));
9282
9328
  const disabledDayOfWeek = import_react14.default.useMemo(
9283
- () => schedule == null ? void 0 : schedule.filter((d) => !d.enabled).map((d) => DAY_MAP[d.day]).filter((n) => n !== void 0),
9329
+ () => schedule == null ? void 0 : schedule.filter((d) => !d.enabled).map((d) => DAY_MAP2[d.day]).filter((n) => n !== void 0),
9284
9330
  [schedule]
9285
9331
  );
9286
9332
  const today = import_react14.default.useMemo(() => /* @__PURE__ */ new Date(), []);
@@ -9369,7 +9415,12 @@ function AppointmentBookDialog({
9369
9415
  {
9370
9416
  clients,
9371
9417
  value: clientId,
9372
- onValueChange: setClientId
9418
+ onValueChange: setClientId,
9419
+ onSearch: onSearchClients,
9420
+ isSearching: isSearchingClients,
9421
+ hasMore: hasMoreClients,
9422
+ onLoadMore: onLoadMoreClients,
9423
+ isLoadingMore: isLoadingMoreClients
9373
9424
  }
9374
9425
  )
9375
9426
  ] }),
@@ -9427,7 +9478,7 @@ function AppointmentBookDialog({
9427
9478
  )
9428
9479
  ] })
9429
9480
  ] }),
9430
- !(isClientMode && defaultMeetingFormat) && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
9481
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
9431
9482
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Label, { children: "Meeting format" }),
9432
9483
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
9433
9484
  MeetingFormatSection,
@@ -9441,7 +9492,7 @@ function AppointmentBookDialog({
9441
9492
  advisorOfficeAddress,
9442
9493
  clientHomeAddress,
9443
9494
  isClientMode,
9444
- formatOptions: isClientMode ? CLIENT_FORMAT_OPTIONS : FORMAT_OPTIONS
9495
+ formatOptions: getFormatOptions(onlinePlatform)
9445
9496
  }
9446
9497
  )
9447
9498
  ] }),
@@ -10041,6 +10092,9 @@ function AppointmentDetailSheet({
10041
10092
  clientProfile,
10042
10093
  amSlots,
10043
10094
  pmSlots,
10095
+ onDateChange,
10096
+ schedule,
10097
+ isLoadingSlots,
10044
10098
  onAccept,
10045
10099
  onDecline,
10046
10100
  onReschedule,
@@ -10241,6 +10295,9 @@ function AppointmentDetailSheet({
10241
10295
  currentDate: appointment.date,
10242
10296
  currentTimeStart: appointment.timeStart,
10243
10297
  currentTimeEnd: appointment.timeEnd,
10298
+ onDateChange,
10299
+ schedule,
10300
+ isLoadingSlots,
10244
10301
  onReschedule: (date, slot, note) => {
10245
10302
  onReschedule == null ? void 0 : onReschedule(appointment.id, date, slot, note);
10246
10303
  setRescheduleOpen(false);
package/dist/index.mjs CHANGED
@@ -364,7 +364,7 @@ import {
364
364
  } from "./chunk-R2ON6CAN.mjs";
365
365
  import {
366
366
  AppointmentBookDialog
367
- } from "./chunk-NEMWMXGL.mjs";
367
+ } from "./chunk-3HFOSFOM.mjs";
368
368
  import {
369
369
  RadioGroup,
370
370
  RadioGroupCard,
@@ -384,7 +384,7 @@ import {
384
384
  } from "./chunk-V23CBULF.mjs";
385
385
  import {
386
386
  AppointmentDetailSheet
387
- } from "./chunk-F3MIRXRF.mjs";
387
+ } from "./chunk-ONYADWSO.mjs";
388
388
  import {
389
389
  AppointmentGmailConnect
390
390
  } from "./chunk-7TMPOZDE.mjs";
@@ -408,7 +408,7 @@ import {
408
408
  import {
409
409
  AppointmentConfirmDialog,
410
410
  AppointmentRescheduleDialog
411
- } from "./chunk-SET6GFGL.mjs";
411
+ } from "./chunk-ZSMQZ3VN.mjs";
412
412
  import {
413
413
  AppointmentSlotSection,
414
414
  AppointmentTimeSlotPicker
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wealthx/shadcn",
3
- "version": "1.5.21",
3
+ "version": "1.5.23",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -138,9 +138,25 @@ export interface AppointmentRescheduleDialogProps {
138
138
  currentDate?: string;
139
139
  currentTimeStart?: string;
140
140
  currentTimeEnd?: string;
141
+ /**
142
+ * Fired when the user selects a different date in the calendar.
143
+ * Use this to fetch fresh `amSlots`/`pmSlots` for the new date.
144
+ */
145
+ onDateChange?: (date: Date) => void;
146
+ /**
147
+ * Advisor's weekly availability schedule. Days with `enabled: false` are
148
+ * disabled in the date picker so the user cannot select them.
149
+ */
150
+ schedule?: { day: string; enabled: boolean }[];
151
+ /** True while slots are being fetched for the selected date. */
152
+ isLoadingSlots?: boolean;
141
153
  onReschedule: (date: Date, slot: AppointmentTimeSlot, note: string) => void;
142
154
  }
143
155
 
156
+ const DAY_MAP: Record<string, number> = {
157
+ Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6,
158
+ };
159
+
144
160
  export function AppointmentRescheduleDialog({
145
161
  open,
146
162
  onOpenChange,
@@ -149,12 +165,26 @@ export function AppointmentRescheduleDialog({
149
165
  currentDate,
150
166
  currentTimeStart,
151
167
  currentTimeEnd,
168
+ onDateChange,
169
+ schedule,
170
+ isLoadingSlots,
152
171
  onReschedule,
153
172
  }: AppointmentRescheduleDialogProps) {
154
173
  const [date, setDate] = React.useState<Date | undefined>(new Date());
155
174
  const [slot, setSlot] = React.useState<AppointmentTimeSlot | undefined>();
156
175
  const [note, setNote] = React.useState("");
157
176
 
177
+ const today = React.useMemo(() => new Date(), []);
178
+
179
+ const disabledDayOfWeek = React.useMemo(
180
+ () =>
181
+ schedule
182
+ ?.filter((d) => !d.enabled)
183
+ .map((d) => DAY_MAP[d.day])
184
+ .filter((n): n is number => n !== undefined),
185
+ [schedule],
186
+ );
187
+
158
188
  const handleOpenChange = (next: boolean) => {
159
189
  if (!next) {
160
190
  setDate(new Date());
@@ -181,6 +211,7 @@ export function AppointmentRescheduleDialog({
181
211
 
182
212
  <Separator />
183
213
 
214
+ <div className="flex flex-col gap-4 overflow-y-auto max-h-[calc(90vh-200px)]">
184
215
  {/* Current booking */}
185
216
  {(currentDate || currentTimeStart) && (
186
217
  <div className="flex flex-col gap-1.5">
@@ -215,10 +246,15 @@ export function AppointmentRescheduleDialog({
215
246
  onSelect={(d) => {
216
247
  setDate(d);
217
248
  setSlot(undefined);
249
+ if (d) onDateChange?.(d);
218
250
  }}
219
251
  captionLayout="label"
220
- fromDate={new Date()}
221
- disabled={{ before: new Date() }}
252
+ fromDate={today}
253
+ disabled={
254
+ disabledDayOfWeek && disabledDayOfWeek.length > 0
255
+ ? [{ before: today }, { dayOfWeek: disabledDayOfWeek }]
256
+ : { before: today }
257
+ }
222
258
  className="border border-border"
223
259
  />
224
260
  </div>
@@ -226,26 +262,35 @@ export function AppointmentRescheduleDialog({
226
262
  {/* Time slots */}
227
263
  <div className="flex flex-col gap-5">
228
264
  {date ? (
229
- <>
230
- <div className="flex items-center justify-between">
231
- <p className="text-sm font-semibold">Select a time slot</p>
232
- <span className="text-xs text-muted-foreground">
233
- {totalAvailable} available
234
- </span>
265
+ isLoadingSlots ? (
266
+ <div className="flex h-full flex-col items-center justify-center gap-2 py-8 text-center">
267
+ <p className="text-sm font-semibold">Loading slots…</p>
268
+ <p className="text-xs text-muted-foreground">
269
+ Fetching available times for the selected date.
270
+ </p>
235
271
  </div>
236
- <AppointmentSlotSection
237
- label="Morning"
238
- slots={amSlots}
239
- selectedSlotId={slot?.id}
240
- onSelect={setSlot}
241
- />
242
- <AppointmentSlotSection
243
- label="Afternoon"
244
- slots={pmSlots}
245
- selectedSlotId={slot?.id}
246
- onSelect={setSlot}
247
- />
248
- </>
272
+ ) : (
273
+ <>
274
+ <div className="flex items-center justify-between">
275
+ <p className="text-sm font-semibold">Select a time slot</p>
276
+ <span className="text-xs text-muted-foreground">
277
+ {totalAvailable} available
278
+ </span>
279
+ </div>
280
+ <AppointmentSlotSection
281
+ label="Morning"
282
+ slots={amSlots}
283
+ selectedSlotId={slot?.id}
284
+ onSelect={setSlot}
285
+ />
286
+ <AppointmentSlotSection
287
+ label="Afternoon"
288
+ slots={pmSlots}
289
+ selectedSlotId={slot?.id}
290
+ onSelect={setSlot}
291
+ />
292
+ </>
293
+ )
249
294
  ) : (
250
295
  <div className="flex h-full flex-col items-center justify-center gap-2 py-8 text-center">
251
296
  <p className="text-sm font-semibold">Select a time slot</p>
@@ -274,6 +319,7 @@ export function AppointmentRescheduleDialog({
274
319
  rows={2}
275
320
  />
276
321
  </div>
322
+ </div>
277
323
 
278
324
  <DialogFooter>
279
325
  <DialogClose render={<Button variant="outline" />}>