@fluid-app/portal-sdk 0.1.373 → 0.1.374

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 (74) hide show
  1. package/dist/{ContactsScreen-BVAzJjVs.cjs → ContactsScreen-C28RoYHu.cjs} +2 -2
  2. package/dist/{ContactsScreen-BVAzJjVs.cjs.map → ContactsScreen-C28RoYHu.cjs.map} +1 -1
  3. package/dist/{ContactsScreen-BKKowprR.cjs → ContactsScreen-CCXFgzcN.cjs} +2 -2
  4. package/dist/{ContactsScreen-BHy20ML1.mjs → ContactsScreen-DvPP4F22.mjs} +2 -2
  5. package/dist/{ContactsScreen-BHy20ML1.mjs.map → ContactsScreen-DvPP4F22.mjs.map} +1 -1
  6. package/dist/{ContactsTranslationBridge-BGeau5_N.mjs → ContactsTranslationBridge-Ckjj_Iu9.mjs} +3 -1
  7. package/dist/{ContactsTranslationBridge-BGeau5_N.mjs.map → ContactsTranslationBridge-Ckjj_Iu9.mjs.map} +1 -1
  8. package/dist/{ContactsTranslationBridge-D9gEIAp3.cjs → ContactsTranslationBridge-Dx_1jAxz.cjs} +3 -1
  9. package/dist/{ContactsTranslationBridge-D9gEIAp3.cjs.map → ContactsTranslationBridge-Dx_1jAxz.cjs.map} +1 -1
  10. package/dist/{FluidProvider-CURmntkw.cjs → FluidProvider-BvVjhwrv.cjs} +7 -6
  11. package/dist/{FluidProvider-CURmntkw.cjs.map → FluidProvider-BvVjhwrv.cjs.map} +1 -1
  12. package/dist/{FluidProvider-Bu9N22BI.mjs → FluidProvider-Cx0wNNF7.mjs} +7 -6
  13. package/dist/{FluidProvider-Bu9N22BI.mjs.map → FluidProvider-Cx0wNNF7.mjs.map} +1 -1
  14. package/dist/{MessagingScreen-NE1Na8rN.cjs → MessagingScreen-6fuZJ_0Q.cjs} +5 -5
  15. package/dist/{MessagingScreen-D8_oGy_k.mjs → MessagingScreen-BEVwNvC3.mjs} +2 -2
  16. package/dist/{MessagingScreen-D8_oGy_k.mjs.map → MessagingScreen-BEVwNvC3.mjs.map} +1 -1
  17. package/dist/{MessagingScreen-YvwfkYbW.cjs → MessagingScreen-BTcBx8v4.cjs} +2 -2
  18. package/dist/{MessagingScreen-YvwfkYbW.cjs.map → MessagingScreen-BTcBx8v4.cjs.map} +1 -1
  19. package/dist/{MessagingScreen-DwmciO1o.mjs → MessagingScreen-Bw3pawb5.mjs} +5 -5
  20. package/dist/{OrdersScreen-B81SHSOU.cjs → OrdersScreen-BZUSgBTy.cjs} +2 -2
  21. package/dist/{OrdersScreen-B81SHSOU.cjs.map → OrdersScreen-BZUSgBTy.cjs.map} +1 -1
  22. package/dist/{OrdersScreen-BtgUuBky.cjs → OrdersScreen-CViKIru5.cjs} +5 -5
  23. package/dist/{OrdersScreen-DYjOpQa2.mjs → OrdersScreen-DcA6QoGF.mjs} +5 -5
  24. package/dist/{OrdersScreen-BycEvuSw.mjs → OrdersScreen-Kkk88xDy.mjs} +2 -2
  25. package/dist/{OrdersScreen-BycEvuSw.mjs.map → OrdersScreen-Kkk88xDy.mjs.map} +1 -1
  26. package/dist/{ProfileScreen-DisEMe-1.cjs → ProfileScreen-C2-nARkW.cjs} +6 -6
  27. package/dist/{ProfileScreen-BVRnPuRm.mjs → ProfileScreen-CZJA8dZS.mjs} +4 -4
  28. package/dist/{ProfileScreen-BVRnPuRm.mjs.map → ProfileScreen-CZJA8dZS.mjs.map} +1 -1
  29. package/dist/{ProfileScreen-Ckf1a-zh.mjs → ProfileScreen-DjCRBTlH.mjs} +6 -6
  30. package/dist/{ProfileScreen-B0yVzqyg.cjs → ProfileScreen-j-5VwfqY.cjs} +4 -4
  31. package/dist/{ProfileScreen-B0yVzqyg.cjs.map → ProfileScreen-j-5VwfqY.cjs.map} +1 -1
  32. package/dist/{ShopScreen-DN5Y1HQ7.mjs → ShopScreen-BT5QKVbL.mjs} +5 -5
  33. package/dist/{ShopScreen-CPpJB-Kt.mjs → ShopScreen-CyRX0aOs.mjs} +48 -42
  34. package/dist/ShopScreen-CyRX0aOs.mjs.map +1 -0
  35. package/dist/{ShopScreen-CEB0iwmF.cjs → ShopScreen-D3Pzmg6Q.cjs} +5 -5
  36. package/dist/{ShopScreen-B9uHdQP7.cjs → ShopScreen-DVlQAOHj.cjs} +48 -42
  37. package/dist/ShopScreen-DVlQAOHj.cjs.map +1 -0
  38. package/dist/{ShopWidget-BbCPV2_Y.cjs → ShopWidget-C_R5RGoe.cjs} +13 -23
  39. package/dist/ShopWidget-C_R5RGoe.cjs.map +1 -0
  40. package/dist/{ShopWidget-BF0zWjS6.cjs → ShopWidget-CaPQVMlr.cjs} +1 -1
  41. package/dist/{ShopWidget-YMXis5SJ.mjs → ShopWidget-N0OqYNM-.mjs} +13 -23
  42. package/dist/ShopWidget-N0OqYNM-.mjs.map +1 -0
  43. package/dist/{SubscriptionsScreen-DdazqD1V.mjs → SubscriptionsScreen-C-UXQeNC.mjs} +4 -4
  44. package/dist/{SubscriptionsScreen-DdazqD1V.mjs.map → SubscriptionsScreen-C-UXQeNC.mjs.map} +1 -1
  45. package/dist/{SubscriptionsScreen-BTQuJNaY.cjs → SubscriptionsScreen-OITPOl3Y.cjs} +4 -4
  46. package/dist/{SubscriptionsScreen-BTQuJNaY.cjs.map → SubscriptionsScreen-OITPOl3Y.cjs.map} +1 -1
  47. package/dist/{SubscriptionsScreen-C_7vhN3q.cjs → SubscriptionsScreen-amFuxymL.cjs} +6 -6
  48. package/dist/{SubscriptionsScreen-ZkjeOBHH.mjs → SubscriptionsScreen-lvMO6Bkp.mjs} +6 -6
  49. package/dist/{ToDoWidget-3bkkxozQ.cjs → ToDoWidget-Cr0VP7k6.cjs} +1 -1
  50. package/dist/{ToDoWidget-BZyJIX1X.mjs → ToDoWidget-DiGklfKG.mjs} +32 -39
  51. package/dist/ToDoWidget-DiGklfKG.mjs.map +1 -0
  52. package/dist/{ToDoWidget-CgFDCQNX.cjs → ToDoWidget-aSYTB02O.cjs} +31 -38
  53. package/dist/ToDoWidget-aSYTB02O.cjs.map +1 -0
  54. package/dist/index.cjs +24 -24
  55. package/dist/index.d.cts.map +1 -1
  56. package/dist/index.d.mts.map +1 -1
  57. package/dist/index.mjs +24 -24
  58. package/dist/preview/preview-entry.cjs +5 -5
  59. package/dist/preview/preview-entry.mjs +5 -5
  60. package/dist/{use-portal-card-add-3ds-flow-CphCh5cu.mjs → use-portal-card-add-3ds-flow-BLjXfmje.mjs} +2 -2
  61. package/dist/{use-portal-card-add-3ds-flow-CphCh5cu.mjs.map → use-portal-card-add-3ds-flow-BLjXfmje.mjs.map} +1 -1
  62. package/dist/{use-portal-card-add-3ds-flow-BxFF4Wew.cjs → use-portal-card-add-3ds-flow-C2uzzhr8.cjs} +2 -2
  63. package/dist/{use-portal-card-add-3ds-flow-BxFF4Wew.cjs.map → use-portal-card-add-3ds-flow-C2uzzhr8.cjs.map} +1 -1
  64. package/dist/{widget-package-utils-BS8mCzgZ.mjs → widget-package-utils-DG2ibxqS.mjs} +2 -2
  65. package/dist/{widget-package-utils-BS8mCzgZ.mjs.map → widget-package-utils-DG2ibxqS.mjs.map} +1 -1
  66. package/dist/{widget-package-utils-C0WYunNq.cjs → widget-package-utils-pdLitM72.cjs} +2 -2
  67. package/dist/{widget-package-utils-C0WYunNq.cjs.map → widget-package-utils-pdLitM72.cjs.map} +1 -1
  68. package/package.json +14 -14
  69. package/dist/ShopScreen-B9uHdQP7.cjs.map +0 -1
  70. package/dist/ShopScreen-CPpJB-Kt.mjs.map +0 -1
  71. package/dist/ShopWidget-BbCPV2_Y.cjs.map +0 -1
  72. package/dist/ShopWidget-YMXis5SJ.mjs.map +0 -1
  73. package/dist/ToDoWidget-BZyJIX1X.mjs.map +0 -1
  74. package/dist/ToDoWidget-CgFDCQNX.cjs.map +0 -1
