const require_chunk = require("./chunk-9hOWP6kD.cjs");
const require_registry_context = require("./registry-context-D737a2Jb.cjs");
const require_error_state = require("./error-state-BY6if4vN.cjs");
const require_task_composer_form = require("./task-composer-form-BMw6N_M4.cjs");
const require_registries = require("./registries-DgzzKX8_.cjs");
const require_preview_context = require("./preview-context-J_Viyhgm.cjs");
const require_src = require("./src-CMer7k-Y.cjs");
let react = require("react");
react = require_chunk.__toESM(react);
let _tanstack_react_query = require("@tanstack/react-query");
let react_jsx_runtime = require("react/jsx-runtime");
let lucide_react = require("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. The `state` arg is included so the active/completed lists
* cache independently.
*/
function todosQueryKey(args) {
	return [
		"portal-widget-use",
		"todos",
		args.isPreview ? "preview" : args.baseUrl,
		args.state ?? "incomplete"
	];
}
function todosQueryKeyPrefix(args) {
	return [
		"portal-widget-use",
		"todos",
		args.isPreview ? "preview" : args.baseUrl
	];
}
function useTodos(options) {
	const widgetsApi = require_error_state.useWidgetsApi();
	const { isPreview } = require_preview_context.useWidgetPreviewContext();
	const { baseUrl } = require_registry_context.useDataSourceRegistryConfig();
	const state = options?.state;
	return (0, _tanstack_react_query.useQuery)({
		queryKey: todosQueryKey(state !== void 0 ? {
			baseUrl,
			isPreview,
			state
		} : {
			baseUrl,
			isPreview
		}),
		queryFn: ({ signal }) => widgetsApi.fetchTodos(state !== void 0 ? { state } : void 0, signal),
		enabled: !isPreview,
		...isPreview && (state === void 0 || state === "incomplete") && { placeholderData: PREVIEW_DATA },
		...isPreview && state !== void 0 && state !== "incomplete" && { placeholderData: [] }
	});
}
//#endregion
//#region ../widgets/src/hooks/use-update-todo.ts
function useUpdateTodo() {
	const widgetsApi = require_error_state.useWidgetsApi();
	const queryClient = (0, _tanstack_react_query.useQueryClient)();
	const { isPreview } = require_preview_context.useWidgetPreviewContext();
	const { baseUrl } = require_registry_context.useDataSourceRegistryConfig();
	const queryKey = todosQueryKey({
		baseUrl,
		isPreview
	});
	return (0, _tanstack_react_query.useMutation)({
		mutationFn: ({ id, body, due_at, completed, contact_id }) => {
			if (isPreview) return Promise.reject(/* @__PURE__ */ new Error("Todo updates are disabled in preview mode"));
			const input = {};
			if (body !== void 0) input.body = body;
			if (due_at !== void 0) input.due_at = due_at;
			if (completed !== void 0) input.completed = completed;
			if (contact_id !== void 0) input.contact_id = contact_id;
			if (Object.keys(input).length === 0) return Promise.reject(/* @__PURE__ */ new Error("Todo update requires at least one changed field"));
			return widgetsApi.updateTodo(id, input);
		},
		onMutate: async (variables) => {
			if (!(variables.completed !== void 0 && variables.body === void 0 && variables.due_at === void 0 && variables.contact_id === void 0)) return { previous: void 0 };
			await queryClient.cancelQueries({ queryKey });
			const previous = queryClient.getQueryData(queryKey);
			queryClient.setQueryData(queryKey, (current) => (current ?? []).map((todo) => todo.id === variables.id ? {
				...todo,
				completedAt: variables.completed ? (/* @__PURE__ */ new Date()).toISOString() : null
			} : todo));
			return { previous };
		},
		onSuccess: (updated, variables) => {
			queryClient.setQueryData(queryKey, (current) => (current ?? []).map((todo) => todo.id === updated.id ? updated : todo));
			if (variables.completed !== void 0 && variables.body === void 0 && variables.due_at === void 0 && variables.contact_id === void 0) {
				if (variables.completed) require_src.fluidToast({
					title: "Todo completed",
					type: "success"
				});
			} else require_src.fluidToast({
				title: "Todo updated",
				type: "success"
			});
			const contactIdsToInvalidate = new Set([variables.previousContactId, variables.contact_id].filter((contactId) => contactId != null));
			for (const contactId of contactIdsToInvalidate) queryClient.invalidateQueries({ queryKey: require_task_composer_form.contactsKeys.tasks(String(contactId)) });
		},
		onError: (error, variables, context) => {
			if (context?.previous) queryClient.setQueryData(queryKey, context.previous);
			console.error("[portal-widgets] Failed to update todo", {
				error,
				todoId: variables.id,
				requestedContactId: variables.contact_id,
				previousContactId: variables.previousContactId
			});
			require_src.fluidToast({
				title: "Failed to update todo",
				type: "error"
			});
		},
		onSettled: () => {
			queryClient.invalidateQueries({ queryKey: todosQueryKeyPrefix({
				baseUrl,
				isPreview
			}) });
		}
	});
}
//#endregion
//#region ../widgets/src/hooks/use-delete-todo.ts
function useDeleteTodo() {
	const widgetsApi = require_error_state.useWidgetsApi();
	const queryClient = (0, _tanstack_react_query.useQueryClient)();
	const { isPreview } = require_preview_context.useWidgetPreviewContext();
	const { baseUrl } = require_registry_context.useDataSourceRegistryConfig();
	const queryKey = todosQueryKey({
		baseUrl,
		isPreview
	});
	return (0, _tanstack_react_query.useMutation)({
		mutationFn: ({ id }) => {
			if (isPreview) return Promise.reject(/* @__PURE__ */ new Error("Todo deletion is disabled in preview mode"));
			return widgetsApi.deleteTodo(id);
		},
		onMutate: async ({ id }) => {
			await queryClient.cancelQueries({ queryKey });
			const previous = queryClient.getQueryData(queryKey);
			queryClient.setQueryData(queryKey, (current) => (current ?? []).filter((todo) => todo.id !== id));
			return { previous };
		},
		onSuccess: (_result, variables) => {
			require_src.fluidToast({
				title: "Todo deleted",
				type: "success"
			});
			if (variables.contactId != null) queryClient.invalidateQueries({ queryKey: require_task_composer_form.contactsKeys.tasks(String(variables.contactId)) });
		},
		onError: (_error, _variables, context) => {
			if (context?.previous) queryClient.setQueryData(queryKey, context.previous);
			require_src.fluidToast({
				title: "Failed to delete todo",
				type: "error"
			});
		},
		onSettled: () => {
			queryClient.invalidateQueries({ queryKey: todosQueryKeyPrefix({
				baseUrl,
				isPreview
			}) });
		}
	});
}
//#endregion
//#region ../widgets/src/hooks/use-create-todo.ts
function useCreateTodo() {
	const widgetsApi = require_error_state.useWidgetsApi();
	const queryClient = (0, _tanstack_react_query.useQueryClient)();
	const { isPreview } = require_preview_context.useWidgetPreviewContext();
	const { baseUrl } = require_registry_context.useDataSourceRegistryConfig();
	return (0, _tanstack_react_query.useMutation)({
		mutationFn: (variables) => {
			if (isPreview) return Promise.reject(/* @__PURE__ */ new Error("Todo creation is disabled in preview mode"));
			return widgetsApi.createTodo(variables);
		},
		onSuccess: (_created, variables) => {
			require_src.fluidToast({
				title: "Todo created",
				type: "success"
			});
			queryClient.invalidateQueries({ queryKey: todosQueryKey({
				baseUrl,
				isPreview
			}) });
			if (variables.contact_id != null) queryClient.invalidateQueries({ queryKey: require_task_composer_form.contactsKeys.tasks(String(variables.contact_id)) });
		},
		onError: () => {
			require_src.fluidToast({
				title: "Failed to create todo",
				type: "error"
			});
		}
	});
}
//#endregion
//#region ../widgets/src/widgets/CreateTodoDialog.tsx
const SEARCH_DEBOUNCE_MS = 200;
function getContactInitials(contact) {
	const firstInitial = contact.first_name?.trim().charAt(0);
	const lastInitial = contact.last_name?.trim().charAt(0);
	const explicitInitials = `${firstInitial ?? ""}${lastInitial ?? ""}`;
	if (explicitInitials) return explicitInitials.toUpperCase();
	return contact.full_name.trim().split(/\s+/).slice(0, 2).map((part) => part.charAt(0)).join("").toUpperCase() || void 0;
}
function ContactBadgeAvatar({ contact }) {
	const initials = contact ? getContactInitials(contact) : void 0;
	return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Avatar, {
		className: "size-4 text-current",
		"aria-hidden": "true",
		children: [contact?.avatar_url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AvatarImage, {
			src: contact.avatar_url,
			alt: ""
		}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.AvatarFallback, {
			className: "bg-transparent text-[0.625rem] font-semibold text-current",
			children: initials ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.UserRound, {
				className: "size-3",
				"aria-hidden": "true"
			})
		})]
	});
}
function CreateTodoDialog({ open, onOpenChange }) {
	const [selectedContact, setSelectedContact] = (0, react.useState)(null);
	const [themedPopoverContainer, setThemedPopoverContainer] = (0, react.useState)(null);
	const { t } = require_task_composer_form.useContactsTranslation();
	const createTodo = useCreateTodo();
	(0, react.useEffect)(() => {
		if (!open) setSelectedContact(null);
	}, [open]);
	const handleSubmit = (values) => {
		createTodo.mutate({
			body: values.body,
			due_at: values.due_at,
			contact_id: selectedContact ? selectedContact.id : null
		}, { onSuccess: () => onOpenChange(false) });
	};
	return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Dialog, {
		open,
		onOpenChange,
		children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DialogContent, {
			className: "gap-4 p-4 sm:max-w-xl sm:gap-6 sm:p-6",
			children: [
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogTitle, { children: t("add_todo_dialog_title") }) }),
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.PortalContainerProvider, {
					container: themedPopoverContainer,
					children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
						className: "mt-2 flex flex-col gap-3",
						children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_task_composer_form.TaskComposerForm, {
							onSubmit: handleSubmit,
							isSubmitting: createTodo.isPending,
							metadataControls: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactPicker, {
								value: selectedContact,
								onSelect: setSelectedContact,
								onClear: () => setSelectedContact(null)
							})
						})
					})
				}),
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
					ref: setThemedPopoverContainer,
					className: "pointer-events-none fixed inset-0 z-[9998]"
				})
			]
		})
	});
}
function ContactPicker({ value, onSelect, onClear }) {
	const { t } = require_task_composer_form.useContactsTranslation();
	const [open, setOpen] = (0, react.useState)(false);
	const [searchInput, setSearchInput] = (0, react.useState)("");
	const [debouncedSearch, setDebouncedSearch] = (0, react.useState)("");
	const sentinelRef = (0, react.useRef)(null);
	(0, react.useEffect)(() => {
		const handle = window.setTimeout(() => {
			setDebouncedSearch(searchInput.trim());
		}, SEARCH_DEBOUNCE_MS);
		return () => window.clearTimeout(handle);
	}, [searchInput]);
	const { data, isLoading, isError, hasNextPage, isFetchingNextPage, fetchNextPage } = require_task_composer_form.useInfiniteContacts((0, react.useMemo)(() => ({
		...debouncedSearch ? { search_query: debouncedSearch } : {},
		sort_by: "full_name",
		sort_direction: "asc",
		per_page: 25
	}), [debouncedSearch]));
	const contacts = (0, react.useMemo)(() => data?.pages.flatMap((page) => page.contacts ?? []) ?? [], [data]);
	(0, react.useEffect)(() => {
		if (!open) return;
		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();
	}, [
		open,
		hasNextPage,
		isFetchingNextPage,
		fetchNextPage
	]);
	const selectedContactLabel = value ? value.full_name : t("select_contact");
	return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
		className: "flex min-w-0 items-center",
		children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Popover, {
			open,
			onOpenChange: setOpen,
			children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.PopoverTrigger, {
				asChild: true,
				children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
					type: "button",
					"aria-label": t("contact_picker_trigger_aria", { selection: selectedContactLabel }),
					"aria-haspopup": "listbox",
					"aria-expanded": open,
					className: require_src.cn("flex max-w-40 min-w-0 shrink items-center gap-1 rounded-full px-3 py-1 text-left text-xs font-medium transition-colors", value ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/70"),
					children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactBadgeAvatar, { contact: value }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
						className: "min-w-0 truncate",
						children: selectedContactLabel
					})]
				})
			}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.PopoverContent, {
				align: "start",
				className: "bg-popover text-popover-foreground pointer-events-auto z-[9999] w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-md border p-0 shadow-md",
				children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
					className: "border-border flex items-center gap-2 border-b px-3 py-2",
					children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Search, {
						className: "text-muted-foreground size-4 shrink-0",
						"aria-hidden": "true"
					}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.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__ */ (0, react_jsx_runtime.jsxs)("div", {
					className: "max-h-64 overflow-y-auto py-1",
					role: "listbox",
					children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
						type: "button",
						role: "option",
						"aria-selected": value === null,
						onClick: () => {
							onClear();
							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__ */ (0, react_jsx_runtime.jsx)("span", {
							className: "text-muted-foreground truncate italic",
							children: t("no_contact")
						}), value === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
							className: "text-primary size-4 shrink-0",
							"aria-hidden": "true"
						})]
					}), isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
						className: "text-muted-foreground px-3 py-6 text-center text-xs",
						children: t("loading")
					}) : isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
						className: "text-destructive px-3 py-6 text-center text-xs",
						children: t("error_loading_list")
					}) : contacts.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
						contacts.map((contact) => {
							const isSelected = value?.id === contact.id;
							return /* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsx)("span", {
									className: "truncate",
									children: contact.full_name
								}), isSelected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, {
									className: "text-primary size-4 shrink-0",
									"aria-hidden": "true"
								})]
							}, contact.id);
						}),
						hasNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
							ref: sentinelRef,
							"aria-hidden": "true",
							className: "h-4"
						}),
						isFetchingNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
							className: "text-muted-foreground px-3 py-2 text-center text-xs",
							children: t("loading_more")
						})
					] })]
				})]
			})]
		})
	});
}
//#endregion
//#region ../widgets/src/widgets/EditTodoDialog.tsx
function getTodoContact(todo) {
	if (todo.contactId == null) return null;
	return {
		id: todo.contactId,
		full_name: todo.contactName ?? String(todo.contactId),
		status: null,
		metadata: {}
	};
}
function EditTodoDialog({ open, onOpenChange, todo }) {
	const { t } = require_task_composer_form.useContactsTranslation();
	const updateTodo = useUpdateTodo();
	const [selectedContactOverride, setSelectedContactOverride] = (0, react.useState)(null);
	const [themedPopoverContainer, setThemedPopoverContainer] = (0, react.useState)(null);
	if (!todo) return null;
	const selectedContact = selectedContactOverride?.todoId === todo.id ? selectedContactOverride.contact : getTodoContact(todo);
	const handleOpenChange = (nextOpen) => {
		if (!nextOpen) setSelectedContactOverride(null);
		onOpenChange(nextOpen);
	};
	const handleSelectContact = (contact) => {
		setSelectedContactOverride({
			todoId: todo.id,
			contact
		});
	};
	const handleClearContact = () => {
		setSelectedContactOverride({
			todoId: todo.id,
			contact: null
		});
	};
	const handleSubmit = (values) => {
		updateTodo.mutate({
			id: todo.id,
			body: values.body,
			due_at: values.due_at,
			contact_id: selectedContact ? selectedContact.id : null,
			previousContactId: todo.contactId
		}, { onSuccess: () => handleOpenChange(false) });
	};
	return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Dialog, {
		open,
		onOpenChange: handleOpenChange,
		children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DialogContent, {
			className: "gap-4 p-4 sm:max-w-xl sm:gap-6 sm:p-6",
			children: [
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DialogTitle, { children: t("edit_todo") }) }),
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.PortalContainerProvider, {
					container: themedPopoverContainer,
					children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_task_composer_form.TaskComposerForm, {
						initialBody: todo.body,
						initialDueAt: todo.dueAt,
						onSubmit: handleSubmit,
						isSubmitting: updateTodo.isPending,
						submitLabel: t("save_changes"),
						submittingLabel: t("saving"),
						metadataControls: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContactPicker, {
							value: selectedContact,
							onSelect: handleSelectContact,
							onClear: handleClearContact
						})
					}, todo.id)
				}),
				/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
					ref: setThemedPopoverContainer,
					className: "pointer-events-none fixed inset-0 z-[9998]"
				})
			]
		})
	});
}
//#endregion
//#region ../widgets/src/widgets/ToDoWidget.tsx
const pluralRulesCache = /* @__PURE__ */ new Map();
function usesSingularPluralForm(locale, count) {
	const tag = locale.replace(/_/g, "-");
	let rules = pluralRulesCache.get(tag);
	if (!rules) {
		rules = new Intl.PluralRules(tag);
		pluralRulesCache.set(tag, rules);
	}
	return rules.select(count) === "one";
}
/**
* Format a todo due_at ISO string as a short date + time (e.g. "Jun 1, 9:00 AM").
* Mirrors the mobile widget formatting.
*
* All-day / date-only values (bare YYYY-MM-DD, plus Z-designated midnight ISO
* strings) are formatted in UTC so the displayed calendar date matches the
* source prefix without shifting. Non-Z timed values stay on the timed path;
* offset values are formatted as instants in the viewer's local timezone.
*/
function formatDueAt(dateStr, locale) {
	const normalizedLocale = locale?.replace(/_/g, "-") ?? "en-US";
	if (require_task_composer_form.ALL_DAY_PATTERN.test(dateStr) && !require_task_composer_form.LOCAL_TIMED_DUE_AT_PATTERN.test(dateStr)) {
		const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(dateStr);
		if (match?.[1] && match[2] && match[3]) return new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))).toLocaleDateString(normalizedLocale, {
			month: "short",
			day: "numeric",
			timeZone: "UTC"
		});
	}
	return new Date(dateStr).toLocaleString(normalizedLocale, {
		month: "short",
		day: "numeric",
		hour: "numeric",
		minute: "2-digit"
	});
}
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 { t, locale } = require_task_composer_form.useContactsTranslation();
	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 { data: completedTodos = [], isLoading: isCompletedLoading } = useTodos({ state: "completed" });
	const updateTodo = useUpdateTodo();
	const deleteTodo = useDeleteTodo();
	const { isPreview } = require_preview_context.useWidgetPreviewContext();
	const [pendingIds, setPendingIds] = (0, react.useState)(/* @__PURE__ */ new Set());
	const [isCreateDialogOpen, setIsCreateDialogOpen] = (0, react.useState)(false);
	const [editingTodo, setEditingTodo] = (0, react.useState)(null);
	const [isDoneExpanded, setIsDoneExpanded] = (0, react.useState)(false);
	const toggleTodo = (todo, completed) => {
		if (isPreview || pendingIds.has(todo.id)) return;
		setPendingIds((prev) => new Set(prev).add(todo.id));
		updateTodo.mutate({
			id: todo.id,
			completed,
			previousContactId: todo.contactId
		}, { onSettled: () => setPendingIds((prev) => {
			const next = new Set(prev);
			next.delete(todo.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 activeTaskLabel = usesSingularPluralForm(locale, activeTodos.length) ? t("todo_open_tasks_one", { count: activeTodos.length }) : t("todo_open_tasks_other", { count: activeTodos.length });
	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;
	}, []);
	const doneCount = completedTodos.length;
	const handleRowClick = (todo) => {
		if (isPreview) return;
		setEditingTodo(todo);
	};
	const handleDelete = (todo) => {
		if (isPreview) return;
		deleteTodo.mutate({
			id: todo.id,
			contactId: todo.contactId
		});
	};
	return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
		className: `overflow-hidden rounded-${borderRadius} ${require_registries.borderWidthClasses[borderWidth]} ${borderWidth !== "none" ? require_registries.borderColorClasses[borderColor] : ""} bg-${backgroundColor} text-${textColor} p-${padding} ${className ?? ""}`,
		style: { backgroundImage },
		...props,
		children: [
			/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
				className: "mb-4 flex items-start justify-between gap-4",
				children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
					className: "flex min-w-0 flex-col gap-1",
					children: [titleEnabled && titleText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
						className: `text-${titleFontSize} font-header leading-tight font-bold text-${titleColor}`,
						children: titleText
					}), !isEmpty && !isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
						className: `w-fit rounded-full bg-${accentColor}/10 px-2.5 py-1 text-[11px] leading-none font-semibold text-${accentColor}`,
						children: activeTaskLabel
					})]
				}), !isPreview && !isEmpty && !isLoading && !isError && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
					type: "button",
					onClick: () => setIsCreateDialogOpen(true),
					className: `inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor} px-3.5 py-2 text-xs font-semibold text-${accentColor}-foreground shadow-sm transition-colors hover:bg-${accentColor}/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-${accentColor}/40`,
					children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
						className: "size-3.5",
						"aria-hidden": "true"
					}), t("add_todo")]
				})]
			}),
			isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
				className: "flex min-h-[120px] items-center justify-center",
				children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "size-6 animate-spin rounded-full border-2 border-current border-t-transparent" })
			}) : isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_error_state.ErrorState, {}) : isEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
				className: "flex flex-col items-center justify-center gap-3 py-8",
				children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
					className: `text-center text-${textColor}/60`,
					children: t("todo_empty")
				}), !isPreview && /* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, {
						className: "size-3.5",
						"aria-hidden": "true"
					}), t("add_todo")]
				})]
			}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
				className: "flex flex-col gap-4",
				children: groupedTodos.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
					className: "flex flex-col",
					children: [group.contactName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
						className: `mb-2 text-xs leading-none font-semibold tracking-wide uppercase text-${textColor}/60`,
						children: group.contactName
					}), group.todos.map((todo, index) => {
						return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
							className: `group/row grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 py-2.5 ${index !== group.todos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
							children: [
								/* @__PURE__ */ (0, react_jsx_runtime.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, event.target.checked),
									onClick: (event) => event.stopPropagation(),
									"aria-label": t("mark_as_completed")
								}),
								/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
									type: "button",
									onClick: () => handleRowClick(todo),
									disabled: isPreview,
									"aria-label": t("edit_todo"),
									className: "flex min-w-0 flex-col items-start gap-1 text-left not-disabled:cursor-pointer",
									children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
										className: "line-clamp-1 text-base leading-snug font-medium",
										children: require_task_composer_form.parseTaskBody(todo.body).title
									}), todo.dueAt && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
										className: `text-sm leading-none text-${textColor}/60`,
										children: formatDueAt(todo.dueAt, locale)
									})]
								}),
								!isPreview && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuTrigger, {
									asChild: true,
									children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
										variant: "ghost",
										size: "icon-xs",
										onClick: (event) => event.stopPropagation(),
										"aria-label": t("task_actions"),
										className: `text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`,
										children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.EllipsisVertical, { className: "size-4" })
									})
								}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenuContent, {
									align: "end",
									children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
										onClick: (event) => {
											event.stopPropagation();
											handleRowClick(todo);
										},
										children: t("edit_todo")
									}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
										className: "text-destructive",
										onClick: (event) => {
											event.stopPropagation();
											handleDelete(todo);
										},
										children: t("delete_todo")
									})]
								})] })
							]
						}, todo.id);
					})]
				}, group.contactId ?? "__unassigned__"))
			}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
				className: "mt-1 flex items-center justify-between",
				children: remainingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
					className: `text-sm text-${textColor}/50 underline`,
					children: usesSingularPluralForm(locale, remainingCount) ? t("todo_more_tasks_one", { count: remainingCount }) : t("todo_more_tasks_other", { count: remainingCount })
				})
			})] }),
			!isPreview && !isLoading && !isError && !isCompletedLoading && doneCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
				className: `mt-4 border-t border-${textColor}/10 pt-3`,
				children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
					type: "button",
					onClick: () => setIsDoneExpanded((v) => !v),
					className: `flex w-full items-center justify-between gap-2 rounded-md py-1.5 text-sm font-medium text-${textColor}/60 hover:bg-${textColor}/5`,
					"aria-expanded": isDoneExpanded,
					children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: isDoneExpanded ? t("hide_done") : t("view_done", { count: doneCount }) }), isDoneExpanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronUp, {
						className: "size-4",
						"aria-hidden": "true"
					}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {
						className: "size-4",
						"aria-hidden": "true"
					})]
				}), isDoneExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
					className: "mt-1 flex flex-col",
					children: completedTodos.map((todo, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
						className: `group/row flex items-center gap-3 py-2 ${index !== completedTodos.length - 1 ? `border-b border-${textColor}/10` : ""}`,
						children: [
							/* @__PURE__ */ (0, react_jsx_runtime.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: true,
								disabled: pendingIds.has(todo.id),
								onChange: () => toggleTodo(todo, false),
								onClick: (event) => event.stopPropagation(),
								"aria-label": t("mark_as_open")
							}),
							/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
								type: "button",
								onClick: () => handleRowClick(todo),
								"aria-label": t("edit_todo"),
								className: `flex min-w-0 flex-1 cursor-pointer flex-col items-start gap-0.5 text-left text-${textColor}/60 line-through`,
								children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
									className: "line-clamp-1 text-sm",
									children: require_task_composer_form.parseTaskBody(todo.body).title
								}), todo.dueAt && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
									className: "text-xs",
									children: formatDueAt(todo.dueAt, locale)
								})]
							}),
							/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuTrigger, {
								asChild: true,
								children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
									variant: "ghost",
									size: "icon-xs",
									onClick: (event) => event.stopPropagation(),
									"aria-label": t("task_actions"),
									className: "opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100",
									children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.EllipsisVertical, { className: "size-4" })
								})
							}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenuContent, {
								align: "end",
								children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
									onClick: (event) => {
										event.stopPropagation();
										handleRowClick(todo);
									},
									children: t("edit_todo")
								}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.DropdownMenuItem, {
									className: "text-destructive",
									onClick: (event) => {
										event.stopPropagation();
										handleDelete(todo);
									},
									children: t("delete_todo")
								})]
							})] })
						]
					}, todo.id))
				})]
			}),
			!isPreview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateTodoDialog, {
				open: isCreateDialogOpen,
				onOpenChange: setIsCreateDialogOpen
			}),
			!isPreview && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditTodoDialog, {
				open: editingTodo !== null,
				onOpenChange: (open) => {
					if (!open) setEditingTodo(null);
				},
				todo: editingTodo
			})
		]
	});
}
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"
		},
		require_registries.getFontSizeField({
			key: "titleFontSize",
			label: "Title Font Size",
			description: "Font size for the widget title",
			defaultValue: "xl",
			tab: "styling",
			group: "Title",
			requiresKeyToBeTrue: "titleEnabled"
		}),
		require_registries.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"
		},
		require_registries.getColorField({
			key: "textColor",
			label: "Text Color",
			description: "Default text color for todo items",
			defaultValue: "foreground",
			tab: "styling",
			group: "Design"
		}),
		require_registries.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"
		},
		require_registries.getPaddingField({
			key: "padding",
			label: "Padding",
			description: "Padding around the widget container",
			defaultValue: 4,
			tab: "styling",
			group: "Design"
		}),
		require_registries.getBorderRadiusField({
			key: "borderRadius",
			label: "Border Radius",
			description: "Border radius for the widget container",
			defaultValue: "md",
			tab: "styling",
			group: "Design"
		}),
		require_registries.getBorderWidthField({
			key: "borderWidth",
			label: "Border Width",
			description: "Border width for the widget",
			defaultValue: "none",
			tab: "styling",
			group: "Design"
		}),
		require_registries.getBorderColorField({
			key: "borderColor",
			label: "Border Color",
			description: "Border color for the widget",
			defaultValue: "muted",
			tab: "styling",
			group: "Design"
		})
	]
};
//#endregion
Object.defineProperty(exports, "ToDoWidget", {
	enumerable: true,
	get: function() {
		return ToDoWidget;
	}
});
Object.defineProperty(exports, "toDoWidgetPropertySchema", {
	enumerable: true,
	get: function() {
		return toDoWidgetPropertySchema;
	}
});

//# sourceMappingURL=ToDoWidget-DUWIEc9d.cjs.map