@fayz-ai/tasks 0.0.0-stage → 0.48.0

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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -2
  3. package/dist/chunk-5DWYILP2.js +102 -0
  4. package/dist/chunk-5DWYILP2.js.map +1 -0
  5. package/dist/chunk-BHNXKHFW.js +29 -0
  6. package/dist/chunk-BHNXKHFW.js.map +1 -0
  7. package/dist/chunk-H435KYKF.js +26 -0
  8. package/dist/chunk-H435KYKF.js.map +1 -0
  9. package/dist/chunk-IHQV22ML.js +128 -0
  10. package/dist/chunk-IHQV22ML.js.map +1 -0
  11. package/dist/chunk-IYQSFHSK.js +175 -0
  12. package/dist/chunk-IYQSFHSK.js.map +1 -0
  13. package/dist/chunk-KM7XZUFP.js +114 -0
  14. package/dist/chunk-KM7XZUFP.js.map +1 -0
  15. package/dist/chunk-KUO4ZJPR.js +187 -0
  16. package/dist/chunk-KUO4ZJPR.js.map +1 -0
  17. package/dist/chunk-KXUCWLLG.js +80 -0
  18. package/dist/chunk-KXUCWLLG.js.map +1 -0
  19. package/dist/chunk-LY4RNYRG.js +57 -0
  20. package/dist/chunk-LY4RNYRG.js.map +1 -0
  21. package/dist/chunk-Q5FSJ2D6.js +60 -0
  22. package/dist/chunk-Q5FSJ2D6.js.map +1 -0
  23. package/dist/chunk-WNIXFQM4.js +22 -0
  24. package/dist/chunk-WNIXFQM4.js.map +1 -0
  25. package/dist/chunk-XVAIXG7V.js +42 -0
  26. package/dist/chunk-XVAIXG7V.js.map +1 -0
  27. package/dist/components/TaskCard.d.ts +17 -0
  28. package/dist/components/TaskCard.d.ts.map +1 -0
  29. package/dist/components/TaskCard.js +7 -0
  30. package/dist/components/TaskCard.js.map +1 -0
  31. package/dist/components/TaskDetail.d.ts +21 -0
  32. package/dist/components/TaskDetail.d.ts.map +1 -0
  33. package/dist/components/TaskDetail.js +10 -0
  34. package/dist/components/TaskDetail.js.map +1 -0
  35. package/dist/components/TaskLabelBadge.d.ts +11 -0
  36. package/dist/components/TaskLabelBadge.d.ts.map +1 -0
  37. package/dist/components/TaskLabelBadge.js +4 -0
  38. package/dist/components/TaskLabelBadge.js.map +1 -0
  39. package/dist/components/TaskPriorityBadge.d.ts +7 -0
  40. package/dist/components/TaskPriorityBadge.d.ts.map +1 -0
  41. package/dist/components/TaskPriorityBadge.js +3 -0
  42. package/dist/components/TaskPriorityBadge.js.map +1 -0
  43. package/dist/components/TaskQuickAdd.d.ts +8 -0
  44. package/dist/components/TaskQuickAdd.d.ts.map +1 -0
  45. package/dist/components/TaskQuickAdd.js +3 -0
  46. package/dist/components/TaskQuickAdd.js.map +1 -0
  47. package/dist/components/TaskSourceBadge.d.ts +5 -0
  48. package/dist/components/TaskSourceBadge.d.ts.map +1 -0
  49. package/dist/components/TaskSourceBadge.js +4 -0
  50. package/dist/components/TaskSourceBadge.js.map +1 -0
  51. package/dist/components/TaskStatusGroup.d.ts +16 -0
  52. package/dist/components/TaskStatusGroup.d.ts.map +1 -0
  53. package/dist/components/TaskStatusGroup.js +3 -0
  54. package/dist/components/TaskStatusGroup.js.map +1 -0
  55. package/dist/components/TasksFilterBar.d.ts +13 -0
  56. package/dist/components/TasksFilterBar.d.ts.map +1 -0
  57. package/dist/components/TasksFilterBar.js +4 -0
  58. package/dist/components/TasksFilterBar.js.map +1 -0
  59. package/dist/data/mock.d.ts +3 -0
  60. package/dist/data/mock.d.ts.map +1 -0
  61. package/dist/data/mock.js +4 -0
  62. package/dist/data/mock.js.map +1 -0
  63. package/dist/data/provider.d.ts +13 -0
  64. package/dist/data/provider.d.ts.map +1 -0
  65. package/dist/data/provider.js +3 -0
  66. package/dist/data/provider.js.map +1 -0
  67. package/dist/data/supabase.d.ts +9 -0
  68. package/dist/data/supabase.d.ts.map +1 -0
  69. package/dist/data/supabase.js +4 -0
  70. package/dist/data/supabase.js.map +1 -0
  71. package/dist/domain/task.d.ts +121 -0
  72. package/dist/domain/task.d.ts.map +1 -0
  73. package/dist/domain/task.js +3 -0
  74. package/dist/domain/task.js.map +1 -0
  75. package/dist/store.d.ts +35 -0
  76. package/dist/store.d.ts.map +1 -0
  77. package/dist/store.js +6 -0
  78. package/dist/store.js.map +1 -0
  79. package/dist/views/TasksPanel.d.ts +8 -0
  80. package/dist/views/TasksPanel.d.ts.map +1 -0
  81. package/dist/views/TasksPanel.js +103 -0
  82. package/dist/views/TasksPanel.js.map +1 -0
  83. package/package.json +51 -3