@@ -1,10 +1,10 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- require("./FluidProvider-CURmntkw.cjs");
2
+ require("./FluidProvider-BvVjhwrv.cjs");
3
3
  require("./ScreenRenderer-Ce8MJPnR.cjs");
4
4
  require("./use-account-BBSExzPb.cjs");
5
5
  require("./use-optional-store-CIM4spDu.cjs");
6
6
  require("./mysite-api-context-DtEXblIV.cjs");
7
- require("./ContactsTranslationBridge-D9gEIAp3.cjs");
7
+ require("./ContactsTranslationBridge-Dx_1jAxz.cjs");
8
8
  require("./registry-context-Ds0_y48D.cjs");
9
9
  require("./WidgetInteractionContext-BiQvdkPb.cjs");
10
10
  require("./RepUserContext-BiKQBY47.cjs");
@@ -18,7 +18,7 @@ require("./shell-translation-api-context-uGur0Kah.cjs");
18
18
  require("./error-state-bqYzR5FT.cjs");
19
19
  require("./PointsWidget-CcENjU_h.cjs");
20
20
  require("./task-composer-form-C8r1rqjv.cjs");
21
- require("./ShopWidget-BbCPV2_Y.cjs");
21
+ require("./ShopWidget-C_R5RGoe.cjs");
22
22
  require("./LayoutWidget-CzM4gn1-.cjs");
23
23
  require("./registries-DiCQ4oFx.cjs");
24
24
  require("./fields-D__8kJeJ.cjs");
@@ -46,16 +46,16 @@ require("./RecentActivityWidget-7tACMQqV.cjs");
46
46
  require("./SeparatorWidget-DVnbbshP.cjs");
47
47
  require("./SpacerWidget-5oBsx8_A.cjs");
48
48
  require("./TableWidget-BrDvinFN.cjs");
49
- require("./ToDoWidget-CgFDCQNX.cjs");
49
+ require("./ToDoWidget-aSYTB02O.cjs");
50
50
  require("./VideoWidget-B4SzSxTv.cjs");
51
51
  require("./translation-adapter-CkgtjiOT.cjs");
52
52
  require("./translation-adapter-BMvv-Nqx.cjs");
53
53
  require("./translation-adapter-CRb5EiLL.cjs");
54
54
  require("./ScreenHeaderContext-DI1qj4qi.cjs");
55
- require("./use-portal-card-add-3ds-flow-BxFF4Wew.cjs");
55
+ require("./use-portal-card-add-3ds-flow-C2uzzhr8.cjs");
56
56
  require("./display-JuYIguJK.cjs");
57
57
  require("./Combobox-S8AArI13.cjs");
58
58
  require("./prop-types-DNooHnDZ.cjs");
59
- const require_SubscriptionsScreen = require("./SubscriptionsScreen-BTQuJNaY.cjs");
59
+ const require_SubscriptionsScreen = require("./SubscriptionsScreen-OITPOl3Y.cjs");
60
60
  exports.SubscriptionsScreen = require_SubscriptionsScreen.SubscriptionsScreen;
61
61
  exports.subscriptionsScreenPropertySchema = require_SubscriptionsScreen.subscriptionsScreenPropertySchema;
@@ -1,9 +1,9 @@
1
1
  import "./use-account-D70N5avv.mjs";
2
- import "./FluidProvider-Bu9N22BI.mjs";
2
+ import "./FluidProvider-Cx0wNNF7.mjs";
3
3
  import "./ScreenRenderer-CcT_j-WQ.mjs";
4
4
  import "./use-optional-store-BPICvfcP.mjs";
5
5
  import "./mysite-api-context-CoLr9vIf.mjs";
6
- import "./ContactsTranslationBridge-BGeau5_N.mjs";
6
+ import "./ContactsTranslationBridge-Ckjj_Iu9.mjs";
7
7
  import "./registry-context-DFsxRMN8.mjs";
8
8
  import "./WidgetInteractionContext-CDHSzzws.mjs";
9
9
  import "./RepUserContext-D1G5lEuo.mjs";
@@ -17,7 +17,7 @@ import "./shell-translation-api-context-SVRZkj2i.mjs";
17
17
  import "./error-state-7TTlrxt0.mjs";
18
18
  import "./PointsWidget-C3oNVD91.mjs";
19
19
  import "./task-composer-form-DyIXwTgx.mjs";
20
- import "./ShopWidget-YMXis5SJ.mjs";
20
+ import "./ShopWidget-N0OqYNM-.mjs";
21
21
  import "./LayoutWidget-DXPXYYd_.mjs";
22
22
  import "./registries-CSAha0WD.mjs";
23
23
  import "./fields-Ba-Ep01A.mjs";
@@ -32,7 +32,7 @@ import "./MediaRenderer-Dl1imLwm.mjs";
32
32
  import "./CarouselWidget-BTbRZPWn.mjs";
33
33
  import "./CatchUpWidget-qNrDLfXJ.mjs";
34
34
  import "./src-BPYu8cbo.mjs";
35
- import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-DdazqD1V.mjs";
35
+ import { n as subscriptionsScreenPropertySchema, t as SubscriptionsScreen } from "./SubscriptionsScreen-C-UXQeNC.mjs";
36
36
  import "./ChartWidget-B4vLdttP.mjs";
37
37
  import "./ContainerWidget-C1Nk6GPZ.mjs";
38
38
  import "./EmbedWidget-p_Ybl0em.mjs";
@@ -46,13 +46,13 @@ import "./RecentActivityWidget-C4UFPIIB.mjs";
46
46
  import "./SeparatorWidget-DxTG9J7u.mjs";
47
47
  import "./SpacerWidget-Bj79fK_w.mjs";
48
48
  import "./TableWidget-CG5nu9Bl.mjs";
49
- import "./ToDoWidget-BZyJIX1X.mjs";
49
+ import "./ToDoWidget-DiGklfKG.mjs";
50
50
  import "./VideoWidget-DSIgaM1q.mjs";
51
51
  import "./translation-adapter-C7MxrAX_.mjs";
52
52
  import "./translation-adapter-CPr3Y0RW.mjs";
53
53
  import "./translation-adapter-nPt5lYdk.mjs";
54
54
  import "./ScreenHeaderContext-mFd2T-WH.mjs";
55
- import "./use-portal-card-add-3ds-flow-CphCh5cu.mjs";
55
+ import "./use-portal-card-add-3ds-flow-BLjXfmje.mjs";
56
56
  import "./display-B3BfkaBm.mjs";
57
57
  import "./Combobox-BXuJfcpL.mjs";
58
58
  export { SubscriptionsScreen, subscriptionsScreenPropertySchema };
@@ -7,5 +7,5 @@ require("./registries-DiCQ4oFx.cjs");
7
7
  require("./fields-D__8kJeJ.cjs");
8
8
  require("./preview-context-DI7oT-If.cjs");
9
9
  require("./src-DCYVQ8hc.cjs");
10
- const require_ToDoWidget = require("./ToDoWidget-CgFDCQNX.cjs");
10
+ const require_ToDoWidget = require("./ToDoWidget-aSYTB02O.cjs");
11
11
  exports.toDoWidgetPropertySchema = require_ToDoWidget.toDoWidgetPropertySchema;
