Initial commit: personal task planner with categories, projects, and tiered assignment

Personal-first lists/tasks/reminders/recurrence, plus an admin-editable status
pipeline, shared categories, project containers, and a three-tier permission
model for cross-user work: assign (hand off your own tasks), reallocate
(dispatcher — move anyone's tasks), admin (reserved, full manage-all).
This commit is contained in:
jtricerolph 2026-09-17 08:07:00 +00:00
commit d8dc4e2b81
54 changed files with 10908 additions and 0 deletions

58
seed-app.js Normal file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env node
// Run from tasks/ dir: DATABASE_URL=... node seed-app.js
import pg from 'pg'
const { Pool } = pg
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
await pool.query(`
INSERT INTO apps (slug, name, description, base_path, icon, theme_color, category, internal_host, internal_port)
VALUES ('tasks', 'Tasks', 'Personal task planner — lists, reminders and recurring to-dos', '/tasks', 'ListChecks', '#0d9488', 'Operations', '10.10.10.130', 3080)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
base_path = EXCLUDED.base_path,
icon = EXCLUDED.icon,
theme_color = EXCLUDED.theme_color,
category = EXCLUDED.category,
internal_host = EXCLUDED.internal_host,
internal_port = EXCLUDED.internal_port
`)
// Seed capabilities. 'admin' stays reserved for a future "view/manage every
// field of every user's tasks" surface — not built yet. 'reallocate' is the
// narrower dispatcher power: see everyone's boards and move tasks between
// people, without the full weight of editing/deleting someone else's task.
await pool.query(`
INSERT INTO app_capabilities (app_id, slug, name, description, sort_order)
SELECT a.id, c.slug, c.name, c.description, c.sort_order
FROM apps a, (VALUES
('use', 'Use Tasks', 'Create and manage your own lists and tasks', 1),
('assign', 'Assign Tasks', 'Hand off your own tasks to other users', 2),
('reallocate', 'Reallocate Tasks', 'View every user''s board and move any task between people', 3),
('admin', 'Manage All', 'View and manage every field of every user''s tasks (not yet built)', 4),
('settings', 'Settings', 'Configure the shared status pipeline and categories', 5)
) AS c(slug, name, description, sort_order)
WHERE a.slug = 'tasks'
ON CONFLICT (app_id, slug) DO NOTHING
`)
// Grant 'use' to the Staff role if they have no tasks caps yet.
await pool.query(`
INSERT INTO role_capabilities (role_id, capability_id)
SELECT r.id, ac.id
FROM roles r
JOIN app_capabilities ac ON ac.slug = 'use'
JOIN apps a ON a.id = ac.app_id AND a.slug = 'tasks'
WHERE r.name = 'Staff'
AND NOT EXISTS (
SELECT 1 FROM role_capabilities rc2
JOIN app_capabilities ac2 ON rc2.capability_id = ac2.id
JOIN apps a2 ON ac2.app_id = a2.id AND a2.slug = 'tasks'
WHERE rc2.role_id = r.id
)
ON CONFLICT DO NOTHING
`)
console.log('tasks app seeded.')
await pool.end()