@@ -0,0 +1,114 @@
1
+ import { createMockTasksProvider } from './chunk-IHQV22ML.js';
2
+ import { createSupabaseTasksProvider } from './chunk-IYQSFHSK.js';
3
+ import { listQuery } from './chunk-5DWYILP2.js';
4
+ import { create } from 'zustand';
5
+ import { createSafeDataProvider } from '@fayz-ai/core';
6
+
7
+ var provider = null;
8
+ function getTasksProvider() {
9
+ provider ?? (provider = createSafeDataProvider(createSupabaseTasksProvider, createMockTasksProvider));
10
+ return provider;
11
+ }
12
+ function setTasksProvider(next) {
13
+ provider = next;
14
+ }
15
+ var inflight = /* @__PURE__ */ new Map();
16
+ function shared(key, read) {
17
+ const running = inflight.get(key);
18
+ if (running) return running;
19
+ const promise = read().finally(() => inflight.delete(key));
20
+ inflight.set(key, promise);
21
+ return promise;
22
+ }
23
+ var useTasksStore = create((set, get) => ({
24
+ tasks: [],
25
+ tasksLoading: false,
26
+ labels: [],
27
+ summary: null,
28
+ summaryLoading: false,
29
+ drawerOpen: false,
30
+ selectedTaskId: null,
31
+ activeFilter: "all",
32
+ mine: false,
33
+ currentUserId: null,
34
+ async fetchTasks(query) {
35
+ const { activeFilter, mine, currentUserId } = get();
36
+ const key = `tasks:list:${activeFilter}:${mine ? currentUserId ?? "me" : "all"}`;
37
+ set({ tasksLoading: true });
38
+ try {
39
+ const q = listQuery({ filter: activeFilter, mine, currentUserId }, query);
40
+ const tasks = await shared(key, () => getTasksProvider().getTasks(q));
41
+ set({ tasks, tasksLoading: false });
42
+ } catch {
43
+ set({ tasksLoading: false });
44
+ }
45
+ },
46
+ async fetchLabels() {
47
+ try {
48
+ set({ labels: await shared("tasks:labels", () => getTasksProvider().getLabels()) });
49
+ } catch {
50
+ }
51
+ },
52
+ async fetchSummary() {
53
+ set({ summaryLoading: true });
54
+ try {
55
+ const summary = await shared("tasks:summary", () => getTasksProvider().getSummary());
56
+ set({ summary, summaryLoading: false });
57
+ } catch {
58
+ set({ summaryLoading: false });
59
+ }
60
+ },
61
+ async createTask(input) {
62
+ const task = await getTasksProvider().createTask(input);
63
+ await get().fetchTasks();
64
+ await get().fetchSummary();
65
+ return task;
66
+ },
67
+ async updateTask(id, input) {
68
+ const task = await getTasksProvider().updateTask(id, input);
69
+ set((s) => ({ tasks: s.tasks.map((t) => t.id === id ? task : t) }));
70
+ await get().fetchSummary();
71
+ return task;
72
+ },
73
+ async deleteTask(id) {
74
+ await getTasksProvider().deleteTask(id);
75
+ set((s) => ({ tasks: s.tasks.filter((t) => t.id !== id), selectedTaskId: null }));
76
+ await get().fetchSummary();
77
+ },
78
+ async reorderTask(id, newPosition, status) {
79
+ await getTasksProvider().reorderTask(id, newPosition, status);
80
+ await get().fetchTasks();
81
+ },
82
+ openDrawer() {
83
+ set({ drawerOpen: true });
84
+ void get().fetchTasks();
85
+ void get().fetchLabels();
86
+ void get().fetchSummary();
87
+ },
88
+ closeDrawer() {
89
+ set({ drawerOpen: false, selectedTaskId: null });
90
+ },
91
+ toggleDrawer() {
92
+ if (get().drawerOpen) get().closeDrawer();
93
+ else get().openDrawer();
94
+ },
95
+ selectTask(id) {
96
+ set({ selectedTaskId: id });
97
+ },
98
+ setFilter(filter) {
99
+ set({ activeFilter: filter });
100
+ void get().fetchTasks();
101
+ },
102
+ setMine(mine) {
103
+ set({ mine });
104
+ void get().fetchTasks();
105
+ },
106
+ setCurrentUserId(id) {
107
+ if (get().currentUserId === id) return;
108
+ set({ currentUserId: id });
109
+ }
110
+ }));
111
+
112
+ export { getTasksProvider, setTasksProvider, useTasksStore };
113
+ //# sourceMappingURL=chunk-KM7XZUFP.js.map
114
+ //# sourceMappingURL=chunk-KM7XZUFP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store.ts"],"names":[],"mappings":";;;;;;AAaA,IAAI,QAAA,GAAqC,IAAA;AAGlC,SAAS,gBAAA,GAAsC;AACpD,EAAA,QAAA,KAAA,QAAA,GAAa,sBAAA,CAAuB,6BAA6B,uBAAuB,CAAA,CAAA;AACxF,EAAA,OAAO,QAAA;AACT;AAGO,SAAS,iBAAiB,IAAA,EAAsC;AACrE,EAAA,QAAA,GAAW,IAAA;AACb;AAEA,IAAM,QAAA,uBAAe,GAAA,EAA8B;AAEnD,SAAS,MAAA,CAAU,KAAa,IAAA,EAAoC;AAClE,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AAChC,EAAA,IAAI,SAAS,OAAO,OAAA;AACpB,EAAA,MAAM,OAAA,GAAU,MAAK,CAAE,OAAA,CAAQ,MAAM,QAAA,CAAS,MAAA,CAAO,GAAG,CAAC,CAAA;AACzD,EAAA,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AACzB,EAAA,OAAO,OAAA;AACT;AAiCO,IAAM,aAAA,GAAgB,MAAA,CAAqB,CAAC,GAAA,EAAK,GAAA,MAAS;AAAA,EAC/D,OAAO,EAAC;AAAA,EACR,YAAA,EAAc,KAAA;AAAA,EACd,QAAQ,EAAC;AAAA,EACT,OAAA,EAAS,IAAA;AAAA,EACT,cAAA,EAAgB,KAAA;AAAA,EAEhB,UAAA,EAAY,KAAA;AAAA,EACZ,cAAA,EAAgB,IAAA;AAAA,EAChB,YAAA,EAAc,KAAA;AAAA,EACd,IAAA,EAAM,KAAA;AAAA,EACN,aAAA,EAAe,IAAA;AAAA,EAEf,MAAM,WAAW,KAAA,EAAO;AACtB,IAAA,MAAM,EAAE,YAAA,EAAc,IAAA,EAAM,aAAA,KAAkB,GAAA,EAAI;AAElD,IAAA,MAAM,MAAM,CAAA,WAAA,EAAc,YAAY,IAAI,IAAA,GAAO,aAAA,IAAiB,OAAO,KAAK,CAAA,CAAA;AAC9E,IAAA,GAAA,CAAI,EAAE,YAAA,EAAc,IAAA,EAAM,CAAA;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,UAAU,EAAE,MAAA,EAAQ,cAAc,IAAA,EAAM,aAAA,IAAiB,KAAK,CAAA;AACxE,MAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,GAAA,EAAK,MAAM,gBAAA,EAAiB,CAAE,QAAA,CAAS,CAAC,CAAC,CAAA;AACpE,MAAA,GAAA,CAAI,EAAE,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,CAAA;AAAA,IACpC,CAAA,CAAA,MAAQ;AACN,MAAA,GAAA,CAAI,EAAE,YAAA,EAAc,KAAA,EAAO,CAAA;AAAA,IAC7B;AAAA,EACF,CAAA;AAAA,EAEA,MAAM,WAAA,GAAc;AAClB,IAAA,IAAI;AACF,MAAA,GAAA,CAAI,EAAE,MAAA,EAAQ,MAAM,MAAA,CAAO,cAAA,EAAgB,MAAM,gBAAA,EAAiB,CAAE,SAAA,EAAW,CAAA,EAAG,CAAA;AAAA,IACpF,CAAA,CAAA,MAAQ;AAAA,IAAkD;AAAA,EAC5D,CAAA;AAAA,EAEA,MAAM,YAAA,GAAe;AACnB,IAAA,GAAA,CAAI,EAAE,cAAA,EAAgB,IAAA,EAAM,CAAA;AAC5B,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,eAAA,EAAiB,MAAM,gBAAA,EAAiB,CAAE,YAAY,CAAA;AACnF,MAAA,GAAA,CAAI,EAAE,OAAA,EAAS,cAAA,EAAgB,KAAA,EAAO,CAAA;AAAA,IACxC,CAAA,CAAA,MAAQ;AACN,MAAA,GAAA,CAAI,EAAE,cAAA,EAAgB,KAAA,EAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AAAA,EAEA,MAAM,WAAW,KAAA,EAAO;AACtB,IAAA,MAAM,IAAA,GAAO,MAAM,gBAAA,EAAiB,CAAE,WAAW,KAAK,CAAA;AACtD,IAAA,MAAM,GAAA,GAAM,UAAA,EAAW;AACvB,IAAA,MAAM,GAAA,GAAM,YAAA,EAAa;AACzB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAAA,EAEA,MAAM,UAAA,CAAW,EAAA,EAAI,KAAA,EAAO;AAC1B,IAAA,MAAM,OAAO,MAAM,gBAAA,EAAiB,CAAE,UAAA,CAAW,IAAI,KAAK,CAAA;AAC1D,IAAA,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,KAAA,EAAO,EAAE,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAO,EAAE,EAAA,KAAO,EAAA,GAAK,IAAA,GAAO,CAAE,GAAE,CAAE,CAAA;AACpE,IAAA,MAAM,GAAA,GAAM,YAAA,EAAa;AACzB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAAA,EAEA,MAAM,WAAW,EAAA,EAAI;AACnB,IAAA,MAAM,gBAAA,EAAiB,CAAE,UAAA,CAAW,EAAE,CAAA;AACtC,IAAA,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,KAAA,EAAO,EAAE,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,EAAA,KAAO,EAAE,CAAA,EAAG,cAAA,EAAgB,MAAK,CAAE,CAAA;AAChF,IAAA,MAAM,GAAA,GAAM,YAAA,EAAa;AAAA,EAC3B,CAAA;AAAA,EAEA,MAAM,WAAA,CAAY,EAAA,EAAI,WAAA,EAAa,MAAA,EAAQ;AACzC,IAAA,MAAM,gBAAA,EAAiB,CAAE,WAAA,CAAY,EAAA,EAAI,aAAa,MAAM,CAAA;AAC5D,IAAA,MAAM,GAAA,GAAM,UAAA,EAAW;AAAA,EACzB,CAAA;AAAA,EAEA,UAAA,GAAa;AACX,IAAA,GAAA,CAAI,EAAE,UAAA,EAAY,IAAA,EAAM,CAAA;AACxB,IAAA,KAAK,GAAA,GAAM,UAAA,EAAW;AACtB,IAAA,KAAK,GAAA,GAAM,WAAA,EAAY;AACvB,IAAA,KAAK,GAAA,GAAM,YAAA,EAAa;AAAA,EAC1B,CAAA;AAAA,EAEA,WAAA,GAAc;AACZ,IAAA,GAAA,CAAI,EAAE,UAAA,EAAY,KAAA,EAAO,cAAA,EAAgB,MAAM,CAAA;AAAA,EACjD,CAAA;AAAA,EAEA,YAAA,GAAe;AACb,IAAA,IAAI,GAAA,EAAI,CAAE,UAAA,EAAY,GAAA,GAAM,WAAA,EAAY;AAAA,SACnC,GAAA,GAAM,UAAA,EAAW;AAAA,EACxB,CAAA;AAAA,EAEA,WAAW,EAAA,EAAI;AACb,IAAA,GAAA,CAAI,EAAE,cAAA,EAAgB,EAAA,EAAI,CAAA;AAAA,EAC5B,CAAA;AAAA,EAEA,UAAU,MAAA,EAAQ;AAChB,IAAA,GAAA,CAAI,EAAE,YAAA,EAAc,MAAA,EAAQ,CAAA;AAC5B,IAAA,KAAK,GAAA,GAAM,UAAA,EAAW;AAAA,EACxB,CAAA;AAAA,EAEA,QAAQ,IAAA,EAAM;AACZ,IAAA,GAAA,CAAI,EAAE,MAAM,CAAA;AACZ,IAAA,KAAK,GAAA,GAAM,UAAA,EAAW;AAAA,EACxB,CAAA;AAAA,EAEA,iBAAiB,EAAA,EAAI;AACnB,IAAA,IAAI,GAAA,EAAI,CAAE,aAAA,KAAkB,EAAA,EAAI;AAChC,IAAA,GAAA,CAAI,EAAE,aAAA,EAAe,EAAA,EAAI,CAAA;AAAA,EAC3B;AACF,CAAA,CAAE","file":"chunk-KM7XZUFP.js","sourcesContent":["import { create } from 'zustand'\nimport { createSafeDataProvider } from '@fayz-ai/core'\nimport { createSupabaseTasksProvider } from './data/supabase'\nimport { createMockTasksProvider } from './data/mock'\nimport type { TasksDataProvider } from './data/provider'\nimport {\n listQuery,\n type CreateTaskInput, type Task, type TaskFilter, type TaskLabel, type TaskQuery, type TaskStatus,\n type TasksSummary, type UpdateTaskInput,\n} from './domain/task'\n\n// One store for every surface: the rail tab, the plugin's sheet and the getting-started checklist\n// read the same rows, and a second store would never see the answer to a read the first one started.\nlet provider: TasksDataProvider | null = null\n\n/** Non-React readers (the plugin's adapters, the onboarding seam). */\nexport function getTasksProvider(): TasksDataProvider {\n provider ??= createSafeDataProvider(createSupabaseTasksProvider, createMockTasksProvider)\n return provider\n}\n\n/** For tests, and for an app that brings its own backend. */\nexport function setTasksProvider(next: TasksDataProvider | null): void {\n provider = next\n}\n\nconst inflight = new Map<string, Promise<unknown>>()\n\nfunction shared<T>(key: string, read: () => Promise<T>): Promise<T> {\n const running = inflight.get(key) as Promise<T> | undefined\n if (running) return running\n const promise = read().finally(() => inflight.delete(key))\n inflight.set(key, promise)\n return promise\n}\n\nexport interface TasksUIState {\n tasks: Task[]\n tasksLoading: boolean\n labels: TaskLabel[]\n summary: TasksSummary | null\n summaryLoading: boolean\n\n drawerOpen: boolean\n selectedTaskId: string | null\n activeFilter: TaskFilter\n /** Opt-in \"assigned to me\". Off by default — the tab is the workspace's. */\n mine: boolean\n currentUserId: string | null\n\n fetchTasks(query?: TaskQuery): Promise<void>\n fetchLabels(): Promise<void>\n fetchSummary(): Promise<void>\n createTask(input: CreateTaskInput): Promise<Task>\n updateTask(id: string, input: UpdateTaskInput): Promise<Task>\n deleteTask(id: string): Promise<void>\n reorderTask(id: string, newPosition: number, status: TaskStatus): Promise<void>\n\n openDrawer(): void\n closeDrawer(): void\n toggleDrawer(): void\n selectTask(id: string | null): void\n setFilter(filter: TaskFilter): void\n setMine(mine: boolean): void\n setCurrentUserId(id: string | null): void\n}\n\nexport const useTasksStore = create<TasksUIState>((set, get) => ({\n tasks: [],\n tasksLoading: false,\n labels: [],\n summary: null,\n summaryLoading: false,\n\n drawerOpen: false,\n selectedTaskId: null,\n activeFilter: 'all',\n mine: false,\n currentUserId: null,\n\n async fetchTasks(query) {\n const { activeFilter, mine, currentUserId } = get()\n // The scope is part of the key, or switching filter mid-read hands back the previous filter's rows.\n const key = `tasks:list:${activeFilter}:${mine ? currentUserId ?? 'me' : 'all'}`\n set({ tasksLoading: true })\n try {\n const q = listQuery({ filter: activeFilter, mine, currentUserId }, query)\n const tasks = await shared(key, () => getTasksProvider().getTasks(q))\n set({ tasks, tasksLoading: false })\n } catch {\n set({ tasksLoading: false })\n }\n },\n\n async fetchLabels() {\n try {\n set({ labels: await shared('tasks:labels', () => getTasksProvider().getLabels()) })\n } catch { /* an app without plugin-tasks has no labels */ }\n },\n\n async fetchSummary() {\n set({ summaryLoading: true })\n try {\n const summary = await shared('tasks:summary', () => getTasksProvider().getSummary())\n set({ summary, summaryLoading: false })\n } catch {\n set({ summaryLoading: false })\n }\n },\n\n async createTask(input) {\n const task = await getTasksProvider().createTask(input)\n await get().fetchTasks()\n await get().fetchSummary()\n return task\n },\n\n async updateTask(id, input) {\n const task = await getTasksProvider().updateTask(id, input)\n set((s) => ({ tasks: s.tasks.map((t) => (t.id === id ? task : t)) }))\n await get().fetchSummary()\n return task\n },\n\n async deleteTask(id) {\n await getTasksProvider().deleteTask(id)\n set((s) => ({ tasks: s.tasks.filter((t) => t.id !== id), selectedTaskId: null }))\n await get().fetchSummary()\n },\n\n async reorderTask(id, newPosition, status) {\n await getTasksProvider().reorderTask(id, newPosition, status)\n await get().fetchTasks()\n },\n\n openDrawer() {\n set({ drawerOpen: true })\n void get().fetchTasks()\n void get().fetchLabels()\n void get().fetchSummary()\n },\n\n closeDrawer() {\n set({ drawerOpen: false, selectedTaskId: null })\n },\n\n toggleDrawer() {\n if (get().drawerOpen) get().closeDrawer()\n else get().openDrawer()\n },\n\n selectTask(id) {\n set({ selectedTaskId: id })\n },\n\n setFilter(filter) {\n set({ activeFilter: filter })\n void get().fetchTasks()\n },\n\n setMine(mine) {\n set({ mine })\n void get().fetchTasks()\n },\n\n setCurrentUserId(id) {\n if (get().currentUserId === id) return\n set({ currentUserId: id })\n },\n}))\n"]}
@@ -0,0 +1,187 @@
1
+ import { TaskCard } from './chunk-KXUCWLLG.js';
2
+ import { TaskLabelBadges } from './chunk-BHNXKHFW.js';
3
+ import { TaskQuickAdd } from './chunk-XVAIXG7V.js';
4
+ import { TASK_STATUSES, TASK_PRIORITIES } from './chunk-5DWYILP2.js';
5
+ import { STATUS_DOT_CLASS } from './chunk-LY4RNYRG.js';
6
+ import { useState, useEffect, useCallback } from 'react';
7
+ import { ArrowLeft, Sparkles, Trash2 } from 'lucide-react';
8
+ import { Button, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DatePicker, Textarea, ConfirmDialog, cn } from '@fayz-ai/ui';
9
+ import { useTranslation } from '@fayz-ai/core';
10
+ import { jsxs, jsx } from 'react/jsx-runtime';
11
+
12
+ var BARE_FIELD = "rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 focus-visible:ring-offset-0";
13
+ var PILL = "h-auto w-auto gap-1.5 rounded-full border-border bg-background py-1 pl-2 pr-1.5 text-xs font-medium shadow-none transition-colors hover:border-primary/40 focus:ring-0 focus:ring-offset-0 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground [&>svg]:opacity-100";
14
+ function formatDateTime(iso) {
15
+ const d = new Date(iso);
16
+ const sameDay = d.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
17
+ 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" });
18
+ }
19
+ function TaskDetail({
20
+ task,
21
+ labels,
22
+ locale,
23
+ subtasks = [],
24
+ onSelect,
25
+ onUpdate,
26
+ onDelete,
27
+ onCreate,
28
+ onDelegate,
29
+ renderActions
30
+ }) {
31
+ const t = useTranslation();
32
+ const [title, setTitle] = useState("");
33
+ const [description, setDescription] = useState("");
34
+ const [confirmingDelete, setConfirmingDelete] = useState(false);
35
+ useEffect(() => {
36
+ if (task) {
37
+ setTitle(task.title);
38
+ setDescription(task.description ?? "");
39
+ }
40
+ }, [task]);
41
+ const handleTitleBlur = useCallback(() => {
42
+ if (task && title.trim() && title !== task.title) onUpdate(task.id, { title: title.trim() });
43
+ }, [task, title, onUpdate]);
44
+ const handleDescriptionBlur = useCallback(() => {
45
+ if (task && description !== (task.description ?? "")) onUpdate(task.id, { description: description || null });
46
+ }, [task, description, onUpdate]);
47
+ const confirmDelete = useCallback(() => {
48
+ if (task) onDelete(task.id);
49
+ setConfirmingDelete(false);
50
+ }, [task, onDelete]);
51
+ if (!task) {
52
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-muted-foreground", children: [
53
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: "Task not found" }),
54
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "bare", size: "bare", onClick: () => onSelect(null), className: "mt-2 text-xs font-normal text-primary hover:underline", children: t("tasks.detail.back") })
55
+ ] });
56
+ }
57
+ const deleteButton = (size) => /* @__PURE__ */ jsx(
58
+ Button,
59
+ {
60
+ type: "button",
61
+ variant: "ghost",
62
+ size: "icon",
63
+ onClick: () => setConfirmingDelete(true),
64
+ "aria-label": t("tasks.detail.delete"),
65
+ title: t("tasks.detail.delete"),
66
+ className: cn(size === "header" ? "h-8 w-8" : "h-7 w-7", "rounded-lg text-muted-foreground hover:text-destructive"),
67
+ children: /* @__PURE__ */ jsx(Trash2, { className: size === "header" ? "h-4 w-4" : "h-3.5 w-3.5" })
68
+ }
69
+ );
70
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col", children: [
71
+ renderActions ? renderActions(deleteButton("header")) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 border-b border-border/40 px-2 py-1.5", children: [
72
+ /* @__PURE__ */ jsxs(
73
+ Button,
74
+ {
75
+ type: "button",
76
+ variant: "ghost",
77
+ onClick: () => onSelect(null),
78
+ className: "h-auto gap-1 rounded-lg px-1.5 py-1 text-[11.5px] font-normal text-muted-foreground",
79
+ children: [
80
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
81
+ t("tasks.drawer.title")
82
+ ]
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsx("span", { className: "flex-1" }),
86
+ deleteButton("inline")
87
+ ] }),
88
+ /* @__PURE__ */ jsx(
89
+ Input,
90
+ {
91
+ type: "text",
92
+ value: title,
93
+ onChange: (e) => setTitle(e.target.value),
94
+ onBlur: handleTitleBlur,
95
+ onKeyDown: (e) => {
96
+ if (e.key === "Enter") e.currentTarget.blur();
97
+ },
98
+ className: BARE_FIELD + " h-auto px-4 pb-0 pt-3 font-semibold"
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 px-4 pt-2", children: [
102
+ /* @__PURE__ */ jsxs(Select, { value: task.status, onValueChange: (v) => onUpdate(task.id, { status: v }), children: [
103
+ /* @__PURE__ */ jsxs(SelectTrigger, { className: PILL, children: [
104
+ /* @__PURE__ */ jsx("span", { className: `h-2 w-2 shrink-0 rounded-full ${STATUS_DOT_CLASS[task.status]}` }),
105
+ /* @__PURE__ */ jsx(SelectValue, {})
106
+ ] }),
107
+ /* @__PURE__ */ jsx(SelectContent, { children: TASK_STATUSES.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s, children: t(`tasks.status.${s}`) }, s)) })
108
+ ] }),
109
+ /* @__PURE__ */ jsxs(Select, { value: task.priority, onValueChange: (v) => onUpdate(task.id, { priority: v }), children: [
110
+ /* @__PURE__ */ jsx(SelectTrigger, { className: PILL, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
111
+ /* @__PURE__ */ jsx(SelectContent, { children: TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx(SelectItem, { value: p, children: t(`tasks.priority.${p}`) }, p)) })
112
+ ] }),
113
+ /* @__PURE__ */ jsx(
114
+ DatePicker,
115
+ {
116
+ variant: "toolbar",
117
+ format: "numeric",
118
+ value: task.dueDate ?? "",
119
+ onChange: (v) => onUpdate(task.id, { dueDate: v || null }),
120
+ triggerClassName: PILL + " pr-2 hover:bg-background"
121
+ }
122
+ ),
123
+ /* @__PURE__ */ jsx(TaskLabelBadges, { labelIds: task.labels, labels, max: 3 })
124
+ ] }),
125
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 [&>div]:flex", children: /* @__PURE__ */ jsx(
126
+ Textarea,
127
+ {
128
+ value: description,
129
+ onChange: (e) => setDescription(e.target.value),
130
+ onBlur: handleDescriptionBlur,
131
+ placeholder: t("tasks.detail.descriptionPlaceholder"),
132
+ className: BARE_FIELD + " min-h-[72px] flex-1 resize-none px-4 py-3 leading-relaxed placeholder:text-muted-foreground/60"
133
+ }
134
+ ) }),
135
+ onDelegate && /* @__PURE__ */ jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsxs(
136
+ Button,
137
+ {
138
+ type: "button",
139
+ onClick: () => onDelegate(
140
+ `${t("tasks.detail.chatWithAi.prompt")}
141
+
142
+ "${task.title}"${task.description ? `
143
+ ${task.description}` : ""}${task.dueDate ? `
144
+ (${t("tasks.detail.dueDate")}: ${task.dueDate})` : ""}`
145
+ ),
146
+ className: "group flex h-auto w-full gap-2 rounded-xl border-0 bg-gradient-to-r from-primary via-primary/80 to-primary bg-[length:200%_100%] bg-left px-3 py-2 text-[13px] shadow-sm transition-all duration-500 hover:bg-right hover:shadow-md focus-visible:ring-offset-0 active:shadow-md",
147
+ children: [
148
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 transition-transform duration-300 group-hover:rotate-12" }),
149
+ t("tasks.detail.chatWithAi")
150
+ ]
151
+ }
152
+ ) }),
153
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-border/40 px-4 py-2.5", children: [
154
+ /* @__PURE__ */ jsxs("p", { className: "pb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60", children: [
155
+ t("tasks.detail.subtasks"),
156
+ subtasks.length > 0 ? ` \xB7 ${subtasks.length}` : ""
157
+ ] }),
158
+ /* @__PURE__ */ jsx("div", { className: "-mx-3", children: /* @__PURE__ */ jsx(TaskQuickAdd, { parentId: task.id, onCreate }) }),
159
+ subtasks.length > 0 && /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/40", children: subtasks.map((st) => /* @__PURE__ */ jsx(TaskCard, { task: st, labels, locale, onSelect, onUpdate }, st.id)) })
160
+ ] }),
161
+ /* @__PURE__ */ jsx(
162
+ ConfirmDialog,
163
+ {
164
+ open: confirmingDelete,
165
+ title: t("tasks.detail.delete"),
166
+ description: t("tasks.detail.deleteConfirm"),
167
+ variant: "destructive",
168
+ confirmLabel: t("tasks.detail.delete"),
169
+ onConfirm: confirmDelete,
170
+ onCancel: () => setConfirmingDelete(false)
171
+ }
172
+ ),
173
+ /* @__PURE__ */ jsxs("p", { className: "border-t border-border/40 px-4 py-1.5 text-[10px] text-muted-foreground/60", children: [
174
+ t("tasks.detail.created"),
175
+ " ",
176
+ formatDateTime(task.createdAt),
177
+ " \xB7 ",
178
+ t("tasks.detail.updated"),
179
+ " ",
180
+ formatDateTime(task.updatedAt)
181
+ ] })
182
+ ] });
183
+ }
184
+
185
+ export { TaskDetail };
186
+ //# sourceMappingURL=chunk-KUO4ZJPR.js.map
187
+ //# sourceMappingURL=chunk-KUO4ZJPR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TaskDetail.tsx"],"names":[],"mappings":"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","file":"chunk-KUO4ZJPR.js","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react'\nimport { ArrowLeft, Sparkles, Trash2 } from 'lucide-react'\nimport {\n Button, ConfirmDialog, DatePicker, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, cn,\n} from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n TASK_PRIORITIES, TASK_STATUSES,\n type CreateTaskInput, type Task, type TaskLabel, type TaskPriority, type TaskStatus, type UpdateTaskInput,\n} from '../domain/task'\nimport { TaskLabelBadges } from './TaskLabelBadge'\nimport { TaskQuickAdd } from './TaskQuickAdd'\nimport { TaskCard } from './TaskCard'\nimport { STATUS_DOT_CLASS } from './TaskStatusGroup'\n\n// Strips the field chrome: these sit flush in the panel and edit in place.\nconst BARE_FIELD = 'rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 focus-visible:ring-offset-0'\nconst PILL = 'h-auto w-auto gap-1.5 rounded-full border-border bg-background py-1 pl-2 pr-1.5 text-xs font-medium shadow-none transition-colors hover:border-primary/40 focus:ring-0 focus:ring-offset-0 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground [&>svg]:opacity-100'\n\n/** Same footer format as the Notes tab: time when today, short date otherwise. */\nfunction formatDateTime(iso: string): string {\n const d = new Date(iso)\n const sameDay = d.toDateString() === new Date().toDateString()\n return sameDay\n ? d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })\n : d.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: '2-digit' })\n}\n\nexport interface TaskDetailProps {\n task: Task | undefined\n labels: TaskLabel[]\n locale?: string\n subtasks?: Task[]\n onSelect(id: string | null): void\n onUpdate(id: string, input: UpdateTaskInput): void\n onDelete(id: string): void\n onCreate(input: CreateTaskInput): Promise<unknown>\n /** Hands the task to the assistant. The button is drawn only when the host has one. */\n onDelegate?(prompt: string): void\n /**\n * Where the delete action goes when a host owns the chrome (the rail's header).\n * Without it the panel draws its own back-and-delete bar.\n */\n renderActions?(actions: React.ReactNode): React.ReactNode\n}\n\nexport function TaskDetail({\n task, labels, locale, subtasks = [], onSelect, onUpdate, onDelete, onCreate, onDelegate, renderActions,\n}: TaskDetailProps) {\n const t = useTranslation()\n const [title, setTitle] = useState('')\n const [description, setDescription] = useState('')\n const [confirmingDelete, setConfirmingDelete] = useState(false)\n\n useEffect(() => {\n if (task) {\n setTitle(task.title)\n setDescription(task.description ?? '')\n }\n }, [task])\n\n const handleTitleBlur = useCallback(() => {\n if (task && title.trim() && title !== task.title) onUpdate(task.id, { title: title.trim() })\n }, [task, title, onUpdate])\n\n const handleDescriptionBlur = useCallback(() => {\n if (task && description !== (task.description ?? '')) onUpdate(task.id, { description: description || null })\n }, [task, description, onUpdate])\n\n const confirmDelete = useCallback(() => {\n if (task) onDelete(task.id)\n setConfirmingDelete(false)\n }, [task, onDelete])\n\n if (!task) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-muted-foreground\">\n <p className=\"text-sm\">Task not found</p>\n <Button type=\"button\" variant=\"bare\" size=\"bare\" onClick={() => onSelect(null)} className=\"mt-2 text-xs font-normal text-primary hover:underline\">\n {t('tasks.detail.back')}\n </Button>\n </div>\n )\n }\n\n const deleteButton = (size: 'header' | 'inline') => (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setConfirmingDelete(true)}\n aria-label={t('tasks.detail.delete')}\n title={t('tasks.detail.delete')}\n className={cn(size === 'header' ? 'h-8 w-8' : 'h-7 w-7', 'rounded-lg text-muted-foreground hover:text-destructive')}\n >\n <Trash2 className={size === 'header' ? 'h-4 w-4' : 'h-3.5 w-3.5'} />\n </Button>\n )\n\n return (\n <div className=\"flex min-h-full flex-col\">\n {renderActions ? (\n renderActions(deleteButton('header'))\n ) : (\n <div className=\"flex items-center gap-1 border-b border-border/40 px-2 py-1.5\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={() => onSelect(null)}\n className=\"h-auto gap-1 rounded-lg px-1.5 py-1 text-[11.5px] font-normal text-muted-foreground\"\n >\n <ArrowLeft className=\"h-3.5 w-3.5\" />\n {t('tasks.drawer.title')}\n </Button>\n <span className=\"flex-1\" />\n {deleteButton('inline')}\n </div>\n )}\n\n <Input\n type=\"text\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n onBlur={handleTitleBlur}\n onKeyDown={(e) => { if (e.key === 'Enter') e.currentTarget.blur() }}\n className={BARE_FIELD + ' h-auto px-4 pb-0 pt-3 font-semibold'}\n />\n\n <div className=\"flex flex-wrap items-center gap-1.5 px-4 pt-2\">\n <Select value={task.status} onValueChange={(v) => onUpdate(task.id, { status: v as TaskStatus })}>\n <SelectTrigger className={PILL}>\n <span className={`h-2 w-2 shrink-0 rounded-full ${STATUS_DOT_CLASS[task.status]}`} />\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TASK_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>{t(`tasks.status.${s}`)}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select value={task.priority} onValueChange={(v) => onUpdate(task.id, { priority: v as TaskPriority })}>\n <SelectTrigger className={PILL}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TASK_PRIORITIES.map((p) => (\n <SelectItem key={p} value={p}>{t(`tasks.priority.${p}`)}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <DatePicker\n variant=\"toolbar\"\n format=\"numeric\"\n value={task.dueDate ?? ''}\n onChange={(v) => onUpdate(task.id, { dueDate: v || null })}\n triggerClassName={PILL + ' pr-2 hover:bg-background'}\n />\n\n <TaskLabelBadges labelIds={task.labels} labels={labels} max={3} />\n </div>\n\n <div className=\"flex flex-1 [&>div]:flex\">\n <Textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onBlur={handleDescriptionBlur}\n placeholder={t('tasks.detail.descriptionPlaceholder')}\n className={BARE_FIELD + ' min-h-[72px] flex-1 resize-none px-4 py-3 leading-relaxed placeholder:text-muted-foreground/60'}\n />\n </div>\n\n {onDelegate && (\n <div className=\"px-4 pb-2\">\n <Button\n type=\"button\"\n onClick={() =>\n onDelegate(\n `${t('tasks.detail.chatWithAi.prompt')}\\n\\n\"${task.title}\"${task.description ? `\\n${task.description}` : ''}${task.dueDate ? `\\n(${t('tasks.detail.dueDate')}: ${task.dueDate})` : ''}`,\n )\n }\n className=\"group flex h-auto w-full gap-2 rounded-xl border-0 bg-gradient-to-r from-primary via-primary/80 to-primary bg-[length:200%_100%] bg-left px-3 py-2 text-[13px] shadow-sm transition-all duration-500 hover:bg-right hover:shadow-md focus-visible:ring-offset-0 active:shadow-md\"\n >\n <Sparkles className=\"h-4 w-4 transition-transform duration-300 group-hover:rotate-12\" />\n {t('tasks.detail.chatWithAi')}\n </Button>\n </div>\n )}\n\n <div className=\"border-t border-border/40 px-4 py-2.5\">\n <p className=\"pb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground/60\">\n {t('tasks.detail.subtasks')}{subtasks.length > 0 ? ` · ${subtasks.length}` : ''}\n </p>\n <div className=\"-mx-3\">\n <TaskQuickAdd parentId={task.id} onCreate={onCreate} />\n </div>\n {subtasks.length > 0 && (\n <div className=\"divide-y divide-border/40\">\n {subtasks.map((st) => (\n <TaskCard key={st.id} task={st} labels={labels} locale={locale} onSelect={onSelect} onUpdate={onUpdate} />\n ))}\n </div>\n )}\n </div>\n\n <ConfirmDialog\n open={confirmingDelete}\n title={t('tasks.detail.delete')}\n description={t('tasks.detail.deleteConfirm')}\n variant=\"destructive\"\n confirmLabel={t('tasks.detail.delete')}\n onConfirm={confirmDelete}\n onCancel={() => setConfirmingDelete(false)}\n />\n\n <p className=\"border-t border-border/40 px-4 py-1.5 text-[10px] text-muted-foreground/60\">\n {t('tasks.detail.created')} {formatDateTime(task.createdAt)} · {t('tasks.detail.updated')} {formatDateTime(task.updatedAt)}\n </p>\n </div>\n )\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { TaskLabelBadges } from './chunk-BHNXKHFW.js';
2
+ import { TaskPriorityBadge } from './chunk-WNIXFQM4.js';
3
+ import { TaskSourceBadge } from './chunk-H435KYKF.js';
4
+ import { dueTone, isoDay, showsPriority, toggledStatus } from './chunk-5DWYILP2.js';
5
+ import { ListChecks } from 'lucide-react';
6
+ import { Checkbox } from '@fayz-ai/ui';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ var DUE_TONE_CLASS = {
10
+ overdue: "font-medium text-destructive",
11
+ today: "font-medium text-warning",
12
+ none: "text-muted-foreground"
13
+ };
14
+ function formatDueDate(dueDate, locale) {
15
+ const d = /* @__PURE__ */ new Date(dueDate + "T00:00:00");
16
+ const month = d.toLocaleDateString(locale, { month: "short" }).replace(/\.$/, "").replace(/^de\s+/i, "");
17
+ return `${d.getDate()} ${month}`;
18
+ }
19
+ function initials(name) {
20
+ return name.split(" ").map((n) => n[0]).join("").slice(0, 2).toUpperCase();
21
+ }
22
+ function TaskCard({ task, labels, locale, subtaskCount, showSource = true, onSelect, onUpdate }) {
23
+ const isDone = task.status === "done";
24
+ const tone = dueTone(task, isoDay());
25
+ const showPriority = showsPriority(task);
26
+ const sourceTag = showSource ? task.source : null;
27
+ const hasSubtasks = subtaskCount != null && subtaskCount.total > 0;
28
+ const hasMeta = Boolean(sourceTag) || showPriority || Boolean(task.dueDate) || hasSubtasks || task.labels.length > 0;
29
+ const handleCheck = (e) => {
30
+ e.stopPropagation();
31
+ onUpdate(task.id, { status: toggledStatus(task) });
32
+ };
33
+ return (
34
+ // One line per task: the title truncates and the meta is a fixed cluster on the right,
35
+ // so a list of eight still reads as eight rows.
36
+ /* @__PURE__ */ jsxs(
37
+ "div",
38
+ {
39
+ onClick: () => onSelect(task.id),
40
+ className: "group flex cursor-pointer items-center gap-2.5 rounded-button px-3 py-1.5 transition-colors hover:bg-muted",
41
+ children: [
42
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", onClick: handleCheck, children: /* @__PURE__ */ jsx(Checkbox, { checked: isDone, onChange: () => {
43
+ } }) }),
44
+ /* @__PURE__ */ jsx(
45
+ "p",
46
+ {
47
+ title: task.title,
48
+ className: `min-w-0 flex-1 truncate text-sm leading-snug ${isDone ? "text-muted-foreground line-through" : ""}`,
49
+ children: task.title
50
+ }
51
+ ),
52
+ hasMeta && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
53
+ /* @__PURE__ */ jsx(TaskSourceBadge, { source: sourceTag }),
54
+ showPriority && /* @__PURE__ */ jsx(TaskPriorityBadge, { priority: task.priority }),
55
+ /* @__PURE__ */ jsx(TaskLabelBadges, { labelIds: task.labels, labels, max: 1 }),
56
+ hasSubtasks && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 whitespace-nowrap text-[11px] tabular-nums text-muted-foreground", children: [
57
+ /* @__PURE__ */ jsx(ListChecks, { className: "h-3 w-3" }),
58
+ subtaskCount.done,
59
+ "/",
60
+ subtaskCount.total
61
+ ] }),
62
+ task.dueDate && /* @__PURE__ */ jsx("span", { className: `whitespace-nowrap text-[11px] tabular-nums ${DUE_TONE_CLASS[tone]}`, children: formatDueDate(task.dueDate, locale) })
63
+ ] }),
64
+ task.assignedToName && /* @__PURE__ */ jsx(
65
+ "div",
66
+ {
67
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary/10 text-[10px] font-medium text-primary",
68
+ title: task.assignedToName,
69
+ children: initials(task.assignedToName)
70
+ }
71
+ )
72
+ ]
73
+ }
74
+ )
75
+ );
76
+ }
77
+
78
+ export { TaskCard };
79
+ //# sourceMappingURL=chunk-KXUCWLLG.js.map
80
+ //# sourceMappingURL=chunk-KXUCWLLG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TaskCard.tsx"],"names":[],"mappings":"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","file":"chunk-KXUCWLLG.js","sourcesContent":["import React from 'react'\nimport { ListChecks } from 'lucide-react'\nimport { Checkbox } from '@fayz-ai/ui'\nimport {\n dueTone, isoDay, showsPriority, toggledStatus,\n type Task, type TaskDueTone, type TaskLabel, type UpdateTaskInput,\n} from '../domain/task'\nimport { TaskPriorityBadge } from './TaskPriorityBadge'\nimport { TaskLabelBadges } from './TaskLabelBadge'\nimport { TaskSourceBadge } from './TaskSourceBadge'\n\nconst DUE_TONE_CLASS: Record<TaskDueTone, string> = {\n overdue: 'font-medium text-destructive',\n today: 'font-medium text-warning',\n none: 'text-muted-foreground',\n}\n\n/** '27 ago', not '27 de ago.': the rail is narrow and every character the date spends, the title loses. */\nfunction formatDueDate(dueDate: string, locale?: string): string {\n const d = new Date(dueDate + 'T00:00:00')\n const month = d\n .toLocaleDateString(locale, { month: 'short' })\n .replace(/\\.$/, '')\n .replace(/^de\\s+/i, '')\n return `${d.getDate()} ${month}`\n}\n\nfunction initials(name: string): string {\n return name.split(' ').map((n) => n[0]).join('').slice(0, 2).toUpperCase()\n}\n\nexport interface TaskCardProps {\n task: Task\n labels: TaskLabel[]\n locale?: string\n subtaskCount?: { done: number; total: number }\n /** Off inside a group whose header already names the source. */\n showSource?: boolean\n onSelect(id: string): void\n onUpdate(id: string, input: UpdateTaskInput): void\n}\n\nexport function TaskCard({ task, labels, locale, subtaskCount, showSource = true, onSelect, onUpdate }: TaskCardProps) {\n const isDone = task.status === 'done'\n const tone = dueTone(task, isoDay())\n const showPriority = showsPriority(task)\n const sourceTag = showSource ? task.source : null\n const hasSubtasks = subtaskCount != null && subtaskCount.total > 0\n const hasMeta = Boolean(sourceTag) || showPriority || Boolean(task.dueDate) || hasSubtasks || task.labels.length > 0\n\n const handleCheck = (e: React.MouseEvent) => {\n e.stopPropagation()\n onUpdate(task.id, { status: toggledStatus(task) })\n }\n\n return (\n // One line per task: the title truncates and the meta is a fixed cluster on the right,\n // so a list of eight still reads as eight rows.\n <div\n onClick={() => onSelect(task.id)}\n className=\"group flex cursor-pointer items-center gap-2.5 rounded-button px-3 py-1.5 transition-colors hover:bg-muted\"\n >\n <div className=\"shrink-0\" onClick={handleCheck}>\n <Checkbox checked={isDone} onChange={() => {}} />\n </div>\n\n <p\n title={task.title}\n className={`min-w-0 flex-1 truncate text-sm leading-snug ${isDone ? 'text-muted-foreground line-through' : ''}`}\n >\n {task.title}\n </p>\n\n {hasMeta && (\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <TaskSourceBadge source={sourceTag} />\n {showPriority && <TaskPriorityBadge priority={task.priority} />}\n <TaskLabelBadges labelIds={task.labels} labels={labels} max={1} />\n {hasSubtasks && (\n <span className=\"inline-flex items-center gap-1 whitespace-nowrap text-[11px] tabular-nums text-muted-foreground\">\n <ListChecks className=\"h-3 w-3\" />\n {subtaskCount.done}/{subtaskCount.total}\n </span>\n )}\n {task.dueDate && (\n <span className={`whitespace-nowrap text-[11px] tabular-nums ${DUE_TONE_CLASS[tone]}`}>\n {formatDueDate(task.dueDate, locale)}\n </span>\n )}\n </div>\n )}\n\n {task.assignedToName && (\n <div\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary/10 text-[10px] font-medium text-primary\"\n title={task.assignedToName}\n >\n {initials(task.assignedToName)}\n </div>\n )}\n </div>\n )\n}\n"]}
@@ -0,0 +1,57 @@
1
+ import { useState } from 'react';
2
+ import { ChevronRight, ChevronDown } from 'lucide-react';
3
+ import { useTranslation } from '@fayz-ai/core';
4
+ import { Button } from '@fayz-ai/ui';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/components/TaskStatusGroup.tsx
8
+ var STATUS_DOT_CLASS = {
9
+ todo: "bg-muted-foreground",
10
+ in_progress: "bg-info",
11
+ done: "bg-success",
12
+ cancelled: "bg-muted-foreground/60"
13
+ };
14
+ function TaskGroup({ title, dotClass, tasks, defaultCollapsed = false, renderTask }) {
15
+ const [collapsed, setCollapsed] = useState(defaultCollapsed);
16
+ if (tasks.length === 0) return null;
17
+ const Chevron = collapsed ? ChevronRight : ChevronDown;
18
+ return /* @__PURE__ */ jsxs("div", { children: [
19
+ /* @__PURE__ */ jsxs(
20
+ Button,
21
+ {
22
+ type: "button",
23
+ variant: "bare",
24
+ size: "bare",
25
+ onClick: () => setCollapsed(!collapsed),
26
+ className: "flex w-full justify-start gap-2 rounded-none px-3 py-1.5 text-xs uppercase tracking-wider text-muted-foreground hover:text-foreground",
27
+ children: [
28
+ /* @__PURE__ */ jsx("span", { className: `h-2 w-2 rounded-full ${dotClass}` }),
29
+ /* @__PURE__ */ jsx("span", { children: title }),
30
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-normal", children: [
31
+ "(",
32
+ tasks.length,
33
+ ")"
34
+ ] }),
35
+ /* @__PURE__ */ jsx(Chevron, { className: "ml-auto h-3.5 w-3.5" })
36
+ ]
37
+ }
38
+ ),
39
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "px-1", children: tasks.map(renderTask) })
40
+ ] });
41
+ }
42
+ function TaskStatusGroup({ status, ...rest }) {
43
+ const t = useTranslation();
44
+ return /* @__PURE__ */ jsx(
45
+ TaskGroup,
46
+ {
47
+ title: t(`tasks.status.${status}`),
48
+ dotClass: STATUS_DOT_CLASS[status],
49
+ defaultCollapsed: status === "cancelled",
50
+ ...rest
51
+ }
52
+ );
53
+ }
54
+
55
+ export { STATUS_DOT_CLASS, TaskGroup, TaskStatusGroup };
56
+ //# sourceMappingURL=chunk-LY4RNYRG.js.map
57
+ //# sourceMappingURL=chunk-LY4RNYRG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TaskStatusGroup.tsx"],"names":[],"mappings":";;;;;;;AAMO,IAAM,gBAAA,GAA+C;AAAA,EAC1D,IAAA,EAAM,qBAAA;AAAA,EACN,WAAA,EAAa,SAAA;AAAA,EACb,IAAA,EAAM,YAAA;AAAA,EACN,SAAA,EAAW;AACb;AAWO,SAAS,SAAA,CAAU,EAAE,KAAA,EAAO,QAAA,EAAU,OAAO,gBAAA,GAAmB,KAAA,EAAO,YAAW,EAAmB;AAC1G,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,gBAAgB,CAAA;AAC3D,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAE/B,EAAA,MAAM,OAAA,GAAU,YAAY,YAAA,GAAe,WAAA;AAE3C,EAAA,4BACG,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,OAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAK,MAAA;AAAA,QACL,OAAA,EAAS,MAAM,YAAA,CAAa,CAAC,SAAS,CAAA;AAAA,QACtC,SAAA,EAAU,uIAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,CAAA,qBAAA,EAAwB,QAAQ,CAAA,CAAA,EAAI,CAAA;AAAA,0BACrD,GAAA,CAAC,UAAM,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,0BACb,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yBAAA,EAA0B,QAAA,EAAA;AAAA,YAAA,GAAA;AAAA,YAAE,KAAA,CAAM,MAAA;AAAA,YAAO;AAAA,WAAA,EAAC,CAAA;AAAA,0BAC1D,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAU,qBAAA,EAAsB;AAAA;AAAA;AAAA,KAC3C;AAAA,IAEC,CAAC,6BAAa,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,MAAA,EAAQ,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,UAAU,CAAA,EAAE;AAAA,GAAA,EAC9D,CAAA;AAEJ;AAEO,SAAS,eAAA,CAAgB,EAAE,MAAA,EAAQ,GAAG,MAAK,EAA0E;AAC1H,EAAA,MAAM,IAAI,cAAA,EAAe;AACzB,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,CAAE,CAAA,aAAA,EAAgB,MAAM,CAAA,CAAE,CAAA;AAAA,MACjC,QAAA,EAAU,iBAAiB,MAAM,CAAA;AAAA,MACjC,kBAAkB,MAAA,KAAW,WAAA;AAAA,MAC5B,GAAG;AAAA;AAAA,GACN;AAEJ","file":"chunk-LY4RNYRG.js","sourcesContent":["import React, { useState } from 'react'\nimport { ChevronDown, ChevronRight } from 'lucide-react'\nimport { useTranslation } from '@fayz-ai/core'\nimport { Button } from '@fayz-ai/ui'\nimport type { Task, TaskStatus } from '../domain/task'\n\nexport const STATUS_DOT_CLASS: Record<TaskStatus, string> = {\n todo: 'bg-muted-foreground',\n in_progress: 'bg-info',\n done: 'bg-success',\n cancelled: 'bg-muted-foreground/60',\n}\n\nexport interface TaskGroupProps {\n title: string\n dotClass: string\n tasks: Task[]\n defaultCollapsed?: boolean\n renderTask(task: Task): React.ReactNode\n}\n\n/** One collapsible section of the list; `title` and `dotClass` let a non-status group share the shape. */\nexport function TaskGroup({ title, dotClass, tasks, defaultCollapsed = false, renderTask }: TaskGroupProps) {\n const [collapsed, setCollapsed] = useState(defaultCollapsed)\n if (tasks.length === 0) return null\n\n const Chevron = collapsed ? ChevronRight : ChevronDown\n\n return (\n <div>\n <Button\n type=\"button\"\n variant=\"bare\"\n size=\"bare\"\n onClick={() => setCollapsed(!collapsed)}\n className=\"flex w-full justify-start gap-2 rounded-none px-3 py-1.5 text-xs uppercase tracking-wider text-muted-foreground hover:text-foreground\"\n >\n <span className={`h-2 w-2 rounded-full ${dotClass}`} />\n <span>{title}</span>\n <span className=\"text-[10px] font-normal\">({tasks.length})</span>\n <Chevron className=\"ml-auto h-3.5 w-3.5\" />\n </Button>\n\n {!collapsed && <div className=\"px-1\">{tasks.map(renderTask)}</div>}\n </div>\n )\n}\n\nexport function TaskStatusGroup({ status, ...rest }: { status: TaskStatus } & Pick<TaskGroupProps, 'tasks' | 'renderTask'>) {\n const t = useTranslation()\n return (\n <TaskGroup\n title={t(`tasks.status.${status}`)}\n dotClass={STATUS_DOT_CLASS[status]}\n defaultCollapsed={status === 'cancelled'}\n {...rest}\n />\n )\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { TASK_FILTERS, countForFilter } from './chunk-5DWYILP2.js';
2
+ import { User } from 'lucide-react';
3
+ import { useTranslation } from '@fayz-ai/core';
4
+ import { Button } from '@fayz-ai/ui';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var FILTER_LABEL_KEY = {
8
+ all: "tasks.filter.all",
9
+ todo: "tasks.filter.todo",
10
+ in_progress: "tasks.filter.inProgress",
11
+ done: "tasks.filter.done"
12
+ };
13
+ var CHIP = "rounded-full px-3 py-1 text-xs font-medium";
14
+ var CHIP_ON = "bg-primary text-primary-foreground";
15
+ var CHIP_OFF = "text-muted-foreground hover:bg-muted hover:text-foreground";
16
+ function TasksFilterBar({ activeFilter, summary, mine, currentUserId, onFilterChange, onMineChange }) {
17
+ const t = useTranslation();
18
+ return (
19
+ // h-10 matches the page's tab strip across the screen, so the rule under it does not jump between panels.
20
+ /* @__PURE__ */ jsxs("div", { className: "flex h-10 shrink-0 items-center gap-1 overflow-x-auto border-b px-3", children: [
21
+ TASK_FILTERS.map((filter) => {
22
+ const active = activeFilter === filter;
23
+ const count = countForFilter(summary, filter);
24
+ return /* @__PURE__ */ jsxs(
25
+ Button,
26
+ {
27
+ type: "button",
28
+ variant: "bare",
29
+ size: "bare",
30
+ onClick: () => onFilterChange(filter),
31
+ className: `${CHIP} ${active ? CHIP_ON : CHIP_OFF}`,
32
+ children: [
33
+ t(FILTER_LABEL_KEY[filter]),
34
+ count != null && /* @__PURE__ */ jsx("span", { className: `text-[10px] ${active ? "text-primary-foreground/80" : "text-muted-foreground"}`, children: count })
35
+ ]
36
+ },
37
+ filter
38
+ );
39
+ }),
40
+ currentUserId && /* @__PURE__ */ jsxs(
41
+ Button,
42
+ {
43
+ type: "button",
44
+ variant: "bare",
45
+ size: "bare",
46
+ onClick: () => onMineChange(!mine),
47
+ className: `${CHIP} ml-auto ${mine ? CHIP_ON : CHIP_OFF}`,
48
+ children: [
49
+ /* @__PURE__ */ jsx(User, { className: "h-3 w-3" }),
50
+ t("tasks.filter.mine")
51
+ ]
52
+ }
53
+ )
54
+ ] })
55
+ );
56
+ }
57
+
58
+ export { TasksFilterBar };
59
+ //# sourceMappingURL=chunk-Q5FSJ2D6.js.map
60
+ //# sourceMappingURL=chunk-Q5FSJ2D6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TasksFilterBar.tsx"],"names":[],"mappings":";;;;;;AAMA,IAAM,gBAAA,GAAkE;AAAA,EACtE,GAAA,EAAK,kBAAA;AAAA,EACL,IAAA,EAAM,mBAAA;AAAA,EACN,WAAA,EAAa,yBAAA;AAAA,EACb,IAAA,EAAM;AACR,CAAA;AAEA,IAAM,IAAA,GAAO,4CAAA;AACb,IAAM,OAAA,GAAU,oCAAA;AAChB,IAAM,QAAA,GAAW,4DAAA;AAYV,SAAS,cAAA,CAAe,EAAE,YAAA,EAAc,OAAA,EAAS,MAAM,aAAA,EAAe,cAAA,EAAgB,cAAa,EAAwB;AAChI,EAAA,MAAM,IAAI,cAAA,EAAe;AAEzB,EAAA;AAAA;AAAA,oBAEE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qEAAA,EACZ,QAAA,EAAA;AAAA,MAAA,YAAA,CAAa,GAAA,CAAI,CAAC,MAAA,KAAW;AAC5B,QAAA,MAAM,SAAS,YAAA,KAAiB,MAAA;AAChC,QAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,OAAA,EAAS,MAAM,CAAA;AAC5C,QAAA,uBACE,IAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YAEC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAQ,MAAA;AAAA,YACR,IAAA,EAAK,MAAA;AAAA,YACL,OAAA,EAAS,MAAM,cAAA,CAAe,MAAM,CAAA;AAAA,YACpC,WAAW,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,MAAA,GAAS,UAAU,QAAQ,CAAA,CAAA;AAAA,YAEhD,QAAA,EAAA;AAAA,cAAA,CAAA,CAAE,gBAAA,CAAiB,MAAM,CAAC,CAAA;AAAA,cAC1B,KAAA,IAAS,IAAA,oBACR,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,eAAe,MAAA,GAAS,4BAAA,GAA+B,uBAAuB,CAAA,CAAA,EAC5F,QAAA,EAAA,KAAA,EACH;AAAA;AAAA,WAAA;AAAA,UAXG;AAAA,SAaP;AAAA,MAEJ,CAAC,CAAA;AAAA,MAEA,aAAA,oBACC,IAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,QAAA;AAAA,UACL,OAAA,EAAQ,MAAA;AAAA,UACR,IAAA,EAAK,MAAA;AAAA,UACL,OAAA,EAAS,MAAM,YAAA,CAAa,CAAC,IAAI,CAAA;AAAA,UACjC,WAAW,CAAA,EAAG,IAAI,CAAA,SAAA,EAAY,IAAA,GAAO,UAAU,QAAQ,CAAA,CAAA;AAAA,UAEvD,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,WAAU,SAAA,EAAU,CAAA;AAAA,YACzB,EAAE,mBAAmB;AAAA;AAAA;AAAA;AACxB,KAAA,EAEJ;AAAA;AAEJ","file":"chunk-Q5FSJ2D6.js","sourcesContent":["import React from 'react'\nimport { User } from 'lucide-react'\nimport { useTranslation } from '@fayz-ai/core'\nimport { Button } from '@fayz-ai/ui'\nimport { TASK_FILTERS, countForFilter, type TaskFilter, type TasksSummary } from '../domain/task'\n\nconst FILTER_LABEL_KEY: Record<(typeof TASK_FILTERS)[number], string> = {\n all: 'tasks.filter.all',\n todo: 'tasks.filter.todo',\n in_progress: 'tasks.filter.inProgress',\n done: 'tasks.filter.done',\n}\n\nconst CHIP = 'rounded-full px-3 py-1 text-xs font-medium'\nconst CHIP_ON = 'bg-primary text-primary-foreground'\nconst CHIP_OFF = 'text-muted-foreground hover:bg-muted hover:text-foreground'\n\nexport interface TasksFilterBarProps {\n activeFilter: TaskFilter\n summary: TasksSummary | null\n mine: boolean\n /** \"Mine\" is offered only once we know who is asking. */\n currentUserId: string | null\n onFilterChange(filter: TaskFilter): void\n onMineChange(mine: boolean): void\n}\n\nexport function TasksFilterBar({ activeFilter, summary, mine, currentUserId, onFilterChange, onMineChange }: TasksFilterBarProps) {\n const t = useTranslation()\n\n return (\n // h-10 matches the page's tab strip across the screen, so the rule under it does not jump between panels.\n <div className=\"flex h-10 shrink-0 items-center gap-1 overflow-x-auto border-b px-3\">\n {TASK_FILTERS.map((filter) => {\n const active = activeFilter === filter\n const count = countForFilter(summary, filter)\n return (\n <Button\n key={filter}\n type=\"button\"\n variant=\"bare\"\n size=\"bare\"\n onClick={() => onFilterChange(filter)}\n className={`${CHIP} ${active ? CHIP_ON : CHIP_OFF}`}\n >\n {t(FILTER_LABEL_KEY[filter])}\n {count != null && (\n <span className={`text-[10px] ${active ? 'text-primary-foreground/80' : 'text-muted-foreground'}`}>\n {count}\n </span>\n )}\n </Button>\n )\n })}\n\n {currentUserId && (\n <Button\n type=\"button\"\n variant=\"bare\"\n size=\"bare\"\n onClick={() => onMineChange(!mine)}\n className={`${CHIP} ml-auto ${mine ? CHIP_ON : CHIP_OFF}`}\n >\n <User className=\"h-3 w-3\" />\n {t('tasks.filter.mine')}\n </Button>\n )}\n </div>\n )\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { AlertTriangle, ArrowUp, Minus, ArrowDown } from 'lucide-react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/components/TaskPriorityBadge.tsx
5
+ var PRIORITY_CONFIG = {
6
+ low: { color: "text-muted-foreground", bg: "bg-muted", icon: ArrowDown },
7
+ medium: { color: "text-info-soft-foreground", bg: "bg-info-soft", icon: Minus },
8
+ high: { color: "text-warning-soft-foreground", bg: "bg-warning-soft", icon: ArrowUp },
9
+ urgent: { color: "text-destructive-soft-foreground", bg: "bg-destructive-soft", icon: AlertTriangle }
10
+ };
11
+ function TaskPriorityBadge({ priority, showLabel }) {
12
+ const cfg = PRIORITY_CONFIG[priority];
13
+ const Icon = cfg.icon;
14
+ return /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium ${cfg.color} ${cfg.bg}`, children: [
15
+ /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
16
+ showLabel && /* @__PURE__ */ jsx("span", { className: "capitalize", children: priority })
17
+ ] });
18
+ }
19
+
20
+ export { TaskPriorityBadge };
21
+ //# sourceMappingURL=chunk-WNIXFQM4.js.map
22
+ //# sourceMappingURL=chunk-WNIXFQM4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TaskPriorityBadge.tsx"],"names":[],"mappings":";;;;AAIA,IAAM,eAAA,GAA0H;AAAA,EAC9H,KAAK,EAAE,KAAA,EAAO,yBAAyB,EAAA,EAAI,UAAA,EAAY,MAAM,SAAA,EAAU;AAAA,EACvE,QAAQ,EAAE,KAAA,EAAO,6BAA6B,EAAA,EAAI,cAAA,EAAgB,MAAM,KAAA,EAAM;AAAA,EAC9E,MAAM,EAAE,KAAA,EAAO,gCAAgC,EAAA,EAAI,iBAAA,EAAmB,MAAM,OAAA,EAAQ;AAAA,EACpF,QAAQ,EAAE,KAAA,EAAO,oCAAoC,EAAA,EAAI,qBAAA,EAAuB,MAAM,aAAA;AACxF,CAAA;AAEO,SAAS,iBAAA,CAAkB,EAAE,QAAA,EAAU,SAAA,EAAU,EAAoD;AAC1G,EAAA,MAAM,GAAA,GAAM,gBAAgB,QAAQ,CAAA;AACpC,EAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AACjB,EAAA,uBACE,IAAA,CAAC,UAAK,SAAA,EAAW,CAAA,yEAAA,EAA4E,IAAI,KAAK,CAAA,CAAA,EAAI,GAAA,CAAI,EAAE,CAAA,CAAA,EAC9G,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,WAAU,SAAA,EAAU,CAAA;AAAA,IACzB,SAAA,oBAAa,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,cAAc,QAAA,EAAA,QAAA,EAAS;AAAA,GAAA,EACvD,CAAA;AAEJ","file":"chunk-WNIXFQM4.js","sourcesContent":["import React from 'react'\nimport { AlertTriangle, ArrowUp, ArrowDown, Minus } from 'lucide-react'\nimport type { TaskPriority } from '../domain/task'\n\nconst PRIORITY_CONFIG: Record<TaskPriority, { color: string; bg: string; icon: React.ComponentType<{ className?: string }> }> = {\n low: { color: 'text-muted-foreground', bg: 'bg-muted', icon: ArrowDown },\n medium: { color: 'text-info-soft-foreground', bg: 'bg-info-soft', icon: Minus },\n high: { color: 'text-warning-soft-foreground', bg: 'bg-warning-soft', icon: ArrowUp },\n urgent: { color: 'text-destructive-soft-foreground', bg: 'bg-destructive-soft', icon: AlertTriangle },\n}\n\nexport function TaskPriorityBadge({ priority, showLabel }: { priority: TaskPriority; showLabel?: boolean }) {\n const cfg = PRIORITY_CONFIG[priority]\n const Icon = cfg.icon\n return (\n <span className={`inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium ${cfg.color} ${cfg.bg}`}>\n <Icon className=\"h-3 w-3\" />\n {showLabel && <span className=\"capitalize\">{priority}</span>}\n </span>\n )\n}\n"]}