@@ -8,7 +8,7 @@ import { $ as Dialog, H as DropdownMenu, Kn as cn, M as Input, Q as DropdownMenu
8
8
  import { useEffect, useMemo, useRef, useState } from "react";
9
9
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
- import { Check, ChevronDown, ChevronUp, EllipsisVertical, ListChecks, Plus, Search, UserRound } from "lucide-react";
11
+ import { Check, ChevronDown, ChevronUp, EllipsisVertical, Plus, Search, UserRound } from "lucide-react";
12
12
  //#region ../widgets/src/hooks/use-todos.preview.ts
13
13
  const now = /* @__PURE__ */ new Date();
14
14
  function daysFromNow(days) {
@@ -542,6 +542,7 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
542
542
  const todosToShow = activeTodos.slice(0, maxItems);
543
543
  const remainingCount = activeTodos.length - todosToShow.length;
544
544
  const isEmpty = activeTodos.length === 0;
545
+ const activeTaskLabel = usesSingularPluralForm(locale, activeTodos.length) ? t("todo_open_tasks_one", { count: activeTodos.length }) : t("todo_open_tasks_other", { count: activeTodos.length });
545
546
  const groupedTodos = todosToShow.reduce((groups, todo) => {
546
547
  const existing = groups.find((g) => g.contactId === todo.contactId);
547
548
  if (existing) existing.todos.push(todo);
@@ -570,22 +571,24 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
570
571
  ...props,
571
572
  children: [
572
573
  /* @__PURE__ */ jsxs("div", {
573
- className: "mb-3 flex items-center justify-between",
574
- children: [/* @__PURE__ */ jsx("div", {
575
- className: "flex items-center gap-3",
576
- children: titleEnabled && titleText && /* @__PURE__ */ jsx("h2", {
577
- className: `text-${titleFontSize} font-header font-bold text-${titleColor}`,
574
+ className: "mb-4 flex items-start justify-between gap-4",
575
+ children: [/* @__PURE__ */ jsxs("div", {
576
+ className: "flex min-w-0 flex-col gap-1",
577
+ children: [titleEnabled && titleText && /* @__PURE__ */ jsx("h2", {
578
+ className: `text-${titleFontSize} font-header leading-tight font-bold text-${titleColor}`,
578
579
  children: titleText
579
- })
580
- }), /* @__PURE__ */ jsxs("div", {
581
- className: "flex items-center gap-2",
582
- children: [!isEmpty && !isLoading && /* @__PURE__ */ jsx("span", {
583
- className: `text-2xl font-bold text-${accentColor}`,
584
- children: activeTodos.length
585
- }), /* @__PURE__ */ jsx("div", {
586
- className: `flex size-10 shrink-0 items-center justify-center`,
587
- children: /* @__PURE__ */ jsx(ListChecks, { className: `h-5 w-5 text-${accentColor}-foreground` })
580
+ }), !isEmpty && !isLoading && /* @__PURE__ */ jsx("span", {
581
+ className: `w-fit rounded-full bg-${accentColor}/10 px-2.5 py-1 text-[11px] leading-none font-semibold text-${accentColor}`,
582
+ children: activeTaskLabel
588
583
  })]
584
+ }), !isPreview && !isEmpty && !isLoading && !isError && /* @__PURE__ */ jsxs("button", {
585
+ type: "button",
586
+ onClick: () => setIsCreateDialogOpen(true),
587
+ className: `inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor} px-3.5 py-2 text-xs font-semibold text-${accentColor}-foreground shadow-sm transition-colors hover:bg-${accentColor}/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-${accentColor}/40`,
588
+ children: [/* @__PURE__ */ jsx(Plus, {
589
+ className: "size-3.5",
590
+ "aria-hidden": "true"
591
+ }), t("add_todo")]
589
592
  })]
590
593
  }),
591
594
  isLoading ? /* @__PURE__ */ jsx("div", {
@@ -606,15 +609,15 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
606
609
  }), t("add_todo")]
607
610
  })]
608
611
  }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
609
- className: "flex flex-col gap-3",
612
+ className: "flex flex-col gap-4",
610
613
  children: groupedTodos.map((group) => /* @__PURE__ */ jsxs("div", {
611
614
  className: "flex flex-col",
612
615
  children: [group.contactName && /* @__PURE__ */ jsx("div", {
613
- className: `mb-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`,
616
+ className: `mb-2 text-xs leading-none font-semibold tracking-wide uppercase text-${textColor}/60`,
614
617
  children: group.contactName
615
618
  }), group.todos.map((todo, index) => {
616
619
  return /* @__PURE__ */ jsxs("div", {
617
- className: `group/row flex items-center gap-3 py-2 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
620
+ className: `group/row grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 py-2.5 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
618
621
  children: [
619
622
  /* @__PURE__ */ jsx("input", {
620
623
  type: "checkbox",
@@ -630,12 +633,12 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
630
633
  onClick: () => handleRowClick(todo),
631
634
  disabled: isPreview,
632
635
  "aria-label": t("edit_todo"),
633
- className: "flex min-w-0 flex-1 flex-col items-start gap-0.5 text-left not-disabled:cursor-pointer",
636
+ className: "flex min-w-0 flex-col items-start gap-1 text-left not-disabled:cursor-pointer",
634
637
  children: [/* @__PURE__ */ jsx("span", {
635
- className: "line-clamp-1 text-sm",
638
+ className: "line-clamp-1 text-base leading-snug font-medium",
636
639
  children: parseTaskBody(todo.body).title
637
640
  }), todo.dueAt && /* @__PURE__ */ jsx("span", {
638
- className: `text-xs text-${textColor}/60`,
641
+ className: `text-sm leading-none text-${textColor}/60`,
639
642
  children: formatDueAt(todo.dueAt, locale)
640
643
  })]
641
644
  }),
@@ -646,7 +649,7 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
646
649
  size: "icon-xs",
647
650
  onClick: (event) => event.stopPropagation(),
648
651
  "aria-label": t("task_actions"),
649
- className: "opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100",
652
+ className: `text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`,
650
653
  children: /* @__PURE__ */ jsx(EllipsisVertical, { className: "size-4" })
651
654
  })
652
655
  }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
@@ -670,29 +673,19 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
670
673
  }, todo.id);
671
674
  })]
672
675
  }, group.contactId ?? "__unassigned__"))
673
- }), /* @__PURE__ */ jsxs("div", {
674
- className: "mt-2 flex items-center justify-between",
675
- children: [remainingCount > 0 && /* @__PURE__ */ jsx("span", {
676
+ }), /* @__PURE__ */ jsx("div", {
677
+ className: "mt-1 flex items-center justify-between",
678
+ children: remainingCount > 0 && /* @__PURE__ */ jsx("span", {
676
679
  className: `text-sm text-${textColor}/50 underline`,
677
680
  children: usesSingularPluralForm(locale, remainingCount) ? t("todo_more_tasks_one", { count: remainingCount }) : t("todo_more_tasks_other", { count: remainingCount })
678
- }), /* @__PURE__ */ jsx("div", {
679
- className: "ml-auto",
680
- children: /* @__PURE__ */ jsx("button", {
681
- type: "button",
682
- onClick: () => setIsCreateDialogOpen(true),
683
- disabled: isPreview,
684
- "aria-label": t("add_todo"),
685
- className: `flex h-8 w-8 items-center justify-center rounded-full transition-colors not-disabled:cursor-pointer hover:bg-${textColor}/10 disabled:opacity-60`,
686
- children: /* @__PURE__ */ jsx(Plus, { className: `h-5 w-5 text-${textColor}/50` })
687
- })
688
- })]
681
+ })
689
682
  })] }),
