@fluid-app/portal-sdk 0.1.239 → 0.1.241
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/{CalendarWidget-BXxNxHDV.mjs → CalendarWidget-C03VcDLQ.mjs} +2 -2
- package/dist/{CalendarWidget-BXxNxHDV.mjs.map → CalendarWidget-C03VcDLQ.mjs.map} +1 -1
- package/dist/{CalendarWidget-VjQXZW8B.cjs → CalendarWidget-C4Dv_TUk.cjs} +2 -2
- package/dist/{CalendarWidget-VjQXZW8B.cjs.map → CalendarWidget-C4Dv_TUk.cjs.map} +1 -1
- package/dist/{CardWidget-BNkMlxQ3.mjs → CardWidget-2wcjCf2M.mjs} +2 -2
- package/dist/{CardWidget-BNkMlxQ3.mjs.map → CardWidget-2wcjCf2M.mjs.map} +1 -1
- package/dist/{CardWidget-CraWMttj.cjs → CardWidget-BQl-8Xrt.cjs} +3 -3
- package/dist/{CardWidget-C3I9OwgS.cjs → CardWidget-D-KEikO_.cjs} +2 -2
- package/dist/{CardWidget-C3I9OwgS.cjs.map → CardWidget-D-KEikO_.cjs.map} +1 -1
- package/dist/{CarouselWidget-p8Z6L-G5.mjs → CarouselWidget-Cn5P4FVN.mjs} +92 -83
- package/dist/CarouselWidget-Cn5P4FVN.mjs.map +1 -0
- package/dist/{CarouselWidget-CiKlooUF.cjs → CarouselWidget-Cu1b3ERq.cjs} +92 -83
- package/dist/CarouselWidget-Cu1b3ERq.cjs.map +1 -0
- package/dist/{CatchUpWidget-B9DQlzd_.mjs → CatchUpWidget-B9CI7lq0.mjs} +2 -2
- package/dist/{CatchUpWidget-B9DQlzd_.mjs.map → CatchUpWidget-B9CI7lq0.mjs.map} +1 -1
- package/dist/{CatchUpWidget-SY7hOHk6.cjs → CatchUpWidget-BzgybvuC.cjs} +2 -2
- package/dist/{CatchUpWidget-SY7hOHk6.cjs.map → CatchUpWidget-BzgybvuC.cjs.map} +1 -1
- package/dist/ContainerWidget-CDbB8c0E.cjs +8 -0
- package/dist/{ContainerWidget-CpwvcxFm.mjs → ContainerWidget-DNenbORS.mjs} +2 -2
- package/dist/{ContainerWidget-CpwvcxFm.mjs.map → ContainerWidget-DNenbORS.mjs.map} +1 -1
- package/dist/{ContainerWidget-BXpdj06u.cjs → ContainerWidget-DbAhQKbY.cjs} +2 -2
- package/dist/{ContainerWidget-BXpdj06u.cjs.map → ContainerWidget-DbAhQKbY.cjs.map} +1 -1
- package/dist/{FluidProvider-DL5rChtj.mjs → FluidProvider-BwMwMCxW.mjs} +23 -23
- package/dist/{FluidProvider-DL5rChtj.mjs.map → FluidProvider-BwMwMCxW.mjs.map} +1 -1
- package/dist/{FluidProvider-D_PHVlUU.cjs → FluidProvider-eT4gA6Io.cjs} +23 -23
- package/dist/{FluidProvider-D_PHVlUU.cjs.map → FluidProvider-eT4gA6Io.cjs.map} +1 -1
- package/dist/{LayoutWidget-BIfNHlVE.cjs → LayoutWidget-Bt2I2XMy.cjs} +2 -2
- package/dist/{LayoutWidget-BIfNHlVE.cjs.map → LayoutWidget-Bt2I2XMy.cjs.map} +1 -1
- package/dist/{LayoutWidget-LdF_cKrB.cjs → LayoutWidget-DKqZgCMu.cjs} +3 -3
- package/dist/{LayoutWidget-CC3oK78H.mjs → LayoutWidget-UI5fbsx4.mjs} +2 -2
- package/dist/{LayoutWidget-CC3oK78H.mjs.map → LayoutWidget-UI5fbsx4.mjs.map} +1 -1
- package/dist/{MessagingScreen-Dxy2VQbO.cjs → MessagingScreen-BHYX1Kc6.cjs} +14 -14
- package/dist/{MessagingScreen-mt1u3Bs1.mjs → MessagingScreen-CZddjqma.mjs} +2 -2
- package/dist/{MessagingScreen-mt1u3Bs1.mjs.map → MessagingScreen-CZddjqma.mjs.map} +1 -1
- package/dist/{MessagingScreen-Dkh3Dsp7.cjs → MessagingScreen-D3S230Ba.cjs} +2 -2
- package/dist/{MessagingScreen-Dkh3Dsp7.cjs.map → MessagingScreen-D3S230Ba.cjs.map} +1 -1
- package/dist/{MySiteWidget-BnjPrQxE.mjs → MySiteWidget-CQNASVaF.mjs} +2 -2
- package/dist/{MySiteWidget-BnjPrQxE.mjs.map → MySiteWidget-CQNASVaF.mjs.map} +1 -1
- package/dist/{MySiteWidget-C8eFWHOT.cjs → MySiteWidget-CYiH2lmX.cjs} +2 -2
- package/dist/{MySiteWidget-C8eFWHOT.cjs.map → MySiteWidget-CYiH2lmX.cjs.map} +1 -1
- package/dist/{PointsWidget-C2KB4k48.mjs → PointsWidget-BwA6aGVZ.mjs} +5 -8
- package/dist/PointsWidget-BwA6aGVZ.mjs.map +1 -0
- package/dist/{PointsWidget-DLp-PYus.cjs → PointsWidget-D1FV8l1h.cjs} +5 -8
- package/dist/PointsWidget-D1FV8l1h.cjs.map +1 -0
- package/dist/{ProfileScreen-ChCZZ91o.cjs → ProfileScreen-BYJj4D1W.cjs} +14 -14
- package/dist/{ProfileScreen-BMe-dQi7.cjs → ProfileScreen-DAujb81k.cjs} +2 -2
- package/dist/{ProfileScreen-BMe-dQi7.cjs.map → ProfileScreen-DAujb81k.cjs.map} +1 -1
- package/dist/{ProfileScreen-_1GlBr7z.mjs → ProfileScreen-DlMEqXXg.mjs} +2 -2
- package/dist/{ProfileScreen-_1GlBr7z.mjs.map → ProfileScreen-DlMEqXXg.mjs.map} +1 -1
- package/dist/{RecentActivityWidget-DelPdiwR.mjs → RecentActivityWidget-D1AlZgfV.mjs} +2 -2
- package/dist/{RecentActivityWidget-DelPdiwR.mjs.map → RecentActivityWidget-D1AlZgfV.mjs.map} +1 -1
- package/dist/{RecentActivityWidget-BNW9aFT4.cjs → RecentActivityWidget-DNyhUZNs.cjs} +2 -2
- package/dist/{RecentActivityWidget-BNW9aFT4.cjs.map → RecentActivityWidget-DNyhUZNs.cjs.map} +1 -1
- package/dist/{ScreenRenderer-ClYgfQf_.mjs → ScreenRenderer-Cl2aAJ7D.mjs} +2 -2
- package/dist/{ScreenRenderer-ClYgfQf_.mjs.map → ScreenRenderer-Cl2aAJ7D.mjs.map} +1 -1
- package/dist/{ScreenRenderer-Ct1w4PNu.cjs → ScreenRenderer-xH01YkEQ.cjs} +2 -2
- package/dist/{ScreenRenderer-Ct1w4PNu.cjs.map → ScreenRenderer-xH01YkEQ.cjs.map} +1 -1
- package/dist/{ShopScreen-DWLGH2gt.cjs → ShopScreen-1yvcCk7l.cjs} +6 -2
- package/dist/ShopScreen-1yvcCk7l.cjs.map +1 -0
- package/dist/{ShopScreen-CZ_290EP.cjs → ShopScreen-6nEKrNjK.cjs} +14 -14
- package/dist/{ShopScreen-BBfOte5o.mjs → ShopScreen-U7G6Jf67.mjs} +6 -2
- package/dist/ShopScreen-U7G6Jf67.mjs.map +1 -0
- package/dist/{ToDoWidget-CYDsZA0Z.mjs → ToDoWidget-BciI_D70.mjs} +2 -2
- package/dist/{ToDoWidget-CYDsZA0Z.mjs.map → ToDoWidget-BciI_D70.mjs.map} +1 -1
- package/dist/{ToDoWidget-Bjoan2Rm.cjs → ToDoWidget-OxT9z59F.cjs} +2 -2
- package/dist/{ToDoWidget-Bjoan2Rm.cjs.map → ToDoWidget-OxT9z59F.cjs.map} +1 -1
- package/dist/{ToDoWidget-C_CvWdLi.cjs → ToDoWidget-mSGQgnu3.cjs} +2 -2
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +22 -22
- package/dist/{registry-context-BahYMRqn.mjs → registry-context-CTHUCfEc.mjs} +6 -5
- package/dist/{registry-context-bf52ZIJX.cjs.map → registry-context-CTHUCfEc.mjs.map} +1 -1
- package/dist/{registry-context-bf52ZIJX.cjs → registry-context-CcZYS15Q.cjs} +6 -5
- package/dist/{registry-context-BahYMRqn.mjs.map → registry-context-CcZYS15Q.cjs.map} +1 -1
- package/package.json +10 -10
- package/dist/CarouselWidget-CiKlooUF.cjs.map +0 -1
- package/dist/CarouselWidget-p8Z6L-G5.mjs.map +0 -1
- package/dist/ContainerWidget-CyfPYEAv.cjs +0 -8
- package/dist/PointsWidget-C2KB4k48.mjs.map +0 -1
- package/dist/PointsWidget-DLp-PYus.cjs.map +0 -1
- package/dist/ShopScreen-BBfOte5o.mjs.map +0 -1
- package/dist/ShopScreen-DWLGH2gt.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToDoWidget-CYDsZA0Z.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/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 { 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\";\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.\n */\nexport function todosQueryKey(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 function useTodos(): UseQueryResult<Todo[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: todosQueryKey({ baseUrl, isPreview }),\n queryFn: ({ signal }) => widgetsApi.fetchTodos(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\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 type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey } from \"./use-todos\";\n\ntype UpdateVariables = { id: number; completed: boolean };\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, completed }: UpdateVariables) => {\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 return widgetsApi.updateTodo(id, completed);\n },\n onMutate: async ({ id, completed }) => {\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 === id\n ? {\n ...todo,\n completedAt: completed ? new Date().toISOString() : null,\n }\n : todo,\n ),\n );\n return { previous };\n },\n onSuccess: (updated, { completed }) => {\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. The widget today only ever fires completed=true, but\n // gating here keeps the contract honest for any future caller that\n // toggles either direction.\n if (completed) {\n fluidToast({ title: \"Todo completed\", type: \"success\" });\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 // Belt-and-suspenders refetch to reconcile any race we couldn't see\n // (e.g., a different tab mutated the same todo). Cheap — one GET per\n // toggle.\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\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 {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport { ListChecks, 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 { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { ErrorState } from \"../components/error-state\";\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\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 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 updateTodo = useUpdateTodo();\n const { isPreview } = useWidgetPreviewContext();\n const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n\n const toggleTodo = (id: number, completed: boolean) => {\n if (isPreview || pendingIds.has(id)) return;\n setPendingIds((prev) => new Set(prev).add(id));\n updateTodo.mutate(\n { id, completed },\n {\n onSettled: () =>\n setPendingIds((prev) => {\n const next = new Set(prev);\n next.delete(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\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 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-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n {titleEnabled && titleText && (\n <h2\n className={`text-${titleFontSize} font-header font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {!isEmpty && !isLoading && (\n <span className={`text-2xl font-bold text-${accentColor}`}>\n {activeTodos.length}\n </span>\n )}\n <div\n className={`flex h-10 w-10 shrink-0 items-center justify-center`}\n >\n <ListChecks className={`h-5 w-5 text-${accentColor}-foreground`} />\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[120px] items-center justify-center\">\n <div className=\"h-6 w-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 py-8\">\n <p className={`text-center text-${textColor}/60`}>\n You've got nothing else To-Do!\n </p>\n </div>\n ) : (\n /* Todo List */\n <>\n <div className=\"flex flex-col gap-3\">\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-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`}\n >\n {group.contactName}\n </div>\n )}\n {group.todos.map((todo, index) => (\n <div\n key={todo.id}\n className={`flex items-center gap-3 py-2 ${\n index !== group.todos.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={!!todo.completedAt}\n disabled={isPreview || pendingIds.has(todo.id)}\n onChange={(event) =>\n toggleTodo(todo.id, event.target.checked)\n }\n />\n <span className=\"line-clamp-1 flex-1 text-sm\">\n {parseTaskBody(todo.body).title}\n </span>\n </div>\n ))}\n </div>\n ))}\n </div>\n\n {/* Footer */}\n <div className=\"mt-2 flex items-center justify-between\">\n {remainingCount > 0 && (\n <span className={`text-sm text-${textColor}/50 underline`}>\n {remainingCount} more task{remainingCount > 1 ? \"s\" : \"\"}\n </span>\n )}\n <div className=\"ml-auto\">\n <Plus className={`h-5 w-5 text-${textColor}/50`} />\n </div>\n </div>\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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KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
1
|
+
{"version":3,"file":"ToDoWidget-BciI_D70.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/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 { 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\";\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.\n */\nexport function todosQueryKey(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 function useTodos(): UseQueryResult<Todo[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: todosQueryKey({ baseUrl, isPreview }),\n queryFn: ({ signal }) => widgetsApi.fetchTodos(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\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 type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey } from \"./use-todos\";\n\ntype UpdateVariables = { id: number; completed: boolean };\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, completed }: UpdateVariables) => {\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 return widgetsApi.updateTodo(id, completed);\n },\n onMutate: async ({ id, completed }) => {\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 === id\n ? {\n ...todo,\n completedAt: completed ? new Date().toISOString() : null,\n }\n : todo,\n ),\n );\n return { previous };\n },\n onSuccess: (updated, { completed }) => {\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. The widget today only ever fires completed=true, but\n // gating here keeps the contract honest for any future caller that\n // toggles either direction.\n if (completed) {\n fluidToast({ title: \"Todo completed\", type: \"success\" });\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 // Belt-and-suspenders refetch to reconcile any race we couldn't see\n // (e.g., a different tab mutated the same todo). Cheap — one GET per\n // toggle.\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\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 {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport { ListChecks, 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 { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { ErrorState } from \"../components/error-state\";\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\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 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 updateTodo = useUpdateTodo();\n const { isPreview } = useWidgetPreviewContext();\n const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n\n const toggleTodo = (id: number, completed: boolean) => {\n if (isPreview || pendingIds.has(id)) return;\n setPendingIds((prev) => new Set(prev).add(id));\n updateTodo.mutate(\n { id, completed },\n {\n onSettled: () =>\n setPendingIds((prev) => {\n const next = new Set(prev);\n next.delete(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\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 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-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n {titleEnabled && titleText && (\n <h2\n className={`text-${titleFontSize} font-header font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {!isEmpty && !isLoading && (\n <span className={`text-2xl font-bold text-${accentColor}`}>\n {activeTodos.length}\n </span>\n )}\n <div\n className={`flex h-10 w-10 shrink-0 items-center justify-center`}\n >\n <ListChecks className={`h-5 w-5 text-${accentColor}-foreground`} />\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[120px] items-center justify-center\">\n <div className=\"h-6 w-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 py-8\">\n <p className={`text-center text-${textColor}/60`}>\n You've got nothing else To-Do!\n </p>\n </div>\n ) : (\n /* Todo List */\n <>\n <div className=\"flex flex-col gap-3\">\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-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`}\n >\n {group.contactName}\n </div>\n )}\n {group.todos.map((todo, index) => (\n <div\n key={todo.id}\n className={`flex items-center gap-3 py-2 ${\n index !== group.todos.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={!!todo.completedAt}\n disabled={isPreview || pendingIds.has(todo.id)}\n onChange={(event) =>\n toggleTodo(todo.id, event.target.checked)\n }\n />\n <span className=\"line-clamp-1 flex-1 text-sm\">\n {parseTaskBody(todo.body).title}\n </span>\n </div>\n ))}\n </div>\n ))}\n </div>\n\n {/* Footer */}\n <div className=\"mt-2 flex items-center justify-between\">\n {remainingCount > 0 && (\n <span className={`text-sm text-${textColor}/50 underline`}>\n {remainingCount} more task{remainingCount > 1 ? \"s\" : \"\"}\n </span>\n )}\n <div className=\"ml-auto\">\n <Plus className={`h-5 w-5 text-${textColor}/50`} />\n </div>\n </div>\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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KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF;GACE,KAAK;GACL,MAAM;GACN,OAAO;GACP,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,aAAa;GACb,KAAK;GACL,KAAK;GACL,MAAM;GACN,cAAc;GACd,KAAK;GACL,OAAO;GACR;EACD,gBAAgB;GACd,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,qBAAqB;GACnB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACF,oBAAoB;GAClB,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACR,CAAC;EACH;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
|
-
const require_registry_context = require("./registry-context-
|
|
2
|
+
const require_registry_context = require("./registry-context-CcZYS15Q.cjs");
|
|
3
3
|
const require_error_state = require("./error-state-BDhSltIa.cjs");
|
|
4
4
|
const require_registries = require("./registries-DBb6VjAX.cjs");
|
|
5
5
|
const require_src = require("./src-uhf6Szlw.cjs");
|
|
@@ -359,4 +359,4 @@ Object.defineProperty(exports, "toDoWidgetPropertySchema", {
|
|
|
359
359
|
}
|
|
360
360
|
});
|
|
361
361
|
|
|
362
|
-
//# sourceMappingURL=ToDoWidget-
|
|
362
|
+
//# sourceMappingURL=ToDoWidget-OxT9z59F.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToDoWidget-Bjoan2Rm.cjs","names":["useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","useWidgetPreviewContext","borderWidthClasses","borderColorClasses","ListChecks","ErrorState","parseTaskBody","Plus","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/hooks/use-todos.preview.ts","../../widgets/src/hooks/use-todos.ts","../../widgets/src/hooks/use-update-todo.ts","../../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 { 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\";\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.\n */\nexport function todosQueryKey(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 function useTodos(): UseQueryResult<Todo[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: todosQueryKey({ baseUrl, isPreview }),\n queryFn: ({ signal }) => widgetsApi.fetchTodos(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\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 type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey } from \"./use-todos\";\n\ntype UpdateVariables = { id: number; completed: boolean };\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, completed }: UpdateVariables) => {\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 return widgetsApi.updateTodo(id, completed);\n },\n onMutate: async ({ id, completed }) => {\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 === id\n ? {\n ...todo,\n completedAt: completed ? new Date().toISOString() : null,\n }\n : todo,\n ),\n );\n return { previous };\n },\n onSuccess: (updated, { completed }) => {\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. The widget today only ever fires completed=true, but\n // gating here keeps the contract honest for any future caller that\n // toggles either direction.\n if (completed) {\n fluidToast({ title: \"Todo completed\", type: \"success\" });\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 // Belt-and-suspenders refetch to reconcile any race we couldn't see\n // (e.g., a different tab mutated the same todo). Cheap — one GET per\n // toggle.\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\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 {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport { ListChecks, 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 { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { ErrorState } from \"../components/error-state\";\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\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 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 updateTodo = useUpdateTodo();\n const { isPreview } = useWidgetPreviewContext();\n const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n\n const toggleTodo = (id: number, completed: boolean) => {\n if (isPreview || pendingIds.has(id)) return;\n setPendingIds((prev) => new Set(prev).add(id));\n updateTodo.mutate(\n { id, completed },\n {\n onSettled: () =>\n setPendingIds((prev) => {\n const next = new Set(prev);\n next.delete(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\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 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-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n {titleEnabled && titleText && (\n <h2\n className={`text-${titleFontSize} font-header font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {!isEmpty && !isLoading && (\n <span className={`text-2xl font-bold text-${accentColor}`}>\n {activeTodos.length}\n </span>\n )}\n <div\n className={`flex h-10 w-10 shrink-0 items-center justify-center`}\n >\n <ListChecks className={`h-5 w-5 text-${accentColor}-foreground`} />\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[120px] items-center justify-center\">\n <div className=\"h-6 w-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 py-8\">\n <p className={`text-center text-${textColor}/60`}>\n You've got nothing else To-Do!\n </p>\n </div>\n ) : (\n /* Todo List */\n <>\n <div className=\"flex flex-col gap-3\">\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-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`}\n >\n {group.contactName}\n </div>\n )}\n {group.todos.map((todo, index) => (\n <div\n key={todo.id}\n className={`flex items-center gap-3 py-2 ${\n index !== group.todos.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={!!todo.completedAt}\n disabled={isPreview || pendingIds.has(todo.id)}\n onChange={(event) =>\n toggleTodo(todo.id, event.target.checked)\n }\n />\n <span className=\"line-clamp-1 flex-1 text-sm\">\n {parseTaskBody(todo.body).title}\n </span>\n </div>\n ))}\n </div>\n ))}\n </div>\n\n {/* Footer */}\n <div className=\"mt-2 flex items-center justify-between\">\n {remainingCount > 0 && (\n <span className={`text-sm text-${textColor}/50 underline`}>\n {remainingCount} more task{remainingCount > 1 ? \"s\" : \"\"}\n </span>\n )}\n <div className=\"ml-auto\">\n <Plus className={`h-5 w-5 text-${textColor}/50`} />\n </div>\n </div>\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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"}
|
|
1
|
+
{"version":3,"file":"ToDoWidget-OxT9z59F.cjs","names":["useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","useWidgetsApi","useWidgetPreviewContext","useDataSourceRegistryConfig","useWidgetPreviewContext","borderWidthClasses","borderColorClasses","ListChecks","ErrorState","parseTaskBody","Plus","getFontSizeField","getColorField","getPaddingField","getBorderRadiusField","getBorderWidthField","getBorderColorField"],"sources":["../../widgets/src/hooks/use-todos.preview.ts","../../widgets/src/hooks/use-todos.ts","../../widgets/src/hooks/use-update-todo.ts","../../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 { 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\";\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.\n */\nexport function todosQueryKey(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 function useTodos(): UseQueryResult<Todo[], Error> {\n const widgetsApi = useWidgetsApi();\n const { isPreview } = useWidgetPreviewContext();\n const { baseUrl } = useDataSourceRegistryConfig();\n\n return useQuery({\n queryKey: todosQueryKey({ baseUrl, isPreview }),\n queryFn: ({ signal }) => widgetsApi.fetchTodos(signal),\n enabled: !isPreview,\n ...(isPreview && { placeholderData: PREVIEW_DATA }),\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 type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey } from \"./use-todos\";\n\ntype UpdateVariables = { id: number; completed: boolean };\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, completed }: UpdateVariables) => {\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 return widgetsApi.updateTodo(id, completed);\n },\n onMutate: async ({ id, completed }) => {\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 === id\n ? {\n ...todo,\n completedAt: completed ? new Date().toISOString() : null,\n }\n : todo,\n ),\n );\n return { previous };\n },\n onSuccess: (updated, { completed }) => {\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. The widget today only ever fires completed=true, but\n // gating here keeps the contract honest for any future caller that\n // toggles either direction.\n if (completed) {\n fluidToast({ title: \"Todo completed\", type: \"success\" });\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 // Belt-and-suspenders refetch to reconcile any race we couldn't see\n // (e.g., a different tab mutated the same todo). Cheap — one GET per\n // toggle.\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey });\n },\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 {\n getBorderRadiusField,\n getBorderWidthField,\n getBorderColorField,\n borderWidthClasses,\n borderColorClasses,\n getColorField,\n getFontSizeField,\n getPaddingField,\n} from \"../core/fields\";\nimport { ListChecks, 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 { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { ErrorState } from \"../components/error-state\";\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\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 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 updateTodo = useUpdateTodo();\n const { isPreview } = useWidgetPreviewContext();\n const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n\n const toggleTodo = (id: number, completed: boolean) => {\n if (isPreview || pendingIds.has(id)) return;\n setPendingIds((prev) => new Set(prev).add(id));\n updateTodo.mutate(\n { id, completed },\n {\n onSettled: () =>\n setPendingIds((prev) => {\n const next = new Set(prev);\n next.delete(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\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 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-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n {titleEnabled && titleText && (\n <h2\n className={`text-${titleFontSize} font-header font-bold text-${titleColor}`}\n >\n {titleText}\n </h2>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {!isEmpty && !isLoading && (\n <span className={`text-2xl font-bold text-${accentColor}`}>\n {activeTodos.length}\n </span>\n )}\n <div\n className={`flex h-10 w-10 shrink-0 items-center justify-center`}\n >\n <ListChecks className={`h-5 w-5 text-${accentColor}-foreground`} />\n </div>\n </div>\n </div>\n\n {/* Loading state */}\n {isLoading ? (\n <div className=\"flex min-h-[120px] items-center justify-center\">\n <div className=\"h-6 w-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 py-8\">\n <p className={`text-center text-${textColor}/60`}>\n You've got nothing else To-Do!\n </p>\n </div>\n ) : (\n /* Todo List */\n <>\n <div className=\"flex flex-col gap-3\">\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-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`}\n >\n {group.contactName}\n </div>\n )}\n {group.todos.map((todo, index) => (\n <div\n key={todo.id}\n className={`flex items-center gap-3 py-2 ${\n index !== group.todos.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={!!todo.completedAt}\n disabled={isPreview || pendingIds.has(todo.id)}\n onChange={(event) =>\n toggleTodo(todo.id, event.target.checked)\n }\n />\n <span className=\"line-clamp-1 flex-1 text-sm\">\n {parseTaskBody(todo.body).title}\n </span>\n </div>\n ))}\n </div>\n ))}\n </div>\n\n {/* Footer */}\n <div className=\"mt-2 flex items-center justify-between\">\n {remainingCount > 0 && (\n <span className={`text-sm text-${textColor}/50 underline`}>\n {remainingCount} more task{remainingCount > 1 ? \"s\" : \"\"}\n </span>\n )}\n <div className=\"ml-auto\">\n <Plus className={`h-5 w-5 text-${textColor}/50`} />\n </div>\n </div>\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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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require("./chunk-9hOWP6kD.cjs");
|
|
2
|
-
require("./registry-context-
|
|
2
|
+
require("./registry-context-CcZYS15Q.cjs");
|
|
3
3
|
require("./error-state-BDhSltIa.cjs");
|
|
4
4
|
require("./registries-DBb6VjAX.cjs");
|
|
5
5
|
require("./fields-COJ84ouS.cjs");
|
|
6
6
|
require("./src-uhf6Szlw.cjs");
|
|
7
|
-
const require_ToDoWidget = require("./ToDoWidget-
|
|
7
|
+
const require_ToDoWidget = require("./ToDoWidget-OxT9z59F.cjs");
|
|
8
8
|
exports.toDoWidgetPropertySchema = require_ToDoWidget.toDoWidgetPropertySchema;
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk-9hOWP6kD.cjs");
|
|
3
|
-
const require_FluidProvider = require("./FluidProvider-
|
|
4
|
-
const require_ScreenRenderer = require("./ScreenRenderer-
|
|
3
|
+
const require_FluidProvider = require("./FluidProvider-eT4gA6Io.cjs");
|
|
4
|
+
const require_ScreenRenderer = require("./ScreenRenderer-xH01YkEQ.cjs");
|
|
5
5
|
require("./account-api-context-DZP9IiGg.cjs");
|
|
6
6
|
require("./store-api-context-D1gZn22Z.cjs");
|
|
7
7
|
require("./pay-api-context-_o5ZEXYt.cjs");
|
|
@@ -9,48 +9,48 @@ require("./mysite-api-context-Dz4lI5Xd.cjs");
|
|
|
9
9
|
require("./countries-api-context-C0C0K9gJ.cjs");
|
|
10
10
|
const require_src = require("./src-DiFCBegK.cjs");
|
|
11
11
|
const require_PortalTenantClientProvider = require("./PortalTenantClientProvider-BT5Y4YyL.cjs");
|
|
12
|
-
require("./registry-context-
|
|
12
|
+
require("./registry-context-CcZYS15Q.cjs");
|
|
13
13
|
const require_WidgetInteractionContext = require("./WidgetInteractionContext-DvPmzGqB.cjs");
|
|
14
14
|
const require_EmbedWidget = require("./EmbedWidget-Q45pAeCI.cjs");
|
|
15
15
|
require("./error-state-BDhSltIa.cjs");
|
|
16
|
-
const require_LayoutWidget = require("./LayoutWidget-
|
|
16
|
+
const require_LayoutWidget = require("./LayoutWidget-Bt2I2XMy.cjs");
|
|
17
17
|
const require_registries = require("./registries-DBb6VjAX.cjs");
|
|
18
18
|
require("./fields-COJ84ouS.cjs");
|
|
19
19
|
const require_TextWidget = require("./TextWidget-C7oY0X3r.cjs");
|
|
20
20
|
const require_AlertWidget = require("./AlertWidget-BZ67Uqfh.cjs");
|
|
21
21
|
const require_BulletListWidget = require("./BulletListWidget-iGJI33Ed.cjs");
|
|
22
|
-
const require_CalendarWidget = require("./CalendarWidget-
|
|
23
|
-
const require_CardWidget = require("./CardWidget-
|
|
22
|
+
const require_CalendarWidget = require("./CalendarWidget-C4Dv_TUk.cjs");
|
|
23
|
+
const require_CardWidget = require("./CardWidget-D-KEikO_.cjs");
|
|
24
24
|
require("./purify.es-CxBw_WJp.cjs");
|
|
25
25
|
require("./MediaRenderer-CvN8Ku0i.cjs");
|
|
26
|
-
const require_CarouselWidget = require("./CarouselWidget-
|
|
27
|
-
const require_CatchUpWidget = require("./CatchUpWidget-
|
|
26
|
+
const require_CarouselWidget = require("./CarouselWidget-Cu1b3ERq.cjs");
|
|
27
|
+
const require_CatchUpWidget = require("./CatchUpWidget-BzgybvuC.cjs");
|
|
28
28
|
const require_src$1 = require("./src-uhf6Szlw.cjs");
|
|
29
29
|
const require_ChartWidget = require("./ChartWidget-CFB4Yg0k.cjs");
|
|
30
|
-
const require_ContainerWidget = require("./ContainerWidget-
|
|
30
|
+
const require_ContainerWidget = require("./ContainerWidget-DbAhQKbY.cjs");
|
|
31
31
|
const require_ImageWidget = require("./ImageWidget-CbQVxMe3.cjs");
|
|
32
32
|
const require_LinkWidget = require("./LinkWidget-BoR7nVbH.cjs");
|
|
33
33
|
const require_ListWidget = require("./ListWidget-CzljZ1LA.cjs");
|
|
34
|
-
const require_MySiteWidget = require("./MySiteWidget-
|
|
34
|
+
const require_MySiteWidget = require("./MySiteWidget-CYiH2lmX.cjs");
|
|
35
35
|
require("./scroll-arrows-DhwdUQBY.cjs");
|
|
36
36
|
const require_NestedWidget = require("./NestedWidget-CEoAO2sp.cjs");
|
|
37
|
-
const require_PointsWidget = require("./PointsWidget-
|
|
37
|
+
const require_PointsWidget = require("./PointsWidget-D1FV8l1h.cjs");
|
|
38
38
|
const require_QuickLinksWidget = require("./QuickLinksWidget-DBH-HRvz.cjs");
|
|
39
39
|
const require_QuickShareWidget = require("./QuickShareWidget-Btoe8CyN.cjs");
|
|
40
|
-
const require_RecentActivityWidget = require("./RecentActivityWidget-
|
|
40
|
+
const require_RecentActivityWidget = require("./RecentActivityWidget-DNyhUZNs.cjs");
|
|
41
41
|
const require_SeparatorWidget = require("./SeparatorWidget-BCbzDxnz.cjs");
|
|
42
42
|
const require_SpacerWidget = require("./SpacerWidget-CI3to-88.cjs");
|
|
43
43
|
const require_TableWidget = require("./TableWidget-BIn1oRiJ.cjs");
|
|
44
|
-
const require_ToDoWidget = require("./ToDoWidget-
|
|
44
|
+
const require_ToDoWidget = require("./ToDoWidget-OxT9z59F.cjs");
|
|
45
45
|
const require_VideoWidget = require("./VideoWidget-CDcV0J5W.cjs");
|
|
46
46
|
const require_ScreenHeaderContext = require("./ScreenHeaderContext-oIu5Bvhs.cjs");
|
|
47
47
|
const require_use_store = require("./use-store-D2S1FywW.cjs");
|
|
48
48
|
const require_CustomersScreen = require("./CustomersScreen-DW3BuhBs.cjs");
|
|
49
49
|
const require_AppNavigationContext = require("./AppNavigationContext-C1-hd9Rw.cjs");
|
|
50
50
|
const require_use_account = require("./use-account-C1X-VLY-.cjs");
|
|
51
|
-
const require_MessagingScreen = require("./MessagingScreen-
|
|
51
|
+
const require_MessagingScreen = require("./MessagingScreen-D3S230Ba.cjs");
|
|
52
52
|
require("./fluid-pay-api-adapter-BszgrFL6.cjs");
|
|
53
|
-
const require_ProfileScreen = require("./ProfileScreen-
|
|
53
|
+
const require_ProfileScreen = require("./ProfileScreen-DAujb81k.cjs");
|
|
54
54
|
const require_SubscriptionsScreen = require("./SubscriptionsScreen-DrDSwMXS.cjs");
|
|
55
55
|
require("./dist-DbRTQ2QF.cjs");
|
|
56
56
|
require("./es-UfEBhcZD.cjs");
|
|
@@ -64,7 +64,7 @@ require("./dist-BQZkLGL6.cjs");
|
|
|
64
64
|
const require_MySiteScreen = require("./MySiteScreen-CUyJteDm.cjs");
|
|
65
65
|
const require_ShareablesScreen = require("./ShareablesScreen-A69L0Nok.cjs");
|
|
66
66
|
require("./PortalProductsApiProvider-DL8nl7To.cjs");
|
|
67
|
-
const require_ShopScreen = require("./ShopScreen-
|
|
67
|
+
const require_ShopScreen = require("./ShopScreen-1yvcCk7l.cjs");
|
|
68
68
|
require("./UpgradeScreen-DAKe_hiv.cjs");
|
|
69
69
|
require("./AppDownloadScreen-BXnl23_d.cjs");
|
|
70
70
|
let react = require("react");
|
|
@@ -2074,12 +2074,12 @@ function AccountManageLayout({ children }) {
|
|
|
2074
2074
|
}
|
|
2075
2075
|
//#endregion
|
|
2076
2076
|
//#region src/shell/system-screen-map.ts
|
|
2077
|
-
const ProfileScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ProfileScreen-
|
|
2077
|
+
const ProfileScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ProfileScreen-BYJj4D1W.cjs")).then((m) => ({ default: m.ProfileScreen })));
|
|
2078
2078
|
const OrdersScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./OrdersScreen-Cuch7aki.cjs")).then((m) => ({ default: m.OrdersScreen })));
|
|
2079
2079
|
const SubscriptionsScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./SubscriptionsScreen-t5wtDGfB.cjs")).then((m) => ({ default: m.SubscriptionsScreen })));
|
|
2080
|
-
const MessagingScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./MessagingScreen-
|
|
2080
|
+
const MessagingScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./MessagingScreen-BHYX1Kc6.cjs")).then((m) => ({ default: m.MessagingScreen })));
|
|
2081
2081
|
const ContactsScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ContactsScreen-CH_P8WxC.cjs")).then((m) => ({ default: m.ContactsScreen })));
|
|
2082
|
-
const ShopScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ShopScreen-
|
|
2082
|
+
const ShopScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ShopScreen-6nEKrNjK.cjs")).then((m) => ({ default: m.ShopScreen })));
|
|
2083
2083
|
const CustomersScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./CustomersScreen-DW3BuhBs.cjs")).then((n) => n.CustomersScreen_exports).then((m) => ({ default: m.CustomersScreen })));
|
|
2084
2084
|
const ShareablesScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./ShareablesScreen-BUYG-mjj.cjs")).then((m) => ({ default: m.ShareablesScreen })));
|
|
2085
2085
|
const MySiteScreen$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("./MySiteScreen-B_16cPgD.cjs")).then((m) => ({ default: m.MySiteScreen })));
|
|
@@ -3776,15 +3776,15 @@ zod.z.object({
|
|
|
3776
3776
|
//#endregion
|
|
3777
3777
|
//#region src/screens/index.ts
|
|
3778
3778
|
const screenPropertySchemas = {
|
|
3779
|
-
ProfileScreen: () => Promise.resolve().then(() => require("./ProfileScreen-
|
|
3780
|
-
MessagingScreen: () => Promise.resolve().then(() => require("./MessagingScreen-
|
|
3779
|
+
ProfileScreen: () => Promise.resolve().then(() => require("./ProfileScreen-BYJj4D1W.cjs")).then((m) => m.profileScreenPropertySchema),
|
|
3780
|
+
MessagingScreen: () => Promise.resolve().then(() => require("./MessagingScreen-BHYX1Kc6.cjs")).then((m) => m.messagingScreenPropertySchema),
|
|
3781
3781
|
ContactsScreen: () => Promise.resolve().then(() => require("./ContactsScreen-CH_P8WxC.cjs")).then((m) => m.contactsScreenPropertySchema),
|
|
3782
3782
|
OrdersScreen: () => Promise.resolve().then(() => require("./OrdersScreen-Cuch7aki.cjs")).then((m) => m.ordersScreenPropertySchema),
|
|
3783
3783
|
SubscriptionsScreen: () => Promise.resolve().then(() => require("./SubscriptionsScreen-t5wtDGfB.cjs")).then((m) => m.subscriptionsScreenPropertySchema),
|
|
3784
3784
|
CustomersScreen: () => Promise.resolve().then(() => require("./CustomersScreen-DW3BuhBs.cjs")).then((n) => n.CustomersScreen_exports).then((m) => m.customersScreenPropertySchema),
|
|
3785
3785
|
MySiteScreen: () => Promise.resolve().then(() => require("./MySiteScreen-B_16cPgD.cjs")).then((m) => m.mySiteScreenPropertySchema),
|
|
3786
3786
|
ShareablesScreen: () => Promise.resolve().then(() => require("./ShareablesScreen-BUYG-mjj.cjs")).then((m) => m.shareablesScreenPropertySchema),
|
|
3787
|
-
ShopScreen: () => Promise.resolve().then(() => require("./ShopScreen-
|
|
3787
|
+
ShopScreen: () => Promise.resolve().then(() => require("./ShopScreen-6nEKrNjK.cjs")).then((m) => m.shopScreenPropertySchema),
|
|
3788
3788
|
UpgradeScreen: () => Promise.resolve().then(() => require("./UpgradeScreen-DUvg-WZv.cjs")).then((m) => m.upgradeScreenPropertySchema),
|
|
3789
3789
|
AppDownloadScreen: () => Promise.resolve().then(() => require("./AppDownloadScreen-BXnl23_d.cjs")).then((n) => n.AppDownloadScreen_exports).then((m) => m.appDownloadScreenPropertySchema)
|
|
3790
3790
|
};
|