@fluid-app/portal-sdk 0.1.373 → 0.1.375
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/{ContactsScreen-BVAzJjVs.cjs → ContactsScreen-C28RoYHu.cjs} +2 -2
- package/dist/{ContactsScreen-BVAzJjVs.cjs.map → ContactsScreen-C28RoYHu.cjs.map} +1 -1
- package/dist/{ContactsScreen-BKKowprR.cjs → ContactsScreen-CCXFgzcN.cjs} +2 -2
- package/dist/{ContactsScreen-BHy20ML1.mjs → ContactsScreen-DvPP4F22.mjs} +2 -2
- package/dist/{ContactsScreen-BHy20ML1.mjs.map → ContactsScreen-DvPP4F22.mjs.map} +1 -1
- package/dist/{ContactsTranslationBridge-BGeau5_N.mjs → ContactsTranslationBridge-Ckjj_Iu9.mjs} +3 -1
- package/dist/{ContactsTranslationBridge-BGeau5_N.mjs.map → ContactsTranslationBridge-Ckjj_Iu9.mjs.map} +1 -1
- package/dist/{ContactsTranslationBridge-D9gEIAp3.cjs → ContactsTranslationBridge-Dx_1jAxz.cjs} +3 -1
- package/dist/{ContactsTranslationBridge-D9gEIAp3.cjs.map → ContactsTranslationBridge-Dx_1jAxz.cjs.map} +1 -1
- package/dist/{FluidProvider-CURmntkw.cjs → FluidProvider-BvVjhwrv.cjs} +7 -6
- package/dist/{FluidProvider-CURmntkw.cjs.map → FluidProvider-BvVjhwrv.cjs.map} +1 -1
- package/dist/{FluidProvider-Bu9N22BI.mjs → FluidProvider-Cx0wNNF7.mjs} +7 -6
- package/dist/{FluidProvider-Bu9N22BI.mjs.map → FluidProvider-Cx0wNNF7.mjs.map} +1 -1
- package/dist/{MessagingScreen-NE1Na8rN.cjs → MessagingScreen-6fuZJ_0Q.cjs} +5 -5
- package/dist/{MessagingScreen-D8_oGy_k.mjs → MessagingScreen-BEVwNvC3.mjs} +2 -2
- package/dist/{MessagingScreen-D8_oGy_k.mjs.map → MessagingScreen-BEVwNvC3.mjs.map} +1 -1
- package/dist/{MessagingScreen-YvwfkYbW.cjs → MessagingScreen-BTcBx8v4.cjs} +2 -2
- package/dist/{MessagingScreen-YvwfkYbW.cjs.map → MessagingScreen-BTcBx8v4.cjs.map} +1 -1
- package/dist/{MessagingScreen-DwmciO1o.mjs → MessagingScreen-Bw3pawb5.mjs} +5 -5
- package/dist/{OrdersScreen-B81SHSOU.cjs → OrdersScreen-BZUSgBTy.cjs} +2 -2
- package/dist/{OrdersScreen-B81SHSOU.cjs.map → OrdersScreen-BZUSgBTy.cjs.map} +1 -1
- package/dist/{OrdersScreen-BtgUuBky.cjs → OrdersScreen-CViKIru5.cjs} +5 -5
- package/dist/{OrdersScreen-DYjOpQa2.mjs → OrdersScreen-DcA6QoGF.mjs} +5 -5
- package/dist/{OrdersScreen-BycEvuSw.mjs → OrdersScreen-Kkk88xDy.mjs} +2 -2
- package/dist/{OrdersScreen-BycEvuSw.mjs.map → OrdersScreen-Kkk88xDy.mjs.map} +1 -1
- package/dist/{ProfileScreen-DisEMe-1.cjs → ProfileScreen-C2-nARkW.cjs} +6 -6
- package/dist/{ProfileScreen-BVRnPuRm.mjs → ProfileScreen-CZJA8dZS.mjs} +4 -4
- package/dist/{ProfileScreen-BVRnPuRm.mjs.map → ProfileScreen-CZJA8dZS.mjs.map} +1 -1
- package/dist/{ProfileScreen-Ckf1a-zh.mjs → ProfileScreen-DjCRBTlH.mjs} +6 -6
- package/dist/{ProfileScreen-B0yVzqyg.cjs → ProfileScreen-j-5VwfqY.cjs} +4 -4
- package/dist/{ProfileScreen-B0yVzqyg.cjs.map → ProfileScreen-j-5VwfqY.cjs.map} +1 -1
- package/dist/{ShopScreen-DN5Y1HQ7.mjs → ShopScreen-BT5QKVbL.mjs} +5 -5
- package/dist/{ShopScreen-CPpJB-Kt.mjs → ShopScreen-CyRX0aOs.mjs} +48 -42
- package/dist/ShopScreen-CyRX0aOs.mjs.map +1 -0
- package/dist/{ShopScreen-CEB0iwmF.cjs → ShopScreen-D3Pzmg6Q.cjs} +5 -5
- package/dist/{ShopScreen-B9uHdQP7.cjs → ShopScreen-DVlQAOHj.cjs} +48 -42
- package/dist/ShopScreen-DVlQAOHj.cjs.map +1 -0
- package/dist/{ShopWidget-BbCPV2_Y.cjs → ShopWidget-C_R5RGoe.cjs} +13 -23
- package/dist/ShopWidget-C_R5RGoe.cjs.map +1 -0
- package/dist/{ShopWidget-BF0zWjS6.cjs → ShopWidget-CaPQVMlr.cjs} +1 -1
- package/dist/{ShopWidget-YMXis5SJ.mjs → ShopWidget-N0OqYNM-.mjs} +13 -23
- package/dist/ShopWidget-N0OqYNM-.mjs.map +1 -0
- package/dist/{SubscriptionsScreen-DdazqD1V.mjs → SubscriptionsScreen-C-UXQeNC.mjs} +4 -4
- package/dist/{SubscriptionsScreen-DdazqD1V.mjs.map → SubscriptionsScreen-C-UXQeNC.mjs.map} +1 -1
- package/dist/{SubscriptionsScreen-BTQuJNaY.cjs → SubscriptionsScreen-OITPOl3Y.cjs} +4 -4
- package/dist/{SubscriptionsScreen-BTQuJNaY.cjs.map → SubscriptionsScreen-OITPOl3Y.cjs.map} +1 -1
- package/dist/{SubscriptionsScreen-C_7vhN3q.cjs → SubscriptionsScreen-amFuxymL.cjs} +6 -6
- package/dist/{SubscriptionsScreen-ZkjeOBHH.mjs → SubscriptionsScreen-lvMO6Bkp.mjs} +6 -6
- package/dist/{ToDoWidget-3bkkxozQ.cjs → ToDoWidget-Cr0VP7k6.cjs} +1 -1
- package/dist/{ToDoWidget-BZyJIX1X.mjs → ToDoWidget-DiGklfKG.mjs} +32 -39
- package/dist/ToDoWidget-DiGklfKG.mjs.map +1 -0
- package/dist/{ToDoWidget-CgFDCQNX.cjs → ToDoWidget-aSYTB02O.cjs} +31 -38
- package/dist/ToDoWidget-aSYTB02O.cjs.map +1 -0
- package/dist/index.cjs +24 -24
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +24 -24
- package/dist/preview/preview-entry.cjs +5 -5
- package/dist/preview/preview-entry.mjs +5 -5
- package/dist/{use-portal-card-add-3ds-flow-CphCh5cu.mjs → use-portal-card-add-3ds-flow-BLjXfmje.mjs} +2 -2
- package/dist/{use-portal-card-add-3ds-flow-CphCh5cu.mjs.map → use-portal-card-add-3ds-flow-BLjXfmje.mjs.map} +1 -1
- package/dist/{use-portal-card-add-3ds-flow-BxFF4Wew.cjs → use-portal-card-add-3ds-flow-C2uzzhr8.cjs} +2 -2
- package/dist/{use-portal-card-add-3ds-flow-BxFF4Wew.cjs.map → use-portal-card-add-3ds-flow-C2uzzhr8.cjs.map} +1 -1
- package/dist/{widget-package-utils-BS8mCzgZ.mjs → widget-package-utils-DG2ibxqS.mjs} +2 -2
- package/dist/{widget-package-utils-BS8mCzgZ.mjs.map → widget-package-utils-DG2ibxqS.mjs.map} +1 -1
- package/dist/{widget-package-utils-C0WYunNq.cjs → widget-package-utils-pdLitM72.cjs} +2 -2
- package/dist/{widget-package-utils-C0WYunNq.cjs.map → widget-package-utils-pdLitM72.cjs.map} +1 -1
- package/package.json +19 -19
- package/dist/ShopScreen-B9uHdQP7.cjs.map +0 -1
- package/dist/ShopScreen-CPpJB-Kt.mjs.map +0 -1
- package/dist/ShopWidget-BbCPV2_Y.cjs.map +0 -1
- package/dist/ShopWidget-YMXis5SJ.mjs.map +0 -1
- package/dist/ToDoWidget-BZyJIX1X.mjs.map +0 -1
- package/dist/ToDoWidget-CgFDCQNX.cjs.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
|
-
require("./FluidProvider-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
|
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-
|
|
574
|
-
children: [/* @__PURE__ */
|
|
575
|
-
className: "flex
|
|
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
|
-
|
|
581
|
-
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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__ */
|
|
674
|
-
className: "mt-
|
|
675
|
-
children:
|
|
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
|
-
})
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
571
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
572
|
-
className: "flex
|
|
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
|
-
|
|
578
|
-
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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.
|
|
671
|
-
className: "mt-
|
|
672
|
-
children:
|
|
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
|
-
})
|
|
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-
|
|
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-
|
|
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-
|
|
903
|
+
//# sourceMappingURL=ToDoWidget-aSYTB02O.cjs.map
|