@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.
- package/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/chunk-5DWYILP2.js +102 -0
- package/dist/chunk-5DWYILP2.js.map +1 -0
- package/dist/chunk-BHNXKHFW.js +29 -0
- package/dist/chunk-BHNXKHFW.js.map +1 -0
- package/dist/chunk-H435KYKF.js +26 -0
- package/dist/chunk-H435KYKF.js.map +1 -0
- package/dist/chunk-IHQV22ML.js +128 -0
- package/dist/chunk-IHQV22ML.js.map +1 -0
- package/dist/chunk-IYQSFHSK.js +175 -0
- package/dist/chunk-IYQSFHSK.js.map +1 -0
- package/dist/chunk-KM7XZUFP.js +114 -0
- package/dist/chunk-KM7XZUFP.js.map +1 -0
- package/dist/chunk-KUO4ZJPR.js +187 -0
- package/dist/chunk-KUO4ZJPR.js.map +1 -0
- package/dist/chunk-KXUCWLLG.js +80 -0
- package/dist/chunk-KXUCWLLG.js.map +1 -0
- package/dist/chunk-LY4RNYRG.js +57 -0
- package/dist/chunk-LY4RNYRG.js.map +1 -0
- package/dist/chunk-Q5FSJ2D6.js +60 -0
- package/dist/chunk-Q5FSJ2D6.js.map +1 -0
- package/dist/chunk-WNIXFQM4.js +22 -0
- package/dist/chunk-WNIXFQM4.js.map +1 -0
- package/dist/chunk-XVAIXG7V.js +42 -0
- package/dist/chunk-XVAIXG7V.js.map +1 -0
- package/dist/components/TaskCard.d.ts +17 -0
- package/dist/components/TaskCard.d.ts.map +1 -0
- package/dist/components/TaskCard.js +7 -0
- package/dist/components/TaskCard.js.map +1 -0
- package/dist/components/TaskDetail.d.ts +21 -0
- package/dist/components/TaskDetail.d.ts.map +1 -0
- package/dist/components/TaskDetail.js +10 -0
- package/dist/components/TaskDetail.js.map +1 -0
- package/dist/components/TaskLabelBadge.d.ts +11 -0
- package/dist/components/TaskLabelBadge.d.ts.map +1 -0
- package/dist/components/TaskLabelBadge.js +4 -0
- package/dist/components/TaskLabelBadge.js.map +1 -0
- package/dist/components/TaskPriorityBadge.d.ts +7 -0
- package/dist/components/TaskPriorityBadge.d.ts.map +1 -0
- package/dist/components/TaskPriorityBadge.js +3 -0
- package/dist/components/TaskPriorityBadge.js.map +1 -0
- package/dist/components/TaskQuickAdd.d.ts +8 -0
- package/dist/components/TaskQuickAdd.d.ts.map +1 -0
- package/dist/components/TaskQuickAdd.js +3 -0
- package/dist/components/TaskQuickAdd.js.map +1 -0
- package/dist/components/TaskSourceBadge.d.ts +5 -0
- package/dist/components/TaskSourceBadge.d.ts.map +1 -0
- package/dist/components/TaskSourceBadge.js +4 -0
- package/dist/components/TaskSourceBadge.js.map +1 -0
- package/dist/components/TaskStatusGroup.d.ts +16 -0
- package/dist/components/TaskStatusGroup.d.ts.map +1 -0
- package/dist/components/TaskStatusGroup.js +3 -0
- package/dist/components/TaskStatusGroup.js.map +1 -0
- package/dist/components/TasksFilterBar.d.ts +13 -0
- package/dist/components/TasksFilterBar.d.ts.map +1 -0
- package/dist/components/TasksFilterBar.js +4 -0
- package/dist/components/TasksFilterBar.js.map +1 -0
- package/dist/data/mock.d.ts +3 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/mock.js +4 -0
- package/dist/data/mock.js.map +1 -0
- package/dist/data/provider.d.ts +13 -0
- package/dist/data/provider.d.ts.map +1 -0
- package/dist/data/provider.js +3 -0
- package/dist/data/provider.js.map +1 -0
- package/dist/data/supabase.d.ts +9 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/supabase.js +4 -0
- package/dist/data/supabase.js.map +1 -0
- package/dist/domain/task.d.ts +121 -0
- package/dist/domain/task.d.ts.map +1 -0
- package/dist/domain/task.js +3 -0
- package/dist/domain/task.js.map +1 -0
- package/dist/store.d.ts +35 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +6 -0
- package/dist/store.js.map +1 -0
- package/dist/views/TasksPanel.d.ts +8 -0
- package/dist/views/TasksPanel.d.ts.map +1 -0
- package/dist/views/TasksPanel.js +103 -0
- package/dist/views/TasksPanel.js.map +1 -0
- package/package.json +51 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Faya Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fayz-ai/tasks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> The tasks bounded context: what a task is, the rules about it, where it is stored, and the UI over it.
|
|
4
|
+
|
|
5
|
+
**Status:** beta — extracted from `@fayz-ai/admin`, which still re-exports every name it had. Pre-1.0: minor APIs may change before 1.0.
|
|
6
|
+
|
|
7
|
+
The reference shape for every context in the SDK ([docs/platform/PATTERNS.md](../../docs/platform/PATTERNS.md)).
|
|
8
|
+
There is no entry file: each module is imported by its path.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @fayz-ai/tasks
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Modules
|
|
17
|
+
|
|
18
|
+
| Import | What it holds |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `@fayz-ai/tasks/domain/task` | `Task`, `TaskLabel`, `TasksSummary` and the pure rules (`isOverdue`, `summarizeTasks`, `splitOnboardingTasks`, `groupByStatus`, `listQuery`…) |
|
|
21
|
+
| `@fayz-ai/tasks/data/provider` | `TasksDataProvider`, the repository port |
|
|
22
|
+
| `@fayz-ai/tasks/data/supabase` | `createSupabaseTasksProvider`, `taskEntityDef` — the only module that names a table |
|
|
23
|
+
| `@fayz-ai/tasks/data/mock` | `createMockTasksProvider`, the same contract in memory |
|
|
24
|
+
| `@fayz-ai/tasks/store` | `useTasksStore`, `getTasksProvider`, `setTasksProvider` |
|
|
25
|
+
| `@fayz-ai/tasks/components/*` | presentational pieces: `TaskCard`, `TaskDetail`, `TasksFilterBar`, badges… |
|
|
26
|
+
| `@fayz-ai/tasks/views/TasksPanel` | the list-and-detail panel over the store; the host passes locale, assistant and header slot |
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { isOverdue, isoDay } from '@fayz-ai/tasks/domain/task'
|
|
30
|
+
import { useTasksStore } from '@fayz-ai/tasks/store'
|
|
31
|
+
|
|
32
|
+
const late = useTasksStore((s) => s.tasks.filter((t) => isOverdue(t, isoDay())))
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Words used here are defined in [GLOSSARY.md](./GLOSSARY.md).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/domain/task.ts
|
|
2
|
+
var TASK_STATUSES = ["todo", "in_progress", "done", "cancelled"];
|
|
3
|
+
var TASK_PRIORITIES = ["low", "medium", "high", "urgent"];
|
|
4
|
+
var TASK_FILTERS = ["all", "todo", "in_progress", "done"];
|
|
5
|
+
var ONBOARDING_TASK_SOURCE = "onboarding";
|
|
6
|
+
var EMPTY_TASKS_SUMMARY = {
|
|
7
|
+
total: 0,
|
|
8
|
+
todo: 0,
|
|
9
|
+
inProgress: 0,
|
|
10
|
+
done: 0,
|
|
11
|
+
overdue: 0,
|
|
12
|
+
dueToday: 0,
|
|
13
|
+
highPriority: 0
|
|
14
|
+
};
|
|
15
|
+
function isoDay(at = /* @__PURE__ */ new Date()) {
|
|
16
|
+
return at.toISOString().slice(0, 10);
|
|
17
|
+
}
|
|
18
|
+
function isOpen(task) {
|
|
19
|
+
return task.status !== "done" && task.status !== "cancelled";
|
|
20
|
+
}
|
|
21
|
+
function isHighPriority(priority) {
|
|
22
|
+
return priority === "high" || priority === "urgent";
|
|
23
|
+
}
|
|
24
|
+
function isOverdue(task, today) {
|
|
25
|
+
return task.dueDate != null && task.dueDate < today && isOpen(task);
|
|
26
|
+
}
|
|
27
|
+
function isDueToday(task, today) {
|
|
28
|
+
return task.dueDate === today && isOpen(task);
|
|
29
|
+
}
|
|
30
|
+
function dueTone(task, today) {
|
|
31
|
+
if (task.status === "done" || !task.dueDate) return "none";
|
|
32
|
+
if (task.dueDate < today) return "overdue";
|
|
33
|
+
return task.dueDate === today ? "today" : "none";
|
|
34
|
+
}
|
|
35
|
+
function summarizeTasks(tasks, today) {
|
|
36
|
+
return {
|
|
37
|
+
total: tasks.length,
|
|
38
|
+
todo: tasks.filter((t) => t.status === "todo").length,
|
|
39
|
+
inProgress: tasks.filter((t) => t.status === "in_progress").length,
|
|
40
|
+
done: tasks.filter((t) => t.status === "done").length,
|
|
41
|
+
overdue: tasks.filter((t) => isOverdue(t, today)).length,
|
|
42
|
+
dueToday: tasks.filter((t) => isDueToday(t, today)).length,
|
|
43
|
+
highPriority: tasks.filter((t) => isHighPriority(t.priority) && isOpen(t)).length
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function openCount(summary) {
|
|
47
|
+
return summary ? summary.todo + summary.inProgress : 0;
|
|
48
|
+
}
|
|
49
|
+
function countForFilter(summary, filter) {
|
|
50
|
+
if (!summary) return void 0;
|
|
51
|
+
switch (filter) {
|
|
52
|
+
case "all":
|
|
53
|
+
return summary.total;
|
|
54
|
+
case "todo":
|
|
55
|
+
return summary.todo;
|
|
56
|
+
case "in_progress":
|
|
57
|
+
return summary.inProgress;
|
|
58
|
+
case "done":
|
|
59
|
+
return summary.done;
|
|
60
|
+
default:
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function completedAtFor(status, now) {
|
|
65
|
+
return status === "done" ? now : null;
|
|
66
|
+
}
|
|
67
|
+
function toggledStatus(task) {
|
|
68
|
+
return task.status === "done" ? "todo" : "done";
|
|
69
|
+
}
|
|
70
|
+
function nextPosition(siblingPositions) {
|
|
71
|
+
return siblingPositions.reduce((max, p) => Math.max(max, p), -1) + 1;
|
|
72
|
+
}
|
|
73
|
+
function listQuery(scope, query) {
|
|
74
|
+
return {
|
|
75
|
+
parentId: null,
|
|
76
|
+
...query,
|
|
77
|
+
...scope.filter !== "all" ? { status: scope.filter } : {},
|
|
78
|
+
...scope.mine && scope.currentUserId ? { assignedToId: scope.currentUserId } : {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function splitOnboardingTasks(tasks) {
|
|
82
|
+
const onboarding = [];
|
|
83
|
+
const rest = [];
|
|
84
|
+
for (const task of tasks) {
|
|
85
|
+
if (task.source === ONBOARDING_TASK_SOURCE) onboarding.push(task);
|
|
86
|
+
else rest.push(task);
|
|
87
|
+
}
|
|
88
|
+
return { onboarding, rest };
|
|
89
|
+
}
|
|
90
|
+
function groupByStatus(tasks) {
|
|
91
|
+
return TASK_STATUSES.map((status) => ({ status, tasks: tasks.filter((t) => t.status === status) })).filter((group) => group.status !== "cancelled" || group.tasks.length > 0);
|
|
92
|
+
}
|
|
93
|
+
function showsPriority(task) {
|
|
94
|
+
return task.priority !== "medium";
|
|
95
|
+
}
|
|
96
|
+
function resolveLabels(labelIds, labels) {
|
|
97
|
+
return labelIds.flatMap((id) => labels.find((l) => l.id === id) ?? []);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export { EMPTY_TASKS_SUMMARY, ONBOARDING_TASK_SOURCE, TASK_FILTERS, TASK_PRIORITIES, TASK_STATUSES, completedAtFor, countForFilter, dueTone, groupByStatus, isDueToday, isHighPriority, isOpen, isOverdue, isoDay, listQuery, nextPosition, openCount, resolveLabels, showsPriority, splitOnboardingTasks, summarizeTasks, toggledStatus };
|
|
101
|
+
//# sourceMappingURL=chunk-5DWYILP2.js.map
|
|
102
|
+
//# sourceMappingURL=chunk-5DWYILP2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/task.ts"],"names":[],"mappings":"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","file":"chunk-5DWYILP2.js","sourcesContent":["export type TaskStatus = 'todo' | 'in_progress' | 'done' | 'cancelled'\nexport type TaskPriority = 'low' | 'medium' | 'high' | 'urgent'\nexport type TaskFilter = TaskStatus | 'all'\nexport type TaskDueTone = 'overdue' | 'today' | 'none'\n\nexport const TASK_STATUSES: TaskStatus[] = ['todo', 'in_progress', 'done', 'cancelled']\nexport const TASK_PRIORITIES: TaskPriority[] = ['low', 'medium', 'high', 'urgent']\nexport const TASK_FILTERS = ['all', 'todo', 'in_progress', 'done'] as const satisfies readonly TaskFilter[]\n\n/** Tasks the setup wizard seeded. Paired with a `sourceKey` from the catalog. */\nexport const ONBOARDING_TASK_SOURCE = 'onboarding'\n\nexport interface Task {\n id: string\n title: string\n description: string | null\n status: TaskStatus\n priority: TaskPriority\n dueDate: string | null\n completedAt: string | null\n assignedToId: string | null\n assignedToName: string | null\n parentId: string | null\n labels: string[]\n position: number\n /** Which feature created this row ('onboarding', 'agent'), null when a person did. */\n source: string | null\n /** Stable key within that source — what makes re-seeding idempotent. */\n sourceKey: string | null\n createdBy: string | null\n createdByName: string | null\n tenantId: string\n createdAt: string\n updatedAt: string\n}\n\nexport interface TaskLabel {\n id: string\n name: string\n color: string\n isActive: boolean\n tenantId: string\n createdAt: string\n}\n\nexport interface CreateTaskInput {\n title: string\n description?: string\n status?: TaskStatus\n priority?: TaskPriority\n dueDate?: string | null\n assignedToId?: string | null\n assignedToName?: string | null\n parentId?: string | null\n labels?: string[]\n source?: string | null\n sourceKey?: string | null\n}\n\nexport interface UpdateTaskInput {\n title?: string\n description?: string | null\n status?: TaskStatus\n priority?: TaskPriority\n dueDate?: string | null\n assignedToId?: string | null\n labels?: string[]\n position?: number\n}\n\nexport interface TaskQuery {\n status?: TaskStatus | TaskStatus[]\n priority?: TaskPriority | TaskPriority[]\n assignedToId?: string\n parentId?: string | null\n search?: string\n overdue?: boolean\n}\n\nexport interface TasksSummary {\n total: number\n todo: number\n inProgress: number\n done: number\n overdue: number\n dueToday: number\n highPriority: number\n}\n\nexport interface TaskListScope {\n filter: TaskFilter\n mine: boolean\n currentUserId: string | null\n}\n\nexport const EMPTY_TASKS_SUMMARY: TasksSummary = {\n total: 0, todo: 0, inProgress: 0, done: 0, overdue: 0, dueToday: 0, highPriority: 0,\n}\n\n/** Due dates are calendar days compared as 'YYYY-MM-DD' strings, in UTC. */\nexport function isoDay(at: Date = new Date()): string {\n return at.toISOString().slice(0, 10)\n}\n\nexport function isOpen(task: Pick<Task, 'status'>): boolean {\n return task.status !== 'done' && task.status !== 'cancelled'\n}\n\nexport function isHighPriority(priority: TaskPriority): boolean {\n return priority === 'high' || priority === 'urgent'\n}\n\nexport function isOverdue(task: Pick<Task, 'status' | 'dueDate'>, today: string): boolean {\n return task.dueDate != null && task.dueDate < today && isOpen(task)\n}\n\nexport function isDueToday(task: Pick<Task, 'status' | 'dueDate'>, today: string): boolean {\n return task.dueDate === today && isOpen(task)\n}\n\n/** How a row colours its date. Only a done task goes quiet; a cancelled one still shows. */\nexport function dueTone(task: Pick<Task, 'status' | 'dueDate'>, today: string): TaskDueTone {\n if (task.status === 'done' || !task.dueDate) return 'none'\n if (task.dueDate < today) return 'overdue'\n return task.dueDate === today ? 'today' : 'none'\n}\n\n/** Counts over top-level tasks; the caller is the one that knows how to leave subtasks out. */\nexport function summarizeTasks(\n tasks: Pick<Task, 'status' | 'priority' | 'dueDate'>[],\n today: string,\n): TasksSummary {\n return {\n total: tasks.length,\n todo: tasks.filter((t) => t.status === 'todo').length,\n inProgress: tasks.filter((t) => t.status === 'in_progress').length,\n done: tasks.filter((t) => t.status === 'done').length,\n overdue: tasks.filter((t) => isOverdue(t, today)).length,\n dueToday: tasks.filter((t) => isDueToday(t, today)).length,\n highPriority: tasks.filter((t) => isHighPriority(t.priority) && isOpen(t)).length,\n }\n}\n\n/** What the rail badge counts. */\nexport function openCount(summary: TasksSummary | null): number {\n return summary ? summary.todo + summary.inProgress : 0\n}\n\nexport function countForFilter(summary: TasksSummary | null, filter: TaskFilter): number | undefined {\n if (!summary) return undefined\n switch (filter) {\n case 'all': return summary.total\n case 'todo': return summary.todo\n case 'in_progress': return summary.inProgress\n case 'done': return summary.done\n default: return undefined\n }\n}\n\n/** The checklist latches on completedAt, so it moves with the status instead of being set by hand. */\nexport function completedAtFor(status: TaskStatus, now: string): string | null {\n return status === 'done' ? now : null\n}\n\nexport function toggledStatus(task: Pick<Task, 'status'>): TaskStatus {\n return task.status === 'done' ? 'todo' : 'done'\n}\n\n/** A new task goes to the end of its column: one past the highest position among its siblings. */\nexport function nextPosition(siblingPositions: number[]): number {\n return siblingPositions.reduce((max, p) => Math.max(max, p), -1) + 1\n}\n\n/** The tab is the workspace's: \"mine\" narrows only once we know who is asking. */\nexport function listQuery(scope: TaskListScope, query?: TaskQuery): TaskQuery {\n return {\n parentId: null,\n ...query,\n ...(scope.filter !== 'all' ? { status: scope.filter } : {}),\n ...(scope.mine && scope.currentUserId ? { assignedToId: scope.currentUserId } : {}),\n }\n}\n\n/** The setup checklist reads as one thing, so it is lifted out before the status grouping runs. */\nexport function splitOnboardingTasks(tasks: Task[]): { onboarding: Task[]; rest: Task[] } {\n const onboarding: Task[] = []\n const rest: Task[] = []\n for (const task of tasks) {\n if (task.source === ONBOARDING_TASK_SOURCE) onboarding.push(task)\n else rest.push(task)\n }\n return { onboarding, rest }\n}\n\n/** Status sections in board order. Cancelled appears only when something is in it. */\nexport function groupByStatus(tasks: Task[]): Array<{ status: TaskStatus; tasks: Task[] }> {\n return TASK_STATUSES\n .map((status) => ({ status, tasks: tasks.filter((t) => t.status === status) }))\n .filter((group) => group.status !== 'cancelled' || group.tasks.length > 0)\n}\n\n/** 'medium' is the default nobody chose, so it earns no mark on a row. */\nexport function showsPriority(task: Pick<Task, 'priority'>): boolean {\n return task.priority !== 'medium'\n}\n\n/** `labels` also holds free-form strings (the notes bridge writes 'note:<id>'); only real labels resolve. */\nexport function resolveLabels(labelIds: string[], labels: TaskLabel[]): TaskLabel[] {\n return labelIds.flatMap((id) => labels.find((l) => l.id === id) ?? [])\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { resolveLabels } from './chunk-5DWYILP2.js';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
function TaskLabelBadge({ label }) {
|
|
5
|
+
return /* @__PURE__ */ jsx(
|
|
6
|
+
"span",
|
|
7
|
+
{
|
|
8
|
+
className: "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium leading-none",
|
|
9
|
+
style: { backgroundColor: label.color + "20", color: label.color },
|
|
10
|
+
children: label.name
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
function TaskLabelBadges({ labelIds, labels, max = 2 }) {
|
|
15
|
+
const known = resolveLabels(labelIds, labels);
|
|
16
|
+
if (known.length === 0) return null;
|
|
17
|
+
const overflow = known.length - max;
|
|
18
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
19
|
+
known.slice(0, max).map((label) => /* @__PURE__ */ jsx(TaskLabelBadge, { label }, label.id)),
|
|
20
|
+
overflow > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
21
|
+
"+",
|
|
22
|
+
overflow
|
|
23
|
+
] })
|
|
24
|
+
] });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { TaskLabelBadge, TaskLabelBadges };
|
|
28
|
+
//# sourceMappingURL=chunk-BHNXKHFW.js.map
|
|
29
|
+
//# sourceMappingURL=chunk-BHNXKHFW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/TaskLabelBadge.tsx"],"names":[],"mappings":";;;AAGO,SAAS,cAAA,CAAe,EAAE,KAAA,EAAM,EAAyB;AAC9D,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,wFAAA;AAAA,MACV,KAAA,EAAO,EAAE,eAAA,EAAiB,KAAA,CAAM,QAAQ,IAAA,EAAM,KAAA,EAAO,MAAM,KAAA,EAAM;AAAA,MAEhE,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA,GACT;AAEJ;AAEO,SAAS,gBAAgB,EAAE,QAAA,EAAU,MAAA,EAAQ,GAAA,GAAM,GAAE,EAA8D;AAExH,EAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,QAAA,EAAU,MAAM,CAAA;AAC5C,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,MAAM,QAAA,GAAW,MAAM,MAAA,GAAS,GAAA;AAEhC,EAAA,uBACE,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gCAAA,EACb,QAAA,EAAA;AAAA,IAAA,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,qBACxB,GAAA,CAAC,cAAA,EAAA,EAA8B,KAAA,EAAA,EAAV,KAAA,CAAM,EAAkB,CAC9C,CAAA;AAAA,IACA,QAAA,GAAW,CAAA,oBAAK,IAAA,CAAC,MAAA,EAAA,EAAK,WAAU,mCAAA,EAAoC,QAAA,EAAA;AAAA,MAAA,GAAA;AAAA,MAAE;AAAA,KAAA,EAAS;AAAA,GAAA,EAClF,CAAA;AAEJ","file":"chunk-BHNXKHFW.js","sourcesContent":["import React from 'react'\nimport { resolveLabels, type TaskLabel } from '../domain/task'\n\nexport function TaskLabelBadge({ label }: { label: TaskLabel }) {\n return (\n <span\n className=\"inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium leading-none\"\n style={{ backgroundColor: label.color + '20', color: label.color }}\n >\n {label.name}\n </span>\n )\n}\n\nexport function TaskLabelBadges({ labelIds, labels, max = 2 }: { labelIds: string[]; labels: TaskLabel[]; max?: number }) {\n // The overflow counts what resolves, not what was passed: \"+2\" for two free-form strings counts nothing.\n const known = resolveLabels(labelIds, labels)\n if (known.length === 0) return null\n const overflow = known.length - max\n\n return (\n <span className=\"inline-flex items-center gap-1\">\n {known.slice(0, max).map((label) => (\n <TaskLabelBadge key={label.id} label={label} />\n ))}\n {overflow > 0 && <span className=\"text-[10px] text-muted-foreground\">+{overflow}</span>}\n </span>\n )\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ONBOARDING_TASK_SOURCE } from './chunk-5DWYILP2.js';
|
|
2
|
+
import { useTranslation } from '@fayz-ai/core';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var SOURCE_STYLE = {
|
|
6
|
+
[ONBOARDING_TASK_SOURCE]: "bg-primary/10 text-primary",
|
|
7
|
+
agent: "bg-info-soft text-info-soft-foreground"
|
|
8
|
+
};
|
|
9
|
+
function TaskSourceBadge({ source }) {
|
|
10
|
+
const t = useTranslation();
|
|
11
|
+
if (!source) return null;
|
|
12
|
+
const key = `tasks.source.${source}`;
|
|
13
|
+
const label = t(key);
|
|
14
|
+
if (!label || label === key) return null;
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"span",
|
|
17
|
+
{
|
|
18
|
+
className: `inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium leading-none ${SOURCE_STYLE[source] ?? "bg-muted text-muted-foreground"}`,
|
|
19
|
+
children: label
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { TaskSourceBadge };
|
|
25
|
+
//# sourceMappingURL=chunk-H435KYKF.js.map
|
|
26
|
+
//# sourceMappingURL=chunk-H435KYKF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/TaskSourceBadge.tsx"],"names":[],"mappings":";;;;AAMA,IAAM,YAAA,GAAuC;AAAA,EAC3C,CAAC,sBAAsB,GAAG,4BAAA;AAAA,EAC1B,KAAA,EAAO;AACT,CAAA;AAEO,SAAS,eAAA,CAAgB,EAAE,MAAA,EAAO,EAA8B;AACrE,EAAA,MAAM,IAAI,cAAA,EAAe;AACzB,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AAEpB,EAAA,MAAM,GAAA,GAAM,gBAAgB,MAAM,CAAA,CAAA;AAClC,EAAA,MAAM,KAAA,GAAQ,EAAE,GAAG,CAAA;AACnB,EAAA,IAAI,CAAC,KAAA,IAAS,KAAA,KAAU,GAAA,EAAK,OAAO,IAAA;AAEpC,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,CAAA,uFAAA,EACT,YAAA,CAAa,MAAM,KAAK,gCAC1B,CAAA,CAAA;AAAA,MAEC,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ","file":"chunk-H435KYKF.js","sourcesContent":["import React from 'react'\nimport { useTranslation } from '@fayz-ai/core'\nimport { ONBOARDING_TASK_SOURCE } from '../domain/task'\n\n// Keyed by `source`, not by label: TaskLabelBadges drops anything that is not a real label row,\n// so a marker stored in `labels` would be invisible.\nconst SOURCE_STYLE: Record<string, string> = {\n [ONBOARDING_TASK_SOURCE]: 'bg-primary/10 text-primary',\n agent: 'bg-info-soft text-info-soft-foreground',\n}\n\nexport function TaskSourceBadge({ source }: { source: string | null }) {\n const t = useTranslation()\n if (!source) return null\n\n const key = `tasks.source.${source}`\n const label = t(key)\n if (!label || label === key) return null\n\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium leading-none ${\n SOURCE_STYLE[source] ?? 'bg-muted text-muted-foreground'\n }`}\n >\n {label}\n </span>\n )\n}\n"]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { summarizeTasks, isoDay, completedAtFor, nextPosition } from './chunk-5DWYILP2.js';
|
|
2
|
+
|
|
3
|
+
// src/data/mock.ts
|
|
4
|
+
function uid() {
|
|
5
|
+
return crypto.randomUUID?.() ?? String(Date.now());
|
|
6
|
+
}
|
|
7
|
+
function now() {
|
|
8
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9
|
+
}
|
|
10
|
+
function seedLabels() {
|
|
11
|
+
const createdAt = now();
|
|
12
|
+
return [
|
|
13
|
+
{ id: "lbl-bug", name: "Bug", color: "#ef4444", isActive: true, tenantId: "mock", createdAt },
|
|
14
|
+
{ id: "lbl-feature", name: "Feature", color: "#3b82f6", isActive: true, tenantId: "mock", createdAt },
|
|
15
|
+
{ id: "lbl-urgent", name: "Urgent", color: "#f59e0b", isActive: true, tenantId: "mock", createdAt },
|
|
16
|
+
{ id: "lbl-personal", name: "Personal", color: "#8b5cf6", isActive: true, tenantId: "mock", createdAt }
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
function blankTask() {
|
|
20
|
+
const at = now();
|
|
21
|
+
return {
|
|
22
|
+
tenantId: "mock",
|
|
23
|
+
description: null,
|
|
24
|
+
completedAt: null,
|
|
25
|
+
assignedToId: null,
|
|
26
|
+
assignedToName: null,
|
|
27
|
+
parentId: null,
|
|
28
|
+
labels: [],
|
|
29
|
+
source: null,
|
|
30
|
+
sourceKey: null,
|
|
31
|
+
createdBy: null,
|
|
32
|
+
createdByName: null,
|
|
33
|
+
createdAt: at,
|
|
34
|
+
updatedAt: at
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function seedTasks() {
|
|
38
|
+
const today = isoDay();
|
|
39
|
+
return [
|
|
40
|
+
{ ...blankTask(), id: uid(), title: "Setup payment gateway", status: "todo", priority: "high", dueDate: today, position: 0 },
|
|
41
|
+
{ ...blankTask(), id: uid(), title: "Update menu photos", status: "todo", priority: "medium", dueDate: null, position: 1 },
|
|
42
|
+
{ ...blankTask(), id: uid(), title: "Review monthly report", status: "in_progress", priority: "medium", dueDate: null, position: 0 },
|
|
43
|
+
{ ...blankTask(), id: uid(), title: "Fix login bug", status: "in_progress", priority: "urgent", dueDate: today, position: 1, labels: ["lbl-bug"] },
|
|
44
|
+
{ ...blankTask(), id: uid(), title: "Onboard new employee", status: "done", priority: "low", dueDate: null, position: 0 }
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
function matchesQuery(task, query) {
|
|
48
|
+
if (query.status) {
|
|
49
|
+
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
50
|
+
if (!statuses.includes(task.status)) return false;
|
|
51
|
+
}
|
|
52
|
+
if (query.priority) {
|
|
53
|
+
const priorities = Array.isArray(query.priority) ? query.priority : [query.priority];
|
|
54
|
+
if (!priorities.includes(task.priority)) return false;
|
|
55
|
+
}
|
|
56
|
+
if (query.assignedToId && task.assignedToId !== query.assignedToId) return false;
|
|
57
|
+
if (query.parentId !== void 0 && task.parentId !== query.parentId) return false;
|
|
58
|
+
if (query.search && !task.title.toLowerCase().includes(query.search.toLowerCase())) return false;
|
|
59
|
+
if (query.overdue && task.dueDate && task.dueDate >= isoDay()) return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
var byPosition = (a, b) => a.position - b.position;
|
|
63
|
+
function createMockTasksProvider() {
|
|
64
|
+
let tasks = seedTasks();
|
|
65
|
+
const labels = seedLabels();
|
|
66
|
+
return {
|
|
67
|
+
async getTasks(query) {
|
|
68
|
+
return tasks.filter((t) => matchesQuery(t, query)).sort(byPosition);
|
|
69
|
+
},
|
|
70
|
+
async getTaskById(id) {
|
|
71
|
+
return tasks.find((t) => t.id === id) ?? null;
|
|
72
|
+
},
|
|
73
|
+
async getSubtasks(parentId) {
|
|
74
|
+
return tasks.filter((t) => t.parentId === parentId).sort(byPosition);
|
|
75
|
+
},
|
|
76
|
+
async createTask(input) {
|
|
77
|
+
const status = input.status ?? "todo";
|
|
78
|
+
const parentId = input.parentId ?? null;
|
|
79
|
+
const siblings = tasks.filter((t) => t.status === status && t.parentId === parentId);
|
|
80
|
+
const task = {
|
|
81
|
+
...blankTask(),
|
|
82
|
+
id: uid(),
|
|
83
|
+
title: input.title,
|
|
84
|
+
description: input.description ?? null,
|
|
85
|
+
status,
|
|
86
|
+
priority: input.priority ?? "medium",
|
|
87
|
+
dueDate: input.dueDate ?? null,
|
|
88
|
+
assignedToId: input.assignedToId ?? null,
|
|
89
|
+
assignedToName: input.assignedToName ?? null,
|
|
90
|
+
parentId,
|
|
91
|
+
labels: input.labels ?? [],
|
|
92
|
+
position: nextPosition(siblings.map((t) => t.position)),
|
|
93
|
+
source: input.source ?? null,
|
|
94
|
+
sourceKey: input.sourceKey ?? null
|
|
95
|
+
};
|
|
96
|
+
tasks.push(task);
|
|
97
|
+
return task;
|
|
98
|
+
},
|
|
99
|
+
async updateTask(id, input) {
|
|
100
|
+
const idx = tasks.findIndex((t) => t.id === id);
|
|
101
|
+
if (idx === -1) throw new Error("Task not found");
|
|
102
|
+
const at = now();
|
|
103
|
+
const completedAt = input.status === void 0 ? tasks[idx].completedAt : completedAtFor(input.status, at);
|
|
104
|
+
tasks[idx] = { ...tasks[idx], ...input, completedAt, updatedAt: at };
|
|
105
|
+
return tasks[idx];
|
|
106
|
+
},
|
|
107
|
+
async deleteTask(id) {
|
|
108
|
+
tasks = tasks.filter((t) => t.id !== id && t.parentId !== id);
|
|
109
|
+
},
|
|
110
|
+
async reorderTask(id, newPosition, status) {
|
|
111
|
+
const task = tasks.find((t) => t.id === id);
|
|
112
|
+
if (!task) return;
|
|
113
|
+
task.status = status;
|
|
114
|
+
task.position = newPosition;
|
|
115
|
+
task.updatedAt = now();
|
|
116
|
+
},
|
|
117
|
+
async getLabels() {
|
|
118
|
+
return labels.filter((l) => l.isActive);
|
|
119
|
+
},
|
|
120
|
+
async getSummary() {
|
|
121
|
+
return summarizeTasks(tasks.filter((t) => t.parentId === null), isoDay());
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { createMockTasksProvider };
|
|
127
|
+
//# sourceMappingURL=chunk-IHQV22ML.js.map
|
|
128
|
+
//# sourceMappingURL=chunk-IHQV22ML.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/mock.ts"],"names":[],"mappings":"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","file":"chunk-IHQV22ML.js","sourcesContent":["import {\n completedAtFor, isoDay, nextPosition, summarizeTasks,\n type Task, type TaskLabel, type TaskQuery,\n} from '../domain/task'\nimport type { TasksDataProvider } from './provider'\n\nfunction uid(): string { return crypto.randomUUID?.() ?? String(Date.now()) }\nfunction now(): string { return new Date().toISOString() }\n\nfunction seedLabels(): TaskLabel[] {\n const createdAt = now()\n return [\n { id: 'lbl-bug', name: 'Bug', color: '#ef4444', isActive: true, tenantId: 'mock', createdAt },\n { id: 'lbl-feature', name: 'Feature', color: '#3b82f6', isActive: true, tenantId: 'mock', createdAt },\n { id: 'lbl-urgent', name: 'Urgent', color: '#f59e0b', isActive: true, tenantId: 'mock', createdAt },\n { id: 'lbl-personal', name: 'Personal', color: '#8b5cf6', isActive: true, tenantId: 'mock', createdAt },\n ]\n}\n\nfunction blankTask(): Omit<Task, 'id' | 'title' | 'status' | 'priority' | 'dueDate' | 'position'> {\n const at = now()\n return {\n tenantId: 'mock',\n description: null,\n completedAt: null,\n assignedToId: null,\n assignedToName: null,\n parentId: null,\n labels: [],\n source: null,\n sourceKey: null,\n createdBy: null,\n createdByName: null,\n createdAt: at,\n updatedAt: at,\n }\n}\n\nfunction seedTasks(): Task[] {\n const today = isoDay()\n return [\n { ...blankTask(), id: uid(), title: 'Setup payment gateway', status: 'todo', priority: 'high', dueDate: today, position: 0 },\n { ...blankTask(), id: uid(), title: 'Update menu photos', status: 'todo', priority: 'medium', dueDate: null, position: 1 },\n { ...blankTask(), id: uid(), title: 'Review monthly report', status: 'in_progress', priority: 'medium', dueDate: null, position: 0 },\n { ...blankTask(), id: uid(), title: 'Fix login bug', status: 'in_progress', priority: 'urgent', dueDate: today, position: 1, labels: ['lbl-bug'] },\n { ...blankTask(), id: uid(), title: 'Onboard new employee', status: 'done', priority: 'low', dueDate: null, position: 0 },\n ]\n}\n\nfunction matchesQuery(task: Task, query: TaskQuery): boolean {\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status]\n if (!statuses.includes(task.status)) return false\n }\n if (query.priority) {\n const priorities = Array.isArray(query.priority) ? query.priority : [query.priority]\n if (!priorities.includes(task.priority)) return false\n }\n if (query.assignedToId && task.assignedToId !== query.assignedToId) return false\n if (query.parentId !== undefined && task.parentId !== query.parentId) return false\n if (query.search && !task.title.toLowerCase().includes(query.search.toLowerCase())) return false\n if (query.overdue && task.dueDate && task.dueDate >= isoDay()) return false\n return true\n}\n\nconst byPosition = (a: Task, b: Task) => a.position - b.position\n\nexport function createMockTasksProvider(): TasksDataProvider {\n let tasks = seedTasks()\n const labels = seedLabels()\n\n return {\n async getTasks(query) {\n return tasks.filter((t) => matchesQuery(t, query)).sort(byPosition)\n },\n\n async getTaskById(id) {\n return tasks.find((t) => t.id === id) ?? null\n },\n\n async getSubtasks(parentId) {\n return tasks.filter((t) => t.parentId === parentId).sort(byPosition)\n },\n\n async createTask(input) {\n const status = input.status ?? 'todo'\n const parentId = input.parentId ?? null\n const siblings = tasks.filter((t) => t.status === status && t.parentId === parentId)\n const task: Task = {\n ...blankTask(),\n id: uid(),\n title: input.title,\n description: input.description ?? null,\n status,\n priority: input.priority ?? 'medium',\n dueDate: input.dueDate ?? null,\n assignedToId: input.assignedToId ?? null,\n assignedToName: input.assignedToName ?? null,\n parentId,\n labels: input.labels ?? [],\n position: nextPosition(siblings.map((t) => t.position)),\n source: input.source ?? null,\n sourceKey: input.sourceKey ?? null,\n }\n tasks.push(task)\n return task\n },\n\n async updateTask(id, input) {\n const idx = tasks.findIndex((t) => t.id === id)\n if (idx === -1) throw new Error('Task not found')\n const at = now()\n const completedAt = input.status === undefined ? tasks[idx].completedAt : completedAtFor(input.status, at)\n tasks[idx] = { ...tasks[idx], ...input, completedAt, updatedAt: at }\n return tasks[idx]\n },\n\n async deleteTask(id) {\n tasks = tasks.filter((t) => t.id !== id && t.parentId !== id)\n },\n\n async reorderTask(id, newPosition, status) {\n const task = tasks.find((t) => t.id === id)\n if (!task) return\n task.status = status\n task.position = newPosition\n task.updatedAt = now()\n },\n\n async getLabels() {\n return labels.filter((l) => l.isActive)\n },\n\n async getSummary() {\n return summarizeTasks(tasks.filter((t) => t.parentId === null), isoDay())\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { TASK_STATUSES, TASK_PRIORITIES, EMPTY_TASKS_SUMMARY, summarizeTasks, isoDay, completedAtFor, nextPosition } from './chunk-5DWYILP2.js';
|
|
2
|
+
import { getActiveTenantId, isKnownMissing, rememberIfMissing, getSupabaseClientOptional } from '@fayz-ai/core';
|
|
3
|
+
|
|
4
|
+
var TASKS_TABLE = "tasks";
|
|
5
|
+
var TASKS_OBJECT = "tasks";
|
|
6
|
+
var LABELS_TABLE = "plg_tasks_labels";
|
|
7
|
+
function toTask(row) {
|
|
8
|
+
return {
|
|
9
|
+
id: row.id,
|
|
10
|
+
title: row.title,
|
|
11
|
+
description: row.description,
|
|
12
|
+
status: row.status,
|
|
13
|
+
priority: row.priority,
|
|
14
|
+
dueDate: row.due_date,
|
|
15
|
+
completedAt: row.completed_at,
|
|
16
|
+
assignedToId: row.assigned_to_id,
|
|
17
|
+
assignedToName: row.assigned_to_name,
|
|
18
|
+
parentId: row.parent_id,
|
|
19
|
+
labels: Array.isArray(row.labels) ? row.labels : [],
|
|
20
|
+
position: row.position,
|
|
21
|
+
source: row.source,
|
|
22
|
+
sourceKey: row.source_key,
|
|
23
|
+
createdBy: row.created_by,
|
|
24
|
+
createdByName: row.created_by_name,
|
|
25
|
+
tenantId: row.tenant_id,
|
|
26
|
+
createdAt: row.created_at,
|
|
27
|
+
updatedAt: row.updated_at
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function toTaskLabel(row) {
|
|
31
|
+
return {
|
|
32
|
+
id: row.id,
|
|
33
|
+
name: row.name,
|
|
34
|
+
color: row.color,
|
|
35
|
+
isActive: row.is_active,
|
|
36
|
+
tenantId: row.tenant_id,
|
|
37
|
+
createdAt: row.created_at
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function client() {
|
|
41
|
+
const supabase = getSupabaseClientOptional();
|
|
42
|
+
if (!supabase) throw new Error("Supabase not initialized");
|
|
43
|
+
return supabase;
|
|
44
|
+
}
|
|
45
|
+
function createSupabaseTasksProvider() {
|
|
46
|
+
return {
|
|
47
|
+
async getTasks(query) {
|
|
48
|
+
const tenantId = getActiveTenantId();
|
|
49
|
+
if (!tenantId || isKnownMissing(TASKS_OBJECT)) return [];
|
|
50
|
+
let q = client().from(TASKS_TABLE).select("*").eq("tenant_id", tenantId);
|
|
51
|
+
if (query.parentId === null) q = q.is("parent_id", null);
|
|
52
|
+
else if (query.parentId) q = q.eq("parent_id", query.parentId);
|
|
53
|
+
if (query.status) q = q.in("status", Array.isArray(query.status) ? query.status : [query.status]);
|
|
54
|
+
if (query.priority) q = q.in("priority", Array.isArray(query.priority) ? query.priority : [query.priority]);
|
|
55
|
+
if (query.assignedToId) q = q.eq("assigned_to_id", query.assignedToId);
|
|
56
|
+
if (query.search) q = q.ilike("title", `%${query.search}%`);
|
|
57
|
+
if (query.overdue) q = q.lt("due_date", isoDay()).not("status", "in", '("done","cancelled")');
|
|
58
|
+
const { data, error } = await q.order("position", { ascending: true }).order("created_at", { ascending: false });
|
|
59
|
+
if (error) {
|
|
60
|
+
rememberIfMissing(TASKS_OBJECT, error);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return (data ?? []).map(toTask);
|
|
64
|
+
},
|
|
65
|
+
async getTaskById(id) {
|
|
66
|
+
const { data } = await client().from(TASKS_TABLE).select("*").eq("id", id).maybeSingle();
|
|
67
|
+
return data ? toTask(data) : null;
|
|
68
|
+
},
|
|
69
|
+
async getSubtasks(parentId) {
|
|
70
|
+
const { data } = await client().from(TASKS_TABLE).select("*").eq("parent_id", parentId).order("position", { ascending: true });
|
|
71
|
+
return (data ?? []).map(toTask);
|
|
72
|
+
},
|
|
73
|
+
async createTask(input) {
|
|
74
|
+
const tenantId = getActiveTenantId();
|
|
75
|
+
if (!tenantId) throw new Error("No active tenant");
|
|
76
|
+
const status = input.status ?? "todo";
|
|
77
|
+
const { data: last } = await client().from(TASKS_TABLE).select("position").eq("tenant_id", tenantId).eq("status", status).is("parent_id", input.parentId ?? null).order("position", { ascending: false }).limit(1);
|
|
78
|
+
const { data, error } = await client().from(TASKS_TABLE).insert({
|
|
79
|
+
tenant_id: tenantId,
|
|
80
|
+
title: input.title,
|
|
81
|
+
description: input.description ?? null,
|
|
82
|
+
status,
|
|
83
|
+
priority: input.priority ?? "medium",
|
|
84
|
+
due_date: input.dueDate ?? null,
|
|
85
|
+
assigned_to_id: input.assignedToId ?? null,
|
|
86
|
+
assigned_to_name: input.assignedToName ?? null,
|
|
87
|
+
parent_id: input.parentId ?? null,
|
|
88
|
+
labels: input.labels ?? [],
|
|
89
|
+
position: nextPosition((last ?? []).map((r) => r.position)),
|
|
90
|
+
source: input.source ?? null,
|
|
91
|
+
source_key: input.sourceKey ?? null
|
|
92
|
+
}).select("*").single();
|
|
93
|
+
if (error) throw error;
|
|
94
|
+
return toTask(data);
|
|
95
|
+
},
|
|
96
|
+
async updateTask(id, input) {
|
|
97
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
98
|
+
const payload = { updated_at: now };
|
|
99
|
+
if (input.title !== void 0) payload.title = input.title;
|
|
100
|
+
if (input.description !== void 0) payload.description = input.description;
|
|
101
|
+
if (input.priority !== void 0) payload.priority = input.priority;
|
|
102
|
+
if (input.dueDate !== void 0) payload.due_date = input.dueDate;
|
|
103
|
+
if (input.assignedToId !== void 0) payload.assigned_to_id = input.assignedToId;
|
|
104
|
+
if (input.labels !== void 0) payload.labels = input.labels;
|
|
105
|
+
if (input.position !== void 0) payload.position = input.position;
|
|
106
|
+
if (input.status !== void 0) {
|
|
107
|
+
payload.status = input.status;
|
|
108
|
+
payload.completed_at = completedAtFor(input.status, now);
|
|
109
|
+
}
|
|
110
|
+
const { data, error } = await client().from(TASKS_TABLE).update(payload).eq("id", id).select("*").single();
|
|
111
|
+
if (error) throw error;
|
|
112
|
+
return toTask(data);
|
|
113
|
+
},
|
|
114
|
+
async deleteTask(id) {
|
|
115
|
+
await client().from(TASKS_TABLE).delete().eq("id", id);
|
|
116
|
+
},
|
|
117
|
+
async reorderTask(id, newPosition, status) {
|
|
118
|
+
await client().from(TASKS_TABLE).update({ position: newPosition, status, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
119
|
+
},
|
|
120
|
+
async getLabels() {
|
|
121
|
+
const tenantId = getActiveTenantId();
|
|
122
|
+
if (!tenantId) return [];
|
|
123
|
+
try {
|
|
124
|
+
const { data } = await client().from(LABELS_TABLE).select("*").eq("tenant_id", tenantId).eq("is_active", true).order("name");
|
|
125
|
+
return (data ?? []).map(toTaskLabel);
|
|
126
|
+
} catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
async getSummary() {
|
|
131
|
+
const tenantId = getActiveTenantId();
|
|
132
|
+
if (!tenantId || isKnownMissing(TASKS_OBJECT)) return { ...EMPTY_TASKS_SUMMARY };
|
|
133
|
+
const { data, error } = await client().from(TASKS_TABLE).select("status, priority, due_date").eq("tenant_id", tenantId).is("parent_id", null);
|
|
134
|
+
if (error) {
|
|
135
|
+
rememberIfMissing(TASKS_OBJECT, error);
|
|
136
|
+
return { ...EMPTY_TASKS_SUMMARY };
|
|
137
|
+
}
|
|
138
|
+
const rows = data ?? [];
|
|
139
|
+
return summarizeTasks(rows.map((r) => ({ status: r.status, priority: r.priority, dueDate: r.due_date })), isoDay());
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
var taskEntityDef = {
|
|
144
|
+
name: "Task",
|
|
145
|
+
namePlural: "Tasks",
|
|
146
|
+
icon: "CheckSquare",
|
|
147
|
+
displayField: "title",
|
|
148
|
+
defaultSort: "position",
|
|
149
|
+
data: {
|
|
150
|
+
table: TASKS_TABLE,
|
|
151
|
+
tenantScoped: true,
|
|
152
|
+
tenantIdColumn: "tenant_id",
|
|
153
|
+
searchColumns: ["title", "description"]
|
|
154
|
+
},
|
|
155
|
+
fields: [
|
|
156
|
+
{ key: "title", label: "Title", type: "text", required: true, searchable: true, showInTable: true },
|
|
157
|
+
{ key: "description", label: "Description", type: "textarea", searchable: true },
|
|
158
|
+
{ key: "status", label: "Status", type: "select", options: TASK_STATUSES, defaultValue: "todo", showInTable: true },
|
|
159
|
+
{ key: "priority", label: "Priority", type: "select", options: TASK_PRIORITIES, defaultValue: "medium", showInTable: true },
|
|
160
|
+
{ key: "due_date", label: "Due date", type: "date", showInTable: true },
|
|
161
|
+
{
|
|
162
|
+
key: "assigned_to_id",
|
|
163
|
+
label: "Assignee user id",
|
|
164
|
+
type: "text",
|
|
165
|
+
hint: "The auth user this task belongs to. Search the team before setting it."
|
|
166
|
+
},
|
|
167
|
+
{ key: "assigned_to_name", label: "Assignee name", type: "text" },
|
|
168
|
+
// Only the seeding feature sets this; the unique index makes a hand-typed value a write error.
|
|
169
|
+
{ key: "source", label: "Created by feature", type: "text", hint: "'onboarding' for setup checklist items. Leave empty." }
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export { createSupabaseTasksProvider, taskEntityDef };
|
|
174
|
+
//# sourceMappingURL=chunk-IYQSFHSK.js.map
|
|
175
|
+
//# sourceMappingURL=chunk-IYQSFHSK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/supabase.ts"],"names":[],"mappings":"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","file":"chunk-IYQSFHSK.js","sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js'\nimport {\n getActiveTenantId, getSupabaseClientOptional, isKnownMissing, rememberIfMissing, type EntityDef,\n} from '@fayz-ai/core'\nimport {\n EMPTY_TASKS_SUMMARY, TASK_PRIORITIES, TASK_STATUSES, completedAtFor, isoDay, nextPosition, summarizeTasks,\n type Task, type TaskLabel, type TaskPriority, type TaskStatus,\n} from '../domain/task'\nimport type { TasksDataProvider } from './provider'\n\nconst TASKS_TABLE = 'tasks'\n// A pool that never took spine 047 has no `tasks`; the rail and the checklist both read it on boot,\n// so the absence is remembered instead of asked twice per page load.\nconst TASKS_OBJECT = 'tasks'\n// Owned by plugin-tasks: an app without that plugin has no label table, and no labels.\nconst LABELS_TABLE = 'plg_tasks_labels'\n\ninterface TaskRow {\n id: string\n tenant_id: string\n title: string\n description: string | null\n status: TaskStatus\n priority: TaskPriority\n due_date: string | null\n completed_at: string | null\n assigned_to_id: string | null\n assigned_to_name: string | null\n parent_id: string | null\n labels: string[] | null\n position: number\n source: string | null\n source_key: string | null\n created_by: string | null\n created_by_name: string | null\n created_at: string\n updated_at: string\n}\n\ninterface TaskLabelRow {\n id: string\n tenant_id: string\n name: string\n color: string\n is_active: boolean\n created_at: string\n}\n\nfunction toTask(row: TaskRow): Task {\n return {\n id: row.id,\n title: row.title,\n description: row.description,\n status: row.status,\n priority: row.priority,\n dueDate: row.due_date,\n completedAt: row.completed_at,\n assignedToId: row.assigned_to_id,\n assignedToName: row.assigned_to_name,\n parentId: row.parent_id,\n labels: Array.isArray(row.labels) ? row.labels : [],\n position: row.position,\n source: row.source,\n sourceKey: row.source_key,\n createdBy: row.created_by,\n createdByName: row.created_by_name,\n tenantId: row.tenant_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\nfunction toTaskLabel(row: TaskLabelRow): TaskLabel {\n return {\n id: row.id,\n name: row.name,\n color: row.color,\n isActive: row.is_active,\n tenantId: row.tenant_id,\n createdAt: row.created_at,\n }\n}\n\nfunction client(): SupabaseClient {\n const supabase = getSupabaseClientOptional() as SupabaseClient | null\n if (!supabase) throw new Error('Supabase not initialized')\n return supabase\n}\n\nexport function createSupabaseTasksProvider(): TasksDataProvider {\n return {\n async getTasks(query) {\n const tenantId = getActiveTenantId()\n if (!tenantId || isKnownMissing(TASKS_OBJECT)) return []\n let q = client().from(TASKS_TABLE).select('*').eq('tenant_id', tenantId)\n\n if (query.parentId === null) q = q.is('parent_id', null)\n else if (query.parentId) q = q.eq('parent_id', query.parentId)\n if (query.status) q = q.in('status', Array.isArray(query.status) ? query.status : [query.status])\n if (query.priority) q = q.in('priority', Array.isArray(query.priority) ? query.priority : [query.priority])\n if (query.assignedToId) q = q.eq('assigned_to_id', query.assignedToId)\n if (query.search) q = q.ilike('title', `%${query.search}%`)\n if (query.overdue) q = q.lt('due_date', isoDay()).not('status', 'in', '(\"done\",\"cancelled\")')\n\n const { data, error } = await q\n .order('position', { ascending: true })\n .order('created_at', { ascending: false })\n if (error) {\n rememberIfMissing(TASKS_OBJECT, error)\n return []\n }\n return ((data ?? []) as TaskRow[]).map(toTask)\n },\n\n async getTaskById(id) {\n const { data } = await client().from(TASKS_TABLE).select('*').eq('id', id).maybeSingle()\n return data ? toTask(data as TaskRow) : null\n },\n\n async getSubtasks(parentId) {\n const { data } = await client()\n .from(TASKS_TABLE)\n .select('*')\n .eq('parent_id', parentId)\n .order('position', { ascending: true })\n return ((data ?? []) as TaskRow[]).map(toTask)\n },\n\n async createTask(input) {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('No active tenant')\n const status = input.status ?? 'todo'\n\n const { data: last } = await client()\n .from(TASKS_TABLE)\n .select('position')\n .eq('tenant_id', tenantId)\n .eq('status', status)\n .is('parent_id', input.parentId ?? null)\n .order('position', { ascending: false })\n .limit(1)\n\n const { data, error } = await client()\n .from(TASKS_TABLE)\n .insert({\n tenant_id: tenantId,\n title: input.title,\n description: input.description ?? null,\n status,\n priority: input.priority ?? 'medium',\n due_date: input.dueDate ?? null,\n assigned_to_id: input.assignedToId ?? null,\n assigned_to_name: input.assignedToName ?? null,\n parent_id: input.parentId ?? null,\n labels: input.labels ?? [],\n position: nextPosition(((last ?? []) as Pick<TaskRow, 'position'>[]).map((r) => r.position)),\n source: input.source ?? null,\n source_key: input.sourceKey ?? null,\n })\n .select('*')\n .single()\n if (error) throw error\n return toTask(data as TaskRow)\n },\n\n async updateTask(id, input) {\n const now = new Date().toISOString()\n const payload: Partial<TaskRow> = { updated_at: now }\n if (input.title !== undefined) payload.title = input.title\n if (input.description !== undefined) payload.description = input.description\n if (input.priority !== undefined) payload.priority = input.priority\n if (input.dueDate !== undefined) payload.due_date = input.dueDate\n if (input.assignedToId !== undefined) payload.assigned_to_id = input.assignedToId\n if (input.labels !== undefined) payload.labels = input.labels\n if (input.position !== undefined) payload.position = input.position\n if (input.status !== undefined) {\n payload.status = input.status\n payload.completed_at = completedAtFor(input.status, now)\n }\n\n const { data, error } = await client()\n .from(TASKS_TABLE)\n .update(payload)\n .eq('id', id)\n .select('*')\n .single()\n if (error) throw error\n return toTask(data as TaskRow)\n },\n\n async deleteTask(id) {\n await client().from(TASKS_TABLE).delete().eq('id', id)\n },\n\n async reorderTask(id, newPosition, status) {\n await client()\n .from(TASKS_TABLE)\n .update({ position: newPosition, status, updated_at: new Date().toISOString() })\n .eq('id', id)\n },\n\n async getLabels() {\n const tenantId = getActiveTenantId()\n if (!tenantId) return []\n try {\n const { data } = await client()\n .from(LABELS_TABLE)\n .select('*')\n .eq('tenant_id', tenantId)\n .eq('is_active', true)\n .order('name')\n return ((data ?? []) as TaskLabelRow[]).map(toTaskLabel)\n } catch {\n return []\n }\n },\n\n async getSummary() {\n const tenantId = getActiveTenantId()\n if (!tenantId || isKnownMissing(TASKS_OBJECT)) return { ...EMPTY_TASKS_SUMMARY }\n\n const { data, error } = await client()\n .from(TASKS_TABLE)\n .select('status, priority, due_date')\n .eq('tenant_id', tenantId)\n .is('parent_id', null)\n if (error) {\n rememberIfMissing(TASKS_OBJECT, error)\n return { ...EMPTY_TASKS_SUMMARY }\n }\n const rows = (data ?? []) as Pick<TaskRow, 'status' | 'priority' | 'due_date'>[]\n return summarizeTasks(rows.map((r) => ({ status: r.status, priority: r.priority, dueDate: r.due_date })), isoDay())\n },\n }\n}\n\n/**\n * Declared as an entity so the agent can read and close tasks in every app, although the surface is a\n * rail tab. Unit narrowing is left out on purpose: resolveDataProvider turns it on from `scoped_resources`.\n */\nexport const taskEntityDef: EntityDef = {\n name: 'Task',\n namePlural: 'Tasks',\n icon: 'CheckSquare',\n displayField: 'title',\n defaultSort: 'position',\n data: {\n table: TASKS_TABLE,\n tenantScoped: true,\n tenantIdColumn: 'tenant_id',\n searchColumns: ['title', 'description'],\n },\n fields: [\n { key: 'title', label: 'Title', type: 'text', required: true, searchable: true, showInTable: true },\n { key: 'description', label: 'Description', type: 'textarea', searchable: true },\n { key: 'status', label: 'Status', type: 'select', options: TASK_STATUSES, defaultValue: 'todo', showInTable: true },\n { key: 'priority', label: 'Priority', type: 'select', options: TASK_PRIORITIES, defaultValue: 'medium', showInTable: true },\n { key: 'due_date', label: 'Due date', type: 'date', showInTable: true },\n {\n key: 'assigned_to_id',\n label: 'Assignee user id',\n type: 'text',\n hint: 'The auth user this task belongs to. Search the team before setting it.',\n },\n { key: 'assigned_to_name', label: 'Assignee name', type: 'text' },\n // Only the seeding feature sets this; the unique index makes a hand-typed value a write error.\n { key: 'source', label: 'Created by feature', type: 'text', hint: \"'onboarding' for setup checklist items. Leave empty.\" },\n ],\n}\n"]}
|