import { vt as __exportAll } from "./PortalTenantClientProvider-BmRtQAbi.mjs";
import { c as useContactsTranslation, o as parseTaskBody, r as useInfiniteContacts, t as TaskComposerForm } from "./task-composer-form-D_Pbl6qk.mjs";
import { n as useDataSourceRegistryConfig } from "./registry-context-BKvTiuXB.mjs";
import { r as useWidgetsApi, t as ErrorState } from "./error-state-DvzIn9Tz.mjs";
import { i as getBorderColorField, l as getColorField, m as getPaddingField, n as borderWidthClasses, o as getBorderRadiusField, s as getBorderWidthField, t as borderColorClasses, u as getFontSizeField } from "./registries-59Barbhg.mjs";
import { t as useWidgetPreviewContext } from "./preview-context-D9ZzEfWh.mjs";
import { Dn as PortalContainerProvider, Q as DialogContent, X as Dialog, at as Popover, ct as PopoverTrigger, f as fluidToast, it as DialogTitle, k as Input, kn as cn, st as PopoverContent, tt as DialogHeader } from "./src-CJw6JbdS.mjs";
import { useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { Check, ChevronsUpDown, ListChecks, Plus, Search } from "lucide-react";
//#region ../widgets/src/hooks/use-todos.preview.ts
const now = /* @__PURE__ */ new Date();
function daysFromNow(days) {
	const d = new Date(now);
	d.setDate(d.getDate() + days);
	return d.toISOString();
}
const PREVIEW_DATA = [
	{
		id: 1,
		body: "Send follow-up email to new leads",
		dueAt: daysFromNow(1),
		completedAt: null,
		createdAt: daysFromNow(-2),
		contactId: 101,
		contactName: "Sarah Johnson"
	},
	{
		id: 2,
		body: "Prepare slides for team training",
		dueAt: daysFromNow(3),
		completedAt: null,
		createdAt: daysFromNow(-1),
		contactId: null,
		contactName: null
	},
	{
		id: 3,
		body: "Review monthly sales report",
		dueAt: daysFromNow(-1),
		completedAt: null,
		createdAt: daysFromNow(-5),
		contactId: 102,
		contactName: "Mike Chen"
	}
];
//#endregion
//#region ../widgets/src/hooks/use-todos.ts
/**
* Shared cache key for the todos list. Both useTodos (read) and
* useUpdateTodo (write) use this — drift would silently break optimistic
* updates.
*/
function todosQueryKey(args) {
	return [
		"portal-widget-use",
		"todos",
		args.isPreview ? "preview" : args.baseUrl
	];
}
function useTodos() {
	const widgetsApi = useWidgetsApi();
	const { isPreview } = useWidgetPreviewContext();
	const { baseUrl } = useDataSourceRegistryConfig();
	return useQuery({
		queryKey: todosQueryKey({
			baseUrl,
			isPreview
		}),
		queryFn: ({ signal }) => widgetsApi.fetchTodos(signal),
		enabled: !isPreview,
		...isPreview && { placeholderData: PREVIEW_DATA }
	});
}
//#endregion
//#region ../widgets/src/hooks/use-update-todo.ts
function useUpdateTodo() {
	const widgetsApi = useWidgetsApi();
	const queryClient = useQueryClient();
	const { isPreview } = useWidgetPreviewContext();
	const { baseUrl } = useDataSourceRegistryConfig();
	const queryKey = todosQueryKey({
		baseUrl,
		isPreview
	});
	return useMutation({
		mutationFn: ({ id, completed }) => {
			if (isPreview) return Promise.reject(/* @__PURE__ */ new Error("Todo updates are disabled in preview mode"));
			return widgetsApi.updateTodo(id, completed);
		},
		onMutate: async ({ id, completed }) => {
			await queryClient.cancelQueries({ queryKey });
			const previous = queryClient.getQueryData(queryKey);
			queryClient.setQueryData(queryKey, (current) => (current ?? []).map((todo) => todo.id === id ? {
				...todo,
				completedAt: completed ? (/* @__PURE__ */ new Date()).toISOString() : null
			} : todo));
			return { previous };
		},
		onSuccess: (updated, { completed }) => {
			queryClient.setQueryData(queryKey, (current) => (current ?? []).map((todo) => todo.id === updated.id ? updated : todo));
			if (completed) fluidToast({
				title: "Todo completed",
				type: "success"
			});
		},
		onError: (_error, _variables, context) => {
			if (context?.previous) queryClient.setQueryData(queryKey, context.previous);
			fluidToast({
				title: "Failed to update todo",
				type: "error"
			});
		},
		onSettled: () => {
			queryClient.invalidateQueries({ queryKey });
		}
	});
}
//#endregion
//#region ../widgets/src/widgets/CreateTodoDialog.tsx
const SEARCH_DEBOUNCE_MS = 200;
function CreateTodoDialog({ open, onOpenChange }) {
	const queryClient = useQueryClient();
	const { isPreview } = useWidgetPreviewContext();
	const { baseUrl } = useDataSourceRegistryConfig();
	const [selectedContact, setSelectedContact] = useState(null);
	const [popoverContainer, setPopoverContainer] = useState(null);
	useEffect(() => {
		if (!open) setSelectedContact(null);
	}, [open]);
	const handleDone = () => {
		queryClient.invalidateQueries({ queryKey: todosQueryKey({
			baseUrl,
			isPreview
		}) });
		onOpenChange(false);
	};
	return /* @__PURE__ */ jsx(Dialog, {
		open,
		onOpenChange,
		children: /* @__PURE__ */ jsxs(DialogContent, {
			className: "gap-4 p-4 sm:max-w-md sm:gap-6 sm:p-6",
			children: [
				/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Add a to-do" }) }),
				/* @__PURE__ */ jsx(PortalContainerProvider, {
					container: popoverContainer,
					children: /* @__PURE__ */ jsxs("div", {
						className: "mt-2 flex flex-col gap-3",
						children: [/* @__PURE__ */ jsx(ContactPicker, {
							value: selectedContact,
							onSelect: setSelectedContact
						}), selectedContact && /* @__PURE__ */ jsx(TaskComposerForm, {
							contactId: String(selectedContact.id),
							onDone: handleDone
						}, selectedContact.id)]
					})
				}),
				/* @__PURE__ */ jsx("div", {
					ref: setPopoverContainer,
					className: "pointer-events-none fixed inset-0 z-[9998]"
				})
			]
		})
	});
}
function ContactPicker({ value, onSelect }) {
	const { t } = useContactsTranslation();
	const [open, setOpen] = useState(false);
	const [searchInput, setSearchInput] = useState("");
	const [debouncedSearch, setDebouncedSearch] = useState("");
	const sentinelRef = useRef(null);
	useEffect(() => {
		const handle = window.setTimeout(() => {
			setDebouncedSearch(searchInput.trim());
		}, SEARCH_DEBOUNCE_MS);
		return () => window.clearTimeout(handle);
	}, [searchInput]);
	const { data, isLoading, isError, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteContacts(useMemo(() => ({
		...debouncedSearch ? { search_query: debouncedSearch } : {},
		sort_by: "full_name",
		sort_direction: "asc",
		per_page: 25
	}), [debouncedSearch]));
	const contacts = useMemo(() => data?.pages.flatMap((page) => page.contacts ?? []) ?? [], [data]);
	useEffect(() => {
		const sentinel = sentinelRef.current;
		if (!sentinel || !hasNextPage) return;
		const observer = new IntersectionObserver((entries) => {
			if (entries[0]?.isIntersecting && !isFetchingNextPage) fetchNextPage();
		});
		observer.observe(sentinel);
		return () => observer.disconnect();
	}, [
		hasNextPage,
		isFetchingNextPage,
		fetchNextPage
	]);
	return /* @__PURE__ */ jsxs("div", {
		className: "flex flex-col gap-1",
		children: [/* @__PURE__ */ jsx("label", {
			className: "text-muted-foreground text-xs font-medium",
			children: t("contact_label")
		}), /* @__PURE__ */ jsxs(Popover, {
			open,
			onOpenChange: setOpen,
			children: [/* @__PURE__ */ jsx(PopoverTrigger, {
				asChild: true,
				children: /* @__PURE__ */ jsxs("button", {
					type: "button",
					"aria-haspopup": "listbox",
					"aria-expanded": open,
					className: "border-input bg-background hover:bg-muted/40 flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-left text-sm transition-colors",
					children: [/* @__PURE__ */ jsx("span", {
						className: cn("truncate", !value && "text-muted-foreground font-normal"),
						children: value ? value.full_name : t("empty_select_contact")
					}), /* @__PURE__ */ jsx(ChevronsUpDown, {
						className: "text-muted-foreground size-4 shrink-0",
						"aria-hidden": "true"
					})]
				})
			}), /* @__PURE__ */ jsxs(PopoverContent, {
				align: "start",
				className: "bg-popover text-popover-foreground pointer-events-auto z-[9999] w-(--radix-popover-trigger-width) overflow-hidden rounded-md border p-0 shadow-md",
				children: [/* @__PURE__ */ jsxs("div", {
					className: "border-border flex items-center gap-2 border-b px-3 py-2",
					children: [/* @__PURE__ */ jsx(Search, {
						className: "text-muted-foreground size-4 shrink-0",
						"aria-hidden": "true"
					}), /* @__PURE__ */ jsx(Input, {
						value: searchInput,
						onChange: (e) => setSearchInput(e.target.value),
						placeholder: t("search_placeholder"),
						"aria-label": t("search_placeholder"),
						className: "h-7 border-0 px-0 shadow-none focus-visible:ring-0"
					})]
				}), /* @__PURE__ */ jsx("div", {
					className: "max-h-64 overflow-y-auto py-1",
					role: "listbox",
					children: isLoading ? /* @__PURE__ */ jsx("div", {
						className: "text-muted-foreground px-3 py-6 text-center text-xs",
						children: t("loading")
					}) : isError ? /* @__PURE__ */ jsx("div", {
						className: "text-destructive px-3 py-6 text-center text-xs",
						children: t("error_loading_list")
					}) : contacts.length === 0 ? /* @__PURE__ */ jsx("div", {
						className: "text-muted-foreground px-3 py-6 text-center text-xs",
						children: debouncedSearch ? t("no_contacts_search", { term: debouncedSearch }) : t("no_contacts_yet")
					}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
						contacts.map((contact) => {
							const isSelected = value?.id === contact.id;
							return /* @__PURE__ */ jsxs("button", {
								type: "button",
								role: "option",
								"aria-selected": isSelected,
								onClick: () => {
									onSelect(contact);
									setOpen(false);
								},
								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",
								children: [/* @__PURE__ */ jsx("span", {
									className: "truncate",
									children: contact.full_name
								}), isSelected && /* @__PURE__ */ jsx(Check, {
									className: "text-primary size-4 shrink-0",
									"aria-hidden": "true"
								})]
							}, contact.id);
						}),
						hasNextPage && /* @__PURE__ */ jsx("div", {
							ref: sentinelRef,
							"aria-hidden": "true",
							className: "h-4"
						}),
						isFetchingNextPage && /* @__PURE__ */ jsx("div", {
							className: "text-muted-foreground px-3 py-2 text-center text-xs",
							children: t("loading_more")
						})
					] })
				})]
			})]
		})]
	});
}
//#endregion
//#region ../widgets/src/widgets/ToDoWidget.tsx
var ToDoWidget_exports = /* @__PURE__ */ __exportAll({
	ToDoWidget: () => ToDoWidget,
	toDoWidgetPropertySchema: () => toDoWidgetPropertySchema
});
function ToDoWidget({ titleEnabled = true, titleText = "To-Do", titleFontSize = "lg", titleColor = "foreground", background = {
	type: "solid",
	color: "background"
}, textColor = "foreground", accentColor = "primary", padding = 4, borderRadius = "md", borderWidth = "none", borderColor = "muted", maxItems = 5, className, ...props }) {
	const backgroundColor = background.color || "background";
	const backgroundImage = (background.resource?.image_url || background.resource?.imageUrl) && background.type === "image" ? `url(${background.resource.image_url || background.resource.imageUrl})` : "none";
	const { data: todos = [], isLoading, isError } = useTodos();
	const updateTodo = useUpdateTodo();
	const { isPreview } = useWidgetPreviewContext();
	const [pendingIds, setPendingIds] = useState(/* @__PURE__ */ new Set());
	const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
	const toggleTodo = (id, completed) => {
		if (isPreview || pendingIds.has(id)) return;
		setPendingIds((prev) => new Set(prev).add(id));
		updateTodo.mutate({
			id,
			completed
		}, { onSettled: () => setPendingIds((prev) => {
			const next = new Set(prev);
			next.delete(id);
			return next;
		}) });
	};
	const activeTodos = todos.filter((todo) => !todo.completedAt);
	const todosToShow = activeTodos.slice(0, maxItems);
	const remainingCount = activeTodos.length - todosToShow.length;
	const isEmpty = activeTodos.length === 0;
	const groupedTodos = todosToShow.reduce((groups, todo) => {
		const existing = groups.find((g) => g.contactId === todo.contactId);
		if (existing) existing.todos.push(todo);
		else groups.push({
			contactId: todo.contactId,
			contactName: todo.contactName,
			todos: [todo]
		});
		return groups;
	}, []);
	return /* @__PURE__ */ jsxs("div", {
		className: `overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== "none" ? borderColorClasses[borderColor] : ""} bg-${backgroundColor} text-${textColor} p-${padding} ${className ?? ""}`,
		style: { backgroundImage },
		...props,
		children: [
			/* @__PURE__ */ jsxs("div", {
				className: "mb-3 flex items-center justify-between",
				children: [/* @__PURE__ */ jsx("div", {
					className: "flex items-center gap-3",
					children: titleEnabled && titleText && /* @__PURE__ */ jsx("h2", {
						className: `text-${titleFontSize} font-header font-bold text-${titleColor}`,
						children: titleText
					})
				}), /* @__PURE__ */ jsxs("div", {
					className: "flex items-center gap-2",
					children: [!isEmpty && !isLoading && /* @__PURE__ */ jsx("span", {
						className: `text-2xl font-bold text-${accentColor}`,
						children: activeTodos.length
					}), /* @__PURE__ */ jsx("div", {
						className: `flex size-10 shrink-0 items-center justify-center`,
						children: /* @__PURE__ */ jsx(ListChecks, { className: `h-5 w-5 text-${accentColor}-foreground` })
					})]
				})]
			}),
			isLoading ? /* @__PURE__ */ jsx("div", {
				className: "flex min-h-[120px] items-center justify-center",
				children: /* @__PURE__ */ jsx("div", { className: "size-6 animate-spin rounded-full border-2 border-current border-t-transparent" })
			}) : isError ? /* @__PURE__ */ jsx(ErrorState, {}) : isEmpty ? /* @__PURE__ */ jsxs("div", {
				className: "flex flex-col items-center justify-center gap-3 py-8",
				children: [/* @__PURE__ */ jsx("p", {
					className: `text-center text-${textColor}/60`,
					children: "You've got nothing else To-Do!"
				}), !isPreview && /* @__PURE__ */ jsxs("button", {
					type: "button",
					onClick: () => setIsCreateDialogOpen(true),
					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`,
					children: [/* @__PURE__ */ jsx(Plus, {
						className: "size-3.5",
						"aria-hidden": "true"
					}), "Add to-do"]
				})]
			}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
				className: "flex flex-col gap-3",
				children: groupedTodos.map((group) => /* @__PURE__ */ jsxs("div", {
					className: "flex flex-col",
					children: [group.contactName && /* @__PURE__ */ jsx("div", {
						className: `mb-1 text-xs font-semibold tracking-wide uppercase text-${textColor}/60`,
						children: group.contactName
					}), group.todos.map((todo, index) => /* @__PURE__ */ jsxs("div", {
						className: `flex items-center gap-3 py-2 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
						children: [/* @__PURE__ */ jsx("input", {
							type: "checkbox",
							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`,
							checked: !!todo.completedAt,
							disabled: isPreview || pendingIds.has(todo.id),
							onChange: (event) => toggleTodo(todo.id, event.target.checked)
						}), /* @__PURE__ */ jsx("span", {
							className: "line-clamp-1 flex-1 text-sm",
							children: parseTaskBody(todo.body).title
						})]
					}, todo.id))]
				}, group.contactId ?? "__unassigned__"))
			}), /* @__PURE__ */ jsxs("div", {
				className: "mt-2 flex items-center justify-between",
				children: [remainingCount > 0 && /* @__PURE__ */ jsxs("span", {
					className: `text-sm text-${textColor}/50 underline`,
					children: [
						remainingCount,
						" more task",
						remainingCount > 1 ? "s" : ""
					]
				}), /* @__PURE__ */ jsx("div", {
					className: "ml-auto",
					children: /* @__PURE__ */ jsx("button", {
						type: "button",
						onClick: () => setIsCreateDialogOpen(true),
						disabled: isPreview,
						"aria-label": "Add to-do",
						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`,
						children: /* @__PURE__ */ jsx(Plus, { className: `h-5 w-5 text-${textColor}/50` })
					})
				})]
			})] }),
			!isPreview && /* @__PURE__ */ jsx(CreateTodoDialog, {
				open: isCreateDialogOpen,
				onOpenChange: setIsCreateDialogOpen
			})
		]
	});
}
const toDoWidgetPropertySchema = {
	widgetType: "ToDoWidget",
	displayName: "To-Do Widget",
	tabsConfig: [{
		id: "styling",
		label: "Styling"
	}],
	fields: [
		{
			key: "titleEnabled",
			label: "Widget Title",
			type: "boolean",
			description: "Enable the title displayed above the todo list",
			defaultValue: true,
			tab: "styling",
			group: "Title"
		},
		{
			key: "titleText",
			label: "Title",
			type: "text",
			description: "Title text displayed above the todo list",
			defaultValue: "To-Do",
			tab: "styling",
			group: "Title",
			requiresKeyToBeTrue: "titleEnabled"
		},
		getFontSizeField({
			key: "titleFontSize",
			label: "Title Font Size",
			description: "Font size for the widget title",
			defaultValue: "xl",
			tab: "styling",
			group: "Title",
			requiresKeyToBeTrue: "titleEnabled"
		}),
		getColorField({
			key: "titleColor",
			label: "Title Color",
			description: "Color for the widget title",
			defaultValue: "foreground",
			tab: "styling",
			group: "Title",
			requiresKeyToBeTrue: "titleEnabled"
		}),
		{
			type: "background",
			key: "background",
			label: "Background",
			description: "Background for the widget container",
			defaultValue: "background",
			tab: "styling",
			group: "Design"
		},
		getColorField({
			key: "textColor",
			label: "Text Color",
			description: "Default text color for todo items",
			defaultValue: "foreground",
			tab: "styling",
			group: "Design"
		}),
		getColorField({
			key: "accentColor",
			label: "Accent Color",
			description: "Color used for count badge and icon",
			defaultValue: "primary",
			tab: "styling",
			group: "Design"
		}),
		{
			key: "separator",
			type: "separator",
			label: "Separator",
			tab: "styling",
			group: "Design"
		},
		{
			key: "maxItems",
			label: "Max Items",
			type: "number",
			description: "Maximum number of todo items to display",
			min: 1,
			max: 20,
			step: 1,
			defaultValue: 5,
			tab: "styling",
			group: "Design"
		},
		getPaddingField({
			key: "padding",
			label: "Padding",
			description: "Padding around the widget container",
			defaultValue: 4,
			tab: "styling",
			group: "Design"
		}),
		getBorderRadiusField({
			key: "borderRadius",
			label: "Border Radius",
			description: "Border radius for the widget container",
			defaultValue: "md",
			tab: "styling",
			group: "Design"
		}),
		getBorderWidthField({
			key: "borderWidth",
			label: "Border Width",
			description: "Border width for the widget",
			defaultValue: "none",
			tab: "styling",
			group: "Design"
		}),
		getBorderColorField({
			key: "borderColor",
			label: "Border Color",
			description: "Border color for the widget",
			defaultValue: "muted",
			tab: "styling",
			group: "Design"
		})
	]
};
//#endregion
export { ToDoWidget_exports as n, toDoWidgetPropertySchema as r, ToDoWidget as t };

//# sourceMappingURL=ToDoWidget-BaWksZpJ.mjs.map