@fayz-ai/plugin-tasks 0.1.6 → 0.8.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/README.md +2 -0
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +5 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/index.cjs +95 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -52
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +7 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/registries.d.ts.map +1 -1
- package/package.json +7 -4
- package/src/data/capability.test.ts +1 -1
- package/src/data/supabase.ts +11 -10
- package/src/data/tables.ts +6 -0
- package/src/index.ts +9 -56
- package/src/migrations/000_plg_rename.sql +12 -0
- package/src/migrations/001_tasks_base.sql +34 -34
- package/src/migrations/index.ts +89 -0
- package/src/registries.ts +2 -1
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@fayz-ai/plugin-tasks)
|
|
6
6
|
[](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
|
|
7
7
|
|
|
8
|
+
**Status:** beta — capability-complete (Supabase/mock data provider, bundled migration, end-to-end tests). Pre-1.0: APIs may change before 1.0.
|
|
9
|
+
|
|
8
10
|
Work happens between the records. A salon owner needs to "call the supplier," a clinic admin needs to "follow up on lab results" — small things that never fit neatly into a booking or an invoice. `plugin-tasks` gives every Fayz app a lightweight, always-available task list: a topbar button that opens a drawer for quick-add, statuses, priorities, due dates, labels, and assignees.
|
|
9
11
|
|
|
10
12
|
It's deliberately small and zero-config. Drop it in and it owns its own tables (via a bundled migration), picks Supabase or an in-memory mock automatically, and exposes its summary to the AI assistant — so "what's overdue?" just works. No page to wire, no nav slot to claim; tasks ride along with whatever vertical you compose.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAgChD,wBAAgB,2BAA2B,IAAI,iBAAiB,CAiK/D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/data/tables.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;CAGJ,CAAA"}
|
package/dist/index.cjs
CHANGED
|
@@ -152,6 +152,12 @@ function createMockTasksProvider() {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// src/data/tables.ts
|
|
156
|
+
var T = {
|
|
157
|
+
tasks: "plg_tasks_tasks",
|
|
158
|
+
labels: "plg_tasks_labels"
|
|
159
|
+
};
|
|
160
|
+
|
|
155
161
|
// src/data/supabase.ts
|
|
156
162
|
function getTenantId() {
|
|
157
163
|
return core.getActiveTenantId();
|
|
@@ -178,7 +184,7 @@ function createSupabaseTasksProvider() {
|
|
|
178
184
|
async getTasks(query) {
|
|
179
185
|
const tenantId = getTenantId();
|
|
180
186
|
if (!tenantId) return [];
|
|
181
|
-
let q = getClient().from(
|
|
187
|
+
let q = getClient().from(T.tasks).select("*").eq("tenant_id", tenantId);
|
|
182
188
|
if (query.parentId === null) {
|
|
183
189
|
q = q.is("parent_id", null);
|
|
184
190
|
} else if (query.parentId) {
|
|
@@ -206,19 +212,19 @@ function createSupabaseTasksProvider() {
|
|
|
206
212
|
return (data ?? []).map(toTask);
|
|
207
213
|
},
|
|
208
214
|
async getTaskById(id) {
|
|
209
|
-
const { data } = await getClient().from(
|
|
215
|
+
const { data } = await getClient().from(T.tasks).select("*").eq("id", id).maybeSingle();
|
|
210
216
|
return data ? toTask(data) : null;
|
|
211
217
|
},
|
|
212
218
|
async getSubtasks(parentId) {
|
|
213
|
-
const { data } = await getClient().from(
|
|
219
|
+
const { data } = await getClient().from(T.tasks).select("*").eq("parent_id", parentId).order("position", { ascending: true });
|
|
214
220
|
return (data ?? []).map(toTask);
|
|
215
221
|
},
|
|
216
222
|
async createTask(input) {
|
|
217
223
|
const tenantId = getTenantId();
|
|
218
224
|
if (!tenantId) throw new Error("No active tenant");
|
|
219
|
-
const { data: maxData } = await getClient().from(
|
|
225
|
+
const { data: maxData } = await getClient().from(T.tasks).select("position").eq("tenant_id", tenantId).eq("status", input.status ?? "todo").is("parent_id", input.parentId ?? null).order("position", { ascending: false }).limit(1);
|
|
220
226
|
const maxPos = maxData?.[0]?.position ?? -1;
|
|
221
|
-
const { data, error } = await getClient().from(
|
|
227
|
+
const { data, error } = await getClient().from(T.tasks).insert({
|
|
222
228
|
tenant_id: tenantId,
|
|
223
229
|
title: input.title,
|
|
224
230
|
description: input.description ?? null,
|
|
@@ -243,26 +249,26 @@ function createSupabaseTasksProvider() {
|
|
|
243
249
|
if (input.assignedToId !== void 0) payload.assigned_to_id = input.assignedToId;
|
|
244
250
|
if (input.labels !== void 0) payload.labels = input.labels;
|
|
245
251
|
if (input.position !== void 0) payload.position = input.position;
|
|
246
|
-
const { data, error } = await getClient().from(
|
|
252
|
+
const { data, error } = await getClient().from(T.tasks).update(payload).eq("id", id).select("*").single();
|
|
247
253
|
if (error) throw error;
|
|
248
254
|
return toTask(data);
|
|
249
255
|
},
|
|
250
256
|
async deleteTask(id) {
|
|
251
|
-
await getClient().from(
|
|
257
|
+
await getClient().from(T.tasks).delete().eq("id", id);
|
|
252
258
|
},
|
|
253
259
|
async reorderTask(id, newPosition, status) {
|
|
254
|
-
await getClient().from(
|
|
260
|
+
await getClient().from(T.tasks).update({ position: newPosition, status, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
255
261
|
},
|
|
256
262
|
async getLabels() {
|
|
257
263
|
const tenantId = getTenantId();
|
|
258
264
|
if (!tenantId) return [];
|
|
259
|
-
const { data } = await getClient().from(
|
|
265
|
+
const { data } = await getClient().from(T.labels).select("*").eq("tenant_id", tenantId).eq("is_active", true).order("name");
|
|
260
266
|
return (data ?? []).map((r) => snakeToCamel(r));
|
|
261
267
|
},
|
|
262
268
|
async getSummary() {
|
|
263
269
|
const tenantId = getTenantId();
|
|
264
270
|
if (!tenantId) return { total: 0, todo: 0, inProgress: 0, done: 0, overdue: 0, dueToday: 0, highPriority: 0 };
|
|
265
|
-
const { data } = await getClient().from(
|
|
271
|
+
const { data } = await getClient().from(T.tasks).select("status, priority, due_date").eq("tenant_id", tenantId).is("parent_id", null);
|
|
266
272
|
const tasks = data ?? [];
|
|
267
273
|
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
268
274
|
return {
|
|
@@ -473,7 +479,7 @@ var taskLabelEntity = {
|
|
|
473
479
|
{ key: "color", label: "Color", type: "text", showInTable: true, defaultValue: "#6366f1" },
|
|
474
480
|
{ key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
|
|
475
481
|
],
|
|
476
|
-
data: { table:
|
|
482
|
+
data: { table: T.labels, tenantScoped: true }
|
|
477
483
|
};
|
|
478
484
|
var tasksRegistries = [
|
|
479
485
|
{
|
|
@@ -955,40 +961,49 @@ function TasksTopbarButtonInner({ store }) {
|
|
|
955
961
|
);
|
|
956
962
|
}
|
|
957
963
|
|
|
958
|
-
// src/index.ts
|
|
959
|
-
var
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
964
|
+
// src/migrations/index.ts
|
|
965
|
+
var MIGRATION_000_PLG_RENAME = `-- 000_plg_rename.sql \u2014 rename legacy tasks tables to plg_tasks_* for pools
|
|
966
|
+
-- provisioned before the industry-pool rename. Guarded: fires only when legacy
|
|
967
|
+
-- name exists and target does not, so fresh pools skip every branch.
|
|
968
|
+
DO $$
|
|
969
|
+
BEGIN
|
|
970
|
+
IF to_regclass('public.tsk_tasks') IS NOT NULL AND to_regclass('public.plg_tasks_tasks') IS NULL THEN
|
|
971
|
+
ALTER TABLE public.tsk_tasks RENAME TO plg_tasks_tasks;
|
|
972
|
+
END IF;
|
|
973
|
+
IF to_regclass('public.tsk_labels') IS NOT NULL AND to_regclass('public.plg_tasks_labels') IS NULL THEN
|
|
974
|
+
ALTER TABLE public.tsk_labels RENAME TO plg_tasks_labels;
|
|
975
|
+
END IF;
|
|
976
|
+
END $$;
|
|
977
|
+
`;
|
|
978
|
+
var MIGRATION_001_TASKS_BASE = `-- Tasks Plugin: Base Tables
|
|
979
|
+
-- Prefix: plg_tasks_
|
|
980
|
+
|
|
981
|
+
-- Label definitions for categorizing tasks
|
|
982
|
+
CREATE TABLE IF NOT EXISTS public.plg_tasks_labels (
|
|
971
983
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
972
|
-
tenant_id uuid NOT NULL REFERENCES
|
|
984
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
973
985
|
name text NOT NULL,
|
|
974
986
|
color text NOT NULL DEFAULT '#6366f1',
|
|
975
987
|
is_active boolean NOT NULL DEFAULT true,
|
|
976
988
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
977
989
|
);
|
|
978
|
-
ALTER TABLE public.
|
|
979
|
-
CREATE INDEX IF NOT EXISTS
|
|
990
|
+
ALTER TABLE public.plg_tasks_labels ENABLE ROW LEVEL SECURITY;
|
|
991
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_labels_tenant ON public.plg_tasks_labels(tenant_id);
|
|
980
992
|
|
|
981
|
-
|
|
993
|
+
-- Main tasks table (subtasks via parent_id self-reference)
|
|
994
|
+
CREATE TABLE IF NOT EXISTS public.plg_tasks_tasks (
|
|
982
995
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
983
|
-
tenant_id uuid NOT NULL REFERENCES
|
|
996
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
984
997
|
title text NOT NULL,
|
|
985
998
|
description text,
|
|
986
|
-
status text NOT NULL DEFAULT 'todo'
|
|
987
|
-
|
|
999
|
+
status text NOT NULL DEFAULT 'todo'
|
|
1000
|
+
CHECK (status IN ('todo', 'in_progress', 'done', 'cancelled')),
|
|
1001
|
+
priority text NOT NULL DEFAULT 'medium'
|
|
1002
|
+
CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
|
|
988
1003
|
due_date date,
|
|
989
1004
|
assigned_to_id uuid,
|
|
990
1005
|
assigned_to_name text,
|
|
991
|
-
parent_id uuid REFERENCES public.
|
|
1006
|
+
parent_id uuid REFERENCES public.plg_tasks_tasks(id) ON DELETE CASCADE,
|
|
992
1007
|
labels jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
993
1008
|
position integer NOT NULL DEFAULT 0,
|
|
994
1009
|
created_by_id uuid,
|
|
@@ -996,26 +1011,48 @@ CREATE TABLE IF NOT EXISTS public.tsk_tasks (
|
|
|
996
1011
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
997
1012
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
998
1013
|
);
|
|
999
|
-
ALTER TABLE public.
|
|
1000
|
-
CREATE INDEX IF NOT EXISTS
|
|
1001
|
-
CREATE INDEX IF NOT EXISTS
|
|
1002
|
-
CREATE INDEX IF NOT EXISTS
|
|
1003
|
-
CREATE INDEX IF NOT EXISTS
|
|
1004
|
-
CREATE INDEX IF NOT EXISTS
|
|
1014
|
+
ALTER TABLE public.plg_tasks_tasks ENABLE ROW LEVEL SECURITY;
|
|
1015
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_tenant ON public.plg_tasks_tasks(tenant_id);
|
|
1016
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_status ON public.plg_tasks_tasks(tenant_id, status);
|
|
1017
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_parent ON public.plg_tasks_tasks(parent_id);
|
|
1018
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_assigned ON public.plg_tasks_tasks(assigned_to_id);
|
|
1019
|
+
CREATE INDEX IF NOT EXISTS idx_plg_tasks_tasks_due ON public.plg_tasks_tasks(tenant_id, due_date)
|
|
1020
|
+
WHERE due_date IS NOT NULL;
|
|
1021
|
+
|
|
1022
|
+
-- RLS policies for plg_tasks_tasks (idempotent \u2014 DROP IF EXISTS before CREATE)
|
|
1023
|
+
DROP POLICY IF EXISTS plg_tasks_tasks_select ON public.plg_tasks_tasks;
|
|
1024
|
+
CREATE POLICY plg_tasks_tasks_select ON public.plg_tasks_tasks FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1025
|
+
DROP POLICY IF EXISTS plg_tasks_tasks_insert ON public.plg_tasks_tasks;
|
|
1026
|
+
CREATE POLICY plg_tasks_tasks_insert ON public.plg_tasks_tasks FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1027
|
+
DROP POLICY IF EXISTS plg_tasks_tasks_update ON public.plg_tasks_tasks;
|
|
1028
|
+
CREATE POLICY plg_tasks_tasks_update ON public.plg_tasks_tasks FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1029
|
+
DROP POLICY IF EXISTS plg_tasks_tasks_delete ON public.plg_tasks_tasks;
|
|
1030
|
+
CREATE POLICY plg_tasks_tasks_delete ON public.plg_tasks_tasks FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1031
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_tasks_tasks TO authenticated;
|
|
1005
1032
|
|
|
1006
|
-
-- RLS policies for
|
|
1007
|
-
|
|
1008
|
-
CREATE POLICY
|
|
1009
|
-
|
|
1010
|
-
CREATE POLICY
|
|
1011
|
-
|
|
1033
|
+
-- RLS policies for plg_tasks_labels (idempotent)
|
|
1034
|
+
DROP POLICY IF EXISTS plg_tasks_labels_select ON public.plg_tasks_labels;
|
|
1035
|
+
CREATE POLICY plg_tasks_labels_select ON public.plg_tasks_labels FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1036
|
+
DROP POLICY IF EXISTS plg_tasks_labels_insert ON public.plg_tasks_labels;
|
|
1037
|
+
CREATE POLICY plg_tasks_labels_insert ON public.plg_tasks_labels FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1038
|
+
DROP POLICY IF EXISTS plg_tasks_labels_update ON public.plg_tasks_labels;
|
|
1039
|
+
CREATE POLICY plg_tasks_labels_update ON public.plg_tasks_labels FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1040
|
+
DROP POLICY IF EXISTS plg_tasks_labels_delete ON public.plg_tasks_labels;
|
|
1041
|
+
CREATE POLICY plg_tasks_labels_delete ON public.plg_tasks_labels FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
1042
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_tasks_labels TO authenticated;
|
|
1043
|
+
`;
|
|
1012
1044
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1045
|
+
// src/index.ts
|
|
1046
|
+
var DEFAULT_LABELS = {
|
|
1047
|
+
drawerTitle: "Tasks",
|
|
1048
|
+
settingsTitle: "Tasks",
|
|
1049
|
+
quickAddPlaceholder: "Add a task..."
|
|
1050
|
+
};
|
|
1051
|
+
function resolveConfig(options) {
|
|
1052
|
+
return {
|
|
1053
|
+
labels: { ...DEFAULT_LABELS, ...options?.labels }
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1019
1056
|
function createTasksPlugin(options) {
|
|
1020
1057
|
const config = resolveConfig(options);
|
|
1021
1058
|
core.registerTranslations(tasksLocales);
|
|
@@ -1092,11 +1129,17 @@ function createTasksPlugin(options) {
|
|
|
1092
1129
|
}
|
|
1093
1130
|
],
|
|
1094
1131
|
migrations: [
|
|
1132
|
+
{
|
|
1133
|
+
id: "tasks-000-plg-rename",
|
|
1134
|
+
version: "1.0.0",
|
|
1135
|
+
sql: MIGRATION_000_PLG_RENAME,
|
|
1136
|
+
description: "Rename legacy tsk_* tables to plg_tasks_* in-place for pools provisioned before the industry-pool rename (guarded no-op on fresh pools)"
|
|
1137
|
+
},
|
|
1095
1138
|
{
|
|
1096
1139
|
id: "tasks-001-base-tables",
|
|
1097
1140
|
version: "1.0.0",
|
|
1098
|
-
sql:
|
|
1099
|
-
description: "Create
|
|
1141
|
+
sql: MIGRATION_001_TASKS_BASE,
|
|
1142
|
+
description: "Create plg_tasks_tasks and plg_tasks_labels"
|
|
1100
1143
|
}
|
|
1101
1144
|
],
|
|
1102
1145
|
locales: tasksLocales
|