@fayz-ai/plugin-tasks 0.1.7 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +2 -0
  2. package/dist/components/TaskDetail.d.ts.map +1 -1
  3. package/dist/components/TasksDrawer.d.ts +1 -0
  4. package/dist/components/TasksDrawer.d.ts.map +1 -1
  5. package/dist/components/TasksGeneralSettings.d.ts.map +1 -1
  6. package/dist/components/TasksPanel.d.ts +7 -0
  7. package/dist/components/TasksPanel.d.ts.map +1 -0
  8. package/dist/components/TasksTopbarButton.d.ts.map +1 -1
  9. package/dist/data/supabase.d.ts.map +1 -1
  10. package/dist/data/tables.d.ts +5 -0
  11. package/dist/data/tables.d.ts.map +1 -0
  12. package/dist/index.cjs +317 -162
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +320 -165
  16. package/dist/index.js.map +1 -1
  17. package/dist/locales/en.d.ts.map +1 -1
  18. package/dist/locales/pt-BR.d.ts.map +1 -1
  19. package/dist/migrations/index.d.ts +7 -0
  20. package/dist/migrations/index.d.ts.map +1 -0
  21. package/dist/registries.d.ts.map +1 -1
  22. package/package.json +10 -4
  23. package/src/components/TaskDetail.tsx +71 -46
  24. package/src/components/TasksDrawer.tsx +5 -67
  25. package/src/components/TasksGeneralSettings.tsx +14 -2
  26. package/src/components/TasksPanel.tsx +94 -0
  27. package/src/components/TasksTopbarButton.tsx +49 -4
  28. package/src/data/capability.test.ts +1 -1
  29. package/src/data/supabase.ts +11 -10
  30. package/src/data/tables.ts +6 -0
  31. package/src/index.ts +41 -56
  32. package/src/locales/en.ts +2 -0
  33. package/src/locales/pt-BR.ts +2 -0
  34. package/src/migrations/000_plg_rename.sql +12 -0
  35. package/src/migrations/001_tasks_base.sql +34 -34
  36. package/src/migrations/index.ts +89 -0
  37. package/src/registries.ts +2 -1
package/dist/index.cjs CHANGED
@@ -152,6 +152,12 @@ function createMockTasksProvider() {
152
152
  };
153
153
  }
154
154
 
155
+ // src/data/tables.ts
156
+ var T = {
157
+ tasks: "plg_tasks_tasks",
158
+ labels: "plg_tasks_labels"
159
+ };
160
+
155
161
  // src/data/supabase.ts