690
683
  !isPreview && !isLoading && !isError && !isCompletedLoading && doneCount > 0 && /* @__PURE__ */ jsxs("div", {
691
- className: `mt-3 border-t border-${textColor}/10 pt-2`,
684
+ className: `mt-4 border-t border-${textColor}/10 pt-3`,
692
685
  children: [/* @__PURE__ */ jsxs("button", {
693
686
  type: "button",
694
687
  onClick: () => setIsDoneExpanded((v) => !v),
695
- className: `flex w-full items-center justify-between gap-2 rounded-md py-1 text-xs font-medium text-${textColor}/60 hover:bg-${textColor}/5`,
688
+ className: `flex w-full items-center justify-between gap-2 rounded-md py-1.5 text-sm font-medium text-${textColor}/60 hover:bg-${textColor}/5`,
696
689
  "aria-expanded": isDoneExpanded,
697
690
  children: [/* @__PURE__ */ jsx("span", { children: isDoneExpanded ? t("hide_done") : t("view_done", { count: doneCount }) }), isDoneExpanded ? /* @__PURE__ */ jsx(ChevronUp, {
698
691
  className: "size-4",
@@ -899,4 +892,4 @@ const toDoWidgetPropertySchema = {
899
892
  //#endregion
900
893
  export { ToDoWidget_exports as n, toDoWidgetPropertySchema as r, ToDoWidget as t };
901
894
 
902
- //# sourceMappingURL=ToDoWidget-BZyJIX1X.mjs.map
895
+ //# sourceMappingURL=ToDoWidget-DiGklfKG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToDoWidget-DiGklfKG.mjs","names":[],"sources":["../../widgets/src/hooks/use-todos.preview.ts","../../widgets/src/hooks/use-todos.ts","../../widgets/src/hooks/use-update-todo.ts","../../widgets/src/hooks/use-delete-todo.ts","../../widgets/src/hooks/use-create-todo.ts","../../widgets/src/widgets/CreateTodoDialog.tsx","../../widgets/src/widgets/EditTodoDialog.tsx","../../widgets/src/widgets/ToDoWidget.tsx"],"sourcesContent":["import type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nexport const PREVIEW_DATA: Todo[] = [\n {\n id: 1,\n body: \"Send follow-up email to new leads\",\n dueAt: daysFromNow(1),\n completedAt: null,\n createdAt: daysFromNow(-2),\n contactId: 101,\n contactName: \"Sarah Johnson\",\n },\n {\n id: 2,\n body: \"Prepare slides for team training\",\n dueAt: daysFromNow(3),\n completedAt: null,\n createdAt: daysFromNow(-1),\n contactId: null,\n contactName: null,\n },\n {\n id: 3,\n body: \"Review monthly sales report\",\n dueAt: daysFromNow(-1),\n completedAt: null,\n createdAt: daysFromNow(-5),\n contactId: 102,\n contactName: \"Mike Chen\",\n },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport type { TodosStateFilter } from \"@fluid-app/portal-core/widgets-api\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-todos.preview\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nexport type { TodosStateFilter } from \"@fluid-app/portal-core/widgets-api\";\n\n/**\n * Shared cache key for the todos list. Both useTodos (read) and\n * useUpdateTodo (write) use this — drift would silently break optimistic\n * updates. The `state` arg is included so the active/completed lists\n * cache independently.\n */\nexport function todosQueryKey(args: {\n baseUrl: string | undefined;\n isPreview: boolean;\n state?: TodosStateFilter;\n}) {\n return [\n \"portal-widget-use\",\n \"todos\",\n args.isPreview ? \"preview\" : args.baseUrl,\n args.state ?? \"incomplete\",\n ] as const;\n}\n\nexport function todosQueryKeyPrefix(args: {\n baseUrl: string | undefined;\n isPreview: boolean;\n}) {\n return [\n \"portal-widget-use\",\n \"todos\",\n args.isPreview ? \"preview\" : args.baseUrl,\n ] as const;\n}\n\nexport interface UseTodosOptions {\n state?: TodosStateFilter;\n}\n\nexport function useTodos(\n options?: UseTodosOptions,\n): UseQueryResult<Todo[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n const state = options?.state;\n\n return useQuery({\n queryKey: todosQueryKey(\n state !== undefined\n ? { baseUrl, isPreview, state }\n : { baseUrl, isPreview },\n ),\n queryFn: ({ signal }) =>\n widgetsApi.fetchTodos(\n state !== undefined ? { state } : undefined,\n signal,\n ),\n enabled: !isPreview,\n ...(isPreview &&\n (state === undefined || state === \"incomplete\") && {\n placeholderData: PREVIEW_DATA,\n }),\n ...(isPreview &&\n state !== undefined &&\n state !== \"incomplete\" && {\n placeholderData: [],\n }),\n });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey, todosQueryKeyPrefix } from \"./use-todos\";\n\nexport interface UpdateTodoVariables {\n id: number;\n body?: string;\n due_at?: string | null;\n completed?: boolean;\n /**\n * Used to invalidate `contactsKeys.tasks(contactId)` after a successful\n * update so the in-contact tasks view stays in sync.\n */\n contactId?: number | null;\n}\n\nexport function useUpdateTodo() {\n const widgetsApi = useWidgetsApi();\n const queryClient = useQueryClient();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n const queryKey = todosQueryKey({ baseUrl, isPreview });\n\n return useMutation({\n mutationFn: ({ id, body, due_at, completed }: UpdateTodoVariables) => {\n // Defense in depth: ToDoWidget already disables the checkbox in\n // preview mode, but if any other caller fires this mutation while\n // previewing we'd PATCH demo data on the real backend. Fail closed.\n if (isPreview) {\n return Promise.reject(\n new Error(\"Todo updates are disabled in preview mode\"),\n );\n }\n const input: {\n body?: string;\n due_at?: string | null;\n completed?: boolean;\n } = {};\n if (body !== undefined) input.body = body;\n if (due_at !== undefined) input.due_at = due_at;\n if (completed !== undefined) input.completed = completed;\n return widgetsApi.updateTodo(id, input);\n },\n onMutate: async (variables) => {\n // Only apply the optimistic-completion shortcut when the caller is\n // toggling completion alone (the checkbox case). Body/due_at edits\n // come from the dialog and fall back to invalidate-on-success.\n const isCompletionToggle =\n variables.completed !== undefined &&\n variables.body === undefined &&\n variables.due_at === undefined;\n if (!isCompletionToggle) return { previous: undefined };\n\n await queryClient.cancelQueries({ queryKey });\n const previous = queryClient.getQueryData<Todo[]>(queryKey);\n queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n (current ?? []).map((todo) =>\n todo.id === variables.id\n ? {\n ...todo,\n completedAt: variables.completed\n ? new Date().toISOString()\n : null,\n }\n : todo,\n ),\n );\n return { previous };\n },\n onSuccess: (updated, variables) => {\n queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n (current ?? []).map((todo) =>\n todo.id === updated.id ? updated : todo,\n ),\n );\n // Match the documented test plan: completing toasts, un-completing\n // is silent. Body/due_at edits also toast as \"Todo updated\".\n const isCompletionToggle =\n variables.completed !== undefined &&\n variables.body === undefined &&\n variables.due_at === undefined;\n if (isCompletionToggle) {\n if (variables.completed) {\n fluidToast({ title: \"Todo completed\", type: \"success\" });\n }\n } else {\n fluidToast({ title: \"Todo updated\", type: \"success\" });\n }\n // Keep the contact-scoped tasks view in sync when the todo is\n // attached to a contact.\n if (variables.contactId != null) {\n queryClient.invalidateQueries({\n queryKey: contactsKeys.tasks(String(variables.contactId)),\n });\n }\n },\n onError: (_error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData(queryKey, context.previous);\n }\n fluidToast({ title: \"Failed to update todo\", type: \"error\" });\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: todosQueryKeyPrefix({ baseUrl, isPreview }),\n });\n },\n });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey, todosQueryKeyPrefix } from \"./use-todos\";\n\nexport interface DeleteTodoVariables {\n id: number;\n /**\n * Used to invalidate `contactsKeys.tasks(contactId)` after a successful\n * delete so the in-contact tasks view stays in sync.\n */\n contactId?: number | null;\n}\n\nexport function useDeleteTodo() {\n const widgetsApi = useWidgetsApi();\n const queryClient = useQueryClient();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n const queryKey = todosQueryKey({ baseUrl, isPreview });\n\n return useMutation({\n mutationFn: ({ id }: DeleteTodoVariables) => {\n if (isPreview) {\n return Promise.reject(\n new Error(\"Todo deletion is disabled in preview mode\"),\n );\n }\n return widgetsApi.deleteTodo(id);\n },\n onMutate: async ({ id }) => {\n await queryClient.cancelQueries({ queryKey });\n const previous = queryClient.getQueryData<Todo[]>(queryKey);\n queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n (current ?? []).filter((todo) => todo.id !== id),\n );\n return { previous };\n },\n onSuccess: (_result, variables) => {\n fluidToast({ title: \"Todo deleted\", type: \"success\" });\n if (variables.contactId != null) {\n queryClient.invalidateQueries({\n queryKey: contactsKeys.tasks(String(variables.contactId)),\n });\n }\n },\n onError: (_error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData(queryKey, context.previous);\n }\n fluidToast({ title: \"Failed to delete todo\", type: \"error\" });\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: todosQueryKeyPrefix({ baseUrl, isPreview }),\n });\n },\n });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport { todosQueryKey } from \"./use-todos\";\n\nexport interface CreateTodoVariables {\n body: string;\n due_at: string | null;\n contact_id: number | null;\n}\n\nexport function useCreateTodo() {\n const widgetsApi = useWidgetsApi();\n const queryClient = useQueryClient();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useMutation({\n mutationFn: (variables: CreateTodoVariables) => {\n if (isPreview) {\n return Promise.reject(\n new Error(\"Todo creation is disabled in preview mode\"),\n );\n }\n return widgetsApi.createTodo(variables);\n },\n onSuccess: (_created, variables) => {\n fluidToast({ title: \"Todo created\", type: \"success\" });\n queryClient.invalidateQueries({\n queryKey: todosQueryKey({ baseUrl, isPreview }),\n });\n if (variables.contact_id != null) {\n queryClient.invalidateQueries({\n queryKey: contactsKeys.tasks(String(variables.contact_id)),\n });\n }\n },\n onError: () => {\n fluidToast({ title: \"Failed to create todo\", type: \"error\" });\n },\n });\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Check, Search, UserRound } from \"lucide-react\";\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n cn,\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Input,\n Popover,\n PopoverContent,\n PopoverTrigger,\n PortalContainerProvider,\n} from \"@fluid-app/ui-primitives\";\nimport { useInfiniteContacts } from \"@fluid-app/contacts-core/hooks/use-infinite-contacts\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport type { Contact } from \"@fluid-app/contacts-core/types\";\nimport { TaskComposerForm } from \"@fluid-app/contacts-ui/portal/components/tasks/task-composer-form\";\nimport { useCreateTodo } from \"../hooks/use-create-todo\";\n\nconst SEARCH_DEBOUNCE_MS = 200;\n\nfunction getContactInitials(contact: Contact): string | undefined {\n const firstInitial = contact.first_name?.trim().charAt(0);\n const lastInitial = contact.last_name?.trim().charAt(0);\n const explicitInitials = `${firstInitial ?? \"\"}${lastInitial ?? \"\"}`;\n\n if (explicitInitials) return explicitInitials.toUpperCase();\n\n const initials = contact.full_name\n .trim()\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part.charAt(0))\n .join(\"\")\n .toUpperCase();\n\n return initials || undefined;\n}\n\nfunction ContactBadgeAvatar({\n contact,\n}: {\n contact: Contact | null;\n}): React.JSX.Element {\n const initials = contact ? getContactInitials(contact) : undefined;\n\n return (\n <Avatar className=\"size-4 text-current\" aria-hidden=\"true\">\n {contact?.avatar_url ? (\n <AvatarImage src={contact.avatar_url} alt=\"\" />\n ) : null}\n <AvatarFallback className=\"bg-transparent text-[0.625rem] font-semibold text-current\">\n {initials ?? <UserRound className=\"size-3\" aria-hidden=\"true\" />}\n </AvatarFallback>\n </Avatar>\n );\n}\n\nexport interface CreateTodoDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nexport function CreateTodoDialog({\n open,\n onOpenChange,\n}: CreateTodoDialogProps): React.JSX.Element {\n const [selectedContact, setSelectedContact] = useState<Contact | null>(null);\n // Anchor nested popovers inside the dialog's themed portal subtree.\n // ui-primitives PopoverContent reads this via usePortalContainer(), so the\n // task date popover inherits portal theme variables instead of falling back\n // to document.body. Dialog also applies pointer-events: none to the body\n // while open, which kills clicks and scroll on a body-portaled popover in\n // WebKit. See edit-bill-date-dialog for the canonical pattern.\n const [themedPopoverContainer, setThemedPopoverContainer] =\n useState<HTMLDivElement | null>(null);\n const { t } = useContactsTranslation();\n\n const createTodo = useCreateTodo();\n\n // Reset state when the dialog closes so the next open starts fresh.\n useEffect(() => {\n if (!open) setSelectedContact(null);\n }, [open]);\n\n const handleSubmit = (values: { body: string; due_at: string | null }) => {\n createTodo.mutate(\n {\n body: values.body,\n due_at: values.due_at,\n contact_id: selectedContact ? selectedContact.id : null,\n },\n {\n onSuccess: () => onOpenChange(false),\n },\n );\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"gap-4 p-4 sm:max-w-xl sm:gap-6 sm:p-6\">\n <DialogHeader>\n <DialogTitle>{t(\"add_todo_dialog_title\")}</DialogTitle>\n </DialogHeader>\n\n <PortalContainerProvider container={themedPopoverContainer}>\n <div className=\"mt-2 flex flex-col gap-3\">\n <TaskComposerForm\n onSubmit={handleSubmit}\n isSubmitting={createTodo.isPending}\n metadataControls={\n <ContactPicker\n value={selectedContact}\n onSelect={setSelectedContact}\n onClear={() => setSelectedContact(null)}\n />\n }\n />\n </div>\n </PortalContainerProvider>\n {/* Portal target for the popover. Must live INSIDE DialogContent so\n it ends up in the dialog's body-level portal — otherwise it\n renders inline next to the widget, and any transformed ancestor\n (builder canvas zoom, etc.) would break `position: fixed`.\n Intentionally NOT aria-hidden — the popover content portaled\n into this container has its own listbox/option semantics that\n screen readers need to reach. `pointer-events-none` keeps the\n empty overlay from capturing clicks; the popover content sets\n `pointer-events-auto` so it remains interactive. */}\n <div\n ref={setThemedPopoverContainer}\n className=\"pointer-events-none fixed inset-0 z-[9998]\"\n />\n </DialogContent>\n </Dialog>\n );\n}\n\ninterface ContactPickerProps {\n value: Contact | null;\n onSelect: (contact: Contact) => void;\n onClear: () => void;\n}\n\nfunction ContactPicker({\n value,\n onSelect,\n onClear,\n}: ContactPickerProps): React.JSX.Element {\n const { t } = useContactsTranslation();\n const [open, setOpen] = useState(false);\n const [searchInput, setSearchInput] = useState(\"\");\n const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handle = window.setTimeout(() => {\n setDebouncedSearch(searchInput.trim());\n }, SEARCH_DEBOUNCE_MS);\n return () => window.clearTimeout(handle);\n }, [searchInput]);\n\n const queryParams = useMemo(\n () => ({\n ...(debouncedSearch ? { search_query: debouncedSearch } : {}),\n sort_by: \"full_name\",\n sort_direction: \"asc\",\n per_page: 25,\n }),\n [debouncedSearch],\n );\n\n const {\n data,\n isLoading,\n isError,\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n } = useInfiniteContacts(queryParams);\n\n const contacts: Contact[] = useMemo(\n () => data?.pages.flatMap((page) => page.contacts ?? []) ?? [],\n [data],\n );\n\n // Auto-load the next page when the sentinel scrolls into view, so reps\n // with many contacts can reach all of them without typing a precise\n // search query.\n useEffect(() => {\n if (!open) return;\n\n const sentinel = sentinelRef.current;\n if (!sentinel || !hasNextPage) return;\n\n const observer = new IntersectionObserver((entries) => {\n const entry = entries[0];\n if (entry?.isIntersecting && !isFetchingNextPage) {\n fetchNextPage();\n }\n });\n\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [open, hasNextPage, isFetchingNextPage, fetchNextPage]);\n\n const selectedContactLabel = value ? value.full_name : t(\"select_contact\");\n\n return (\n <div className=\"flex min-w-0 items-center\">\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n aria-label={t(\"contact_picker_trigger_aria\", {\n selection: selectedContactLabel,\n })}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n className={cn(\n \"flex max-w-40 min-w-0 shrink items-center gap-1 rounded-full px-3 py-1 text-left text-xs font-medium transition-colors\",\n value\n ? \"bg-primary text-primary-foreground\"\n : \"bg-muted text-muted-foreground hover:bg-muted/70\",\n )}\n >\n <ContactBadgeAvatar contact={value} />\n <span className=\"min-w-0 truncate\">{selectedContactLabel}</span>\n </button>\n </PopoverTrigger>\n <PopoverContent\n align=\"start\"\n className=\"bg-popover text-popover-foreground pointer-events-auto z-[9999] w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-md border p-0 shadow-md\"\n >\n <div className=\"border-border flex items-center gap-2 border-b px-3 py-2\">\n <Search\n className=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n <Input\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n placeholder={t(\"search_placeholder\")}\n aria-label={t(\"search_placeholder\")}\n className=\"h-7 border-0 px-0 shadow-none focus-visible:ring-0\"\n />\n </div>\n <div className=\"max-h-64 overflow-y-auto py-1\" role=\"listbox\">\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={value === null}\n onClick={() => {\n onClear();\n setOpen(false);\n }}\n className=\"hover:bg-muted/50 flex w-full items-center justify-between gap-2 px-3 py-1.5 text-left text-sm transition-colors\"\n >\n <span className=\"text-muted-foreground truncate italic\">\n {t(\"no_contact\")}\n </span>\n {value === null && (\n <Check\n className=\"text-primary size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n {isLoading ? (\n <div className=\"text-muted-foreground px-3 py-6 text-center text-xs\">\n {t(\"loading\")}\n </div>\n ) : isError ? (\n <div className=\"text-destructive px-3 py-6 text-center text-xs\">\n {t(\"error_loading_list\")}\n </div>\n ) : contacts.length === 0 ? (\n <div className=\"text-muted-foreground px-3 py-6 text-center text-xs\">\n {debouncedSearch\n ? t(\"no_contacts_search\", { term: debouncedSearch })\n : t(\"no_contacts_yet\")}\n </div>\n ) : (\n <>\n {contacts.map((contact) => {\n const isSelected = value?.id === contact.id;\n return (\n <button\n key={contact.id}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n onClick={() => {\n onSelect(contact);\n setOpen(false);\n }}\n className=\"hover:bg-muted/50 flex w-full items-center justify-between gap-2 px-3 py-1.5 text-left text-sm transition-colors\"\n >\n <span className=\"truncate\">{contact.full_name}</span>\n {isSelected && (\n <Check\n className=\"text-primary size-4 shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n })}\n {hasNextPage && (\n <div ref={sentinelRef} aria-hidden=\"true\" className=\"h-4\" />\n )}\n {isFetchingNextPage && (\n <div className=\"text-muted-foreground px-3 py-2 text-center text-xs\">\n {t(\"loading_more\")}\n </div>\n )}\n </>\n )}\n </div>\n </PopoverContent>\n </Popover>\n </div>\n );\n}\n","\"use client\";\n\nimport React, { useState } from \"react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n PortalContainerProvider,\n} from \"@fluid-app/ui-primitives\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport { TaskComposerForm } from \"@fluid-app/contacts-ui/portal/components/tasks/task-composer-form\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\n\nexport interface EditTodoDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n todo: Todo | null;\n}\n\nexport function EditTodoDialog({\n open,\n onOpenChange,\n todo,\n}: EditTodoDialogProps): React.JSX.Element | null {\n const { t } = useContactsTranslation();\n const updateTodo = useUpdateTodo();\n // Anchor nested popovers inside the dialog's themed portal subtree.\n // ui-primitives PopoverContent reads this via usePortalContainer(), so the\n // task date popover inherits portal theme variables instead of falling back\n // to document.body.\n const [themedPopoverContainer, setThemedPopoverContainer] =\n useState<HTMLDivElement | null>(null);\n\n if (!todo) return null;\n\n const handleSubmit = (values: { body: string; due_at: string | null }) => {\n updateTodo.mutate(\n {\n id: todo.id,\n body: values.body,\n due_at: values.due_at,\n contactId: todo.contactId,\n },\n {\n onSuccess: () => onOpenChange(false),\n },\n );\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"gap-4 p-4 sm:max-w-md sm:gap-6 sm:p-6\">\n <DialogHeader>\n <DialogTitle>{t(\"edit_todo\")}</DialogTitle>\n </DialogHeader>\n <PortalContainerProvider container={themedPopoverContainer}>\n <TaskComposerForm\n key={todo.id}\n initialBody={todo.body}\n initialDueAt={todo.dueAt}\n onSubmit={handleSubmit}\n isSubmitting={updateTodo.isPending}\n submitLabel={t(\"save_changes\")}\n submittingLabel={t(\"saving\")}\n />\n </PortalContainerProvider>\n <div\n ref={setThemedPopoverContainer}\n className=\"pointer-events-none fixed inset-0 z-[9998]\"\n />\n </DialogContent>\n </Dialog>\n );\n}\n","import { useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n BorderWidthOptions,\n ColorOptions,\n FontSizeOptions,\n PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport { ChevronDown, ChevronUp, EllipsisVertical, Plus } from \"lucide-react\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { parseTaskBody } from \"@fluid-app/contacts-core/parse-task-body\";\nimport {\n ALL_DAY_PATTERN,\n LOCAL_TIMED_DUE_AT_PATTERN,\n} from \"@fluid-app/contacts-ui/portal/utils/format-date\";\nimport { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { useDeleteTodo } from \"../hooks/use-delete-todo\";\nimport { ErrorState } from \"../components/error-state\";\nimport { CreateTodoDialog } from \"./CreateTodoDialog\";\nimport { EditTodoDialog } from \"./EditTodoDialog\";\n\nconst pluralRulesCache = new Map<string, Intl.PluralRules>();\n\nfunction usesSingularPluralForm(locale: string, count: number): boolean {\n const tag = locale.replace(/_/g, \"-\");\n let rules = pluralRulesCache.get(tag);\n if (!rules) {\n rules = new Intl.PluralRules(tag);\n pluralRulesCache.set(tag, rules);\n }\n\n return rules.select(count) === \"one\";\n}\n\ntype ToDoWidgetProps = ComponentProps<\"div\"> & {\n // Title\n titleEnabled?: boolean;\n titleText?: string;\n titleFontSize?: FontSizeOptions;\n titleColor?: ColorOptions;\n\n // Styling\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n borderWidth?: BorderWidthOptions;\n borderColor?: ColorOptions;\n\n // Content\n maxItems?: number;\n};\n\n/**\n * Format a todo due_at ISO string as a short date + time (e.g. \"Jun 1, 9:00 AM\").\n * Mirrors the mobile widget formatting.\n *\n * All-day / date-only values (bare YYYY-MM-DD or timezone-qualified ISO strings\n * whose time component is 00:00) are formatted in UTC so the displayed calendar\n * date matches the source prefix without shifting. Local timed values produced\n * by the task composer, including explicit 00:00, use a single locale-native\n * formatter with both date and time parts.\n */\nfunction formatDueAt(dateStr: string, locale?: string): string {\n const normalizedLocale = locale?.replace(/_/g, \"-\") ?? \"en-US\";\n\n // Detect date-only / all-day: bare YYYY-MM-DD or timezone-qualified ISO whose\n // time is exactly 00:00 (including fractional seconds and offset variants like\n // 2024-06-01T00:00:00.000+02:00). Local timed strings from the task composer\n // remain timed, so 2024-06-01T00:00:00 displays 12:00 AM instead of all-day.\n if (\n ALL_DAY_PATTERN.test(dateStr) &&\n !LOCAL_TIMED_DUE_AT_PATTERN.test(dateStr)\n ) {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(dateStr);\n if (match?.[1] && match[2] && match[3]) {\n const date = new Date(\n Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])),\n );\n return date.toLocaleDateString(normalizedLocale, {\n month: \"short\",\n day: \"numeric\",\n timeZone: \"UTC\",\n });\n }\n }\n\n // Genuinely timed value — use a single formatter so ordering and\n // separators are locale-native.\n const date = new Date(dateStr);\n return date.toLocaleString(normalizedLocale, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n}\n\nexport function ToDoWidget({\n // Title defaults\n titleEnabled = true,\n titleText = \"To-Do\",\n titleFontSize = \"lg\",\n titleColor = \"foreground\",\n\n // Styling defaults\n background = {\n type: \"solid\",\n color: \"background\",\n },\n textColor = \"foreground\",\n accentColor = \"primary\",\n padding = 4,\n borderRadius = \"md\",\n borderWidth = \"none\",\n borderColor = \"muted\",\n\n // Content defaults\n maxItems = 5,\n\n className,\n ...props\n}: ToDoWidgetProps): React.JSX.Element {\n const { t, locale } = useContactsTranslation();\n const backgroundColor = background.color || \"background\";\n const backgroundImage =\n (background.resource?.image_url || background.resource?.imageUrl) &&\n background.type === \"image\"\n ? `url(${background.resource.image_url || background.resource.imageUrl})`\n : \"none\";\n const { data: todos = [], isLoading, isError } = useTodos();\n const { data: completedTodos = [], isLoading: isCompletedLoading } = useTodos(\n { state: \"completed\" },\n );\n const updateTodo = useUpdateTodo();\n const deleteTodo = useDeleteTodo();\n const { isPreview } = useWidgetPreviewContext();\n const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);\n const [editingTodo, setEditingTodo] = useState<Todo | null>(null);\n const [isDoneExpanded, setIsDoneExpanded] = useState(false);\n\n const toggleTodo = (todo: Todo, completed: boolean) => {\n if (isPreview || pendingIds.has(todo.id)) return;\n setPendingIds((prev) => new Set(prev).add(todo.id));\n updateTodo.mutate(\n { id: todo.id, completed, contactId: todo.contactId },\n {\n onSettled: () =>\n setPendingIds((prev) => {\n const next = new Set(prev);\n next.delete(todo.id);\n return next;\n }),\n },\n );\n };\n\n const activeTodos = todos.filter((todo) => !todo.completedAt);\n const todosToShow = activeTodos.slice(0, maxItems);\n const remainingCount = activeTodos.length - todosToShow.length;\n const isEmpty = activeTodos.length === 0;\n const activeTaskLabel = usesSingularPluralForm(locale, activeTodos.length)\n ? t(\"todo_open_tasks_one\", { count: activeTodos.length })\n : t(\"todo_open_tasks_other\", { count: activeTodos.length });\n\n // Group todos by contactId so same-named contacts stay separate.\n const groupedTodos = todosToShow.reduce<\n {\n contactId: number | null;\n contactName: string | null;\n todos: typeof todosToShow;\n }[]\n >((groups, todo) => {\n const existing = groups.find((g) => g.contactId === todo.contactId);\n if (existing) {\n existing.todos.push(todo);\n } else {\n groups.push({\n contactId: todo.contactId,\n contactName: todo.contactName,\n todos: [todo],\n });\n }\n return groups;\n }, []);\n\n const doneCount = completedTodos.length;\n\n const handleRowClick = (todo: Todo) => {\n if (isPreview) return;\n setEditingTodo(todo);\n };\n\n const handleDelete = (todo: Todo) => {\n if (isPreview) return;\n deleteTodo.mutate({ id: todo.id, contactId: todo.contactId });\n };\n\n return (\n <div\n className={`overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} bg-${backgroundColor} text-${textColor} p-${padding} ${className ?? \"\"}`}\n style={{ backgroundImage }}\n {...props}\n >\n {/* Header */}\n <div className=\"mb-4 flex items-start justify-between gap-4\">\n <div className=\"flex min-w-0 flex-col gap-1\">\n {titleEnabled && titleText && (\n <h2\n className={`text-${titleFontSize} font-header leading-tight font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n )}\n {!isEmpty && !isLoading && (\n <span\n className={`w-fit rounded-full bg-${accentColor}/10 px-2.5 py-1 text-[11px] leading-none font-semibold text-${accentColor}`}\n >\n {activeTaskLabel}\n </span>\n )}\n </div>\n {!isPreview && !isEmpty && !isLoading && !isError && (\n <button\n type=\"button\"\n onClick={() => setIsCreateDialogOpen(true)}\n className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor} px-3.5 py-2 text-xs font-semibold text-${accentColor}-foreground shadow-sm transition-colors hover:bg-${accentColor}/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-${accentColor}/40`}\n >\n <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n {t(\"add_todo\")}\n </button>\n )}\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[120px] items-center justify-center\">\n <div className=\"size-6 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n </div>\n ) : isError ? (\n /* Error state */\n <ErrorState />\n ) : isEmpty ? (\n /* Empty state */\n <div className=\"flex flex-col items-center justify-center gap-3 py-8\">\n <p className={`text-center text-${textColor}/60`}>\n {t(\"todo_empty\")}\n </p>\n {!isPreview && (\n <button\n type=\"button\"\n onClick={() => setIsCreateDialogOpen(true)}\n className={`inline-flex items-center gap-1.5 rounded-full bg-${accentColor} text-${accentColor}-foreground hover:bg-${accentColor}/90 px-4 py-1.5 text-xs font-semibold transition-colors`}\n >\n <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n {t(\"add_todo\")}\n </button>\n )}\n </div>\n ) : (\n /* Todo List */\n <>\n <div className=\"flex flex-col gap-4\">\n {groupedTodos.map((group) => (\n <div\n key={group.contactId ?? \"__unassigned__\"}\n className=\"flex flex-col\"\n >\n {group.contactName && (\n <div\n className={`mb-2 text-xs leading-none font-semibold tracking-wide uppercase text-${textColor}/60`}\n >\n {group.contactName}\n </div>\n )}\n {group.todos.map((todo, index) => {\n const showBottomBorder = index !== group.todos.length - 1;\n return (\n <div\n key={todo.id}\n className={`group/row grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 py-2.5 ${\n showBottomBorder\n ? `border-b border-${textColor}/10`\n : \"\"\n }`}\n >\n <input\n type=\"checkbox\"\n className={`h-5 w-5 rounded-full border-2 border-${textColor}/30 bg-transparent not-disabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-60`}\n checked={!!todo.completedAt}\n disabled={isPreview || pendingIds.has(todo.id)}\n onChange={(event) =>\n toggleTodo(todo, event.target.checked)\n }\n onClick={(event) => event.stopPropagation()}\n aria-label={t(\"mark_as_completed\")}\n />\n <button\n type=\"button\"\n onClick={() => handleRowClick(todo)}\n disabled={isPreview}\n aria-label={t(\"edit_todo\")}\n className=\"flex min-w-0 flex-col items-start gap-1 text-left not-disabled:cursor-pointer\"\n >\n <span className=\"line-clamp-1 text-base leading-snug font-medium\">\n {parseTaskBody(todo.body).title}\n </span>\n {todo.dueAt && (\n <span\n className={`text-sm leading-none text-${textColor}/60`}\n >\n {formatDueAt(todo.dueAt, locale)}\n </span>\n )}\n </button>\n {!isPreview && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={(event) => event.stopPropagation()}\n aria-label={t(\"task_actions\")}\n className={`text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`}\n >\n <EllipsisVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(event) => {\n event.stopPropagation();\n handleRowClick(todo);\n }}\n >\n {t(\"edit_todo\")}\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-destructive\"\n onClick={(event) => {\n event.stopPropagation();\n handleDelete(todo);\n }}\n >\n {t(\"delete_todo\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n\n {/* Footer */}\n <div className=\"mt-1 flex items-center justify-between\">\n {remainingCount > 0 && (\n <span className={`text-sm text-${textColor}/50 underline`}>\n {usesSingularPluralForm(locale, remainingCount)\n ? t(\"todo_more_tasks_one\", { count: remainingCount })\n : t(\"todo_more_tasks_other\", { count: remainingCount })}\n </span>\n )}\n </div>\n </>\n )}\n\n {/* Done section */}\n {!isPreview &&\n !isLoading &&\n !isError &&\n !isCompletedLoading &&\n doneCount > 0 && (\n <div className={`mt-4 border-t border-${textColor}/10 pt-3`}>\n <button\n type=\"button\"\n onClick={() => setIsDoneExpanded((v) => !v)}\n className={`flex w-full items-center justify-between gap-2 rounded-md py-1.5 text-sm font-medium text-${textColor}/60 hover:bg-${textColor}/5`}\n aria-expanded={isDoneExpanded}\n >\n <span>\n {isDoneExpanded\n ? t(\"hide_done\")\n : t(\"view_done\", { count: doneCount })}\n </span>\n {isDoneExpanded ? (\n <ChevronUp className=\"size-4\" aria-hidden=\"true\" />\n ) : (\n <ChevronDown className=\"size-4\" aria-hidden=\"true\" />\n )}\n </button>\n {isDoneExpanded && (\n <div className=\"mt-1 flex flex-col\">\n {completedTodos.map((todo, index) => (\n <div\n key={todo.id}\n className={`group/row flex items-center gap-3 py-2 ${\n index !== completedTodos.length - 1\n ? `border-b border-${textColor}/10`\n : \"\"\n }`}\n >\n <input\n type=\"checkbox\"\n className={`h-5 w-5 rounded-full border-2 border-${textColor}/30 bg-transparent not-disabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-60`}\n checked\n disabled={pendingIds.has(todo.id)}\n onChange={() => toggleTodo(todo, false)}\n onClick={(event) => event.stopPropagation()}\n aria-label={t(\"mark_as_open\")}\n />\n <button\n type=\"button\"\n onClick={() => handleRowClick(todo)}\n aria-label={t(\"edit_todo\")}\n className={`flex min-w-0 flex-1 cursor-pointer flex-col items-start gap-0.5 text-left text-${textColor}/60 line-through`}\n >\n <span className=\"line-clamp-1 text-sm\">\n {parseTaskBody(todo.body).title}\n </span>\n {todo.dueAt && (\n <span className=\"text-xs\">\n {formatDueAt(todo.dueAt, locale)}\n </span>\n )}\n </button>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={(event) => event.stopPropagation()}\n aria-label={t(\"task_actions\")}\n className=\"opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100\"\n >\n <EllipsisVertical className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={(event) => {\n event.stopPropagation();\n handleRowClick(todo);\n }}\n >\n {t(\"edit_todo\")}\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"text-destructive\"\n onClick={(event) => {\n event.stopPropagation();\n handleDelete(todo);\n }}\n >\n {t(\"delete_todo\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n\n {!isPreview && (\n <CreateTodoDialog\n open={isCreateDialogOpen}\n onOpenChange={setIsCreateDialogOpen}\n />\n )}\n {!isPreview && (\n <EditTodoDialog\n open={editingTodo !== null}\n onOpenChange={(open) => {\n if (!open) setEditingTodo(null);\n }}\n todo={editingTodo}\n />\n )}\n </div>\n );\n}\n\nexport const toDoWidgetPropertySchema: WidgetPropertySchema = {\n widgetType: \"ToDoWidget\",\n displayName: \"To-Do Widget\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [\n // Styling Tab - Title Group\n {\n key: \"titleEnabled\",\n label: \"Widget Title\",\n type: \"boolean\",\n description: \"Enable the title displayed above the todo list\",\n defaultValue: true,\n tab: \"styling\",\n group: \"Title\",\n },\n {\n key: \"titleText\",\n label: \"Title\",\n type: \"text\",\n description: \"Title text displayed above the todo list\",\n defaultValue: \"To-Do\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n },\n getFontSizeField({\n key: \"titleFontSize\",\n label: \"Title Font Size\",\n description: \"Font size for the widget title\",\n defaultValue: \"xl\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n getColorField({\n key: \"titleColor\",\n label: \"Title Color\",\n description: \"Color for the widget title\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Title\",\n requiresKeyToBeTrue: \"titleEnabled\",\n }),\n\n // Styling Tab - Design Group\n {\n type: \"background\",\n key: \"background\",\n label: \"Background\",\n description: \"Background for the widget container\",\n defaultValue: \"background\",\n tab: \"styling\",\n group: \"Design\",\n },\n getColorField({\n key: \"textColor\",\n label: \"Text Color\",\n description: \"Default text color for todo items\",\n defaultValue: \"foreground\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getColorField({\n key: \"accentColor\",\n label: \"Accent Color\",\n description: \"Color used for count badge and icon\",\n defaultValue: \"primary\",\n tab: \"styling\",\n group: \"Design\",\n }),\n {\n key: \"separator\",\n type: \"separator\",\n label: \"Separator\",\n tab: \"styling\",\n group: \"Design\",\n },\n {\n key: \"maxItems\",\n label: \"Max Items\",\n type: \"number\",\n description: \"Maximum number of todo items to display\",\n min: 1,\n max: 20,\n step: 1,\n defaultValue: 5,\n tab: \"styling\",\n group: \"Design\",\n },\n getPaddingField({\n key: \"padding\",\n label: \"Padding\",\n description: \"Padding around the widget container\",\n defaultValue: 4,\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderRadiusField({\n key: \"borderRadius\",\n label: \"Border Radius\",\n description: \"Border radius for the widget container\",\n defaultValue: \"md\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderWidthField({\n key: \"borderWidth\",\n label: \"Border Width\",\n description: \"Border width for the widget\",\n defaultValue: \"none\",\n tab: \"styling\",\n group: \"Design\",\n }),\n getBorderColorField({\n key: \"borderColor\",\n label: \"Border Color\",\n description: \"Border color for the widget\",\n defaultValue: \"muted\",\n tab: \"styling\",\n group: \"Design\",\n }),\n ],\n} as const satisfies WidgetPropertySchema;\n"],"mappings":"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"}
@@ -539,6 +539,7 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
539
539
  const todosToShow = activeTodos.slice(0, maxItems);
540
540
  const remainingCount = activeTodos.length - todosToShow.length;
541
541
  const isEmpty = activeTodos.length === 0;
542
+ const activeTaskLabel = usesSingularPluralForm(locale, activeTodos.length) ? t("todo_open_tasks_one", { count: activeTodos.length }) : t("todo_open_tasks_other", { count: activeTodos.length });
542
543
  const groupedTodos = todosToShow.reduce((groups, todo) => {
543
544
  const existing = groups.find((g) => g.contactId === todo.contactId);
544
545
  if (existing) existing.todos.push(todo);
@@ -567,22 +568,24 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
567
568
  ...props,
568
569
  children: [
569
570
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
570
- className: "mb-3 flex items-center justify-between",
571
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
572
- className: "flex items-center gap-3",
573
- children: titleEnabled && titleText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
574
- className: `text-${titleFontSize} font-header font-bold text-${titleColor}`,
571
+ className: "mb-4 flex items-start justify-between gap-4",
572
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
573
+ className: "flex min-w-0 flex-col gap-1",
574
+ children: [titleEnabled && titleText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
575
+ className: `text-${titleFontSize} font-header leading-tight font-bold text-${titleColor}`,
575
576
  children: titleText
576
- })
577
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
578
- className: "flex items-center gap-2",
579
- children: [!isEmpty && !isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
580
- className: `text-2xl font-bold text-${accentColor}`,
581
- children: activeTodos.length
582
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
583
- className: `flex size-10 shrink-0 items-center justify-center`,
584
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ListChecks, { className: `h-5 w-5 text-${accentColor}-foreground` })
577
+ }), !isEmpty && !isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
578
+ className: `w-fit rounded-full bg-${accentColor}/10 px-2.5 py-1 text-[11px] leading-none font-semibold text-${accentColor}`,
579
+ children: activeTaskLabel
585
580
  })]
581
+ }), !isPreview && !isEmpty && !isLoading && !isError && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
582
+ type: "button",
583
+ onClick: () => setIsCreateDialogOpen(true),
584
+ className: `inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor} px-3.5 py-2 text-xs font-semibold text-${accentColor}-foreground shadow-sm transition-colors hover:bg-${accentColor}/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-${accentColor}/40`,
585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
586
+ className: "size-3.5",
587
+ "aria-hidden": "true"
588
+ }), t("add_todo")]
586
589
  })]
