{"version":3,"file":"ToDoWidget-CSZaUVPs.mjs","names":[],"sources":["../../widgets/src/hooks/use-todos.preview.ts","../../widgets/src/hooks/use-todos.ts","../../widgets/src/hooks/use-update-todo.ts","../../widgets/src/hooks/use-delete-todo.ts","../../widgets/src/hooks/use-create-todo.ts","../../widgets/src/widgets/CreateTodoDialog.tsx","../../widgets/src/widgets/EditTodoDialog.tsx","../../widgets/src/widgets/ToDoWidget.tsx"],"sourcesContent":["import type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n  const d = new Date(now);\n  d.setDate(d.getDate() + days);\n  return d.toISOString();\n}\n\nexport const PREVIEW_DATA: Todo[] = [\n  {\n    id: 1,\n    body: \"Send follow-up email to new leads\",\n    dueAt: daysFromNow(1),\n    completedAt: null,\n    createdAt: daysFromNow(-2),\n    contactId: 101,\n    contactName: \"Sarah Johnson\",\n  },\n  {\n    id: 2,\n    body: \"Prepare slides for team training\",\n    dueAt: daysFromNow(3),\n    completedAt: null,\n    createdAt: daysFromNow(-1),\n    contactId: null,\n    contactName: null,\n  },\n  {\n    id: 3,\n    body: \"Review monthly sales report\",\n    dueAt: daysFromNow(-1),\n    completedAt: null,\n    createdAt: daysFromNow(-5),\n    contactId: 102,\n    contactName: \"Mike Chen\",\n  },\n];\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport type { TodosStateFilter } from \"@fluid-app/portal-core/widgets-api\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { PREVIEW_DATA } from \"./use-todos.preview\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\n\nexport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nexport type { TodosStateFilter } from \"@fluid-app/portal-core/widgets-api\";\n\n/**\n * Shared cache key for the todos list. Both useTodos (read) and\n * useUpdateTodo (write) use this — drift would silently break optimistic\n * updates. The `state` arg is included so the active/completed lists\n * cache independently.\n */\nexport function todosQueryKey(args: {\n  baseUrl: string | undefined;\n  isPreview: boolean;\n  state?: TodosStateFilter;\n}) {\n  return [\n    \"portal-widget-use\",\n    \"todos\",\n    args.isPreview ? \"preview\" : args.baseUrl,\n    args.state ?? \"incomplete\",\n  ] as const;\n}\n\nexport function todosQueryKeyPrefix(args: {\n  baseUrl: string | undefined;\n  isPreview: boolean;\n}) {\n  return [\n    \"portal-widget-use\",\n    \"todos\",\n    args.isPreview ? \"preview\" : args.baseUrl,\n  ] as const;\n}\n\nexport interface UseTodosOptions {\n  state?: TodosStateFilter;\n}\n\nexport function useTodos(\n  options?: UseTodosOptions,\n): UseQueryResult<Todo[], Error> {\n  const widgetsApi = useWidgetsApi();\n  const { isPreview } = useWidgetPreviewContext();\n  const { baseUrl } = useDataSourceRegistryConfig();\n  const state = options?.state;\n\n  return useQuery({\n    queryKey: todosQueryKey(\n      state !== undefined\n        ? { baseUrl, isPreview, state }\n        : { baseUrl, isPreview },\n    ),\n    queryFn: ({ signal }) =>\n      widgetsApi.fetchTodos(\n        state !== undefined ? { state } : undefined,\n        signal,\n      ),\n    enabled: !isPreview,\n    ...(isPreview &&\n      (state === undefined || state === \"incomplete\") && {\n        placeholderData: PREVIEW_DATA,\n      }),\n    ...(isPreview &&\n      state !== undefined &&\n      state !== \"incomplete\" && {\n        placeholderData: [],\n      }),\n  });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey, todosQueryKeyPrefix } from \"./use-todos\";\n\nexport interface UpdateTodoVariables {\n  id: number;\n  body?: string;\n  due_at?: string | null;\n  completed?: boolean;\n  contact_id?: number | null;\n  /**\n   * Previous contact association. Used only to invalidate the old\n   * `contactsKeys.tasks(previousContactId)` view after a successful update.\n   */\n  previousContactId?: number | null;\n}\n\nexport function useUpdateTodo() {\n  const widgetsApi = useWidgetsApi();\n  const queryClient = useQueryClient();\n  const { isPreview } = useWidgetPreviewContext();\n  const { baseUrl } = useDataSourceRegistryConfig();\n\n  const queryKey = todosQueryKey({ baseUrl, isPreview });\n\n  return useMutation({\n    mutationFn: ({\n      id,\n      body,\n      due_at,\n      completed,\n      contact_id,\n    }: UpdateTodoVariables) => {\n      // Defense in depth: ToDoWidget already disables the checkbox in\n      // preview mode, but if any other caller fires this mutation while\n      // previewing we'd PATCH demo data on the real backend. Fail closed.\n      if (isPreview) {\n        return Promise.reject(\n          new Error(\"Todo updates are disabled in preview mode\"),\n        );\n      }\n      const input: {\n        body?: string;\n        due_at?: string | null;\n        completed?: boolean;\n        contact_id?: number | null;\n      } = {};\n      if (body !== undefined) input.body = body;\n      if (due_at !== undefined) input.due_at = due_at;\n      if (completed !== undefined) input.completed = completed;\n      if (contact_id !== undefined) input.contact_id = contact_id;\n      if (Object.keys(input).length === 0) {\n        return Promise.reject(\n          new Error(\"Todo update requires at least one changed field\"),\n        );\n      }\n      return widgetsApi.updateTodo(id, input);\n    },\n    onMutate: async (variables) => {\n      // Only apply the optimistic-completion shortcut when the caller is\n      // toggling completion alone (the checkbox case). Body/due_at edits\n      // come from the dialog and fall back to invalidate-on-success.\n      const isCompletionToggle =\n        variables.completed !== undefined &&\n        variables.body === undefined &&\n        variables.due_at === undefined &&\n        variables.contact_id === undefined;\n      if (!isCompletionToggle) return { previous: undefined };\n\n      await queryClient.cancelQueries({ queryKey });\n      const previous = queryClient.getQueryData<Todo[]>(queryKey);\n      queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n        (current ?? []).map((todo) =>\n          todo.id === variables.id\n            ? {\n                ...todo,\n                completedAt: variables.completed\n                  ? new Date().toISOString()\n                  : null,\n              }\n            : todo,\n        ),\n      );\n      return { previous };\n    },\n    onSuccess: (updated, variables) => {\n      queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n        (current ?? []).map((todo) =>\n          todo.id === updated.id ? updated : todo,\n        ),\n      );\n      // Match the documented test plan: completing toasts, un-completing\n      // is silent. Body/due_at edits also toast as \"Todo updated\".\n      const isCompletionToggle =\n        variables.completed !== undefined &&\n        variables.body === undefined &&\n        variables.due_at === undefined &&\n        variables.contact_id === undefined;\n      if (isCompletionToggle) {\n        if (variables.completed) {\n          fluidToast({ title: \"Todo completed\", type: \"success\" });\n        }\n      } else {\n        fluidToast({ title: \"Todo updated\", type: \"success\" });\n      }\n      // Keep contact-scoped tasks views in sync when the todo is attached\n      // to, removed from, or moved between contacts. previousContactId is the\n      // old association and contact_id is the requested new association.\n      const contactIdsToInvalidate = new Set(\n        [variables.previousContactId, variables.contact_id].filter(\n          (contactId): contactId is number => contactId != null,\n        ),\n      );\n      for (const contactId of contactIdsToInvalidate) {\n        queryClient.invalidateQueries({\n          queryKey: contactsKeys.tasks(String(contactId)),\n        });\n      }\n    },\n    onError: (error, variables, context) => {\n      if (context?.previous) {\n        queryClient.setQueryData(queryKey, context.previous);\n      }\n      console.error(\"[portal-widgets] Failed to update todo\", {\n        error,\n        todoId: variables.id,\n        requestedContactId: variables.contact_id,\n        previousContactId: variables.previousContactId,\n      });\n      fluidToast({ title: \"Failed to update todo\", type: \"error\" });\n    },\n    onSettled: () => {\n      queryClient.invalidateQueries({\n        queryKey: todosQueryKeyPrefix({ baseUrl, isPreview }),\n      });\n    },\n  });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { todosQueryKey, todosQueryKeyPrefix } from \"./use-todos\";\n\nexport interface DeleteTodoVariables {\n  id: number;\n  /**\n   * Used to invalidate `contactsKeys.tasks(contactId)` after a successful\n   * delete so the in-contact tasks view stays in sync.\n   */\n  contactId?: number | null;\n}\n\nexport function useDeleteTodo() {\n  const widgetsApi = useWidgetsApi();\n  const queryClient = useQueryClient();\n  const { isPreview } = useWidgetPreviewContext();\n  const { baseUrl } = useDataSourceRegistryConfig();\n\n  const queryKey = todosQueryKey({ baseUrl, isPreview });\n\n  return useMutation({\n    mutationFn: ({ id }: DeleteTodoVariables) => {\n      if (isPreview) {\n        return Promise.reject(\n          new Error(\"Todo deletion is disabled in preview mode\"),\n        );\n      }\n      return widgetsApi.deleteTodo(id);\n    },\n    onMutate: async ({ id }) => {\n      await queryClient.cancelQueries({ queryKey });\n      const previous = queryClient.getQueryData<Todo[]>(queryKey);\n      queryClient.setQueryData<Todo[]>(queryKey, (current) =>\n        (current ?? []).filter((todo) => todo.id !== id),\n      );\n      return { previous };\n    },\n    onSuccess: (_result, variables) => {\n      fluidToast({ title: \"Todo deleted\", type: \"success\" });\n      if (variables.contactId != null) {\n        queryClient.invalidateQueries({\n          queryKey: contactsKeys.tasks(String(variables.contactId)),\n        });\n      }\n    },\n    onError: (_error, _variables, context) => {\n      if (context?.previous) {\n        queryClient.setQueryData(queryKey, context.previous);\n      }\n      fluidToast({ title: \"Failed to delete todo\", type: \"error\" });\n    },\n    onSettled: () => {\n      queryClient.invalidateQueries({\n        queryKey: todosQueryKeyPrefix({ baseUrl, isPreview }),\n      });\n    },\n  });\n}\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { fluidToast } from \"@fluid-app/ui-primitives\";\nimport { useWidgetsApi } from \"@fluid-app/portal-core/widgets-api-context\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useDataSourceRegistryConfig } from \"@fluid-app/portal-react/data-sources/registry-context\";\nimport { contactsKeys } from \"@fluid-app/contacts-core/query-keys\";\nimport { todosQueryKey } from \"./use-todos\";\n\nexport interface CreateTodoVariables {\n  body: string;\n  due_at: string | null;\n  contact_id: number | null;\n}\n\nexport function useCreateTodo() {\n  const widgetsApi = useWidgetsApi();\n  const queryClient = useQueryClient();\n  const { isPreview } = useWidgetPreviewContext();\n  const { baseUrl } = useDataSourceRegistryConfig();\n\n  return useMutation({\n    mutationFn: (variables: CreateTodoVariables) => {\n      if (isPreview) {\n        return Promise.reject(\n          new Error(\"Todo creation is disabled in preview mode\"),\n        );\n      }\n      return widgetsApi.createTodo(variables);\n    },\n    onSuccess: (_created, variables) => {\n      fluidToast({ title: \"Todo created\", type: \"success\" });\n      queryClient.invalidateQueries({\n        queryKey: todosQueryKey({ baseUrl, isPreview }),\n      });\n      if (variables.contact_id != null) {\n        queryClient.invalidateQueries({\n          queryKey: contactsKeys.tasks(String(variables.contact_id)),\n        });\n      }\n    },\n    onError: () => {\n      fluidToast({ title: \"Failed to create todo\", type: \"error\" });\n    },\n  });\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Check, Search, UserRound } from \"lucide-react\";\nimport {\n  Avatar,\n  AvatarFallback,\n  AvatarImage,\n  cn,\n  Input,\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n  PortalContainerProvider,\n} from \"@fluid-app/ui-primitives\";\nimport {\n  ResponsiveDialog,\n  type ResponsiveDialogPresentation,\n} from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useInfiniteContacts } from \"@fluid-app/contacts-core/hooks/use-infinite-contacts\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport type { Contact } from \"@fluid-app/contacts-core/types\";\nimport { TaskComposerForm } from \"@fluid-app/contacts-ui/portal/components/tasks/task-composer-form\";\nimport { useCreateTodo } from \"../hooks/use-create-todo\";\n\nconst SEARCH_DEBOUNCE_MS = 200;\n\nfunction getContactInitials(contact: Contact): string | undefined {\n  const firstInitial = contact.first_name?.trim().charAt(0);\n  const lastInitial = contact.last_name?.trim().charAt(0);\n  const explicitInitials = `${firstInitial ?? \"\"}${lastInitial ?? \"\"}`;\n\n  if (explicitInitials) return explicitInitials.toUpperCase();\n\n  const initials = contact.full_name\n    .trim()\n    .split(/\\s+/)\n    .slice(0, 2)\n    .map((part) => part.charAt(0))\n    .join(\"\")\n    .toUpperCase();\n\n  return initials || undefined;\n}\n\nfunction ContactBadgeAvatar({\n  contact,\n}: {\n  contact: Contact | null;\n}): React.JSX.Element {\n  const initials = contact ? getContactInitials(contact) : undefined;\n\n  return (\n    <Avatar className=\"size-4 text-current\" aria-hidden=\"true\">\n      {contact?.avatar_url ? (\n        <AvatarImage src={contact.avatar_url} alt=\"\" />\n      ) : null}\n      <AvatarFallback className=\"bg-transparent text-[0.625rem] font-semibold text-current\">\n        {initials ?? <UserRound className=\"size-3\" aria-hidden=\"true\" />}\n      </AvatarFallback>\n    </Avatar>\n  );\n}\n\nexport interface CreateTodoDialogProps {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  presentation?: ResponsiveDialogPresentation;\n}\n\nexport function CreateTodoDialog({\n  open,\n  onOpenChange,\n  presentation = \"dialog\",\n}: CreateTodoDialogProps): React.JSX.Element {\n  const [selectedContact, setSelectedContact] = useState<Contact | null>(null);\n  // Anchor nested popovers inside the dialog's themed portal subtree.\n  // ui-primitives PopoverContent reads this via usePortalContainer(), so the\n  // task date popover inherits portal theme variables instead of falling back\n  // to document.body. Dialog also applies pointer-events: none to the body\n  // while open, which kills clicks and scroll on a body-portaled popover in\n  // WebKit. See edit-bill-date-dialog for the canonical pattern.\n  const [themedPopoverContainer, setThemedPopoverContainer] =\n    useState<HTMLDivElement | null>(null);\n  const { t } = useContactsTranslation();\n\n  const createTodo = useCreateTodo();\n\n  // Reset state when the dialog closes so the next open starts fresh.\n  useEffect(() => {\n    if (!open) setSelectedContact(null);\n  }, [open]);\n\n  const handleSubmit = (values: { body: string; due_at: string | null }) => {\n    createTodo.mutate(\n      {\n        body: values.body,\n        due_at: values.due_at,\n        contact_id: selectedContact ? selectedContact.id : null,\n      },\n      {\n        onSuccess: () => onOpenChange(false),\n      },\n    );\n  };\n\n  return (\n    <ResponsiveDialog\n      open={open}\n      onOpenChange={onOpenChange}\n      title={t(\"add_todo_dialog_title\")}\n      presentation={presentation}\n      size=\"wide\"\n    >\n      <PortalContainerProvider container={themedPopoverContainer}>\n        <div className=\"flex flex-col gap-3\">\n          <TaskComposerForm\n            onSubmit={handleSubmit}\n            isSubmitting={createTodo.isPending}\n            metadataControls={\n              <ContactPicker\n                value={selectedContact}\n                onSelect={setSelectedContact}\n                onClear={() => setSelectedContact(null)}\n              />\n            }\n          />\n        </div>\n      </PortalContainerProvider>\n      {/* Portal target for the popover. It must live inside the responsive\n            surface so nested overlays inherit the active theme; otherwise it\n            renders inline next to the widget, and any transformed ancestor\n            (builder canvas zoom, etc.) would break `position: fixed`.\n            Intentionally NOT aria-hidden — the popover content portaled\n            into this container has its own listbox/option semantics that\n            screen readers need to reach. `pointer-events-none` keeps the\n            empty overlay from capturing clicks; the popover content sets\n            `pointer-events-auto` so it remains interactive. */}\n      <div\n        ref={setThemedPopoverContainer}\n        className=\"pointer-events-none fixed inset-0 z-[9998]\"\n      />\n    </ResponsiveDialog>\n  );\n}\n\nexport interface ContactPickerProps {\n  value: Contact | null;\n  onSelect: (contact: Contact) => void;\n  onClear: () => void;\n}\n\nexport function ContactPicker({\n  value,\n  onSelect,\n  onClear,\n}: ContactPickerProps): React.JSX.Element {\n  const { t } = useContactsTranslation();\n  const [open, setOpen] = useState(false);\n  const [searchInput, setSearchInput] = useState(\"\");\n  const [debouncedSearch, setDebouncedSearch] = useState(\"\");\n  const sentinelRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const handle = window.setTimeout(() => {\n      setDebouncedSearch(searchInput.trim());\n    }, SEARCH_DEBOUNCE_MS);\n    return () => window.clearTimeout(handle);\n  }, [searchInput]);\n\n  const queryParams = useMemo(\n    () => ({\n      ...(debouncedSearch ? { search_query: debouncedSearch } : {}),\n      sort_by: \"full_name\",\n      sort_direction: \"asc\",\n      per_page: 25,\n    }),\n    [debouncedSearch],\n  );\n\n  const {\n    data,\n    isLoading,\n    isError,\n    hasNextPage,\n    isFetchingNextPage,\n    fetchNextPage,\n  } = useInfiniteContacts(queryParams);\n\n  const contacts: Contact[] = useMemo(\n    () => data?.pages.flatMap((page) => page.contacts ?? []) ?? [],\n    [data],\n  );\n\n  // Auto-load the next page when the sentinel scrolls into view, so reps\n  // with many contacts can reach all of them without typing a precise\n  // search query.\n  useEffect(() => {\n    if (!open) return;\n\n    const sentinel = sentinelRef.current;\n    if (!sentinel || !hasNextPage) return;\n\n    const observer = new IntersectionObserver((entries) => {\n      const entry = entries[0];\n      if (entry?.isIntersecting && !isFetchingNextPage) {\n        fetchNextPage();\n      }\n    });\n\n    observer.observe(sentinel);\n    return () => observer.disconnect();\n  }, [open, hasNextPage, isFetchingNextPage, fetchNextPage]);\n\n  const selectedContactLabel = value ? value.full_name : t(\"select_contact\");\n\n  return (\n    <div className=\"flex min-w-0 items-center\">\n      <Popover open={open} onOpenChange={setOpen}>\n        <PopoverTrigger asChild>\n          <button\n            type=\"button\"\n            aria-label={t(\"contact_picker_trigger_aria\", {\n              selection: selectedContactLabel,\n            })}\n            aria-haspopup=\"listbox\"\n            aria-expanded={open}\n            className={cn(\n              \"flex max-w-40 min-w-0 shrink items-center gap-1 rounded-full px-3 py-1 text-left text-xs font-medium transition-colors\",\n              value\n                ? \"bg-primary text-primary-foreground\"\n                : \"bg-muted text-muted-foreground hover:bg-muted/70\",\n            )}\n          >\n            <ContactBadgeAvatar contact={value} />\n            <span className=\"min-w-0 truncate\">{selectedContactLabel}</span>\n          </button>\n        </PopoverTrigger>\n        <PopoverContent\n          align=\"start\"\n          className=\"bg-popover text-popover-foreground pointer-events-auto z-[9999] w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-md border p-0 shadow-md\"\n        >\n          <div className=\"border-border flex items-center gap-2 border-b px-3 py-2\">\n            <Search\n              className=\"text-muted-foreground size-4 shrink-0\"\n              aria-hidden=\"true\"\n            />\n            <Input\n              value={searchInput}\n              onChange={(e) => setSearchInput(e.target.value)}\n              placeholder={t(\"search_placeholder\")}\n              aria-label={t(\"search_placeholder\")}\n              className=\"h-7 border-0 px-0 shadow-none focus-visible:ring-0\"\n            />\n          </div>\n          <div className=\"max-h-64 overflow-y-auto py-1\" role=\"listbox\">\n            <button\n              type=\"button\"\n              role=\"option\"\n              aria-selected={value === null}\n              onClick={() => {\n                onClear();\n                setOpen(false);\n              }}\n              className=\"hover:bg-muted/50 flex w-full items-center justify-between gap-2 px-3 py-1.5 text-left text-sm transition-colors\"\n            >\n              <span className=\"text-muted-foreground truncate italic\">\n                {t(\"no_contact\")}\n              </span>\n              {value === null && (\n                <Check\n                  className=\"text-primary size-4 shrink-0\"\n                  aria-hidden=\"true\"\n                />\n              )}\n            </button>\n            {isLoading ? (\n              <div className=\"text-muted-foreground px-3 py-6 text-center text-xs\">\n                {t(\"loading\")}\n              </div>\n            ) : isError ? (\n              <div className=\"text-destructive px-3 py-6 text-center text-xs\">\n                {t(\"error_loading_list\")}\n              </div>\n            ) : contacts.length === 0 ? (\n              <div className=\"text-muted-foreground px-3 py-6 text-center text-xs\">\n                {debouncedSearch\n                  ? t(\"no_contacts_search\", { term: debouncedSearch })\n                  : t(\"no_contacts_yet\")}\n              </div>\n            ) : (\n              <>\n                {contacts.map((contact) => {\n                  const isSelected = value?.id === contact.id;\n                  return (\n                    <button\n                      key={contact.id}\n                      type=\"button\"\n                      role=\"option\"\n                      aria-selected={isSelected}\n                      onClick={() => {\n                        onSelect(contact);\n                        setOpen(false);\n                      }}\n                      className=\"hover:bg-muted/50 flex w-full items-center justify-between gap-2 px-3 py-1.5 text-left text-sm transition-colors\"\n                    >\n                      <span className=\"truncate\">{contact.full_name}</span>\n                      {isSelected && (\n                        <Check\n                          className=\"text-primary size-4 shrink-0\"\n                          aria-hidden=\"true\"\n                        />\n                      )}\n                    </button>\n                  );\n                })}\n                {hasNextPage && (\n                  <div ref={sentinelRef} aria-hidden=\"true\" className=\"h-4\" />\n                )}\n                {isFetchingNextPage && (\n                  <div className=\"text-muted-foreground px-3 py-2 text-center text-xs\">\n                    {t(\"loading_more\")}\n                  </div>\n                )}\n              </>\n            )}\n          </div>\n        </PopoverContent>\n      </Popover>\n    </div>\n  );\n}\n","\"use client\";\n\nimport React, { useState } from \"react\";\nimport { PortalContainerProvider } from \"@fluid-app/ui-primitives\";\nimport {\n  ResponsiveDialog,\n  type ResponsiveDialogPresentation,\n} from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport { TaskComposerForm } from \"@fluid-app/contacts-ui/portal/components/tasks/task-composer-form\";\nimport type { Contact } from \"@fluid-app/contacts-core/types\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { ContactPicker } from \"./CreateTodoDialog\";\n\nexport interface EditTodoDialogProps {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  todo: Todo | null;\n  presentation?: ResponsiveDialogPresentation;\n}\n\nfunction getTodoContact(todo: Todo): Contact | null {\n  if (todo.contactId == null) return null;\n\n  return {\n    id: todo.contactId,\n    full_name: todo.contactName ?? String(todo.contactId),\n    status: null,\n    metadata: {},\n  };\n}\n\nexport function EditTodoDialog({\n  open,\n  onOpenChange,\n  todo,\n  presentation = \"dialog\",\n}: EditTodoDialogProps): React.JSX.Element | null {\n  const { t } = useContactsTranslation();\n  const updateTodo = useUpdateTodo();\n  const [selectedContactOverride, setSelectedContactOverride] = useState<{\n    todoId: number;\n    contact: Contact | null;\n  } | null>(null);\n  // Anchor nested popovers inside the dialog's themed portal subtree.\n  // ui-primitives PopoverContent reads this via usePortalContainer(), so the\n  // task date popover inherits portal theme variables instead of falling back\n  // to document.body.\n  const [themedPopoverContainer, setThemedPopoverContainer] =\n    useState<HTMLDivElement | null>(null);\n\n  if (!todo) return null;\n\n  const selectedContact =\n    selectedContactOverride?.todoId === todo.id\n      ? selectedContactOverride.contact\n      : getTodoContact(todo);\n\n  const handleOpenChange = (nextOpen: boolean) => {\n    if (!nextOpen) setSelectedContactOverride(null);\n    onOpenChange(nextOpen);\n  };\n\n  const handleSelectContact = (contact: Contact) => {\n    setSelectedContactOverride({ todoId: todo.id, contact });\n  };\n\n  const handleClearContact = () => {\n    setSelectedContactOverride({ todoId: todo.id, contact: null });\n  };\n\n  const handleSubmit = (values: { body: string; due_at: string | null }) => {\n    updateTodo.mutate(\n      {\n        id: todo.id,\n        body: values.body,\n        due_at: values.due_at,\n        contact_id: selectedContact ? selectedContact.id : null,\n        previousContactId: todo.contactId,\n      },\n      {\n        onSuccess: () => handleOpenChange(false),\n      },\n    );\n  };\n\n  return (\n    <ResponsiveDialog\n      open={open}\n      onOpenChange={handleOpenChange}\n      title={t(\"edit_todo\")}\n      presentation={presentation}\n      size=\"wide\"\n    >\n      <PortalContainerProvider container={themedPopoverContainer}>\n        <TaskComposerForm\n          key={todo.id}\n          initialBody={todo.body}\n          initialDueAt={todo.dueAt}\n          onSubmit={handleSubmit}\n          isSubmitting={updateTodo.isPending}\n          submitLabel={t(\"save_changes\")}\n          submittingLabel={t(\"saving\")}\n          metadataControls={\n            <ContactPicker\n              value={selectedContact}\n              onSelect={handleSelectContact}\n              onClear={handleClearContact}\n            />\n          }\n        />\n      </PortalContainerProvider>\n      <div\n        ref={setThemedPopoverContainer}\n        className=\"pointer-events-none fixed inset-0 z-[9998]\"\n      />\n    </ResponsiveDialog>\n  );\n}\n","import { useState, type ComponentProps } from \"react\";\nimport type React from \"react\";\nimport type {\n  BackgroundValue,\n  BorderRadiusOptions,\n  BorderWidthOptions,\n  ColorOptions,\n  FontSizeOptions,\n  PaddingOptions,\n} from \"@fluid-app/portal-core/types\";\nimport type { WidgetPropertySchema } from \"@fluid-app/portal-core/registries\";\nimport type { Todo } from \"@fluid-app/portal-core/widgets-api-types\";\nimport {\n  Button,\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@fluid-app/ui-primitives\";\nimport { useContactsTranslation } from \"@fluid-app/contacts-core/translation-api-context\";\nimport {\n  getBorderRadiusField,\n  getBorderWidthField,\n  getBorderColorField,\n  borderWidthClasses,\n  borderColorClasses,\n  getColorField,\n  getFontSizeField,\n  getPaddingField,\n} from \"../core/fields\";\nimport { ChevronDown, ChevronUp, EllipsisVertical, Plus } from \"lucide-react\";\nimport { useWidgetPreviewContext } from \"@fluid-app/portal-react/data-sources/preview-context\";\nimport { useIsMobile } from \"@fluid-app/portal-react/shell/use-mobile\";\nimport { MobileActionSheet } from \"@fluid-app/ui-components/components/MobileActionSheet\";\nimport type { ResponsiveDialogPresentation } from \"@fluid-app/ui-components/components/ResponsiveDialog\";\nimport { parseTaskBody } from \"@fluid-app/contacts-core/parse-task-body\";\nimport {\n  ALL_DAY_PATTERN,\n  LOCAL_TIMED_DUE_AT_PATTERN,\n} from \"@fluid-app/contacts-ui/portal/utils/format-date\";\nimport { useTodos } from \"../hooks/use-todos\";\nimport { useUpdateTodo } from \"../hooks/use-update-todo\";\nimport { useDeleteTodo } from \"../hooks/use-delete-todo\";\nimport { ErrorState } from \"../components/error-state\";\nimport { WidgetLoadingSkeleton } from \"../components/WidgetLoadingSkeleton\";\nimport { CreateTodoDialog } from \"./CreateTodoDialog\";\nimport { EditTodoDialog } from \"./EditTodoDialog\";\n\nconst pluralRulesCache = new Map<string, Intl.PluralRules>();\n\nfunction usesSingularPluralForm(locale: string, count: number): boolean {\n  const tag = locale.replace(/_/g, \"-\");\n  let rules = pluralRulesCache.get(tag);\n  if (!rules) {\n    rules = new Intl.PluralRules(tag);\n    pluralRulesCache.set(tag, rules);\n  }\n\n  return rules.select(count) === \"one\";\n}\n\ntype ToDoWidgetProps = ComponentProps<\"div\"> & {\n  // Title\n  titleEnabled?: boolean;\n  titleText?: string;\n  titleFontSize?: FontSizeOptions;\n  titleColor?: ColorOptions;\n\n  // Styling\n  background?: BackgroundValue;\n  textColor?: ColorOptions;\n  accentColor?: ColorOptions;\n  padding?: PaddingOptions;\n  borderRadius?: BorderRadiusOptions;\n  borderWidth?: BorderWidthOptions;\n  borderColor?: ColorOptions;\n\n  // Content\n  maxItems?: number;\n};\n\n/**\n * Format a todo due_at ISO string as a short date + time (e.g. \"Jun 1, 9:00 AM\").\n * Mirrors the mobile widget formatting.\n *\n * All-day / date-only values (bare YYYY-MM-DD, plus Z-designated midnight ISO\n * strings) are formatted in UTC so the displayed calendar date matches the\n * source prefix without shifting. Non-Z timed values stay on the timed path;\n * offset values are formatted as instants in the viewer's local timezone.\n */\nfunction formatDueAt(dateStr: string, locale?: string): string {\n  const normalizedLocale = locale?.replace(/_/g, \"-\") ?? \"en-US\";\n\n  // Detect date-only / all-day: bare YYYY-MM-DD or Z-designated ISO whose time\n  // is exactly 00:00 (including fractional seconds). Non-Z timed strings are\n  // treated as timed values rather than all-day, including legacy/no-offset and\n  // offset midnight values.\n  if (\n    ALL_DAY_PATTERN.test(dateStr) &&\n    !LOCAL_TIMED_DUE_AT_PATTERN.test(dateStr)\n  ) {\n    const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(dateStr);\n    if (match?.[1] && match[2] && match[3]) {\n      const date = new Date(\n        Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])),\n      );\n      return date.toLocaleDateString(normalizedLocale, {\n        month: \"short\",\n        day: \"numeric\",\n        timeZone: \"UTC\",\n      });\n    }\n  }\n\n  // Genuinely timed value — use a single formatter so ordering and\n  // separators are locale-native.\n  const date = new Date(dateStr);\n  return date.toLocaleString(normalizedLocale, {\n    month: \"short\",\n    day: \"numeric\",\n    hour: \"numeric\",\n    minute: \"2-digit\",\n  });\n}\n\nexport function ToDoWidget({\n  // Title defaults\n  titleEnabled = true,\n  titleText = \"To-Do\",\n  titleFontSize = \"lg\",\n  titleColor = \"foreground\",\n\n  // Styling defaults\n  background = {\n    type: \"solid\",\n    color: \"background\",\n  },\n  textColor = \"foreground\",\n  accentColor = \"primary\",\n  padding = 4,\n  borderRadius = \"md\",\n  borderWidth = \"none\",\n  borderColor = \"muted\",\n\n  // Content defaults\n  maxItems = 5,\n\n  className,\n  ...props\n}: ToDoWidgetProps): React.JSX.Element {\n  const { t, locale } = useContactsTranslation();\n  const backgroundColor = background.color || \"background\";\n  const backgroundImage =\n    (background.resource?.image_url || background.resource?.imageUrl) &&\n    background.type === \"image\"\n      ? `url(${background.resource.image_url || background.resource.imageUrl})`\n      : \"none\";\n  const { data: todos = [], isLoading, isError } = useTodos();\n  const { data: completedTodos = [], isLoading: isCompletedLoading } = useTodos(\n    { state: \"completed\" },\n  );\n  const updateTodo = useUpdateTodo();\n  const deleteTodo = useDeleteTodo();\n  const { isPreview } = useWidgetPreviewContext();\n  const isMobile = useIsMobile();\n  const [pendingIds, setPendingIds] = useState<Set<number>>(new Set());\n  const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);\n  const [createPresentation, setCreatePresentation] =\n    useState<ResponsiveDialogPresentation>(\"dialog\");\n  const [editingTodo, setEditingTodo] = useState<Todo | null>(null);\n  const [editPresentation, setEditPresentation] =\n    useState<ResponsiveDialogPresentation>(\"dialog\");\n  const [actionTodo, setActionTodo] = useState<Todo | null>(null);\n  const [isDoneExpanded, setIsDoneExpanded] = useState(false);\n\n  const toggleTodo = (todo: Todo, completed: boolean) => {\n    if (isPreview || pendingIds.has(todo.id)) return;\n    setPendingIds((prev) => new Set(prev).add(todo.id));\n    updateTodo.mutate(\n      { id: todo.id, completed, previousContactId: todo.contactId },\n      {\n        onSettled: () =>\n          setPendingIds((prev) => {\n            const next = new Set(prev);\n            next.delete(todo.id);\n            return next;\n          }),\n      },\n    );\n  };\n\n  const activeTodos = todos.filter((todo) => !todo.completedAt);\n  const todosToShow = activeTodos.slice(0, maxItems);\n  const remainingCount = activeTodos.length - todosToShow.length;\n  const isEmpty = activeTodos.length === 0;\n  const activeTaskLabel = usesSingularPluralForm(locale, activeTodos.length)\n    ? t(\"todo_open_tasks_one\", { count: activeTodos.length })\n    : t(\"todo_open_tasks_other\", { count: activeTodos.length });\n\n  // Group todos by contactId so same-named contacts stay separate.\n  const groupedTodos = todosToShow.reduce<\n    {\n      contactId: number | null;\n      contactName: string | null;\n      todos: typeof todosToShow;\n    }[]\n  >((groups, todo) => {\n    const existing = groups.find((g) => g.contactId === todo.contactId);\n    if (existing) {\n      existing.todos.push(todo);\n    } else {\n      groups.push({\n        contactId: todo.contactId,\n        contactName: todo.contactName,\n        todos: [todo],\n      });\n    }\n    return groups;\n  }, []);\n\n  const doneCount = completedTodos.length;\n\n  const handleRowClick = (todo: Todo) => {\n    if (isPreview) return;\n    setEditPresentation(isMobile ? \"bottom-sheet\" : \"dialog\");\n    setEditingTodo(todo);\n  };\n\n  const handleCreateClick = () => {\n    setCreatePresentation(isMobile ? \"bottom-sheet\" : \"dialog\");\n    setIsCreateDialogOpen(true);\n  };\n\n  const handleDelete = (todo: Todo) => {\n    if (isPreview) return;\n    deleteTodo.mutate({ id: todo.id, contactId: todo.contactId });\n  };\n\n  return (\n    <div\n      className={`overflow-hidden rounded-${borderRadius} ${borderWidthClasses[borderWidth]} ${borderWidth !== \"none\" ? borderColorClasses[borderColor] : \"\"} bg-${backgroundColor} text-${textColor} p-${padding} ${className ?? \"\"}`}\n      style={{ backgroundImage }}\n      {...props}\n    >\n      {/* Header */}\n      <div className=\"mb-4 flex items-start justify-between gap-4\">\n        <div className=\"flex min-w-0 flex-col gap-1\">\n          {titleEnabled && titleText && (\n            <h2\n              className={`text-${titleFontSize} font-header leading-tight font-bold text-${titleColor}`}\n            >\n              {titleText}\n            </h2>\n          )}\n          {!isEmpty && !isLoading && (\n            <span\n              className={`w-fit rounded-full bg-${accentColor}/10 px-2.5 py-1 text-[11px] leading-none font-semibold text-${accentColor}`}\n            >\n              {activeTaskLabel}\n            </span>\n          )}\n        </div>\n        {!isPreview && !isEmpty && !isLoading && !isError && (\n          <button\n            type=\"button\"\n            onClick={handleCreateClick}\n            className={`inline-flex shrink-0 items-center gap-1.5 rounded-full bg-${accentColor} px-3.5 py-2 text-xs font-semibold text-${accentColor}-foreground shadow-sm transition-colors hover:bg-${accentColor}/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-${accentColor}/40`}\n          >\n            <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n            {t(\"add_todo\")}\n          </button>\n        )}\n      </div>\n\n      {/* Loading state */}\n      {isLoading ? (\n        <WidgetLoadingSkeleton minHeight={120} rows={3} />\n      ) : isError ? (\n        /* Error state */\n        <ErrorState />\n      ) : isEmpty ? (\n        /* Empty state */\n        <div className=\"flex flex-col items-center justify-center gap-3 py-8\">\n          <p className={`text-center text-${textColor}/60`}>\n            {t(\"todo_empty\")}\n          </p>\n          {!isPreview && (\n            <button\n              type=\"button\"\n              onClick={handleCreateClick}\n              className={`inline-flex items-center gap-1.5 rounded-full bg-${accentColor} text-${accentColor}-foreground hover:bg-${accentColor}/90 px-4 py-1.5 text-xs font-semibold transition-colors`}\n            >\n              <Plus className=\"size-3.5\" aria-hidden=\"true\" />\n              {t(\"add_todo\")}\n            </button>\n          )}\n        </div>\n      ) : (\n        /* Todo List */\n        <>\n          <div className=\"flex flex-col gap-4\">\n            {groupedTodos.map((group) => (\n              <div\n                key={group.contactId ?? \"__unassigned__\"}\n                className=\"flex flex-col\"\n              >\n                {group.contactName && (\n                  <div\n                    className={`mb-2 text-xs leading-none font-semibold tracking-wide uppercase text-${textColor}/60`}\n                  >\n                    {group.contactName}\n                  </div>\n                )}\n                {group.todos.map((todo, index) => {\n                  const showBottomBorder = index !== group.todos.length - 1;\n                  return (\n                    <div\n                      key={todo.id}\n                      className={`group/row grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 py-2.5 ${\n                        showBottomBorder\n                          ? `border-b border-${textColor}/10`\n                          : \"\"\n                      }`}\n                    >\n                      <input\n                        type=\"checkbox\"\n                        className={`h-5 w-5 rounded-full border-2 border-${textColor}/30 bg-transparent not-disabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-60`}\n                        checked={!!todo.completedAt}\n                        disabled={isPreview || pendingIds.has(todo.id)}\n                        onChange={(event) =>\n                          toggleTodo(todo, event.target.checked)\n                        }\n                        onClick={(event) => event.stopPropagation()}\n                        aria-label={t(\"mark_as_completed\")}\n                      />\n                      <button\n                        type=\"button\"\n                        onClick={() => handleRowClick(todo)}\n                        disabled={isPreview}\n                        aria-label={t(\"edit_todo\")}\n                        className=\"flex min-w-0 flex-col items-start gap-1 text-left not-disabled:cursor-pointer\"\n                      >\n                        <span className=\"line-clamp-1 text-base leading-snug font-medium\">\n                          {parseTaskBody(todo.body).title}\n                        </span>\n                        {todo.dueAt && (\n                          <span\n                            className={`text-sm leading-none text-${textColor}/60`}\n                          >\n                            {formatDueAt(todo.dueAt, locale)}\n                          </span>\n                        )}\n                      </button>\n                      {!isPreview &&\n                        (isMobile ? (\n                          <Button\n                            variant=\"ghost\"\n                            size=\"icon-xs\"\n                            onClick={(event) => {\n                              event.stopPropagation();\n                              setActionTodo(todo);\n                            }}\n                            aria-label={t(\"task_actions\")}\n                            className={`text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`}\n                          >\n                            <EllipsisVertical className=\"size-4\" />\n                          </Button>\n                        ) : (\n                          <DropdownMenu>\n                            <DropdownMenuTrigger asChild>\n                              <Button\n                                variant=\"ghost\"\n                                size=\"icon-xs\"\n                                onClick={(event) => event.stopPropagation()}\n                                aria-label={t(\"task_actions\")}\n                                className={`text-${textColor}/55 opacity-70 transition-opacity hover:bg-${textColor}/5 group-hover/row:opacity-100 focus-visible:opacity-100`}\n                              >\n                                <EllipsisVertical className=\"size-4\" />\n                              </Button>\n                            </DropdownMenuTrigger>\n                            <DropdownMenuContent align=\"end\">\n                              <DropdownMenuItem\n                                onClick={(event) => {\n                                  event.stopPropagation();\n                                  handleRowClick(todo);\n                                }}\n                              >\n                                {t(\"edit_todo\")}\n                              </DropdownMenuItem>\n                              <DropdownMenuItem\n                                className=\"text-destructive\"\n                                onClick={(event) => {\n                                  event.stopPropagation();\n                                  handleDelete(todo);\n                                }}\n                              >\n                                {t(\"delete_todo\")}\n                              </DropdownMenuItem>\n                            </DropdownMenuContent>\n                          </DropdownMenu>\n                        ))}\n                    </div>\n                  );\n                })}\n              </div>\n            ))}\n          </div>\n\n          {/* Footer */}\n          <div className=\"mt-1 flex items-center justify-between\">\n            {remainingCount > 0 && (\n              <span className={`text-sm text-${textColor}/50 underline`}>\n                {usesSingularPluralForm(locale, remainingCount)\n                  ? t(\"todo_more_tasks_one\", { count: remainingCount })\n                  : t(\"todo_more_tasks_other\", { count: remainingCount })}\n              </span>\n            )}\n          </div>\n        </>\n      )}\n\n      {/* Done section */}\n      {!isPreview &&\n        !isLoading &&\n        !isError &&\n        !isCompletedLoading &&\n        doneCount > 0 && (\n          <div className={`mt-4 border-t border-${textColor}/10 pt-3`}>\n            <button\n              type=\"button\"\n              onClick={() => setIsDoneExpanded((v) => !v)}\n              className={`flex w-full items-center justify-between gap-2 rounded-md py-1.5 text-sm font-medium text-${textColor}/60 hover:bg-${textColor}/5`}\n              aria-expanded={isDoneExpanded}\n            >\n              <span>\n                {isDoneExpanded\n                  ? t(\"hide_done\")\n                  : t(\"view_done\", { count: doneCount })}\n              </span>\n              {isDoneExpanded ? (\n                <ChevronUp className=\"size-4\" aria-hidden=\"true\" />\n              ) : (\n                <ChevronDown className=\"size-4\" aria-hidden=\"true\" />\n              )}\n            </button>\n            {isDoneExpanded && (\n              <div className=\"mt-1 flex flex-col\">\n                {completedTodos.map((todo, index) => (\n                  <div\n                    key={todo.id}\n                    className={`group/row flex items-center gap-3 py-2 ${\n                      index !== completedTodos.length - 1\n                        ? `border-b border-${textColor}/10`\n                        : \"\"\n                    }`}\n                  >\n                    <input\n                      type=\"checkbox\"\n                      className={`h-5 w-5 rounded-full border-2 border-${textColor}/30 bg-transparent not-disabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-60`}\n                      checked\n                      disabled={pendingIds.has(todo.id)}\n                      onChange={() => toggleTodo(todo, false)}\n                      onClick={(event) => event.stopPropagation()}\n                      aria-label={t(\"mark_as_open\")}\n                    />\n                    <button\n                      type=\"button\"\n                      onClick={() => handleRowClick(todo)}\n                      aria-label={t(\"edit_todo\")}\n                      className={`flex min-w-0 flex-1 cursor-pointer flex-col items-start gap-0.5 text-left text-${textColor}/60 line-through`}\n                    >\n                      <span className=\"line-clamp-1 text-sm\">\n                        {parseTaskBody(todo.body).title}\n                      </span>\n                      {todo.dueAt && (\n                        <span className=\"text-xs\">\n                          {formatDueAt(todo.dueAt, locale)}\n                        </span>\n                      )}\n                    </button>\n                    {isMobile ? (\n                      <Button\n                        variant=\"ghost\"\n                        size=\"icon-xs\"\n                        onClick={(event) => {\n                          event.stopPropagation();\n                          setActionTodo(todo);\n                        }}\n                        aria-label={t(\"task_actions\")}\n                        className=\"opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100\"\n                      >\n                        <EllipsisVertical className=\"size-4\" />\n                      </Button>\n                    ) : (\n                      <DropdownMenu>\n                        <DropdownMenuTrigger asChild>\n                          <Button\n                            variant=\"ghost\"\n                            size=\"icon-xs\"\n                            onClick={(event) => event.stopPropagation()}\n                            aria-label={t(\"task_actions\")}\n                            className=\"opacity-60 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100\"\n                          >\n                            <EllipsisVertical className=\"size-4\" />\n                          </Button>\n                        </DropdownMenuTrigger>\n                        <DropdownMenuContent align=\"end\">\n                          <DropdownMenuItem\n                            onClick={(event) => {\n                              event.stopPropagation();\n                              handleRowClick(todo);\n                            }}\n                          >\n                            {t(\"edit_todo\")}\n                          </DropdownMenuItem>\n                          <DropdownMenuItem\n                            className=\"text-destructive\"\n                            onClick={(event) => {\n                              event.stopPropagation();\n                              handleDelete(todo);\n                            }}\n                          >\n                            {t(\"delete_todo\")}\n                          </DropdownMenuItem>\n                        </DropdownMenuContent>\n                      </DropdownMenu>\n                    )}\n                  </div>\n                ))}\n              </div>\n            )}\n          </div>\n        )}\n\n      {!isPreview && (\n        <CreateTodoDialog\n          open={isCreateDialogOpen}\n          onOpenChange={setIsCreateDialogOpen}\n          presentation={createPresentation}\n        />\n      )}\n      {!isPreview && (\n        <EditTodoDialog\n          open={editingTodo !== null}\n          onOpenChange={(open) => {\n            if (!open) setEditingTodo(null);\n          }}\n          todo={editingTodo}\n          presentation={editPresentation}\n        />\n      )}\n      {!isPreview && (\n        <MobileActionSheet\n          open={actionTodo !== null}\n          onOpenChange={(open) => {\n            if (!open) setActionTodo(null);\n          }}\n          title={t(\"task_actions\")}\n          actions={[\n            {\n              id: \"edit\",\n              label: t(\"edit_todo\"),\n              onSelect: () => {\n                if (actionTodo) handleRowClick(actionTodo);\n              },\n            },\n            {\n              id: \"delete\",\n              label: t(\"delete_todo\"),\n              variant: \"destructive\",\n              onSelect: () => {\n                if (actionTodo) handleDelete(actionTodo);\n              },\n            },\n          ]}\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: { type: \"solid\", color: \"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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M;GACN,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB;EACD,iBAAiB;GACf,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EACF,cAAc;GACZ,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;GACd,KAAK;GACL,OAAO;GACP,qBAAqB;GACtB,CAAC;EAGF;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACP,aAAa;GACb,cAAc;IAAE,MAAM;IAAS,OAAO;IAAc;GACpD,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"}