feat: add 5 PM features — custom statuses, Gantt, quick-add, automations, notifications

- Custom workflow statuses: per-project configurable status definitions
  replacing the fixed task_status enum. Each project defines its own
  workflow with drag-to-reorder, color coding, and category mapping.
- Gantt/timeline view: full project roadmap with task bars, dependency
  arrows, milestone diamonds, zoom levels (day/week/month), and drag
  to reschedule.
- Natural-language quick-add: NLP parser extracts dates, priorities,
  projects, labels, and recurrence from free text. Floating bar with
  'n' shortcut and live parsed preview.
- Automation rules: no-code trigger-action system per project. Triggers
  on status change, task creation, due date approaching. Actions set
  status/priority, add labels, create notifications.
- Notification center: in-app bell icon with unread badge, slide-out
  panel, real-time SSE updates, mark read/all read. Replaces raw
  activity feed dropdown.

Schema: adds status_definitions, automation_rules, notifications tables.
Migrations: 0007, 0008, 0009. 41 NLP parser tests pass.
This commit is contained in:
2026-08-19 10:54:29 +00:00
parent 1a620f16c1
commit b814a4788d
46 changed files with 4965 additions and 288 deletions
+2
View File
@@ -74,6 +74,8 @@ if ! bun install --frozen-lockfile; then
fi
# ── 6. Database schema + triggers (both idempotent) ───────────────────────
echo "Backfilling custom task statuses (db:statuses) ..."
bun run db:statuses
echo "Migrating database schema + triggers (db:migrate) ..."
bun run db:migrate
+22
View File
@@ -0,0 +1,22 @@
import { fileURLToPath } from "node:url";
import { sql } from "../packages/db/src/client";
// Apply the custom-workflow-statuses migration idempotently.
// 0007_custom_task_statuses.sql uses CREATE ... IF NOT EXISTS, pg_constraint /
// information_schema guards, and idempotent backfill UPDATEs, so it is safe to
// run on every deploy. It must run BEFORE `drizzle-kit push`: the push removes
// the legacy tasks.status column and task_status enum type, and this script
// backfills tasks.status_id from that column first.
const statusesFile = fileURLToPath(
new URL("../drizzle/0007_custom_task_statuses.sql", import.meta.url),
);
try {
console.log(`Applying custom task statuses migration from ${statusesFile} ...`);
await sql.file(statusesFile);
console.log("Custom task statuses migration applied successfully.");
process.exit(0);
} catch (error) {
console.error("Failed to apply custom task statuses migration:", error);
process.exit(1);
}