587
590
  }),
588
591
  isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -603,15 +606,15 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
603
606
  }), t("add_todo")]
604
607
  })]
605
608
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
606
- className: "flex flex-col gap-3",
609
+ className: "flex flex-col gap-4",
607
610
  children: groupedTodos.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
608
611
  className: "flex flex-col",
609
612
  children: [group.contactName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
610
- className: `mb-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`,
613
+ className: `mb-2 text-xs leading-none font-semibold tracking-wide uppercase text-${textColor}/60`,
611
614
  children: group.contactName
612
615
  }), group.todos.map((todo, index) => {
613
616
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
614
- className: `group/row flex items-center gap-3 py-2 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
617
+ className: `group/row grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 py-2.5 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
615
618
  children: [
616
619
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
617
620
  type: "checkbox",
@@ -627,12 +630,12 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
627
630
  onClick: () => handleRowClick(todo),
628
631
  disabled: isPreview,
629
632
  "aria-label": t("edit_todo"),
630
- className: "flex min-w-0 flex-1 flex-col items-start gap-0.5 text-left not-disabled:cursor-pointer",
633
+ className: "flex min-w-0 flex-col items-start gap-1 text-left not-disabled:cursor-pointer",
631
634
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
632
- className: "line-clamp-1 text-sm",
635
+ className: "line-clamp-1 text-base leading-snug font-medium",
633
636
  children: require_task_composer_form.parseTaskBody(todo.body).title
634
637
  }), todo.dueAt && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
635
- className: `text-xs text-${textColor}/60`,
638
+ className: `text-sm leading-none text-${textColor}/60`,
636
639
  children: formatDueAt(todo.dueAt, locale)
637
640
  })]
