Skip to content

Conversation

@mikedane-bitovi
Copy link
Collaborator

No description provided.

Copy link

Copilot AI left a 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.

Comment on lines +78 to +80
const updateData: { status: string; priority?: string } = { status };

await prisma.task.update({ where: { id: taskId }, data: updateData });
Copy link

Copilot AI Nov 28, 2025

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.

Suggested change
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 } });

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +50
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;
Copy link

Copilot AI Nov 28, 2025

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.

Copilot uses AI. Check for mistakes.
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U taskflow" ]
Copy link

Copilot AI Nov 28, 2025

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"].

Suggested change
test: [ "CMD-SHELL", "pg_isready -U taskflow" ]
test: ["CMD-SHELL", "pg_isready -U taskflow"]

Copilot uses AI. Check for mistakes.

let matchedCount = 0;
let unmatchedCount = 0;
const unmatchedTasks: any[] = [];
Copy link

Copilot AI Nov 28, 2025

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.

Copilot uses AI. Check for mistakes.

## IMPORTANT Considerations

- Any modifications to data in the database must use the PostgreSQL MCP server. No newline at end of file
Copy link

Copilot AI Nov 28, 2025

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.

Suggested change
- 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.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +56
'todo', 'Todo', 'TODO',
'in_progress', 'In Progress', 'IN PROGRESS',
'done', 'Done', 'DONE',
'review', 'Review', 'REVIEW'
Copy link

Copilot AI Nov 28, 2025

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'].

Suggested change
'todo', 'Todo', 'TODO',
'in_progress', 'In Progress', 'IN PROGRESS',
'done', 'Done', 'DONE',
'review', 'Review', 'REVIEW'
'todo',
'in_progress',
'done',
'review'

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
# 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"
Copy link

Copilot AI Nov 28, 2025

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.

Suggested change
# 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"

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +67
setTimeout(() => {
if (devServer && !devServer.killed) {
devServer.kill('SIGKILL');
}
}, 5000);
Copy link

Copilot AI Nov 28, 2025

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.

Suggested change
setTimeout(() => {
if (devServer && !devServer.killed) {
devServer.kill('SIGKILL');
}
}, 5000);
const forceKillTimer = setTimeout(() => {
if (devServer && !devServer.killed) {
devServer.kill('SIGKILL');
}
}, 5000);
forceKillTimer.unref();

Copilot uses AI. Check for mistakes.
* 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'
Copy link

Copilot AI Nov 28, 2025

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.

Suggested change
* 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'

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 54
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,
});
Copy link

Copilot AI Nov 28, 2025

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.

Copilot uses AI. Check for mistakes.
@mikedane-bitovi mikedane-bitovi deleted the debug-3 branch December 11, 2025 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants