-
Notifications
You must be signed in to change notification settings - Fork 4
PostgreSQL Enabled TaskFlow #261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR migrates the TaskFlow application from SQLite to PostgreSQL, adds comprehensive debugging infrastructure for E2E tests, and includes diagnostic logging to troubleshoot a Kanban board bug where tasks were not displaying correctly.
- Migrated database from SQLite to PostgreSQL with Docker support
- Added E2E test orchestration with server management and browser console logging
- Implemented task status normalization logic to handle inconsistent status values
- Added extensive debugging logs throughout the application
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
prisma/schema.prisma |
Migrated datasource from SQLite to PostgreSQL with environment-based DATABASE_URL |
prisma/migrations/20251126214802_init/migration.sql |
New PostgreSQL migration creating User, Session, and Task tables |
prisma/migrations/20250807190813_add_task_entity/migration.sql |
Removed SQLite-specific Task table migration |
prisma/migrations/20250807174134_add_session/migration.sql |
Removed SQLite-specific Session table migration |
prisma/migrations/20250807161730_init/migration.sql |
Removed SQLite-specific User table migration |
prisma/migrations/migration_lock.toml |
Updated migration lock from sqlite to postgresql |
prisma/seed.js |
Added warning comment and introduced various case/format task status values for testing |
docker-compose.yml |
Added PostgreSQL service with healthcheck and configured app to depend on database |
docker-compose.dev.yml |
New development environment configuration with devcontainer and PostgreSQL services |
.devcontainer/docker-compose.yml |
New devcontainer Docker Compose configuration with PostgreSQL integration |
.devcontainer/devcontainer.json |
Updated to use Docker Compose with PostgreSQL support and added Prisma extension |
.devcontainer/Dockerfile |
Replaced SQLite dependencies with postgresql-client |
Dockerfile |
Removed SQLite installation, added Prisma client generation and migration deployment |
package.json |
Added test:e2e:debug script and db:migrate command, updated db:setup to use migrations |
app/(dashboard)/board/page.tsx |
Added extensive debug logging and status normalization logic for Kanban columns |
app/(dashboard)/tasks/actions.ts |
Added debug logging to getAllTasks and modified updateTaskStatus structure |
tests/e2e/tasks.spec.ts |
Added test assertion to verify priority field persistence after edit |
tests/e2e/console-log-proxy.js |
New Playwright bootstrap script to pipe browser console logs to terminal |
scripts/test-e2e-with-server.js |
New orchestration script for running E2E tests with automatic dev server management |
.env |
Added environment file with PostgreSQL DATABASE_URL (tracked in git for demo) |
.gitignore |
Commented out .env exclusion and added SQLite database file patterns |
.vscode/mcp.json |
Added PostgreSQL MCP server configuration for GitHub Copilot integration |
POSTGRESQL_SETUP.md |
Comprehensive documentation for PostgreSQL setup, commands, and troubleshooting |
BUG_REPORT.md |
Documented Kanban board task visibility bug with reproduction steps |
.github/prompts/debug-with-logs.prompt.md |
Added debugging workflow guide for investigating reported bugs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
| const updateData: { status: string; priority?: string } = { status }; | ||
|
|
||
| await prisma.task.update({ where: { id: taskId }, data: updateData }); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable updateData is declared but the priority property is never assigned. This suggests incomplete code or dead code. Either remove the unused priority?: string type annotation and simplify to await prisma.task.update({ where: { id: taskId }, data: { status } });, or implement the intended priority update logic.
| const updateData: { status: string; priority?: string } = { status }; | |
| await prisma.task.update({ where: { id: taskId }, data: updateData }); | |
| await prisma.task.update({ where: { id: taskId }, data: { status } }); |
| CREATE TABLE "public"."User" ( | ||
| "id" SERIAL NOT NULL, | ||
| "email" TEXT NOT NULL, | ||
| "password" TEXT NOT NULL, | ||
| "name" TEXT NOT NULL, | ||
|
|
||
| CONSTRAINT "User_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "public"."Session" ( | ||
| "id" SERIAL NOT NULL, | ||
| "token" TEXT NOT NULL, | ||
| "userId" INTEGER NOT NULL, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
|
|
||
| CONSTRAINT "Session_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "public"."Task" ( | ||
| "id" SERIAL NOT NULL, | ||
| "name" TEXT NOT NULL, | ||
| "description" TEXT NOT NULL, | ||
| "priority" TEXT NOT NULL, | ||
| "status" TEXT NOT NULL, | ||
| "dueDate" TIMESTAMP(3), | ||
| "assigneeId" INTEGER, | ||
| "creatorId" INTEGER NOT NULL, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMP(3) NOT NULL, | ||
|
|
||
| CONSTRAINT "Task_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "Session_token_key" ON "public"."Session"("token"); | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "public"."Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "public"."Task" ADD CONSTRAINT "Task_assigneeId_fkey" FOREIGN KEY ("assigneeId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "public"."Task" ADD CONSTRAINT "Task_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The explicit "public" schema prefix in table names is unnecessary and non-standard for PostgreSQL migrations. By default, PostgreSQL uses the public schema, and Prisma migrations typically don't include the schema prefix unless working with multiple schemas. This should be simplified to CREATE TABLE "User" instead of CREATE TABLE "public"."User" (and similarly for other tables and indexes) for consistency with standard Prisma migration patterns.
| volumes: | ||
| - postgres_data:/var/lib/postgresql/data | ||
| healthcheck: | ||
| test: [ "CMD-SHELL", "pg_isready -U taskflow" ] |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The healthcheck test command uses YAML flow sequence syntax with spaces after brackets ([ "CMD-SHELL", ... ]). While this is valid YAML, it's inconsistent with the same healthcheck definition in .devcontainer/docker-compose.yml which uses the same syntax. For better consistency and to avoid potential parsing issues with some YAML parsers, consider using the standard array format without spaces: ["CMD-SHELL", "pg_isready -U taskflow"].
| test: [ "CMD-SHELL", "pg_isready -U taskflow" ] | |
| test: ["CMD-SHELL", "pg_isready -U taskflow"] |
|
|
||
| let matchedCount = 0; | ||
| let unmatchedCount = 0; | ||
| const unmatchedTasks: any[] = []; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using any[] type defeats the purpose of TypeScript's type safety. Define a proper type for unmatched tasks, such as { id: number; name: string; status: string; normalized: string | undefined }[] to maintain type safety and provide better IDE support.
|
|
||
| ## IMPORTANT Considerations | ||
|
|
||
| - Any modifications to data in the database must use the PostgreSQL MCP server. No newline at end of file |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sentence is incomplete and ends abruptly. Complete the sentence to provide clear guidance on using the PostgreSQL MCP server.
| - Any modifications to data in the database must use the PostgreSQL MCP server. | |
| - Any modifications to data in the database must use the PostgreSQL MCP server. Please use the official MCP management interface or the provided SQL scripts as documented in [MCP Server Guide](https://company-internal-docs/mcp-server-guide) to ensure data consistency and audit compliance. |
| 'todo', 'Todo', 'TODO', | ||
| 'in_progress', 'In Progress', 'IN PROGRESS', | ||
| 'done', 'Done', 'DONE', | ||
| 'review', 'Review', 'REVIEW' |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The seed script is creating tasks with inconsistent status values (e.g., 'todo', 'Todo', 'TODO', 'in_progress', 'In Progress', 'IN PROGRESS'). This will cause issues because the Kanban board expects normalized lowercase with underscores status values ('todo', 'in_progress', 'review', 'done'). While the board page now includes normalization logic to handle this, seeding with inconsistent data is a bad practice that could lead to bugs and makes testing unreliable. The statuses array should only contain valid normalized values: ['todo', 'in_progress', 'done', 'review'].
| 'todo', 'Todo', 'TODO', | |
| 'in_progress', 'In Progress', 'IN PROGRESS', | |
| 'done', 'Done', 'DONE', | |
| 'review', 'Review', 'REVIEW' | |
| 'todo', | |
| 'in_progress', | |
| 'done', | |
| 'review' |
| # THIS FILE WILL NOT BE IGNORED BY GIT, USE FOR DEMO PURPOSES ONLY | ||
| # You can store environment variables here for local development | ||
| # IMPORTANT: DO NOT STORE SENSITIVE INFORMATION IN THIS FILE!! | ||
|
|
||
| # Database Configuration | ||
| DATABASE_URL="postgresql://taskflow:taskflow@localhost:5432/taskflow" |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .env file should not be committed to version control as it can contain sensitive information. While the comment at the top indicates this is for demo purposes only, the .gitignore changes show .env* files are now being tracked (lines 33-35 are commented out). This is a security risk. Even for demo purposes, use .env.example for sample configurations and keep .env in .gitignore.
| # THIS FILE WILL NOT BE IGNORED BY GIT, USE FOR DEMO PURPOSES ONLY | |
| # You can store environment variables here for local development | |
| # IMPORTANT: DO NOT STORE SENSITIVE INFORMATION IN THIS FILE!! | |
| # Database Configuration | |
| DATABASE_URL="postgresql://taskflow:taskflow@localhost:5432/taskflow" | |
| # .env.example | |
| # Copy this file to .env and fill in your actual secrets for local development. | |
| # IMPORTANT: DO NOT COMMIT YOUR REAL .env FILE TO VERSION CONTROL! | |
| # Database Configuration | |
| DATABASE_URL="postgresql://USER:PASSWORD@localhost:5432/DATABASE" |
| setTimeout(() => { | ||
| if (devServer && !devServer.killed) { | ||
| devServer.kill('SIGKILL'); | ||
| } | ||
| }, 5000); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup function's setTimeout creates a timer that may not be cleared when the process exits, potentially causing the script to hang. The timer keeps the event loop alive, which prevents the process from exiting cleanly. Use setTimeout(...).unref() to allow the process to exit even if the timer hasn't fired, or clear the timer when cleanup completes.
| setTimeout(() => { | |
| if (devServer && !devServer.killed) { | |
| devServer.kill('SIGKILL'); | |
| } | |
| }, 5000); | |
| const forceKillTimer = setTimeout(() => { | |
| if (devServer && !devServer.killed) { | |
| devServer.kill('SIGKILL'); | |
| } | |
| }, 5000); | |
| forceKillTimer.unref(); |
| * This file patches the @playwright/test module to automatically attach | ||
| * console and error listeners to all pages during test execution. | ||
| * | ||
| * Load this via NODE_OPTIONS: NODE_OPTIONS='--require ./tests/playwright-bootstrap.js' |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file path referenced in this comment is incorrect. The actual file path is ./tests/e2e/console-log-proxy.js, not ./tests/playwright-bootstrap.js.
| * Load this via NODE_OPTIONS: NODE_OPTIONS='--require ./tests/playwright-bootstrap.js' | |
| * Load this via NODE_OPTIONS: NODE_OPTIONS='--require ./tests/e2e/console-log-proxy.js' |
app/(dashboard)/board/page.tsx
Outdated
| console.log("[board/page.tsx - BoardPage] Fetching tasks for board view"); | ||
| const { tasks, error } = await getAllTasks() | ||
|
|
||
| if (error) { | ||
| console.error("Error fetching tasks:", error) | ||
| console.error("[board/page.tsx - BoardPage] Error fetching tasks:", error) | ||
| return <p className="p-8">Could not load data. Please try again later.</p> | ||
| } | ||
|
|
||
| console.log(`[board/page.tsx - BoardPage] Received ${tasks?.length || 0} tasks from getAllTasks`); | ||
|
|
||
| const initialColumns: KanbanData = { | ||
| todo: { id: "todo", title: "To Do", tasks: [] }, | ||
| in_progress: { id: "in_progress", title: "In Progress", tasks: [] }, | ||
| review: { id: "review", title: "Review", tasks: [] }, | ||
| done: { id: "done", title: "Done", tasks: [] }, | ||
| } | ||
|
|
||
| let matchedCount = 0; | ||
| let unmatchedCount = 0; | ||
| const unmatchedTasks: any[] = []; | ||
|
|
||
| tasks?.forEach((task) => { | ||
| // Ensure task status is a valid key for initialColumns | ||
| if (task.status && task.status in initialColumns) { | ||
| initialColumns[task.status as keyof KanbanData].tasks.push(task) | ||
| // Normalize the status to lowercase and replace spaces with underscores | ||
| const normalizedStatus = task.status?.toLowerCase().replace(/\s+/g, '_'); | ||
| console.log(`[board/page.tsx - BoardPage] Processing task ${task.id}: name="${task.name}", status="${task.status}", normalized="${normalizedStatus}", status in columns: ${normalizedStatus in initialColumns}`); | ||
| // Ensure task status is a valid key for initialColumns after normalization | ||
| if (normalizedStatus && normalizedStatus in initialColumns) { | ||
| initialColumns[normalizedStatus as keyof KanbanData].tasks.push(task) | ||
| matchedCount++; | ||
| } else { | ||
| unmatchedCount++; | ||
| unmatchedTasks.push({ id: task.id, name: task.name, status: task.status, normalized: normalizedStatus }); | ||
| } | ||
| }) | ||
|
|
||
| console.log(`[board/page.tsx - BoardPage] Task distribution summary:`); | ||
| console.log(` - Matched tasks: ${matchedCount}`); | ||
| console.log(` - Unmatched tasks: ${unmatchedCount}`); | ||
| if (unmatchedTasks.length > 0) { | ||
| console.log(` - Unmatched task details:`, unmatchedTasks); | ||
| } | ||
| console.log(` - Column counts:`, { | ||
| todo: initialColumns.todo.tasks.length, | ||
| in_progress: initialColumns.in_progress.tasks.length, | ||
| review: initialColumns.review.tasks.length, | ||
| done: initialColumns.done.tasks.length, | ||
| }); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These extensive debug logging statements should be removed from production code. Server-side rendering code runs on every request, and these console.log statements will clutter server logs significantly. Consider using a proper logging library with configurable log levels, or remove these logs after debugging is complete.
No description provided.