156
162
  function getTenantId() {
157
163
  return core.getActiveTenantId();
@@ -178,7 +184,7 @@ function createSupabaseTasksProvider() {
178
184
  async getTasks(query) {
179
185
  const tenantId = getTenantId();
180
186
  if (!tenantId) return [];
181
- let q = getClient().from("tsk_tasks").select("*").eq("tenant_id", tenantId);
187
+ let q = getClient().from(T.tasks).select("*").eq("tenant_id", tenantId);
182
188
  if (query.parentId === null) {
183
189
  q = q.is("parent_id", null);
184
190
  } else if (query.parentId) {
@@ -206,19 +212,19 @@ function createSupabaseTasksProvider() {
206
212
  return (data ?? []).map(toTask);
207
213
  },
208
214
  async getTaskById(id) {
209
- const { data } = await getClient().from("tsk_tasks").select("*").eq("id", id).maybeSingle();
215
+ const { data } = await getClient().from(T.tasks).select("*").eq("id", id).maybeSingle();
210
216
  return data ? toTask(data) : null;
211
217
  },
212
218
  async getSubtasks(parentId) {
213
- const { data } = await getClient().from("tsk_tasks").select("*").eq("parent_id", parentId).order("position", { ascending: true });
219
+ const { data } = await getClient().from(T.tasks).select("*").eq("parent_id", parentId).order("position", { ascending: true });
214
220
  return (data ?? []).map(toTask);
215
221
  },
216
222
  async createTask(input) {
217
223
  const tenantId = getTenantId();
218
224
  if (!tenantId) throw new Error("No active tenant");
219
- const { data: maxData } = await getClient().from("tsk_tasks").select("position").eq("tenant_id", tenantId).eq("status", input.status ?? "todo").is("parent_id", input.parentId ?? null).order("position", { ascending: false }).limit(1);
225
+ const { data: maxData } = await getClient().from(T.tasks).select("position").eq("tenant_id", tenantId).eq("status", input.status ?? "todo").is("parent_id", input.parentId ?? null).order("position", { ascending: false }).limit(1);
220
226
  const maxPos = maxData?.[0]?.position ?? -1;
221
- const { data, error } = await getClient().from("tsk_tasks").insert({
227
+ const { data, error } = await getClient().from(T.tasks).insert({
222
228
  tenant_id: tenantId,
223
229
  title: input.title,
224
230
  description: input.description ?? null,
@@ -243,26 +249,26 @@ function createSupabaseTasksProvider() {
243
249
  if (input.assignedToId !== void 0) payload.assigned_to_id = input.assignedToId;
244
250
  if (input.labels !== void 0) payload.labels = input.labels;
245
251
  if (input.position !== void 0) payload.position = input.position;
246
- const { data, error } = await getClient().from("tsk_tasks").update(payload).eq("id", id).select("*").single();
252
+ const { data, error } = await getClient().from(T.tasks).update(payload).eq("id", id).select("*").single();
247
253
  if (error) throw error;
248
254
  return toTask(data);
249
255
  },
250
256
  async deleteTask(id) {
251
- await getClient().from("tsk_tasks").delete().eq("id", id);
257
+ await getClient().from(T.tasks).delete().eq("id", id);
252
258
  },
253
259
  async reorderTask(id, newPosition, status) {
254
- await getClient().from("tsk_tasks").update({ position: newPosition, status, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
260
+ await getClient().from(T.tasks).update({ position: newPosition, status, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
255
261
  },
256
262
  async getLabels() {
257
263
  const tenantId = getTenantId();
258
264
  if (!tenantId) return [];
259
- const { data } = await getClient().from("tsk_labels").select("*").eq("tenant_id", tenantId).eq("is_active", true).order("name");
265
+ const { data } = await getClient().from(T.labels).select("*").eq("tenant_id", tenantId).eq("is_active", true).order("name");
260
266
  return (data ?? []).map((r) => snakeToCamel(r));
261
267
  },
262
268
  async getSummary() {
263
269
  const tenantId = getTenantId();
264
270
  if (!tenantId) return { total: 0, todo: 0, inProgress: 0, done: 0, overdue: 0, dueToday: 0, highPriority: 0 };
265
- const { data } = await getClient().from("tsk_tasks").select("status, priority, due_date").eq("tenant_id", tenantId).is("parent_id", null);
271
+ const { data } = await getClient().from(T.tasks).select("status, priority, due_date").eq("tenant_id", tenantId).is("parent_id", null);
266
272
  const tasks = data ?? [];
267
273
  const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
268
274
  return {
@@ -400,6 +406,8 @@ var en = {
400
406
  "tasks.detail.delete": "Delete Task",
401
407
  "tasks.detail.deleteConfirm": "Are you sure you want to delete this task?",
402
408
  "tasks.detail.created": "Created",
409
+ "tasks.detail.chatWithAi": "Chat with AI",
410
+ "tasks.detail.chatWithAi.prompt": "Take over this task and help me resolve it now, using your tools when needed:",
403
411
  "tasks.detail.updated": "Updated",
404
412
  "tasks.detail.addSubtask": "Add subtask...",
405
413
  "tasks.detail.back": "Back to list",
@@ -442,6 +450,8 @@ var ptBR = {
442
450
  "tasks.detail.delete": "Excluir Tarefa",
443
451
  "tasks.detail.deleteConfirm": "Tem certeza que deseja excluir esta tarefa?",
444
452
  "tasks.detail.created": "Criada em",
453
+ "tasks.detail.chatWithAi": "Conversar com IA",
454
+ "tasks.detail.chatWithAi.prompt": "Assuma esta tarefa e me ajude a resolver agora, usando suas ferramentas quando precisar:",
445
455
  "tasks.detail.updated": "Atualizada em",
446
456
  "tasks.detail.addSubtask": "Adicionar subtarefa...",
447
457
  "tasks.detail.back": "Voltar \xE0 lista",
@@ -473,7 +483,7 @@ var taskLabelEntity = {
473
483
  { key: "color", label: "Color", type: "text", showInTable: true, defaultValue: "#6366f1" },
474
484
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
475
485
  ],
476
- data: { table: "tsk_labels", tenantScoped: true }
486
+ data: { table: T.labels, tenantScoped: true }
477
487
  };
478
488
  var tasksRegistries = [
479
489
  {
@@ -490,8 +500,12 @@ var tasksRegistries = [
490
500
  ]
491
501
  }
492
502
  ];
503
+ var TASKS_DEFAULTS = {
504
+ defaultPriority: "medium"
505
+ };
493
506
  function TasksGeneralSettings() {
494
507
  const t = core.useTranslation();
508
+ const s = saas.useTenantPluginSettings("tasks", TASKS_DEFAULTS);
495
509
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
496
510
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
497
511
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold mb-1", children: t("tasks.settings.general") }),
@@ -499,12 +513,20 @@ function TasksGeneralSettings() {
499
513
  ] }),
500
514
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
501
515
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: t("tasks.settings.defaultPriority") }),
502
- /* @__PURE__ */ jsxRuntime.jsxs("select", { className: "w-full max-w-xs rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm", children: [
503
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "low", children: t("tasks.priority.low") }),
504
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "medium", selected: true, children: t("tasks.priority.medium") }),
505
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "high", children: t("tasks.priority.high") }),
506
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "urgent", children: t("tasks.priority.urgent") })
507
- ] })
516
+ /* @__PURE__ */ jsxRuntime.jsxs(
517
+ "select",
518
+ {
519
+ className: "w-full max-w-xs rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm",
520
+ value: s.get("defaultPriority"),
521
+ onChange: (e) => s.set("defaultPriority", e.target.value),
522
+ children: [
523
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "low", children: t("tasks.priority.low") }),
524
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "medium", children: t("tasks.priority.medium") }),
525
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "high", children: t("tasks.priority.high") }),
526
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "urgent", children: t("tasks.priority.urgent") })
527
+ ]
528
+ }
529
+ )
508
530
  ] }) })
509
531
  ] });
510
532
  }
@@ -724,8 +746,10 @@ var STATUS_DOT = {
724
746
  done: "bg-success",
725
747
  cancelled: "bg-muted-foreground/60"
726
748
  };
727
- function formatDate(iso) {
728
- return new Date(iso).toLocaleDateString(void 0, { month: "short", day: "numeric" });
749
+ function formatDateTime(iso) {
750
+ const d = new Date(iso);
751
+ const sameDay = d.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
752
+ return sameDay ? d.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" }) : d.toLocaleDateString(void 0, { day: "2-digit", month: "2-digit", year: "2-digit" });
729
753
  }
730
754
  function TaskDetail({ taskId }) {
731
755
  const t = core.useTranslation();
@@ -734,6 +758,7 @@ function TaskDetail({ taskId }) {
734
758
  const updateTask = useTasksStore((s) => s.updateTask);
735
759
  const deleteTask = useTasksStore((s) => s.deleteTask);
736
760
  const task = tasks.find((tt) => tt.id === taskId);
761
+ const delegate = saas.useDelegateToAssistant();
737
762
  const [title, setTitle] = React4.useState("");
738
763
  const [description, setDescription] = React4.useState("");
739
764
  const [subtasks, setSubtasks] = React4.useState([]);
@@ -767,17 +792,36 @@ function TaskDetail({ taskId }) {
767
792
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => selectTask(null), className: "mt-2 text-xs text-primary hover:underline", children: t("tasks.detail.back") })
768
793
  ] });
769
794
  }
770
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
771
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
772
- /* @__PURE__ */ jsxRuntime.jsx(
773
- "button",
774
- {
775
- type: "button",
776
- onClick: () => selectTask(null),
777
- className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
778
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-4 w-4" })
779
- }
780
- ),
795
+ return (
796
+ // Same frame as the Notes editor: header row with back + icon actions,
797
+ // full-bleed px-4 content, sectioned by hairlines, thin timestamp footer.
798
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-full flex-col", children: [
799
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 border-b border-border/40 px-2 py-1.5", children: [
800
+ /* @__PURE__ */ jsxRuntime.jsxs(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ onClick: () => selectTask(null),
805
+ className: "inline-flex items-center gap-1 rounded-lg px-1.5 py-1 text-[11.5px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
806
+ children: [
807
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-3.5 w-3.5" }),
808
+ t("tasks.drawer.title")
809
+ ]
810
+ }
811
+ ),
812
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
813
+ /* @__PURE__ */ jsxRuntime.jsx(
814
+ "button",
815
+ {
816
+ type: "button",
817
+ onClick: handleDelete,
818
+ "aria-label": t("tasks.detail.delete"),
819
+ title: t("tasks.detail.delete"),
820
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-destructive",
821
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
822
+ }
823
+ )
824
+ ] }),
781
825
  /* @__PURE__ */ jsxRuntime.jsx(
782
826
  "input",
783
827
  {
@@ -788,101 +832,112 @@ function TaskDetail({ taskId }) {
788
832
  onKeyDown: (e) => {
789
833
  if (e.key === "Enter") e.currentTarget.blur();
790
834
  },
791
- className: "flex-1 bg-transparent text-sm font-semibold outline-none"
835
+ className: "bg-transparent px-4 pt-3 text-sm font-semibold text-foreground outline-none"
792
836
  }
793
- )
794
- ] }),
795
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
796
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
797
- /* @__PURE__ */ jsxRuntime.jsx(
798
- "select",
799
- {
800
- value: task.status,
801
- onChange: (e) => updateTask(task.id, { status: e.target.value }),
802
- className: "appearance-none rounded-full border bg-background pl-5 pr-6 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors",
803
- children: TASK_STATUSES.map((s) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: s, children: t(`tasks.status.${s}`) }, s))
804
- }
805
- ),
806
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `absolute left-2 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full ${STATUS_DOT[task.status]}` }),
807
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-1.5 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
808
- ] }),
809
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
810
- /* @__PURE__ */ jsxRuntime.jsx(
811
- "select",
812
- {
813
- value: task.priority,
814
- onChange: (e) => updateTask(task.id, { priority: e.target.value }),
815
- className: "appearance-none rounded-full border bg-background pl-2 pr-6 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors",
816
- children: TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p, children: t(`tasks.priority.${p}`) }, p))
817
- }
818
- ),
819
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-1.5 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
820
- ] }),
821
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
822
- /* @__PURE__ */ jsxRuntime.jsx(
823
- "input",
824
- {
825
- type: "date",
826
- value: task.dueDate ?? "",
827
- onChange: (e) => updateTask(task.id, { dueDate: e.target.value || null }),
828
- className: "appearance-none rounded-full border bg-background pl-6 pr-2 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors w-[120px]"
829
- }
830
- ),
831
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
832
- ] }),
833
- task.labels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TaskLabelBadges, { labelIds: task.labels, max: 3 })
834
- ] }),
835
- /* @__PURE__ */ jsxRuntime.jsx(
836
- "textarea",
837
- {
838
- value: description,
839
- onChange: (e) => setDescription(e.target.value),
840
- onBlur: handleDescriptionBlur,
841
- placeholder: t("tasks.detail.descriptionPlaceholder"),
842
- rows: 3,
843
- className: "w-full rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-xs outline-none resize-none placeholder:text-muted-foreground/50 leading-relaxed"
844
- }
845
- ),
846
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
847
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wider mb-1", children: t("tasks.detail.subtasks") }),
848
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border overflow-hidden", children: [
849
- /* @__PURE__ */ jsxRuntime.jsx(TaskQuickAdd, { parentId: task.id }),
850
- subtasks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: subtasks.map((st) => /* @__PURE__ */ jsxRuntime.jsx(TaskCard, { task: st }, st.id)) })
851
- ] })
852
- ] }),
853
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pt-2 border-t", children: [
854
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
855
- formatDate(task.createdAt),
856
- " \xB7 ",
857
- t("tasks.detail.updated"),
858
- " ",
859
- formatDate(task.updatedAt)
837
+ ),
838
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 px-4 pt-2", children: [
839
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
840
+ /* @__PURE__ */ jsxRuntime.jsx(
841
+ "select",
842
+ {
843
+ value: task.status,
844
+ onChange: (e) => updateTask(task.id, { status: e.target.value }),
845
+ className: "appearance-none rounded-full border bg-background pl-5 pr-6 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors",
846
+ children: TASK_STATUSES.map((s) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: s, children: t(`tasks.status.${s}`) }, s))
847
+ }
848
+ ),
849
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `absolute left-2 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full ${STATUS_DOT[task.status]}` }),
850
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-1.5 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
851
+ ] }),
852
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
853
+ /* @__PURE__ */ jsxRuntime.jsx(
854
+ "select",
855
+ {
856
+ value: task.priority,
857
+ onChange: (e) => updateTask(task.id, { priority: e.target.value }),
858
+ className: "appearance-none rounded-full border bg-background pl-2 pr-6 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors",
859
+ children: TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p, children: t(`tasks.priority.${p}`) }, p))
860
+ }
861
+ ),
862
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-1.5 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
863
+ ] }),
864
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
865
+ /* @__PURE__ */ jsxRuntime.jsx(
866
+ "input",
867
+ {
868
+ type: "date",
869
+ value: task.dueDate ?? "",
870
+ onChange: (e) => updateTask(task.id, { dueDate: e.target.value || null }),
871
+ className: "appearance-none rounded-full border bg-background pl-6 pr-2 py-1 text-xs font-medium cursor-pointer hover:border-primary/40 transition-colors w-[120px]"
872
+ }
873
+ ),
874
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-3 w-3 text-muted-foreground pointer-events-none" })
875
+ ] }),
876
+ task.labels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TaskLabelBadges, { labelIds: task.labels, max: 3 })
860
877
  ] }),
861
- /* @__PURE__ */ jsxRuntime.jsxs(
878
+ /* @__PURE__ */ jsxRuntime.jsx(
879
+ "textarea",
880
+ {
881
+ value: description,
882
+ onChange: (e) => setDescription(e.target.value),
883
+ onBlur: handleDescriptionBlur,
884
+ placeholder: t("tasks.detail.descriptionPlaceholder"),
885
+ className: "min-h-[72px] flex-1 resize-none bg-transparent px-4 py-3 text-sm leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
886
+ }
887
+ ),
888
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
862
889
  "button",
863
890
  {
864
891
  type: "button",
865
- onClick: handleDelete,
866
- className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground hover:text-destructive transition-colors",
892
+ onClick: () => delegate(
893
+ `${t("tasks.detail.chatWithAi.prompt")}
894
+
895
+ "${task.title}"${task.description ? `
896
+ ${task.description}` : ""}${task.dueDate ? `
897
+ (${t("tasks.detail.dueDate")}: ${task.dueDate})` : ""}`
898
+ ),
899
+ className: "group flex w-full items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-primary via-primary/80 to-primary bg-[length:200%_100%] bg-left px-3 py-2 text-[13px] font-semibold text-primary-foreground shadow-sm transition-all duration-500 hover:bg-right hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
867
900
  children: [
868
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" }),
869
- t("tasks.detail.delete")
901
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-4 w-4 transition-transform duration-300 group-hover:rotate-12" }),
902
+ t("tasks.detail.chatWithAi")
870
903
  ]
871
904
  }
872
- )
905
+ ) }),
906
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/40 px-4 py-2.5", children: [
907
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60", children: [
908
+ t("tasks.detail.subtasks"),
909
+ subtasks.length > 0 ? ` \xB7 ${subtasks.length}` : ""
910
+ ] }),
911
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-3", children: /* @__PURE__ */ jsxRuntime.jsx(TaskQuickAdd, { parentId: task.id }) }),
912
+ subtasks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/40", children: subtasks.map((st) => /* @__PURE__ */ jsxRuntime.jsx(TaskCard, { task: st }, st.id)) })
913
+ ] }),
914
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "border-t border-border/40 px-4 py-1.5 text-[10px] text-muted-foreground/60", children: [
915
+ t("tasks.detail.created"),
916
+ " ",
917
+ formatDateTime(task.createdAt),
918
+ " \xB7 ",
919
+ t("tasks.detail.updated"),
920
+ " ",
921
+ formatDateTime(task.updatedAt)
922
+ ] })
873
923
  ] })
874
- ] });
924
+ );
875
925
  }
876
926
  var STATUS_ORDER = ["todo", "in_progress", "done", "cancelled"];
877
- function TasksDrawer() {
927
+ function TasksPanel() {
878
928
  const t = core.useTranslation();
879
- const drawerOpen = useTasksStore((s) => s.drawerOpen);
880
- const closeDrawer = useTasksStore((s) => s.closeDrawer);
881
929
  const selectedTaskId = useTasksStore((s) => s.selectedTaskId);
882
930
  const tasks = useTasksStore((s) => s.tasks);
883
931
  const tasksLoading = useTasksStore((s) => s.tasksLoading);
884
- const summary = useTasksStore((s) => s.summary);
885
932
  const activeFilter = useTasksStore((s) => s.activeFilter);
933
+ const fetchTasks = useTasksStore((s) => s.fetchTasks);
934
+ const fetchLabels = useTasksStore((s) => s.fetchLabels);
935
+ const fetchSummary = useTasksStore((s) => s.fetchSummary);
936
+ React4__default.default.useEffect(() => {
937
+ void fetchTasks();
938
+ void fetchLabels();
939
+ void fetchSummary();
940
+ }, [fetchTasks, fetchLabels, fetchSummary]);
886
941
  const grouped = React4__default.default.useMemo(() => {
887
942
  const map = /* @__PURE__ */ new Map();
888
943
  for (const s of STATUS_ORDER) map.set(s, []);
@@ -892,13 +947,32 @@ function TasksDrawer() {
892
947
  }
893
948
  return map;
894
949
  }, [tasks]);
895
- const summaryLine = React4__default.default.useMemo(() => {
950
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
951
+ !selectedTaskId && /* @__PURE__ */ jsxRuntime.jsx(TasksFilterBar, {}),
952
+ !selectedTaskId && /* @__PURE__ */ jsxRuntime.jsx(TaskQuickAdd, {}),
953
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: selectedTaskId ? /* @__PURE__ */ jsxRuntime.jsx(TaskDetail, { taskId: selectedTaskId }) : tasksLoading && tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-muted-foreground", children: [
954
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { className: "mb-3 h-10 w-10 opacity-40" }),
955
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: t("tasks.empty") }),
956
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs", children: t("tasks.empty.description") })
957
+ ] }) : activeFilter !== "all" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: tasks.map((task) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: /* @__PURE__ */ jsxRuntime.jsx(TaskCard, { task }) }, task.id)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 py-2", children: STATUS_ORDER.filter((s) => s !== "cancelled" || (grouped.get(s)?.length ?? 0) > 0).map((status) => /* @__PURE__ */ jsxRuntime.jsx(TaskStatusGroup, { status, tasks: grouped.get(status) ?? [] }, status)) }) })
958
+ ] });
959
+ }
960
+ function useTasksSummaryLine() {
961
+ const t = core.useTranslation();
962
+ const summary = useTasksStore((s) => s.summary);
963
+ return React4__default.default.useMemo(() => {
896
964
  if (!summary) return "";
897
965
  const parts = [];
898
966
  if (summary.overdue > 0) parts.push(`${summary.overdue} ${t("tasks.summary.overdue")}`);
899
967
  if (summary.dueToday > 0) parts.push(`${summary.dueToday} ${t("tasks.summary.dueToday")}`);
900
968
  return parts.join(" \xB7 ");
901
969
  }, [summary, t]);
970
+ }
971
+ function TasksDrawer() {
972
+ const t = core.useTranslation();
973
+ const drawerOpen = useTasksStore((s) => s.drawerOpen);
974
+ const closeDrawer = useTasksStore((s) => s.closeDrawer);
975
+ const summaryLine = useTasksSummaryLine();
902
976
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Sheet, { open: drawerOpen, onOpenChange: (v) => {
903
977
  if (!v) closeDrawer();
904
978
  }, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.SheetContent, { width: "max-w-lg", overlay: "dim", children: [
@@ -906,26 +980,46 @@ function TasksDrawer() {
906
980
  /* @__PURE__ */ jsxRuntime.jsx(ui.SheetTitle, { children: t("tasks.drawer.title") }),
907
981
  /* @__PURE__ */ jsxRuntime.jsx(ui.SheetDescription, { children: summaryLine || t("tasks.drawer.subtitle") })
908
982
  ] }),
909
- !selectedTaskId && /* @__PURE__ */ jsxRuntime.jsx(TasksFilterBar, {}),
910
- !selectedTaskId && /* @__PURE__ */ jsxRuntime.jsx(TaskQuickAdd, {}),
911
- /* @__PURE__ */ jsxRuntime.jsx(ui.SheetBody, { className: "px-0 py-0", children: selectedTaskId ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(TaskDetail, { taskId: selectedTaskId }) }) : tasksLoading && tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-muted-foreground", children: [
912
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { className: "h-10 w-10 mb-3 opacity-40" }),
913
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: t("tasks.empty") }),
914
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", children: t("tasks.empty.description") })
915
- ] }) : activeFilter !== "all" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: tasks.map((task) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2", children: /* @__PURE__ */ jsxRuntime.jsx(TaskCard, { task }) }, task.id)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2 space-y-2", children: STATUS_ORDER.filter((s) => s !== "cancelled" || (grouped.get(s)?.length ?? 0) > 0).map((status) => /* @__PURE__ */ jsxRuntime.jsx(TaskStatusGroup, { status, tasks: grouped.get(status) ?? [] }, status)) }) })
983
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SheetBody, { className: "flex flex-col px-0 py-0", children: /* @__PURE__ */ jsxRuntime.jsx(TasksPanel, {}) })
916
984
  ] }) });
917
985
  }
918
986
  function TasksTopbarButton({ config }) {
919
987
  const { tasksConfig, tasksProvider, tasksStore } = config;
988
+ const t = core.useTranslation();
989
+ const railMounted = saas.useRightRailStore((s) => s.mounted);
990
+ const summary = zustand.useStore(tasksStore, (s) => s.summary);
991
+ const openCount = summary ? summary.todo + summary.inProgress : 0;
992
+ const Panel = React4__default.default.useMemo(() => {
993
+ const RailPanel = () => /* @__PURE__ */ jsxRuntime.jsx(TasksContextProvider, { config: tasksConfig, provider: tasksProvider, store: tasksStore, children: /* @__PURE__ */ jsxRuntime.jsx(TasksPanel, {}) });
994
+ RailPanel.displayName = "TasksRailPanel";
995
+ return RailPanel;
996
+ }, [tasksConfig, tasksProvider, tasksStore]);
997
+ saas.useRightRailPanel(
998
+ React4__default.default.useMemo(
999
+ () => railMounted ? {
1000
+ id: "tasks",
1001
+ label: t("tasks.drawer.title") || "Tasks",
1002
+ icon: lucideReact.CheckSquare,
1003
+ order: 20,
1004
+ badge: { count: openCount, tone: "destructive" },
1005
+ Component: Panel
1006
+ } : null,
1007
+ [railMounted, t, Panel, openCount]
1008
+ )
1009
+ );
920
1010
  return /* @__PURE__ */ jsxRuntime.jsxs(TasksContextProvider, { config: tasksConfig, provider: tasksProvider, store: tasksStore, children: [
921
- /* @__PURE__ */ jsxRuntime.jsx(TasksTopbarButtonInner, { store: tasksStore }),
922
- /* @__PURE__ */ jsxRuntime.jsx(TasksDrawer, {})
1011
+ /* @__PURE__ */ jsxRuntime.jsx(TasksTopbarButtonInner, { store: tasksStore, railMounted }),
1012
+ !railMounted && /* @__PURE__ */ jsxRuntime.jsx(TasksDrawer, {})
923
1013
  ] });
924
1014
  }
925
- function TasksTopbarButtonInner({ store }) {
1015
+ function TasksTopbarButtonInner({
1016
+ store,
1017
+ railMounted
1018
+ }) {
926
1019
  const toggleDrawer = zustand.useStore(store, (s) => s.toggleDrawer);
927
1020
  const fetchSummary = zustand.useStore(store, (s) => s.fetchSummary);
928
1021
  const summary = zustand.useStore(store, (s) => s.summary);
1022
+ const togglePanel = saas.useRightRailStore((s) => s.togglePanel);
929
1023
  React4.useEffect(() => {
930
1024
  fetchSummary();
931
1025
  }, [fetchSummary]);
@@ -936,7 +1030,7 @@ function TasksTopbarButtonInner({ store }) {
936
1030
  variant: "ghost",
937
1031
  size: "icon",
938
1032
  className: ui.cn("relative text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground"),
939
- onClick: toggleDrawer,
1033
+ onClick: () => railMounted ? togglePanel("tasks") : toggleDrawer(),
940
1034
  "aria-label": "Tasks",
941
1035
  children: [
942
1036
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckSquare, { className: "h-5 w-5" }),
@@ -955,40 +1049,49 @@ function TasksTopbarButtonInner({ store }) {
955
1049
  );
956
1050
  }
957
1051
 
958
- // src/index.ts
959
- var DEFAULT_LABELS = {
960
- drawerTitle: "Tasks",
961
- settingsTitle: "Tasks",
962
- quickAddPlaceholder: "Add a task..."
963
- };
964
- function resolveConfig(options) {
965
- return {
966
- labels: { ...DEFAULT_LABELS, ...options?.labels }
967
- };
968
- }
969
- var MIGRATION_001 = `-- Tasks Plugin: Base Tables
970
- CREATE TABLE IF NOT EXISTS public.tsk_labels (
1052
+ // src/migrations/index.ts
1053
+ var MIGRATION_000_PLG_RENAME = `-- 000_plg_rename.sql \u2014 rename legacy tasks tables to plg_tasks_* for pools
1054
+ -- provisioned before the industry-pool rename. Guarded: fires only when legacy
1055
+ -- name exists and target does not, so fresh pools skip every branch.
1056
+ DO $$
1057
+ BEGIN
1058
+ IF to_regclass('public.tsk_tasks') IS NOT NULL AND to_regclass('public.plg_tasks_tasks') IS NULL THEN
1059
+ ALTER TABLE public.tsk_tasks RENAME TO plg_tasks_tasks;
1060
+ END IF;
1061
+ IF to_regclass('public.tsk_labels') IS NOT NULL AND to_regclass('public.plg_tasks_labels') IS NULL THEN
1062
+ ALTER TABLE public.tsk_labels RENAME TO plg_tasks_labels;
1063
+ END IF;
1064
+ END $$;
1065
+ `;
1066
+ var MIGRATION_001_TASKS_BASE = `-- Tasks Plugin: Base Tables
1067
+ -- Prefix: plg_tasks_
1068
+
1069
+ -- Label definitions for categorizing tasks
1070
+ CREATE TABLE IF NOT EXISTS public.plg_tasks_labels (
971
1071
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
972
- tenant_id uuid NOT NULL REFERENCES saas_core.tenants(id) ON DELETE CASCADE,
1072
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
973
1073
  name text NOT NULL,
974
1074
  color text NOT NULL DEFAULT '#6366f1',
975
1075
  is_active boolean NOT NULL DEFAULT true,
976
1076
  created_at timestamptz NOT NULL DEFAULT now()
977
1077
  );
978
- ALTER TABLE public.tsk_labels ENABLE ROW LEVEL SECURITY;
979
- CREATE INDEX IF NOT EXISTS idx_tsk_labels_tenant ON public.tsk_labels(tenant_id);
1078
+ ALTER TABLE public.plg_tasks_labels ENABLE ROW LEVEL SECURITY;
1079
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_labels_tenant ON public.plg_tasks_labels(tenant_id);
980
1080
 
981
- CREATE TABLE IF NOT EXISTS public.tsk_tasks (
1081
+ -- Main tasks table (subtasks via parent_id self-reference)
1082
+ CREATE TABLE IF NOT EXISTS public.plg_tasks_tasks (
982
1083
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
983
- tenant_id uuid NOT NULL REFERENCES saas_core.tenants(id) ON DELETE CASCADE,
1084
+ tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
984
1085
  title text NOT NULL,
985
1086
  description text,
986
- status text NOT NULL DEFAULT 'todo' CHECK (status IN ('todo','in_progress','done','cancelled')),
987
- priority text NOT NULL DEFAULT 'medium' CHECK (priority IN ('low','medium','high','urgent')),
1087
+ status text NOT NULL DEFAULT 'todo'
1088
+ CHECK (status IN ('todo', 'in_progress', 'done', 'cancelled')),
1089
+ priority text NOT NULL DEFAULT 'medium'
1090
+ CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
988
1091
  due_date date,
989
1092
  assigned_to_id uuid,
990
1093
  assigned_to_name text,
991
- parent_id uuid REFERENCES public.tsk_tasks(id) ON DELETE CASCADE,
1094
+ parent_id uuid REFERENCES public.plg_tasks_tasks(id) ON DELETE CASCADE,
992
1095
  labels jsonb NOT NULL DEFAULT '[]'::jsonb,
993
1096
  position integer NOT NULL DEFAULT 0,
994
1097
  created_by_id uuid,
@@ -996,26 +1099,48 @@ CREATE TABLE IF NOT EXISTS public.tsk_tasks (
996
1099
  created_at timestamptz NOT NULL DEFAULT now(),
997
1100
  updated_at timestamptz NOT NULL DEFAULT now()
998
1101
  );
999
- ALTER TABLE public.tsk_tasks ENABLE ROW LEVEL SECURITY;
1000
- CREATE INDEX IF NOT EXISTS idx_tsk_tasks_tenant ON public.tsk_tasks(tenant_id);
1001
- CREATE INDEX IF NOT EXISTS idx_tsk_tasks_status ON public.tsk_tasks(tenant_id, status);
1002
- CREATE INDEX IF NOT EXISTS idx_tsk_tasks_parent ON public.tsk_tasks(parent_id);
1003
- CREATE INDEX IF NOT EXISTS idx_tsk_tasks_assigned ON public.tsk_tasks(assigned_to_id);
1004
- CREATE INDEX IF NOT EXISTS idx_tsk_tasks_due ON public.tsk_tasks(tenant_id, due_date) WHERE due_date IS NOT NULL;
1102
+ ALTER TABLE public.plg_tasks_tasks ENABLE ROW LEVEL SECURITY;
1103
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_tenant ON public.plg_tasks_tasks(tenant_id);
1104
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_status ON public.plg_tasks_tasks(tenant_id, status);
1105
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_parent ON public.plg_tasks_tasks(parent_id);
1106
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_assigned ON public.plg_tasks_tasks(assigned_to_id);
1107
+ CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_due ON public.plg_tasks_tasks(tenant_id, due_date)
1108
+ WHERE due_date IS NOT NULL;
1109
+
1110
+ -- RLS policies for plg_tasks_tasks (idempotent \u2014 DROP IF EXISTS before CREATE)
1111
+ DROP POLICY IF EXISTS plg_tasks_tasks_select ON public.plg_tasks_tasks;
1112
+ CREATE POLICY plg_tasks_tasks_select ON public.plg_tasks_tasks FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1113
+ DROP POLICY IF EXISTS plg_tasks_tasks_insert ON public.plg_tasks_tasks;
1114
+ CREATE POLICY plg_tasks_tasks_insert ON public.plg_tasks_tasks FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
1115
+ DROP POLICY IF EXISTS plg_tasks_tasks_update ON public.plg_tasks_tasks;
1116
+ CREATE POLICY plg_tasks_tasks_update ON public.plg_tasks_tasks FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1117
+ DROP POLICY IF EXISTS plg_tasks_tasks_delete ON public.plg_tasks_tasks;
1118
+ CREATE POLICY plg_tasks_tasks_delete ON public.plg_tasks_tasks FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1119
+ GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_tasks_tasks TO authenticated;
1005
1120
 
1006
- -- RLS policies for tsk_tasks
1007
- CREATE POLICY tsk_tasks_select ON public.tsk_tasks FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1008
- CREATE POLICY tsk_tasks_insert ON public.tsk_tasks FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
1009
- CREATE POLICY tsk_tasks_update ON public.tsk_tasks FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1010
- CREATE POLICY tsk_tasks_delete ON public.tsk_tasks FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1011
- GRANT SELECT, INSERT, UPDATE, DELETE ON public.tsk_tasks TO authenticated;
1121
+ -- RLS policies for plg_tasks_labels (idempotent)
1122
+ DROP POLICY IF EXISTS plg_tasks_labels_select ON public.plg_tasks_labels;
1123
+ CREATE POLICY plg_tasks_labels_select ON public.plg_tasks_labels FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1124
+ DROP POLICY IF EXISTS plg_tasks_labels_insert ON public.plg_tasks_labels;
1125
+ CREATE POLICY plg_tasks_labels_insert ON public.plg_tasks_labels FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
1126
+ DROP POLICY IF EXISTS plg_tasks_labels_update ON public.plg_tasks_labels;
1127
+ CREATE POLICY plg_tasks_labels_update ON public.plg_tasks_labels FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1128
+ DROP POLICY IF EXISTS plg_tasks_labels_delete ON public.plg_tasks_labels;
1129
+ CREATE POLICY plg_tasks_labels_delete ON public.plg_tasks_labels FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1130
+ GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_tasks_labels TO authenticated;
1131
+ `;
1012
1132
 
1013
- -- RLS policies for tsk_labels
1014
- CREATE POLICY tsk_labels_select ON public.tsk_labels FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1015
- CREATE POLICY tsk_labels_insert ON public.tsk_labels FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
1016
- CREATE POLICY tsk_labels_update ON public.tsk_labels FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1017
- CREATE POLICY tsk_labels_delete ON public.tsk_labels FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
1018
- GRANT SELECT, INSERT, UPDATE, DELETE ON public.tsk_labels TO authenticated;`;
1133
+ // src/index.ts
1134
+ var DEFAULT_LABELS = {
1135
+ drawerTitle: "Tasks",
1136
+ settingsTitle: "Tasks",
1137
+ quickAddPlaceholder: "Add a task..."
1138
+ };
1139
+ function resolveConfig(options) {
1140
+ return {
1141
+ labels: { ...DEFAULT_LABELS, ...options?.labels }
1142
+ };
1143
+ }
1019
1144
  function createTasksPlugin(options) {
1020
1145
  const config = resolveConfig(options);
1021
1146
  core.registerTranslations(tasksLocales);
@@ -1024,6 +1149,30 @@ function createTasksPlugin(options) {
1024
1149
  () => createMockTasksProvider()
1025
1150
  );
1026
1151
  const store = createTasksStore(provider);
1152
+ saas.registerNotesTasksAdapter({
1153
+ async createTodo({ title, noteId, noteTitle }) {
1154
+ const task = await provider.createTask({
1155
+ title,
1156
+ description: noteTitle ? `Nota: ${noteTitle}` : void 0,
1157
+ labels: [`${saas.NOTE_LABEL_PREFIX}${noteId}`]
1158
+ });
1159
+ void store.getState().fetchSummary();
1160
+ return { id: task.id, title: task.title, done: task.status === "done" };
1161
+ },
1162
+ async listTodos(noteId) {
1163
+ const tasks = await provider.getTasks({});
1164
+ const label = `${saas.NOTE_LABEL_PREFIX}${noteId}`;
1165
+ return tasks.filter((t) => t.labels.includes(label)).sort((a, b) => (a.status === "done" ? 1 : 0) - (b.status === "done" ? 1 : 0)).map((t) => ({ id: t.id, title: t.title, done: t.status === "done" }));
1166
+ },
1167
+ async toggleTodo(id, done) {
1168
+ await provider.updateTask(id, { status: done ? "done" : "todo" });
1169
+ void store.getState().fetchSummary();
1170
+ },
1171
+ async removeTodo(id) {
1172
+ await provider.deleteTask(id);
1173
+ void store.getState().fetchSummary();
1174
+ }
1175
+ });
1027
1176
  return {
1028
1177
  id: "tasks",
1029
1178
  name: config.labels.drawerTitle,
@@ -1092,11 +1241,17 @@ function createTasksPlugin(options) {
1092
1241
  }
1093
1242
  ],
1094
1243
  migrations: [
1244
+ {
1245
+ id: "tasks-000-plg-rename",
1246
+ version: "1.0.0",
1247
+ sql: MIGRATION_000_PLG_RENAME,
1248
+ description: "Rename legacy tsk_* tables to plg_tasks_* in-place for pools provisioned before the industry-pool rename (guarded no-op on fresh pools)"
1249
+ },
1095
1250
  {
1096
1251
  id: "tasks-001-base-tables",
1097
1252
  version: "1.0.0",
1098
- sql: MIGRATION_001,
1099
- description: "Create tsk_tasks and tsk_labels tables"
1253
+ sql: MIGRATION_001_TASKS_BASE,
1254
+ description: "Create plg_tasks_tasks and plg_tasks_labels"
1100
1255
  }
1101
1256
  ],
1102
1257
  locales: tasksLocales