638
641
  }),
@@ -643,7 +646,7 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
643
646
  size: "icon-xs",
644
647
  onClick: (event) => event.stopPropagation(),
645
648
  "aria-label": t("task_actions"),
646
- className: "opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100",
649
+ className: `text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`,
647
650
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.EllipsisVertical, { className: "size-4" })
648
651
  })
649
652
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenuContent, {
@@ -667,29 +670,19 @@ function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize =
667
670
  }, todo.id);
668
671
  })]
669
672
  }, group.contactId ?? "__unassigned__"))
670
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
671
- className: "mt-2 flex items-center justify-between",
672
- children: [remainingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
673
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
674
+ className: "mt-1 flex items-center justify-between",
675
+ children: remainingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
673
676
  className: `text-sm text-${textColor}/50 underline`,
674
677
  children: usesSingularPluralForm(locale, remainingCount) ? t("todo_more_tasks_one", { count: remainingCount }) : t("todo_more_tasks_other", { count: remainingCount })
675
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
676
- className: "ml-auto",
677
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
678
- type: "button",
679
- onClick: () => setIsCreateDialogOpen(true),
680
- disabled: isPreview,
681
- "aria-label": t("add_todo"),
682
- className: `flex h-8 w-8 items-center justify-center rounded-full transition-colors not-disabled:cursor-pointer hover:bg-${textColor}/10 disabled:opacity-60`,
683
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: `h-5 w-5 text-${textColor}/50` })
684
- })
685
- })]
678
+ })
686
679
  })] }),
687
680
  !isPreview && !isLoading && !isError && !isCompletedLoading && doneCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
688
- className: `mt-3 border-t border-${textColor}/10 pt-2`,
681
+ className: `mt-4 border-t border-${textColor}/10 pt-3`,
689
682
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
690
683
  type: "button",
691
684
  onClick: () => setIsDoneExpanded((v) => !v),
692
- className: `flex w-full items-center justify-between gap-2 rounded-md py-1 text-xs font-medium text-${textColor}/60 hover:bg-${textColor}/5`,
685
+ className: `flex w-full items-center justify-between gap-2 rounded-md py-1.5 text-sm font-medium text-${textColor}/60 hover:bg-${textColor}/5`,
693
686
  "aria-expanded": isDoneExpanded,
694
687
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: isDoneExpanded ? t("hide_done") : t("view_done", { count: doneCount }) }), isDoneExpanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronUp, {
695
688
  className: "size-4",
@@ -907,4 +900,4 @@ Object.defineProperty(exports, "toDoWidgetPropertySchema", {
907
900
  }
908
901
  });
909
902
 
910
- //# sourceMappingURL=ToDoWidget-CgFDCQNX.cjs.map
903
+ //# sourceMappingURL=ToDoWidget-aSYTB02O.cjs.map