pacatui 0.1.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 +153 -0
- package/generated/prisma/browser.ts +59 -0
- package/generated/prisma/client.ts +81 -0
- package/generated/prisma/commonInputTypes.ts +402 -0
- package/generated/prisma/enums.ts +15 -0
- package/generated/prisma/internal/class.ts +260 -0
- package/generated/prisma/internal/prismaNamespace.ts +1362 -0
- package/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
- package/generated/prisma/models/Customer.ts +1489 -0
- package/generated/prisma/models/Invoice.ts +1837 -0
- package/generated/prisma/models/Project.ts +1981 -0
- package/generated/prisma/models/Setting.ts +1086 -0
- package/generated/prisma/models/Tag.ts +1288 -0
- package/generated/prisma/models/Task.ts +1669 -0
- package/generated/prisma/models/TaskTag.ts +1340 -0
- package/generated/prisma/models/TimeEntry.ts +1602 -0
- package/generated/prisma/models.ts +19 -0
- package/package.json +71 -0
- package/prisma/migrations/20260115051911_init/migration.sql +71 -0
- package/prisma/migrations/20260115062427_add_time_tracking/migration.sql +20 -0
- package/prisma/migrations/20260117233250_add_customers_invoices/migration.sql +81 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +162 -0
- package/src/App.tsx +1492 -0
- package/src/components/CreateInvoiceModal.tsx +222 -0
- package/src/components/CustomerModal.tsx +158 -0
- package/src/components/CustomerSelectModal.tsx +142 -0
- package/src/components/Dashboard.tsx +242 -0
- package/src/components/DateTimePicker.tsx +335 -0
- package/src/components/EditTimeEntryModal.tsx +293 -0
- package/src/components/Header.tsx +65 -0
- package/src/components/HelpView.tsx +109 -0
- package/src/components/InputModal.tsx +79 -0
- package/src/components/InvoicesView.tsx +297 -0
- package/src/components/Modal.tsx +38 -0
- package/src/components/ProjectList.tsx +114 -0
- package/src/components/ProjectModal.tsx +116 -0
- package/src/components/SettingsView.tsx +145 -0
- package/src/components/SplashScreen.tsx +25 -0
- package/src/components/StatusBar.tsx +93 -0
- package/src/components/TaskList.tsx +143 -0
- package/src/components/Timer.tsx +95 -0
- package/src/components/TimerModals.tsx +120 -0
- package/src/components/TimesheetView.tsx +218 -0
- package/src/components/index.ts +17 -0
- package/src/db.ts +629 -0
- package/src/hooks/usePaste.ts +69 -0
- package/src/index.tsx +75 -0
- package/src/stripe.ts +163 -0
- package/src/types.ts +361 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// biome-ignore-all lint: generated file
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/*
|
|
7
|
+
* This is a barrel export file for all models and their related types.
|
|
8
|
+
*
|
|
9
|
+
* 🟢 You can import this file directly.
|
|
10
|
+
*/
|
|
11
|
+
export type * from './models/Customer.ts'
|
|
12
|
+
export type * from './models/Project.ts'
|
|
13
|
+
export type * from './models/TimeEntry.ts'
|
|
14
|
+
export type * from './models/Invoice.ts'
|
|
15
|
+
export type * from './models/Task.ts'
|
|
16
|
+
export type * from './models/Tag.ts'
|
|
17
|
+
export type * from './models/TaskTag.ts'
|
|
18
|
+
export type * from './models/Setting.ts'
|
|
19
|
+
export type * from './commonInputTypes.ts'
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pacatui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A simple tui app for task, timer and invoicing for projects.",
|
|
5
|
+
"module": "src/index.tsx",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"private": false,
|
|
8
|
+
"bin": {
|
|
9
|
+
"paca": "./src/index.tsx"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"prisma",
|
|
14
|
+
"generated"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "bun run src/index.tsx",
|
|
18
|
+
"dev": "bun run --watch src/index.tsx",
|
|
19
|
+
"db:migrate": "bunx prisma migrate dev",
|
|
20
|
+
"db:push": "bunx prisma db push",
|
|
21
|
+
"db:studio": "bunx prisma studio",
|
|
22
|
+
"postinstall": "bunx prisma generate",
|
|
23
|
+
"prepublishOnly": "bunx prisma generate"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"tui",
|
|
27
|
+
"cli",
|
|
28
|
+
"task-manager",
|
|
29
|
+
"todo",
|
|
30
|
+
"project-management",
|
|
31
|
+
"terminal",
|
|
32
|
+
"time-tracking",
|
|
33
|
+
"invoicing",
|
|
34
|
+
"stripe",
|
|
35
|
+
"productivity",
|
|
36
|
+
"timesheet",
|
|
37
|
+
"bun"
|
|
38
|
+
],
|
|
39
|
+
"author": "Wes Edling",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/wes/paca.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/wes/paca#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/wes/paca/issues"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"bun": ">=1.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/bun": "latest",
|
|
54
|
+
"@types/react": "^19.2.8"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"typescript": "^5"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@libsql/client": "^0.17.0",
|
|
61
|
+
"@opentui/core": "^0.1.72",
|
|
62
|
+
"@opentui/react": "^0.1.72",
|
|
63
|
+
"@prisma/adapter-better-sqlite3": "^7.2.0",
|
|
64
|
+
"@prisma/adapter-libsql": "^7.2.0",
|
|
65
|
+
"@prisma/client": "^7.2.0",
|
|
66
|
+
"better-sqlite3": "^12.6.0",
|
|
67
|
+
"prisma": "^7.2.0",
|
|
68
|
+
"react": "^19.2.3",
|
|
69
|
+
"stripe": "^20.2.0"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "Project" (
|
|
3
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
+
"name" TEXT NOT NULL,
|
|
5
|
+
"description" TEXT,
|
|
6
|
+
"color" TEXT NOT NULL DEFAULT '#3b82f6',
|
|
7
|
+
"archived" BOOLEAN NOT NULL DEFAULT false,
|
|
8
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
9
|
+
"updatedAt" DATETIME NOT NULL
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
-- CreateTable
|
|
13
|
+
CREATE TABLE "Task" (
|
|
14
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
15
|
+
"title" TEXT NOT NULL,
|
|
16
|
+
"description" TEXT,
|
|
17
|
+
"status" TEXT NOT NULL DEFAULT 'todo',
|
|
18
|
+
"priority" TEXT NOT NULL DEFAULT 'medium',
|
|
19
|
+
"dueDate" DATETIME,
|
|
20
|
+
"completedAt" DATETIME,
|
|
21
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
22
|
+
"updatedAt" DATETIME NOT NULL,
|
|
23
|
+
"projectId" TEXT NOT NULL,
|
|
24
|
+
CONSTRAINT "Task_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
-- CreateTable
|
|
28
|
+
CREATE TABLE "Tag" (
|
|
29
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
30
|
+
"name" TEXT NOT NULL,
|
|
31
|
+
"color" TEXT NOT NULL DEFAULT '#6b7280',
|
|
32
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- CreateTable
|
|
36
|
+
CREATE TABLE "TaskTag" (
|
|
37
|
+
"taskId" TEXT NOT NULL,
|
|
38
|
+
"tagId" TEXT NOT NULL,
|
|
39
|
+
|
|
40
|
+
PRIMARY KEY ("taskId", "tagId"),
|
|
41
|
+
CONSTRAINT "TaskTag_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "Task" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
42
|
+
CONSTRAINT "TaskTag_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- CreateTable
|
|
46
|
+
CREATE TABLE "Setting" (
|
|
47
|
+
"key" TEXT NOT NULL PRIMARY KEY,
|
|
48
|
+
"value" TEXT NOT NULL,
|
|
49
|
+
"updatedAt" DATETIME NOT NULL
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
-- CreateIndex
|
|
53
|
+
CREATE INDEX "Project_archived_idx" ON "Project"("archived");
|
|
54
|
+
|
|
55
|
+
-- CreateIndex
|
|
56
|
+
CREATE INDEX "Project_createdAt_idx" ON "Project"("createdAt");
|
|
57
|
+
|
|
58
|
+
-- CreateIndex
|
|
59
|
+
CREATE INDEX "Task_projectId_idx" ON "Task"("projectId");
|
|
60
|
+
|
|
61
|
+
-- CreateIndex
|
|
62
|
+
CREATE INDEX "Task_status_idx" ON "Task"("status");
|
|
63
|
+
|
|
64
|
+
-- CreateIndex
|
|
65
|
+
CREATE INDEX "Task_priority_idx" ON "Task"("priority");
|
|
66
|
+
|
|
67
|
+
-- CreateIndex
|
|
68
|
+
CREATE INDEX "Task_dueDate_idx" ON "Task"("dueDate");
|
|
69
|
+
|
|
70
|
+
-- CreateIndex
|
|
71
|
+
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- AlterTable
|
|
2
|
+
ALTER TABLE "Project" ADD COLUMN "hourlyRate" REAL;
|
|
3
|
+
|
|
4
|
+
-- CreateTable
|
|
5
|
+
CREATE TABLE "TimeEntry" (
|
|
6
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
7
|
+
"startTime" DATETIME NOT NULL,
|
|
8
|
+
"endTime" DATETIME,
|
|
9
|
+
"description" TEXT,
|
|
10
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
11
|
+
"updatedAt" DATETIME NOT NULL,
|
|
12
|
+
"projectId" TEXT NOT NULL,
|
|
13
|
+
CONSTRAINT "TimeEntry_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- CreateIndex
|
|
17
|
+
CREATE INDEX "TimeEntry_projectId_idx" ON "TimeEntry"("projectId");
|
|
18
|
+
|
|
19
|
+
-- CreateIndex
|
|
20
|
+
CREATE INDEX "TimeEntry_startTime_idx" ON "TimeEntry"("startTime");
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "Customer" (
|
|
3
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
+
"name" TEXT NOT NULL,
|
|
5
|
+
"email" TEXT NOT NULL,
|
|
6
|
+
"stripeCustomerId" TEXT,
|
|
7
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
"updatedAt" DATETIME NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
-- CreateTable
|
|
12
|
+
CREATE TABLE "Invoice" (
|
|
13
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
14
|
+
"stripeInvoiceId" TEXT,
|
|
15
|
+
"status" TEXT NOT NULL DEFAULT 'draft',
|
|
16
|
+
"totalHours" REAL NOT NULL,
|
|
17
|
+
"totalAmount" REAL NOT NULL,
|
|
18
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
19
|
+
"updatedAt" DATETIME NOT NULL,
|
|
20
|
+
"projectId" TEXT NOT NULL,
|
|
21
|
+
"customerId" TEXT NOT NULL,
|
|
22
|
+
CONSTRAINT "Invoice_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
23
|
+
CONSTRAINT "Invoice_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- RedefineTables
|
|
27
|
+
PRAGMA defer_foreign_keys=ON;
|
|
28
|
+
PRAGMA foreign_keys=OFF;
|
|
29
|
+
CREATE TABLE "new_Project" (
|
|
30
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
31
|
+
"name" TEXT NOT NULL,
|
|
32
|
+
"description" TEXT,
|
|
33
|
+
"color" TEXT NOT NULL DEFAULT '#3b82f6',
|
|
34
|
+
"hourlyRate" REAL,
|
|
35
|
+
"archived" BOOLEAN NOT NULL DEFAULT false,
|
|
36
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
37
|
+
"updatedAt" DATETIME NOT NULL,
|
|
38
|
+
"customerId" TEXT,
|
|
39
|
+
CONSTRAINT "Project_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
40
|
+
);
|
|
41
|
+
INSERT INTO "new_Project" ("archived", "color", "createdAt", "description", "hourlyRate", "id", "name", "updatedAt") SELECT "archived", "color", "createdAt", "description", "hourlyRate", "id", "name", "updatedAt" FROM "Project";
|
|
42
|
+
DROP TABLE "Project";
|
|
43
|
+
ALTER TABLE "new_Project" RENAME TO "Project";
|
|
44
|
+
CREATE INDEX "Project_archived_idx" ON "Project"("archived");
|
|
45
|
+
CREATE INDEX "Project_createdAt_idx" ON "Project"("createdAt");
|
|
46
|
+
CREATE INDEX "Project_customerId_idx" ON "Project"("customerId");
|
|
47
|
+
CREATE TABLE "new_TimeEntry" (
|
|
48
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
49
|
+
"startTime" DATETIME NOT NULL,
|
|
50
|
+
"endTime" DATETIME,
|
|
51
|
+
"description" TEXT,
|
|
52
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
53
|
+
"updatedAt" DATETIME NOT NULL,
|
|
54
|
+
"projectId" TEXT NOT NULL,
|
|
55
|
+
"invoiceId" TEXT,
|
|
56
|
+
CONSTRAINT "TimeEntry_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
57
|
+
CONSTRAINT "TimeEntry_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "Invoice" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
58
|
+
);
|
|
59
|
+
INSERT INTO "new_TimeEntry" ("createdAt", "description", "endTime", "id", "projectId", "startTime", "updatedAt") SELECT "createdAt", "description", "endTime", "id", "projectId", "startTime", "updatedAt" FROM "TimeEntry";
|
|
60
|
+
DROP TABLE "TimeEntry";
|
|
61
|
+
ALTER TABLE "new_TimeEntry" RENAME TO "TimeEntry";
|
|
62
|
+
CREATE INDEX "TimeEntry_projectId_idx" ON "TimeEntry"("projectId");
|
|
63
|
+
CREATE INDEX "TimeEntry_startTime_idx" ON "TimeEntry"("startTime");
|
|
64
|
+
CREATE INDEX "TimeEntry_invoiceId_idx" ON "TimeEntry"("invoiceId");
|
|
65
|
+
PRAGMA foreign_keys=ON;
|
|
66
|
+
PRAGMA defer_foreign_keys=OFF;
|
|
67
|
+
|
|
68
|
+
-- CreateIndex
|
|
69
|
+
CREATE UNIQUE INDEX "Customer_email_key" ON "Customer"("email");
|
|
70
|
+
|
|
71
|
+
-- CreateIndex
|
|
72
|
+
CREATE INDEX "Customer_email_idx" ON "Customer"("email");
|
|
73
|
+
|
|
74
|
+
-- CreateIndex
|
|
75
|
+
CREATE INDEX "Invoice_projectId_idx" ON "Invoice"("projectId");
|
|
76
|
+
|
|
77
|
+
-- CreateIndex
|
|
78
|
+
CREATE INDEX "Invoice_customerId_idx" ON "Invoice"("customerId");
|
|
79
|
+
|
|
80
|
+
-- CreateIndex
|
|
81
|
+
CREATE INDEX "Invoice_status_idx" ON "Invoice"("status");
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Paca - Task Manager Database Schema
|
|
2
|
+
// Learn more: https://pris.ly/d/prisma-schema
|
|
3
|
+
|
|
4
|
+
generator client {
|
|
5
|
+
provider = "prisma-client"
|
|
6
|
+
output = "../generated/prisma"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
datasource db {
|
|
10
|
+
provider = "sqlite"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Customers for invoicing
|
|
14
|
+
model Customer {
|
|
15
|
+
id String @id @default(uuid())
|
|
16
|
+
name String
|
|
17
|
+
email String @unique
|
|
18
|
+
stripeCustomerId String? // Stripe customer ID once created
|
|
19
|
+
createdAt DateTime @default(now())
|
|
20
|
+
updatedAt DateTime @updatedAt
|
|
21
|
+
|
|
22
|
+
// Relations
|
|
23
|
+
projects Project[]
|
|
24
|
+
invoices Invoice[]
|
|
25
|
+
|
|
26
|
+
@@index([email])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Projects are the core organizational unit
|
|
30
|
+
model Project {
|
|
31
|
+
id String @id @default(uuid())
|
|
32
|
+
name String
|
|
33
|
+
description String?
|
|
34
|
+
color String @default("#3b82f6") // Default blue color
|
|
35
|
+
hourlyRate Float? // Hourly rate for billing/tracking
|
|
36
|
+
archived Boolean @default(false)
|
|
37
|
+
createdAt DateTime @default(now())
|
|
38
|
+
updatedAt DateTime @updatedAt
|
|
39
|
+
|
|
40
|
+
// Relations
|
|
41
|
+
tasks Task[]
|
|
42
|
+
timeEntries TimeEntry[]
|
|
43
|
+
customerId String?
|
|
44
|
+
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
|
|
45
|
+
invoices Invoice[]
|
|
46
|
+
|
|
47
|
+
// Future: GitHub integration fields
|
|
48
|
+
// githubRepoId String?
|
|
49
|
+
// githubProjectId String?
|
|
50
|
+
// syncEnabled Boolean @default(false)
|
|
51
|
+
|
|
52
|
+
@@index([archived])
|
|
53
|
+
@@index([createdAt])
|
|
54
|
+
@@index([customerId])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Time tracking entries
|
|
58
|
+
model TimeEntry {
|
|
59
|
+
id String @id @default(uuid())
|
|
60
|
+
startTime DateTime
|
|
61
|
+
endTime DateTime?
|
|
62
|
+
description String?
|
|
63
|
+
createdAt DateTime @default(now())
|
|
64
|
+
updatedAt DateTime @updatedAt
|
|
65
|
+
|
|
66
|
+
// Relations
|
|
67
|
+
projectId String
|
|
68
|
+
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
69
|
+
invoiceId String?
|
|
70
|
+
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
|
|
71
|
+
|
|
72
|
+
@@index([projectId])
|
|
73
|
+
@@index([startTime])
|
|
74
|
+
@@index([invoiceId])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Invoices for time entries
|
|
78
|
+
model Invoice {
|
|
79
|
+
id String @id @default(uuid())
|
|
80
|
+
stripeInvoiceId String? // Stripe invoice ID once created
|
|
81
|
+
status String @default("draft") // draft, sent, paid
|
|
82
|
+
totalHours Float
|
|
83
|
+
totalAmount Float
|
|
84
|
+
createdAt DateTime @default(now())
|
|
85
|
+
updatedAt DateTime @updatedAt
|
|
86
|
+
|
|
87
|
+
// Relations
|
|
88
|
+
projectId String
|
|
89
|
+
project Project @relation(fields: [projectId], references: [id])
|
|
90
|
+
customerId String
|
|
91
|
+
customer Customer @relation(fields: [customerId], references: [id])
|
|
92
|
+
timeEntries TimeEntry[]
|
|
93
|
+
|
|
94
|
+
@@index([projectId])
|
|
95
|
+
@@index([customerId])
|
|
96
|
+
@@index([status])
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Tasks belong to projects
|
|
100
|
+
model Task {
|
|
101
|
+
id String @id @default(uuid())
|
|
102
|
+
title String
|
|
103
|
+
description String?
|
|
104
|
+
status String @default("todo") // todo, in_progress, done
|
|
105
|
+
priority String @default("medium") // low, medium, high, urgent
|
|
106
|
+
dueDate DateTime?
|
|
107
|
+
completedAt DateTime?
|
|
108
|
+
createdAt DateTime @default(now())
|
|
109
|
+
updatedAt DateTime @updatedAt
|
|
110
|
+
|
|
111
|
+
// Relations
|
|
112
|
+
projectId String
|
|
113
|
+
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
114
|
+
tags TaskTag[]
|
|
115
|
+
|
|
116
|
+
// Future: GitHub integration fields
|
|
117
|
+
// githubIssueId String?
|
|
118
|
+
// githubIssueNumber Int?
|
|
119
|
+
// syncEnabled Boolean @default(false)
|
|
120
|
+
|
|
121
|
+
@@index([projectId])
|
|
122
|
+
@@index([status])
|
|
123
|
+
@@index([priority])
|
|
124
|
+
@@index([dueDate])
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Tags for categorizing tasks
|
|
128
|
+
model Tag {
|
|
129
|
+
id String @id @default(uuid())
|
|
130
|
+
name String @unique
|
|
131
|
+
color String @default("#6b7280") // Default gray
|
|
132
|
+
createdAt DateTime @default(now())
|
|
133
|
+
|
|
134
|
+
// Relations
|
|
135
|
+
tasks TaskTag[]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Many-to-many relation between tasks and tags
|
|
139
|
+
model TaskTag {
|
|
140
|
+
taskId String
|
|
141
|
+
tagId String
|
|
142
|
+
task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
143
|
+
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
|
144
|
+
|
|
145
|
+
@@id([taskId, tagId])
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// App settings/preferences
|
|
149
|
+
model Setting {
|
|
150
|
+
key String @id
|
|
151
|
+
value String
|
|
152
|
+
updatedAt DateTime @updatedAt
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Future: User model for cloud sync
|
|
156
|
+
// model User {
|
|
157
|
+
// id String @id @default(uuid())
|
|
158
|
+
// email String @unique
|
|
159
|
+
// name String?
|
|
160
|
+
// createdAt DateTime @default(now())
|
|
161
|
+
// lastSyncAt DateTime?
|
|
162
|
+
// }
|