From c539c9e01da387bc775a295c4e667c43b607febd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andres=20Patin=CC=83o?= <97bermudez.andres@gmail.com> Date: Thu, 14 May 2026 21:32:23 -0500 Subject: [PATCH] feat: add prisma schema and migrations - Define Task and State entities with relations - Add StateHistory for tracking state changes - Include seed script for initial data - Add initial and soft delete migrations --- .../20260513220551_init/migration.sql | 55 ++++++++ .../migration.sql | 5 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 54 ++++++++ prisma/seed.ts | 121 ++++++++++++++++++ 5 files changed, 238 insertions(+) create mode 100644 prisma/migrations/20260513220551_init/migration.sql create mode 100644 prisma/migrations/20260514201510_add_soft_delete/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.ts diff --git a/prisma/migrations/20260513220551_init/migration.sql b/prisma/migrations/20260513220551_init/migration.sql new file mode 100644 index 0000000..076edad --- /dev/null +++ b/prisma/migrations/20260513220551_init/migration.sql @@ -0,0 +1,55 @@ +-- CreateTable +CREATE TABLE "State" ( + "name" TEXT NOT NULL, + + CONSTRAINT "State_pkey" PRIMARY KEY ("name") +); + +-- CreateTable +CREATE TABLE "Task" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "dueDate" TEXT NOT NULL, + "completed" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Task_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "StateHistory" ( + "id" TEXT NOT NULL, + "state" TEXT NOT NULL, + "date" TEXT NOT NULL, + "taskId" TEXT NOT NULL, + "order" INTEGER NOT NULL, + + CONSTRAINT "StateHistory_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Note" ( + "id" TEXT NOT NULL, + "content" TEXT NOT NULL, + "taskId" TEXT NOT NULL, + "order" INTEGER NOT NULL, + + CONSTRAINT "Note_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "StateHistory_taskId_idx" ON "StateHistory"("taskId"); + +-- CreateIndex +CREATE INDEX "Note_taskId_idx" ON "Note"("taskId"); + +-- AddForeignKey +ALTER TABLE "StateHistory" ADD CONSTRAINT "StateHistory_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "Task"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "StateHistory" ADD CONSTRAINT "StateHistory_state_fkey" FOREIGN KEY ("state") REFERENCES "State"("name") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Note" ADD CONSTRAINT "Note_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "Task"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260514201510_add_soft_delete/migration.sql b/prisma/migrations/20260514201510_add_soft_delete/migration.sql new file mode 100644 index 0000000..31cad8a --- /dev/null +++ b/prisma/migrations/20260514201510_add_soft_delete/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Task" ADD COLUMN "deletedAt" TIMESTAMP(3); + +-- CreateIndex +CREATE INDEX "Task_deletedAt_idx" ON "Task"("deletedAt"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..ba3399d --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,54 @@ +generator client { + provider = "prisma-client" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" +} + +model State { + name String @id + + tasks StateHistory[] +} + +model Task { + id String @id @default(uuid()) + title String + description String + dueDate String + completed Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + + stateHistory StateHistory[] + notes Note[] + + @@index([deletedAt]) +} + +model StateHistory { + id String @id @default(uuid()) + state String + date String + taskId String + order Int + + task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) + stateRef State @relation(fields: [state], references: [name]) + + @@index([taskId]) +} + +model Note { + id String @id @default(uuid()) + content String + taskId String + order Int + + task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) + + @@index([taskId]) +} diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..57172fe --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,121 @@ +import "dotenv/config"; +import { PrismaClient } from "../src/generated/prisma/client.js"; +import { PrismaPg } from "@prisma/adapter-pg"; + +const connectionString = process.env.DATABASE_URL; +if (!connectionString) { + throw new Error("DATABASE_URL is not set"); +} + +const adapter = new PrismaPg({ connectionString }); +const prisma = new PrismaClient({ adapter }); + +async function seed() { + console.log("Seeding database..."); + + await prisma.state.createMany({ + data: [ + { name: "new" }, + { name: "active" }, + { name: "resolved" }, + { name: "closed" }, + ], + }); + + const tasks = [ + { + title: "Complete Project Proposal", + description: "Prepare and submit the project proposal for approval.", + dueDate: "2023-12-15", + stateHistory: [ + { state: "new", date: "2023-12-01" }, + { state: "active", date: "2023-12-05" }, + ], + notes: ["Check proposal guidelines", "Include budget estimates"], + }, + { + title: "Design Wireframes", + description: "Create wireframes for the user interface.", + dueDate: "2023-12-20", + stateHistory: [ + { state: "new", date: "2023-12-02" }, + { state: "active", date: "2023-12-06" }, + ], + notes: ["Review design patterns", "Seek feedback from the team"], + }, + { + title: "Implement User Authentication", + description: "Develop user authentication functionality.", + dueDate: "2023-12-25", + stateHistory: [ + { state: "new", date: "2023-12-03" }, + { state: "active", date: "2023-12-07" }, + ], + notes: ["Research secure authentication methods", "Implement password hashing"], + }, + { + title: "Write API Documentation", + description: "Document the RESTful API for external developers.", + dueDate: "2023-12-30", + stateHistory: [ + { state: "new", date: "2023-12-04" }, + { state: "active", date: "2023-12-08" }, + ], + notes: ["Use Swagger/OpenAPI for documentation", "Provide clear examples"], + }, + { + title: "Bug Fixes and Testing", + description: "Address reported bugs and perform thorough testing.", + dueDate: "2024-01-05", + stateHistory: [ + { state: "new", date: "2023-12-05" }, + { state: "active", date: "2023-12-09" }, + ], + notes: ["Create test cases for critical scenarios", "Perform regression testing"], + }, + { + title: "Project Deployment", + description: "Prepare for and deploy the project to production.", + dueDate: "2024-01-10", + stateHistory: [ + { state: "new", date: "2023-12-06" }, + { state: "active", date: "2023-12-10" }, + ], + notes: ["Coordinate with DevOps for deployment", "Monitor performance after deployment"], + }, + ]; + + for (const task of tasks) { + await prisma.task.create({ + data: { + title: task.title, + description: task.description, + dueDate: task.dueDate, + stateHistory: { + create: task.stateHistory.map((entry, index) => ({ + state: entry.state, + date: entry.date, + order: index, + })), + }, + notes: { + create: task.notes.map((content, index) => ({ + content, + order: index, + })), + }, + }, + }); + } + + console.log("Seed completed!"); +} + +seed() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });