Skip to content

Implementación Completa del Task Management System para MicroboxLabs#7

Open
breyeszx wants to merge 8 commits into
microboxlabs:trunkfrom
breyeszx:master
Open

Implementación Completa del Task Management System para MicroboxLabs#7
breyeszx wants to merge 8 commits into
microboxlabs:trunkfrom
breyeszx:master

Conversation

@breyeszx

@breyeszx breyeszx commented Dec 9, 2024

Copy link
Copy Markdown

Este pull request presenta el desarrollo completo del Task Management System, cumpliendo con todos los requisitos core y casos de uso solicitados. La implementación utiliza Next.js para el frontend y backend, y SQLite como base de datos.

Summary by CodeRabbit

  • New Features

    • Introduced a task management system with user authentication and role-based access.
    • Added components for creating, editing, and managing tasks.
    • Implemented a responsive sidebar for navigation and a dashboard for task summaries.
    • Created user registration and login functionality.
    • Added loading skeletons for the dashboard and task cards.
    • Enhanced user interface with new components for task display and management.
    • Implemented a middleware for user authentication and access control.
    • Introduced new API endpoints for user and task management.
  • Bug Fixes

    • Enhanced error handling for API calls and user authentication processes.
  • Documentation

    • Updated metadata in the application layout.
  • Style

    • Improved UI design using Tailwind CSS and Flowbite components.
  • Chores

    • Updated dependencies in the project for improved functionality and security.

@coderabbitai

coderabbitai Bot commented Dec 9, 2024

Copy link
Copy Markdown

Walkthrough

The changes in this pull request encompass the introduction of several new files and components for a task management system built with Next.js. Key additions include user authentication, task management APIs, and user interfaces for both admin and regular users. New types for tasks and users have been defined, and a middleware for authentication has been implemented. The README.md file was deleted, and various components were added to facilitate task creation, editing, and management, along with user registration and login functionalities.

Changes

File Path Change Summary
README.md Deleted the README file containing project instructions and use cases.
nextjs-task-system/Utils/auth.ts Added User interface and getCurrentUser function for user authentication.
nextjs-task-system/Utils/db.ts Introduced db.ts for SQLite database connection setup.
nextjs-task-system/Utils/script.sql Added SQL script for creating users, tasks, and sessions tables, along with initial data and a view for tasks with user details.
nextjs-task-system/Utils/server/auth.ts Added getCurrentUserServer function for server-side user retrieval based on session ID.
nextjs-task-system/app/admin/create/page.tsx Implemented CreateTask component for task creation with user input validation and API interaction.
nextjs-task-system/app/admin/layout.tsx Created AdminLayout component for admin interface layout, including user data fetching and sidebar navigation.
nextjs-task-system/app/admin/page.tsx Added AdminDashboard component for displaying task summaries and related metrics.
nextjs-task-system/app/admin/tasks/[id]/page.tsx Introduced EditTask component for editing existing tasks, including form handling and deletion functionality.
nextjs-task-system/app/admin/tasks/page.tsx Created TasksPage component for managing and filtering tasks.
nextjs-task-system/app/api/auth/currentUser.ts Added API endpoint for retrieving current user information based on session.
nextjs-task-system/app/api/auth/login/route.ts Implemented login API with session management and error handling.
nextjs-task-system/app/api/auth/logout/route.ts Added logout API endpoint to handle user logout and session deletion.
nextjs-task-system/app/api/auth/me/route.ts Introduced API route for retrieving current user data with error handling.
nextjs-task-system/app/api/auth/register/route.ts Implemented user registration API with input validation and error handling.
nextjs-task-system/app/api/tasks/[id]/route.ts Added API endpoints for task management (GET, PUT, UPDATE, DELETE) based on task ID.
nextjs-task-system/app/api/tasks/route.ts Introduced RESTful API for managing tasks (GET, POST, PUT, DELETE).
nextjs-task-system/app/api/tasks/user/[id]/route.ts Added API routes for managing tasks associated with a specific user.
nextjs-task-system/app/api/users/route.ts Implemented user management API for retrieving and creating users.
nextjs-task-system/app/layout.tsx Updated metadata for the application title and description.
nextjs-task-system/app/login/page.tsx Created Home component for user login interface.
nextjs-task-system/app/page.tsx Renamed Home component to HomePage with updated layout and content.
nextjs-task-system/app/register/page.tsx Introduced Register component for user registration form.
nextjs-task-system/app/user/layout.tsx Added UserLayout component for user interface layout with sidebar navigation.
nextjs-task-system/app/user/page.tsx Created UserDashboard component for displaying user's tasks.
nextjs-task-system/app/user/tasks/page.tsx Implemented UserTasksPage for managing user tasks with status updates.
nextjs-task-system/componentes/AdminComponents/TaskCard.tsx Added TaskCard component for displaying task details in admin view.
nextjs-task-system/componentes/Sidebar.tsx Introduced SidebarComponent for responsive navigation sidebar.
nextjs-task-system/componentes/UserComponents/TaskCard.tsx Created TaskCard component for displaying task details in user view.
nextjs-task-system/components/Skeletons/DashboardSkeleton.tsx Added DashboardSkeleton component for loading placeholders in dashboard.
nextjs-task-system/components/Skeletons/TaskCardSkeleton.tsx Introduced TaskCardSkeleton for loading placeholders in task cards.
nextjs-task-system/components/TaskComponents/TaskCard.tsx Created TaskCard component for displaying task details with edit/delete options.
nextjs-task-system/hooks/useAuth.ts Added useAuth hook for managing user authentication state.
nextjs-task-system/middleware.ts Introduced middleware for managing user authentication and access control.
nextjs-task-system/package.json Updated dependencies and added new libraries for functionality.
nextjs-task-system/tipos/tasks.ts Introduced Task type for task object structure.
nextjs-task-system/tipos/usuarios.ts Added User type for user object structure.
nextjs-task-system/tsconfig.json Updated TypeScript configuration for better readability and added target version.
package.json Added lucide-react library as a new dependency.

Poem

In a world of tasks, both big and small,
We’ve built a system to manage them all.
With users and roles, and tasks to create,
Our app is ready, oh, isn’t it great?
So hop on along, let’s get things done,
With each little task, we’ll have lots of fun! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 57

🧹 Outside diff range and nitpick comments (37)
nextjs-task-system/app/page.tsx (1)

7-7: Consider enhancing dark mode implementation.

The current dark mode implementation could be improved by respecting system preferences and providing a smooth transition experience.

Consider adding these Tailwind classes to support system preferences and transitions:

-    <main className="flex min-h-screen flex-col items-center justify-center bg-gray-100 dark:bg-gray-800">
+    <main className="flex min-h-screen flex-col items-center justify-center bg-gray-100 dark:bg-gray-800 transition-colors duration-200">

Also, consider adding this to your tailwind.config.js:

module.exports = {
  darkMode: 'media', // respects system preferences
  // ... rest of your config
}
nextjs-task-system/app/api/auth/login/route.ts (3)

55-60: Enhance Session Cookie Security Settings

To mitigate potential CSRF attacks and enhance security, consider setting the SameSite attribute to 'strict' and ensuring the secure attribute is correctly applied in production environments.

Apply this diff to adjust the cookie settings:

 response.cookies.set("sessionId", sessionId, {
   httpOnly: true,
-  secure: process.env.NODE_ENV === "production",
+  secure: process.env.NODE_ENV !== "development",
-  sameSite: "lax",
+  sameSite: "strict",
   path: "/",
 });

63-66: Avoid Logging Errors Directly to the Console in Production

Logging detailed error information to the console may expose sensitive data in production environments. Consider using a logging library that can differentiate between development and production modes and handle logs appropriately.

Also applies to: 102-107


6-13: Reuse Existing User Interface Definitions

The User interface is manually defined in this file. If there's a shared User interface elsewhere in the codebase, consider importing it to maintain consistency and reduce duplication.

nextjs-task-system/app/api/tasks/[id]/route.ts (1)

31-79: Use PATCH Method for Partial Updates

Since only the status field of a task is being updated, it's more appropriate to use the PATCH method instead of PUT to align with RESTful API conventions for partial updates.

Apply this diff to change the method signature:

-export async function PUT(
+export async function PATCH(
   request: Request,
   { params }: { params: { id: string } },
 ) {
   // Function body...
 }
nextjs-task-system/app/api/tasks/route.ts (1)

139-145: Return Updated Task Data After Modification

After updating or deleting a task, consider returning the updated task data or a confirmation message with details. This provides immediate feedback to the client and can be useful for front-end updates.

Also applies to: 172-178

nextjs-task-system/app/user/layout.tsx (2)

36-49: Add Error Handling for Fetch Requests

In the getCurrentUser function, consider adding error handling for the fetch request to manage unexpected errors and provide better user experience.

Apply this diff to include error handling:

 const getCurrentUser = async () => {
   try {
     const response = await fetch("/api/auth/me");
     if (response.ok) {
       const userData = await response.json();
       setCurrentUser(userData);
     } else {
+      const errorData = await response.json();
+      console.error("Error fetching current user:", errorData);
       router.push("/login");
     }
   } catch (error) {
     console.error("Error fetching current user:", error);
     router.push("/login");
   }
 };

145-152: Improve Accessibility for Mobile Toggle Button

The mobile toggle button lacks an accessible label for screen readers. Adding an aria-label or hidden text improves accessibility for users relying on assistive technologies.

Apply this diff to enhance accessibility:

 <Button onClick={toggleSidebar}>
+  <span className="sr-only">Toggle Sidebar</span>
   <HiMenu className="h-6 w-6" />
 </Button>
nextjs-task-system/app/admin/layout.tsx (1)

81-83: Add aria-label to icon-only buttons for accessibility

Icon-only buttons should have aria-label attributes to improve accessibility for users relying on screen readers.

Apply this diff to add aria-label attributes:

// Close button for mobile (lines 81-83)
<Button
  color="gray"
  className="!p-2"
  onClick={toggleSidebar}
+ aria-label="Close sidebar"
>
  <HiX className="h-6 w-6" />
</Button>

// Toggle button for mobile (lines 160-162)
<Button
  onClick={toggleSidebar}
+ aria-label="Open sidebar"
>
  <HiMenu className="h-6 w-6" />
</Button>

Also applies to: 160-162

nextjs-task-system/app/admin/tasks/[id]/page.tsx (1)

80-82: Replace the browser confirm with a modal dialog

Using the native confirm dialog provides a less consistent user experience and might not align with the application's design. Consider using a Modal component for the confirmation.

Implement a confirmation modal using flowbite-react:

  • Remove the confirm call in handleSubmit.
  • Add state to manage the visibility of the confirmation modal.
  • Display the modal when the form is submitted.
  • Proceed with form submission if the user confirms.
nextjs-task-system/app/user/page.tsx (1)

96-107: Handle fetch errors more gracefully when getting the current user

When fetching the current user, if an error occurs (e.g., network issue), the user is redirected to the login page without an explanation. Consider providing user feedback or retry logic.

Modify the getCurrentUser function to handle errors:

try {
  const response = await fetch("/api/auth/me");
  if (!response.ok) {
    throw new Error("No authenticated user");
  }
  const userData = await response.json();
  setCurrentUserId(userData.id);
} catch (error) {
  console.error("Error fetching current user:", error);
+ // Optionally display an error message or a notification
  router.push("/login");
}
nextjs-task-system/app/api/auth/me/route.ts (2)

8-10: Enhance authentication error handling

The error message "Not authenticated" is too generic. Consider providing more specific error codes and messages to help clients handle different authentication scenarios (e.g., expired token, invalid token, etc.).

 if (!user) {
-  return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
+  return NextResponse.json({
+    error: "authentication_required",
+    message: "Valid authentication credentials are required to access this resource"
+  }, { status: 401 });
 }

4-20: Implement rate limiting for security

The authentication endpoint should implement rate limiting to prevent brute force attacks.

Consider implementing rate limiting middleware using a library like express-rate-limit or implementing a custom solution with Redis. Would you like me to provide an example implementation?

nextjs-task-system/Utils/auth.ts (1)

1-1: Remove unused import

The db import is not used in this file.

-import db from "./db";
nextjs-task-system/hooks/useAuth.ts (1)

16-26: Add refresh token functionality

The current implementation doesn't handle token refresh or session expiry. Consider adding a refresh mechanism.

Would you like me to provide an implementation for token refresh functionality?

nextjs-task-system/components/Skeletons/DashboardSkeleton.tsx (2)

9-14: Consider using more meaningful keys for mapped elements

Using array indices as keys (key={i}) might lead to rendering issues if items are reordered or filtered. While this is less critical for static skeleton loaders, it's a good practice to use more meaningful keys.

-{[1, 2, 3].map((i) => (
+{['status-1', 'status-2', 'status-3'].map((key) => (
   <div
-    key={i}
+    key={key}
     className="h-24 animate-pulse rounded-lg bg-gray-200 dark:bg-gray-700"
   ></div>
 ))}

Also applies to: 19-24


3-26: Enhance accessibility with ARIA attributes

The skeleton loader should indicate its loading state to screen readers.

-<div className="space-y-6">
+<div className="space-y-6" role="status" aria-label="Loading dashboard content">
   {/* Total Tasks Card */}
-  <div className="h-32 w-full animate-pulse rounded-lg bg-gray-200 dark:bg-gray-700"></div>
+  <div className="h-32 w-full animate-pulse rounded-lg bg-gray-200 dark:bg-gray-700" aria-hidden="true"></div>
   ...
+  <span className="sr-only">Loading dashboard...</span>
 </div>
nextjs-task-system/Utils/server/auth.ts (1)

15-15: Remove debug logging statement

Production code should not contain debug console logs.

-  console.log("Debug - sessionId:", sessionId); // Temporary debug log
nextjs-task-system/middleware.ts (1)

11-14: Centralize protected paths configuration

Move the protected paths array to a configuration file to maintain consistency across the application.

Create a new file config/auth.ts:

export const AUTH_CONFIG = {
  protectedPaths: {
    user: ["/user"],
    admin: ["/admin"]
  },
  authPages: ["/login", "/register"]
} as const;
nextjs-task-system/components/Skeletons/TaskCardSkeleton.tsx (1)

1-27: Consider enhancing reusability with customizable dimensions

The skeleton loader effectively matches the TaskCard structure, but could be more reusable across different card sizes.

Consider accepting props for customizing dimensions:

-const TaskCardSkeleton = () => {
+interface TaskCardSkeletonProps {
+  titleWidth?: string;  // e.g., 'w-3/4'
+  descriptionLines?: number;
+}
+
+const TaskCardSkeleton: React.FC<TaskCardSkeletonProps> = ({
+  titleWidth = 'w-3/4',
+  descriptionLines = 2,
+}) => {
nextjs-task-system/app/api/tasks/user/[id]/route.ts (1)

1-8: Add type definition for task creation payload

Consider adding an interface to define the expected shape of the POST request body for better type safety and documentation.

interface CreateTaskPayload {
  title: string;
  description: string;
  due_date: string;
  priority: 'high' | 'medium' | 'low';
  status: 'pending' | 'in progress' | 'completed';
  comments?: string;
}
nextjs-task-system/componentes/UserComponents/TaskCard.tsx (2)

5-17: Improve type safety and documentation

The type definition could be improved with better documentation and stricter types.

+ import { Task } from '@/types/tasks';  // Add this import

  type TaskCardProps = {
-   task: {
-     id: number;
-     title: string;
-     description: string;
-     assigned_to: number;
-     due_date: string;
-     priority: string;
-     status: string;
-     comments?: string;
-   };
+   task: Task;
    /** Determines the display mode of the card */
    variant?: "user" | "admin";
  };

20-44: Extract color mapping to constants

The color mapping functions could be simplified using constants.

+ const PRIORITY_COLORS = {
+   high: "failure",
+   medium: "warning",
+   low: "success",
+ } as const;

+ const STATUS_COLORS = {
+   completed: "success",
+   "in progress": "warning",
+   pending: "gray",
+ } as const;

  const getPriorityColor = (priority: string) => {
-   switch (priority.toLowerCase()) {
-     case "high":
-       return "failure";
-     case "medium":
-       return "warning";
-     case "low":
-       return "success";
-     default:
-       return "info";
-   }
+   return PRIORITY_COLORS[priority.toLowerCase() as keyof typeof PRIORITY_COLORS] ?? "info";
  };

  const getStatusColor = (status: string) => {
-   switch (status.toLowerCase()) {
-     case "completed":
-       return "success";
-     case "in progress":
-       return "warning";
-     case "pending":
-       return "gray";
-     default:
-       return "info";
-   }
+   return STATUS_COLORS[status.toLowerCase() as keyof typeof STATUS_COLORS] ?? "info";
  };
nextjs-task-system/Utils/script.sql (1)

44-68: Optimize view and index configuration

The view and index configuration could be improved for better performance.

  CREATE VIEW IF NOT EXISTS tasks_with_users AS
  SELECT 
    t.id,
    t.title,
    t.description,
    t.due_date,
    t.priority,
    t.status,
    t.comments,
    t.created_at,
    t.updated_at,
    t.user_id,
-   u1.first_name || ' ' || u1.last_name as creator_name,
+   COALESCE(u1.first_name || ' ' || u1.last_name, 'Unknown') as creator_name,
    t.assigned_to,
-   u2.first_name || ' ' || u2.last_name as assignee_name
+   COALESCE(u2.first_name || ' ' || u2.last_name, 'Unknown') as assignee_name
  FROM tasks t
- JOIN users u1 ON t.user_id = u1.id
- JOIN users u2 ON t.assigned_to = u2.id;
+ LEFT JOIN users u1 ON t.user_id = u1.id
+ LEFT JOIN users u2 ON t.assigned_to = u2.id;

  -- Create indexes to optimize queries
  CREATE INDEX IF NOT EXISTS idx_tasks_user_id ON tasks(user_id);
  CREATE INDEX IF NOT EXISTS idx_tasks_assigned_to ON tasks(assigned_to);
  CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
  CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
+ CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date);
+ CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
nextjs-task-system/componentes/Sidebar.tsx (2)

12-15: Consider using a custom hook for window-based initialization

The current window-based initialization could cause hydration mismatches. Consider extracting this logic into a custom hook that handles window resize events properly.

+import { useEffect, useState } from "react";
+
+function useWindowSize() {
+  const [isWide, setIsWide] = useState(false);
+
+  useEffect(() => {
+    function handleResize() {
+      setIsWide(window.innerWidth >= 768);
+    }
+    
+    handleResize();
+    window.addEventListener('resize', handleResize);
+    return () => window.removeEventListener('resize', handleResize);
+  }, []);
+
+  return isWide;
+}

 export function SidebarComponent() {
-  const isClient = typeof window !== "undefined";
-  const [isOpen, setIsOpen] = useState(
-    isClient ? window.innerWidth >= 768 : false,
-  );
+  const isWide = useWindowSize();
+  const [isOpen, setIsOpen] = useState(isWide);

53-55: Consider making logo and brand name configurable

The hardcoded "Flowbite" text and logo path should be made configurable through props.

+interface SidebarComponentProps {
+  logoSrc?: string;
+  brandName?: string;
+}
+
-export function SidebarComponent() {
+export function SidebarComponent({
+  logoSrc = "/favicon.ico",
+  brandName = "Flowbite"
+}: SidebarComponentProps) {
   // ...
-  <Sidebar.Logo href="/" img="/favicon.ico" imgAlt="Flowbite logo">
-    Flowbite
+  <Sidebar.Logo href="/" img={logoSrc} imgAlt={`${brandName} logo`}>
+    {brandName}
   </Sidebar.Logo>
nextjs-task-system/components/TaskComponents/TaskCard.tsx (2)

6-8: Consider moving interfaces to a shared types file

The TaskWithUserName interface extends Task and should be moved to the types directory for better reusability.

Move this interface to @/tipos/tasks.ts:

// @/tipos/tasks.ts
export interface TaskWithUserName extends Task {
  user_name?: string;
}

46-57: Improve date formatting and status visualization

The task details could benefit from better formatting and visual representation.

+import { format } from 'date-fns';
+
 <p>
   <strong>Assigned to:</strong> {task.user_name || "Unassigned"}
 </p>
 <p>
-  <strong>Due Date:</strong> {task.due_date}
+  <strong>Due Date:</strong> {format(new Date(task.due_date), 'PPP')}
 </p>
 <p>
   <strong>Priority:</strong> {task.priority}
 </p>
 <p>
-  <strong>Status:</strong> {task.status}
+  <strong>Status:</strong> 
+  <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
+    ${task.status === 'completed' ? 'bg-green-100 text-green-800' : 
+      task.status === 'in_progress' ? 'bg-yellow-100 text-yellow-800' : 
+      'bg-gray-100 text-gray-800'}`}>
+    {task.status}
+  </span>
 </p>
nextjs-task-system/app/login/page.tsx (1)

82-87: Add loading state feedback

The submit button should show a loading state to prevent multiple submissions.

 <button
   type="submit"
+  disabled={loading}
   className="focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-500 dark:focus:ring-primary-800 w-full rounded-lg bg-blue-500 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-blue-900 focus:outline-none focus:ring-4"
 >
-  Sign in
+  {loading ? (
+    <span className="flex items-center justify-center">
+      <svg className="animate-spin h-5 w-5 mr-3" viewBox="0 0 24 24">
+        <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
+        <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
+      </svg>
+      Signing in...
+    </span>
+  ) : (
+    'Sign in'
+  )}
 </button>
nextjs-task-system/app/admin/page.tsx (2)

1-36: LGTM! Consider exporting the TaskSummary type.

The type definition and state management setup are well-structured. Consider exporting the TaskSummary type if it might be useful in other components.

-type TaskSummary = {
+export type TaskSummary = {

93-104: Enhance accessibility for status cards.

Consider adding ARIA labels and roles to improve screen reader support for the status cards.

             <Card className="bg-yellow-50">
-              <div className="text-center">
+              <div className="text-center" role="status" aria-label="Pending tasks count">
                <h3 className="text-lg font-semibold text-yellow-700">
                  Pending
                </h3>

Also applies to: 156-173

nextjs-task-system/app/admin/tasks/page.tsx (2)

24-54: Optimize filter performance with debouncing.

The current implementation triggers filtering on every state change. Consider debouncing the filter operation to improve performance with large datasets.

+import { useCallback, useEffect } from 'react';
+import debounce from 'lodash/debounce';

 export default function TasksPage() {
+  const debouncedFilter = useCallback(
+    debounce(() => {
+      let filtered = tasks;
+      if (priorityFilter) {
+        filtered = filtered.filter((task) => task.priority === priorityFilter);
+      }
+      if (statusFilter) {
+        filtered = filtered.filter((task) => task.status === statusFilter);
+      }
+      setFilteredTasks(filtered);
+    }, 300),
+    [tasks, priorityFilter, statusFilter]
+  );

   useEffect(() => {
-    filterTasks();
+    debouncedFilter();
+    return () => debouncedFilter.cancel();
   }, [priorityFilter, statusFilter, tasks]);

65-84: Implement optimistic updates for better UX.

Consider implementing optimistic updates to improve the perceived performance of the delete operation.

   const handleDeleteConfirm = async () => {
     if (!taskToDelete) return;
+    const previousTasks = tasks;
+    // Optimistically update UI
+    setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
+    setShowDeleteModal(false);
+    setTaskToDelete(null);

     try {
       const response = await fetch(`/api/tasks/${taskToDelete.id}`, {
         method: "DELETE",
       });

       if (!response.ok) {
         throw new Error("Failed to delete task");
       }
-      // Actualizar la lista de tareas
-      setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
-      setShowDeleteModal(false);
-      setTaskToDelete(null);
     } catch (error) {
       console.error("Error deleting task:", error);
+      // Revert on error
+      setTasks(previousTasks);
+      alert("Failed to delete task. Please try again.");
     }
   };
nextjs-task-system/app/user/tasks/page.tsx (4)

7-13: Consider adding audit fields to the User interface

For better tracking and compliance, consider adding createdAt and updatedAt fields to the User interface.

interface User {
  id: number;
  email: string;
  firstName: string;
  lastName: string;
  role: string;
+ createdAt: string;
+ updatedAt: string;
}

113-135: Refactor color mapping functions for better maintainability

Consider moving color mappings to constants and using object literals for better maintainability.

+ const STATUS_COLORS = {
+   completed: "bg-green-100 text-green-800",
+   "in progress": "bg-blue-100 text-blue-800",
+   pending: "bg-yellow-100 text-yellow-800",
+ } as const;

+ const PRIORITY_COLORS = {
+   high: "bg-red-100 text-red-800",
+   medium: "bg-orange-100 text-orange-800",
+   low: "bg-green-100 text-green-800",
+ } as const;

const getStatusColor = (status: string) => {
-  switch (status.toLowerCase()) {
-    case "completed":
-      return "bg-green-100 text-green-800";
-    case "in progress":
-      return "bg-blue-100 text-blue-800";
-    default:
-      return "bg-yellow-100 text-yellow-800";
-  }
+  return STATUS_COLORS[status.toLowerCase() as keyof typeof STATUS_COLORS] ?? STATUS_COLORS.pending;
};

const getPriorityColor = (priority: string) => {
-  switch (priority.toLowerCase()) {
-    case "high":
-      return "bg-red-100 text-red-800";
-    case "medium":
-      return "bg-orange-100 text-orange-800";
-    case "low":
-      return "bg-green-100 text-green-800";
-    default:
-      return "bg-gray-100 text-gray-800";
-  }
+  return PRIORITY_COLORS[priority.toLowerCase() as keyof typeof PRIORITY_COLORS] ?? "bg-gray-100 text-gray-800";
};

137-139: Enhance loading and empty states

Consider adding more informative loading and empty states with proper accessibility attributes.

- if (!currentUser) return <div>Loading...</div>;
- if (tasks.length === 0) return <div>No tasks found.</div>;
+ if (!currentUser) return (
+   <div role="status" aria-live="polite" className="text-center p-4">
+     <div className="animate-spin h-8 w-8 border-4 border-blue-500 border-t-transparent rounded-full mx-auto mb-2"></div>
+     <p>Loading user data...</p>
+   </div>
+ );
+ if (tasks.length === 0) return (
+   <div role="status" aria-live="polite" className="text-center p-4">
+     <p>No tasks found. Tasks assigned to you will appear here.</p>
+   </div>
+ );

144-170: Extract TaskCard component for better maintainability

Consider extracting the task card into a separate component to improve code organization.

+ interface TaskCardProps {
+   task: Task;
+   onClick: (task: Task) => void;
+ }
+
+ const TaskCard: React.FC<TaskCardProps> = ({ task, onClick }) => (
+   <div
+     className="cursor-pointer rounded-lg border bg-white p-4 shadow-sm transition-shadow hover:shadow-md"
+     onClick={() => onClick(task)}
+   >
+     <div className="mb-2 flex items-start justify-between">
+       <h2 className="text-xl font-semibold">{task.title}</h2>
+       <span
+         className={`rounded-full px-2 py-1 text-xs font-medium ${getPriorityColor(task.priority)}`}
+       >
+         {task.priority}
+       </span>
+     </div>
+     <p className="mb-3 text-gray-600">{task.description}</p>
+     <div className="flex flex-col space-y-2">
+       <span
+         className={`w-fit rounded-full px-2 py-1 text-xs font-medium ${getStatusColor(task.status)}`}
+       >
+         {task.status}
+       </span>
+       <p className="text-sm text-gray-500">
+         Due: {format(new Date(task.dueDate), "MMM dd, yyyy")}
+       </p>
+     </div>
+   </div>
+ );

{tasks.map((task) => (
-  <div key={task.id} className="cursor-pointer rounded-lg border bg-white p-4 shadow-sm transition-shadow hover:shadow-md" onClick={() => openModal(task)}>
-    {/* ... task card content ... */}
-  </div>
+  <TaskCard key={task.id} task={task} onClick={openModal} />
))}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 111e4dd and 6c03c48.

⛔ Files ignored due to path filters (2)
  • nextjs-task-system/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (39)
  • README.md (0 hunks)
  • nextjs-task-system/Utils/auth.ts (1 hunks)
  • nextjs-task-system/Utils/db.ts (1 hunks)
  • nextjs-task-system/Utils/script.sql (1 hunks)
  • nextjs-task-system/Utils/server/auth.ts (1 hunks)
  • nextjs-task-system/app/admin/create/page.tsx (1 hunks)
  • nextjs-task-system/app/admin/layout.tsx (1 hunks)
  • nextjs-task-system/app/admin/page.tsx (1 hunks)
  • nextjs-task-system/app/admin/tasks/[id]/page.tsx (1 hunks)
  • nextjs-task-system/app/admin/tasks/page.tsx (1 hunks)
  • nextjs-task-system/app/api/auth/currentUser.ts (1 hunks)
  • nextjs-task-system/app/api/auth/login/route.ts (1 hunks)
  • nextjs-task-system/app/api/auth/logout/route.ts (1 hunks)
  • nextjs-task-system/app/api/auth/me/route.ts (1 hunks)
  • nextjs-task-system/app/api/auth/register/route.ts (1 hunks)
  • nextjs-task-system/app/api/tasks/[id]/route.ts (1 hunks)
  • nextjs-task-system/app/api/tasks/route.ts (1 hunks)
  • nextjs-task-system/app/api/tasks/user/[id]/route.ts (1 hunks)
  • nextjs-task-system/app/api/users/route.ts (1 hunks)
  • nextjs-task-system/app/layout.tsx (1 hunks)
  • nextjs-task-system/app/login/page.tsx (1 hunks)
  • nextjs-task-system/app/page.tsx (1 hunks)
  • nextjs-task-system/app/register/page.tsx (1 hunks)
  • nextjs-task-system/app/user/layout.tsx (1 hunks)
  • nextjs-task-system/app/user/page.tsx (1 hunks)
  • nextjs-task-system/app/user/tasks/page.tsx (1 hunks)
  • nextjs-task-system/componentes/AdminComponents/TaskCard.tsx (1 hunks)
  • nextjs-task-system/componentes/Sidebar.tsx (1 hunks)
  • nextjs-task-system/componentes/UserComponents/TaskCard.tsx (1 hunks)
  • nextjs-task-system/components/Skeletons/DashboardSkeleton.tsx (1 hunks)
  • nextjs-task-system/components/Skeletons/TaskCardSkeleton.tsx (1 hunks)
  • nextjs-task-system/components/TaskComponents/TaskCard.tsx (1 hunks)
  • nextjs-task-system/hooks/useAuth.ts (1 hunks)
  • nextjs-task-system/middleware.ts (1 hunks)
  • nextjs-task-system/package.json (1 hunks)
  • nextjs-task-system/tipos/tasks.ts (1 hunks)
  • nextjs-task-system/tipos/usuarios.ts (1 hunks)
  • nextjs-task-system/tsconfig.json (2 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • README.md
✅ Files skipped from review due to trivial changes (3)
  • package.json
  • nextjs-task-system/Utils/db.ts
  • nextjs-task-system/tsconfig.json
🧰 Additional context used
🪛 Biome (1.9.4)
nextjs-task-system/app/admin/page.tsx

[error] 63-63: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 68-68: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

nextjs-task-system/app/user/page.tsx

[error] 146-146: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 150-150: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

🔇 Additional comments (10)
nextjs-task-system/app/page.tsx (2)

1-4: LGTM! Clean imports and proper use of client directive.

The imports are minimal and the "use client" directive is correctly used for client-side interactivity.


5-5: LGTM! Clear and descriptive component naming.

The rename from Home to HomePage improves clarity and follows React component naming conventions.

nextjs-task-system/app/api/auth/login/route.ts (1)

15-67: LGTM!

The authentication API endpoints are correctly implemented with proper error handling and security measures.

Also applies to: 69-108

nextjs-task-system/app/api/tasks/[id]/route.ts (2)

1-27: LGTM!

The GET and DELETE methods are implemented correctly with appropriate error handling and validations.

Also applies to: 128-145


81-121: ⚠️ Potential issue

Remove the Non-Standard UPDATE HTTP Method

The UPDATE function (lines 81-121) uses a non-standard HTTP method UPDATE, which is not recognized in the HTTP protocol. Consider removing this function and consolidating its logic into the standard PATCH method for partial updates.

Apply this diff to remove the UPDATE method:

-// PUT: Update task status
-export async function UPDATE(
-  request: Request,
-  { params }: { params: { id: string } },
-) {
-  // Function body...
-}

Likely invalid or redundant comment.

nextjs-task-system/app/api/tasks/route.ts (1)

8-35: LGTM!

The GET method successfully retrieves tasks with proper authentication and authorization checks.

nextjs-task-system/app/user/layout.tsx (1)

1-157: LGTM!

The UserLayout component is well-structured and provides a responsive and user-friendly interface.

nextjs-task-system/package.json (1)

20-20: Verify Next.js major version upgrade compatibility

The upgrade from Next.js 14 to 15 is a major version change that might introduce breaking changes.

✅ Verification successful

Let me gather more information about the app directory structure and any potential breaking changes.


Let me check the specific usage of navigation-related features since they had significant changes in Next.js 15.


Let me check the middleware implementation and the specific usage of navigation methods since these are critical areas in Next.js 15.


Next.js 15 upgrade appears safe to proceed

Based on the comprehensive analysis of the codebase:

  • The app uses the App Router pattern which is fully supported in Next.js 15
  • All route handlers use the stable NextResponse and NextRequest APIs
  • Navigation methods (router.push, router.refresh) are used in a way that's compatible with Next.js 15
  • The middleware implementation follows the current patterns
  • No usage of deprecated data fetching methods (getInitialProps, getServerSideProps, getStaticProps)

The codebase appears to be using modern Next.js patterns that are maintained in version 15, with no deprecated APIs or breaking changes that would affect the current implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for Next.js 15 breaking changes in the codebase
echo "Checking for potential breaking changes..."

# Check for usage of removed or changed APIs
rg -l "getInitialProps|getServerSideProps|getStaticProps|getStaticPaths" 

# Check for app directory structure which might need updates
fd -t d "app" -x echo "Found app directory: {}"

Length of output: 263


Script:

#!/bin/bash
# Check the contents of the app directory and its structure
fd . ./nextjs-task-system/app -t f

# Check for usage of specific Next.js features that might be affected
rg -l "useRouter|Link|Image|next/router|next/navigation" 

# Check package.json to see the previous Next.js version
cat nextjs-task-system/package.json

Length of output: 2632


Script:

#!/bin/bash
# Check for specific usage of navigation features that might need updates
rg -A 2 "useRouter|next/navigation" 

# Check for usage of middleware
fd middleware.ts

# Check for any custom server configuration
fd next.config.js -x cat {}

Length of output: 4148


Script:

#!/bin/bash
# Check middleware implementation
cat nextjs-task-system/middleware.ts

# Check for specific navigation method calls that might need updates
rg -A 2 "router\.(push|replace|back|forward|refresh|prefetch)"

# Check for any usage of deprecated or changed APIs in route handlers
rg -A 2 "NextResponse|NextRequest" ./nextjs-task-system/app/api

Length of output: 21154

nextjs-task-system/app/api/users/route.ts (1)

28-33: ⚠️ Potential issue

Use parameterized queries to prevent SQL injection

The current implementation is vulnerable to SQL injection attacks.

While SQLite's prepare statement provides some protection, ensure consistent usage:

-    const statement = db.prepare(
-      "INSERT INTO users (first_name, last_name, email, password, role) VALUES (?, ?, ?, ?, ?)",
-    );
+    const statement = db.prepare(`
+      INSERT INTO users (
+        first_name, 
+        last_name, 
+        email, 
+        password, 
+        role
+      ) VALUES (
+        @firstName,
+        @lastName,
+        @email,
+        @password,
+        @role
+      )
+    `);

Likely invalid or redundant comment.

nextjs-task-system/app/user/tasks/page.tsx (1)

29-35: LGTM! Clean state management setup

The state management is well-organized with clear separation of concerns for tasks, selected task, modal state, and user data.

Comment thread nextjs-task-system/app/layout.tsx Outdated
Comment thread nextjs-task-system/app/page.tsx
Comment thread nextjs-task-system/app/api/tasks/route.ts
Comment thread nextjs-task-system/app/admin/layout.tsx
Comment thread nextjs-task-system/app/admin/layout.tsx Outdated
Comment on lines +15 to +27
interface Task {
id: number;
title: string;
description: string;
status: string;
priority: string;
due_date: string;
comments: string;
creator_name: string;
assignee_name: string;
created_at: string;
updated_at: string;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Maintain consistent naming conventions in the Task interface

The interface mixes snake_case and camelCase naming conventions. Consider using consistent camelCase for better TypeScript conventions.

interface Task {
  id: number;
  title: string;
  description: string;
  status: string;
  priority: string;
- due_date: string;
+ dueDate: string;
  comments: string;
- creator_name: string;
- assignee_name: string;
- created_at: string;
- updated_at: string;
+ creatorName: string;
+ assigneeName: string;
+ createdAt: string;
+ updatedAt: string;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface Task {
id: number;
title: string;
description: string;
status: string;
priority: string;
due_date: string;
comments: string;
creator_name: string;
assignee_name: string;
created_at: string;
updated_at: string;
}
interface Task {
id: number;
title: string;
description: string;
status: string;
priority: string;
dueDate: string;
comments: string;
creatorName: string;
assigneeName: string;
createdAt: string;
updatedAt: string;
}

Comment on lines +47 to +60
const getCurrentUser = async () => {
try {
const response = await fetch("/api/auth/me");
if (response.ok) {
const userData = await response.json();
setCurrentUser(userData);
} else {
router.push("/login");
}
} catch (error) {
console.error("Error fetching current user:", error);
router.push("/login");
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling in getCurrentUser

The error handling could be enhanced to provide better user feedback and distinguish between different types of errors.

const getCurrentUser = async () => {
  try {
    const response = await fetch("/api/auth/me");
    if (response.ok) {
      const userData = await response.json();
      setCurrentUser(userData);
    } else {
+     const errorData = await response.json();
+     if (response.status === 401) {
        router.push("/login");
+     } else {
+       throw new Error(errorData.message || 'Failed to fetch user data');
+     }
    }
  } catch (error) {
-   console.error("Error fetching current user:", error);
+   const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
+   // Consider using a toast notification system
+   console.error(errorMessage);
    router.push("/login");
  }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getCurrentUser = async () => {
try {
const response = await fetch("/api/auth/me");
if (response.ok) {
const userData = await response.json();
setCurrentUser(userData);
} else {
router.push("/login");
}
} catch (error) {
console.error("Error fetching current user:", error);
router.push("/login");
}
};
const getCurrentUser = async () => {
try {
const response = await fetch("/api/auth/me");
if (response.ok) {
const userData = await response.json();
setCurrentUser(userData);
} else {
const errorData = await response.json();
if (response.status === 401) {
router.push("/login");
} else {
throw new Error(errorData.message || 'Failed to fetch user data');
}
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
// Consider using a toast notification system
console.error(errorMessage);
router.push("/login");
}
};

Comment on lines +62 to +72
const fetchTasks = async () => {
if (!currentUser) return;

try {
const response = await fetch(`/api/tasks/user/${currentUser.id}`);
const data = await response.json();
setTasks(data);
} catch (error) {
console.error("Error fetching tasks:", error);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance fetchTasks with proper loading states and error handling

Consider adding loading states and proper error handling for better user experience.

const fetchTasks = async () => {
  if (!currentUser) return;

+ setIsLoading(true);
  try {
    const response = await fetch(`/api/tasks/user/${currentUser.id}`);
+   if (!response.ok) {
+     const errorData = await response.json();
+     throw new Error(errorData.message || 'Failed to fetch tasks');
+   }
    const data = await response.json();
    setTasks(data);
  } catch (error) {
-   console.error("Error fetching tasks:", error);
+   const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
+   // Consider using a toast notification system
+   console.error(errorMessage);
  } finally {
+   setIsLoading(false);
  }
};

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +84 to +111
const updateStatus = async () => {
if (!selectedTask) return;

try {
const response = await fetch(`/api/tasks/${selectedTask.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status: newStatus }),
});

if (response.ok) {
const updatedTask = await response.json();
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === selectedTask.id
? { ...task, status: updatedTask.status }
: task,
),
);
setShowModal(false);
} else {
const errorData = await response.json();
alert(`Error: ${errorData.error}`);
}
} catch (error) {
console.error("Error updating task status:", error);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance updateStatus with optimistic updates and validation

Consider implementing optimistic updates and status validation for better UX and data integrity.

+ const VALID_STATUSES = ['pending', 'in progress', 'completed'] as const;
+ type TaskStatus = typeof VALID_STATUSES[number];

const updateStatus = async () => {
  if (!selectedTask) return;
+ if (!VALID_STATUSES.includes(newStatus as TaskStatus)) {
+   alert('Invalid status value');
+   return;
+ }

+ // Optimistic update
+ const previousTasks = [...tasks];
+ setTasks((prevTasks) =>
+   prevTasks.map((task) =>
+     task.id === selectedTask.id
+       ? { ...task, status: newStatus }
+       : task
+   )
+ );

  try {
    const response = await fetch(`/api/tasks/${selectedTask.id}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ status: newStatus }),
    });

    if (response.ok) {
      const updatedTask = await response.json();
      setTasks((prevTasks) =>
        prevTasks.map((task) =>
          task.id === selectedTask.id
            ? { ...task, status: updatedTask.status }
            : task
        )
      );
      setShowModal(false);
    } else {
      const errorData = await response.json();
+     // Revert optimistic update on error
+     setTasks(previousTasks);
      alert(`Error: ${errorData.error}`);
    }
  } catch (error) {
    console.error("Error updating task status:", error);
+   // Revert optimistic update on error
+   setTasks(previousTasks);
+   alert('Failed to update task status. Please try again.');
  }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const updateStatus = async () => {
if (!selectedTask) return;
try {
const response = await fetch(`/api/tasks/${selectedTask.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status: newStatus }),
});
if (response.ok) {
const updatedTask = await response.json();
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === selectedTask.id
? { ...task, status: updatedTask.status }
: task,
),
);
setShowModal(false);
} else {
const errorData = await response.json();
alert(`Error: ${errorData.error}`);
}
} catch (error) {
console.error("Error updating task status:", error);
}
};
const VALID_STATUSES = ['pending', 'in progress', 'completed'] as const;
type TaskStatus = typeof VALID_STATUSES[number];
const updateStatus = async () => {
if (!selectedTask) return;
if (!VALID_STATUSES.includes(newStatus as TaskStatus)) {
alert('Invalid status value');
return;
}
// Optimistic update
const previousTasks = [...tasks];
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === selectedTask.id
? { ...task, status: newStatus }
: task
)
);
try {
const response = await fetch(`/api/tasks/${selectedTask.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status: newStatus }),
});
if (response.ok) {
const updatedTask = await response.json();
setTasks((prevTasks) =>
prevTasks.map((task) =>
task.id === selectedTask.id
? { ...task, status: updatedTask.status }
: task
),
);
setShowModal(false);
} else {
const errorData = await response.json();
// Revert optimistic update on error
setTasks(previousTasks);
alert(`Error: ${errorData.error}`);
}
} catch (error) {
console.error("Error updating task status:", error);
// Revert optimistic update on error
setTasks(previousTasks);
alert('Failed to update task status. Please try again.');
}
};

Comment on lines +173 to +253
{selectedTask && (
<Modal show={showModal} onClose={() => setShowModal(false)} size="xl">
<Modal.Header>Task Details</Modal.Header>
<Modal.Body>
<div className="space-y-4">
<div>
<h3 className="mb-2 text-xl font-semibold">
{selectedTask.title}
</h3>
<p className="text-gray-600">{selectedTask.description}</p>
</div>

<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Status</p>
<Select
value={newStatus}
onChange={handleStatusChange}
className="mt-1"
>
<option value="pending">Pending</option>
<option value="in progress">In Progress</option>
<option value="completed">Completed</option>
</Select>
</div>

<div>
<p className="text-sm text-gray-500">Priority</p>
<p
className={`mt-1 w-fit rounded-full px-2 py-1 text-sm font-medium ${getPriorityColor(selectedTask.priority)}`}
>
{selectedTask.priority}
</p>
</div>
</div>

<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Assigned To</p>
<p className="mt-1">{selectedTask.assignee_name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Created By</p>
<p className="mt-1">{selectedTask.creator_name}</p>
</div>
</div>

<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Due Date</p>
<p className="mt-1">
{format(new Date(selectedTask.due_date), "MMMM dd, yyyy")}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Created At</p>
<p className="mt-1">
{format(
new Date(selectedTask.created_at),
"MMM dd, yyyy HH:mm",
)}
</p>
</div>
</div>

{selectedTask.comments && (
<div>
<p className="text-sm text-gray-500">Comments</p>
<p className="mt-1 text-gray-600">{selectedTask.comments}</p>
</div>
)}
</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={updateStatus}>Update Status</Button>
<Button color="gray" onClick={() => setShowModal(false)}>
Close
</Button>
</Modal.Footer>
</Modal>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessibility attributes to modal

The modal component should have proper accessibility attributes for better user experience.

{selectedTask && (
  <Modal 
    show={showModal} 
    onClose={() => setShowModal(false)} 
    size="xl"
+   aria-labelledby="task-modal-title"
+   aria-describedby="task-modal-description"
  >
    <Modal.Header>
-     Task Details
+     <h2 id="task-modal-title">Task Details</h2>
    </Modal.Header>
    <Modal.Body>
-     <div className="space-y-4">
+     <div id="task-modal-description" className="space-y-4">
      {/* ... modal content ... */}
     </div>
    </Modal.Body>
    <Modal.Footer>
-     <Button onClick={updateStatus}>Update Status</Button>
+     <Button onClick={updateStatus} aria-label="Update task status">
+       Update Status
+     </Button>
-     <Button color="gray" onClick={() => setShowModal(false)}>
+     <Button 
+       color="gray" 
+       onClick={() => setShowModal(false)}
+       aria-label="Close modal"
+     >
        Close
      </Button>
    </Modal.Footer>
  </Modal>
)}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{selectedTask && (
<Modal show={showModal} onClose={() => setShowModal(false)} size="xl">
<Modal.Header>Task Details</Modal.Header>
<Modal.Body>
<div className="space-y-4">
<div>
<h3 className="mb-2 text-xl font-semibold">
{selectedTask.title}
</h3>
<p className="text-gray-600">{selectedTask.description}</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Status</p>
<Select
value={newStatus}
onChange={handleStatusChange}
className="mt-1"
>
<option value="pending">Pending</option>
<option value="in progress">In Progress</option>
<option value="completed">Completed</option>
</Select>
</div>
<div>
<p className="text-sm text-gray-500">Priority</p>
<p
className={`mt-1 w-fit rounded-full px-2 py-1 text-sm font-medium ${getPriorityColor(selectedTask.priority)}`}
>
{selectedTask.priority}
</p>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Assigned To</p>
<p className="mt-1">{selectedTask.assignee_name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Created By</p>
<p className="mt-1">{selectedTask.creator_name}</p>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Due Date</p>
<p className="mt-1">
{format(new Date(selectedTask.due_date), "MMMM dd, yyyy")}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Created At</p>
<p className="mt-1">
{format(
new Date(selectedTask.created_at),
"MMM dd, yyyy HH:mm",
)}
</p>
</div>
</div>
{selectedTask.comments && (
<div>
<p className="text-sm text-gray-500">Comments</p>
<p className="mt-1 text-gray-600">{selectedTask.comments}</p>
</div>
)}
</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={updateStatus}>Update Status</Button>
<Button color="gray" onClick={() => setShowModal(false)}>
Close
</Button>
</Modal.Footer>
</Modal>
)}
{selectedTask && (
<Modal
show={showModal}
onClose={() => setShowModal(false)}
size="xl"
aria-labelledby="task-modal-title"
aria-describedby="task-modal-description"
>
<Modal.Header>
<h2 id="task-modal-title">Task Details</h2>
</Modal.Header>
<Modal.Body>
<div id="task-modal-description" className="space-y-4">
<div>
<h3 className="mb-2 text-xl font-semibold">
{selectedTask.title}
</h3>
<p className="text-gray-600">{selectedTask.description}</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Status</p>
<Select
value={newStatus}
onChange={handleStatusChange}
className="mt-1"
>
<option value="pending">Pending</option>
<option value="in progress">In Progress</option>
<option value="completed">Completed</option>
</Select>
</div>
<div>
<p className="text-sm text-gray-500">Priority</p>
<p
className={`mt-1 w-fit rounded-full px-2 py-1 text-sm font-medium ${getPriorityColor(selectedTask.priority)}`}
>
{selectedTask.priority}
</p>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Assigned To</p>
<p className="mt-1">{selectedTask.assignee_name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Created By</p>
<p className="mt-1">{selectedTask.creator_name}</p>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-gray-500">Due Date</p>
<p className="mt-1">
{format(new Date(selectedTask.due_date), "MMMM dd, yyyy")}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Created At</p>
<p className="mt-1">
{format(
new Date(selectedTask.created_at),
"MMM dd, yyyy HH:mm",
)}
</p>
</div>
</div>
{selectedTask.comments && (
<div>
<p className="text-sm text-gray-500">Comments</p>
<p className="mt-1 text-gray-600">{selectedTask.comments}</p>
</div>
)}
</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={updateStatus} aria-label="Update task status">
Update Status
</Button>
<Button
color="gray"
onClick={() => setShowModal(false)}
aria-label="Close modal"
>
Close
</Button>
</Modal.Footer>
</Modal>
)}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (1)
nextjs-task-system/app/admin/layout.tsx (1)

122-123: Enhance Sidebar active state handling for nested routes

Currently, the active prop in the Sidebar.Item components uses strict equality to determine the active state. This may not highlight the menu item correctly when on nested routes like /admin/tasks/123.

Apply this diff to improve active state handling:

- active={pathname === "/admin"}
+ active={pathname.startsWith("/admin")}

Similarly, update the other menu items:

- active={pathname === "/admin/tasks"}
+ active={pathname.startsWith("/admin/tasks")}
- active={pathname === "/admin/create"}
+ active={pathname.startsWith("/admin/create")}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6c03c48 and af1b081.

📒 Files selected for processing (3)
  • nextjs-task-system/app/admin/layout.tsx (1 hunks)
  • nextjs-task-system/app/api/tasks/route.ts (1 hunks)
  • nextjs-task-system/app/layout.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • nextjs-task-system/app/layout.tsx
🔇 Additional comments (1)
nextjs-task-system/app/admin/layout.tsx (1)

59-60: Add user feedback on logout failure

If the logout request fails, the error is logged to the console, but the user is not notified. Consider displaying an error message to inform the user of the issue.

Apply this diff to update the handleLogout function:

  if (response.ok) {
    router.push("/login");
  } else {
    console.error("Logout request failed with status:", response.status);
+   // Optionally display an error message to the user
+   alert("Logout failed. Please try again.");
  }

Comment on lines +171 to +175
console.error(error);
return NextResponse.json(
{ error: "Task not found for deletion" },
{ status: 500 },
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the error message and status code in the DELETE method's catch block

In the DELETE method's catch block, the error message "Task not found for deletion" with a 500 status code may confuse clients. Update the error message to better match the status code.

Apply this diff to correct the error handling:

  } catch (error) {
    console.error(error);
-   return NextResponse.json(
-     { error: "Task not found for deletion" },
-     { status: 500 },
-   );
+   return NextResponse.json(
+     { error: "Failed to delete task" },
+     { status: 500 },
+   );
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.error(error);
return NextResponse.json(
{ error: "Task not found for deletion" },
{ status: 500 },
);
console.error(error);
return NextResponse.json(
{ error: "Failed to delete task" },
{ status: 500 },
);

Comment on lines +147 to +177
export async function DELETE(req: Request) {
try {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");

if (!id) {
return NextResponse.json(
{ error: "Task ID is required" },
{ status: 400 },
);
}

const statement = db.prepare("DELETE FROM tasks WHERE id = ?");
const result = statement.run(id);

if (result.changes === 0) {
return NextResponse.json(
{ error: "Task not found for deletion" },
{ status: 404 },
);
}

return NextResponse.json({ success: true });
} catch (error) {
console.error(error);
return NextResponse.json(
{ error: "Task not found for deletion" },
{ status: 500 },
);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add authentication and authorization checks to the DELETE method

The DELETE method should verify that the user is authenticated and authorized to delete tasks.

Insert the following code at the beginning of the DELETE method:

+ const currentUser = await getCurrentUserServer();
+
+ if (!currentUser) {
+   return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
+ }
+
+ if (currentUser.role !== "admin") {
+   return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
+ }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +141 to +144
console.error(error);
return NextResponse.json({ error: "Task ID is required" }, { status: 500 });
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the error message and status code in the PUT method's catch block

In the PUT method's catch block, the error message "Task ID is required" is misleading, and the status code 500 suggests a server error. Update the error message to accurately reflect the issue.

Apply this diff to correct the error handling:

  } catch (error) {
    console.error(error);
-   return NextResponse.json({ error: "Task ID is required" }, { status: 500 });
+   return NextResponse.json({ error: "Failed to update task" }, { status: 500 });
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.error(error);
return NextResponse.json({ error: "Task ID is required" }, { status: 500 });
}
}
console.error(error);
return NextResponse.json({ error: "Failed to update task" }, { status: 500 });
}
}

Comment on lines +38 to +93
export async function POST(request: Request) {
try {
const body = await request.json();
const {
title,
description,
user_id,
assigned_to,
due_date,
priority,
status,
comments,
} = body;

if (!user_id) {
return NextResponse.json(
{ error: "User ID is required" },
{ status: 400 },
);
}

const result = db
.prepare(
`
INSERT INTO tasks (
title, description, user_id, assigned_to,
due_date, priority, status, comments
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`,
)
.run([
title,
description,
user_id,
assigned_to,
due_date,
priority,
status,
comments,
]);

// Fetch the newly created task with user details
const newTask = db
.prepare("SELECT * FROM tasks_with_users WHERE id = ?")
.get(result.lastInsertRowid);

return NextResponse.json(newTask);
} catch (error) {
console.error("Error creating task:", error);
return NextResponse.json(
{ error: "Failed to create task" },
{ status: 500 },
);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add authentication and authorization checks to the POST method

The POST method lacks authentication checks. This could allow unauthorized users to create tasks. Ensure that only authenticated admin users can perform this action.

Add the following authentication and authorization checks at the beginning of the POST method:

+ const currentUser = await getCurrentUserServer();
+
+ if (!currentUser) {
+   return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
+ }
+
+ if (currentUser.role !== "admin") {
+   return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
+ }

Also, modify the code to use the current user's ID:

- const { title, description, user_id, assigned_to, due_date, priority, status, comments } = body;
+ const { title, description, assigned_to, due_date, priority, status, comments } = body;

- if (!user_id) {
-   return NextResponse.json(
-     { error: "User ID is required" },
-     { status: 400 },
-   );
- }

  // ...

  const result = db
    .prepare(
      `
      INSERT INTO tasks (
-       title, description, user_id, assigned_to, 
+       title, description, user_id, assigned_to,
        due_date, priority, status, comments
      ) 
      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    `,
    )
    .run([
      title,
      description,
-     user_id,
+     currentUser.id,
      assigned_to,
      due_date,
      priority,
      status,
      comments,
    ]);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +96 to +144
export async function PUT(req: Request) {
try {
const {
id,
title,
description,
assigned_to,
due_date,
priority,
status,
comments,
} = await req.json();

if (!id) {
return NextResponse.json(
{ error: "Task ID is required" },
{ status: 400 },
);
}

const statement = db.prepare(`
UPDATE tasks
SET title = ?, description = ?, assigned_to = ?, due_date = ?, priority = ?, status = ?, comments = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
`);
const result = statement.run(
title,
description,
assigned_to,
due_date,
priority,
status,
comments,
id,
);

if (result.changes === 0) {
return NextResponse.json(
{ error: "Task not found for update" },
{ status: 404 },
);
}

return NextResponse.json({ success: true });
} catch (error) {
console.error(error);
return NextResponse.json({ error: "Task ID is required" }, { status: 500 });
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add authentication and authorization checks to the PUT method

The PUT method lacks authentication checks, potentially allowing unauthorized updates. Ensure that only authenticated admin users can update tasks.

Add the following checks at the beginning of the PUT method:

+ const currentUser = await getCurrentUserServer();
+
+ if (!currentUser) {
+   return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
+ }
+
+ if (currentUser.role !== "admin") {
+   return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
+ }

Committable suggestion skipped: line range outside the PR's diff.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (6)
nextjs-task-system/components/TaskComponents/TaskCard.tsx (2)

18-42: Consider moving color utilities to a shared file.

Since these color utility functions are exported and likely used across different components (as mentioned in the AI summary), consider moving them to a shared utilities file (e.g., utils/colors.ts) to promote reusability and maintain consistency.


65-154: Consider improving component structure and accessibility.

While the implementation works, consider these improvements:

  1. Break down the large JSX structure into smaller components
  2. Add ARIA labels for better accessibility
  3. Add keyboard navigation support for the modal
 <Card key={task.id} className="relative">
-  <div className="mb-2 flex items-start justify-between">
+  <div className="mb-2 flex items-start justify-between" role="heading" aria-level={2}>
     <h5 className="text-xl font-bold tracking-tight text-gray-900 dark:text-white">
       {task.title}
     </h5>
     <span
       className={`rounded-full px-2 py-1 text-xs font-medium ${getPriorityColor(
         task.priority,
       )}`}
+      role="status"
+      aria-label={`Priority: ${task.priority}`}
     >
       {task.priority}
     </span>
   </div>
nextjs-task-system/app/admin/layout.tsx (1)

16-22: Consider importing the User interface to avoid duplication

The User interface is defined in this file but also exists in nextjs-task-system/Utils/auth.ts. To promote reusability and maintain consistency across the codebase, consider importing the User interface from a shared location instead of redefining it.

nextjs-task-system/app/admin/tasks/[id]/page.tsx (1)

309-333: Generalize the Error Modal to handle different error scenarios

The error modal currently has a hardcoded title "Error updating task." To make it reusable for different error cases (e.g., updating, deleting), consider making the title dynamic based on the context.

Apply this diff to make the modal title dynamic:

 <Modal
   show={showErrorModal}
   size="md"
   onClose={() => setShowErrorModal(false)}
   popup
 >
   <Modal.Header />
   <Modal.Body>
     <div className="text-center">
       <HiX className="mx-auto mb-4 h-14 w-14 text-red-500" />
       <h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
-        Error updating task
+        {errorMessageTitle}
       </h3>
       <p className="text-sm text-gray-500 dark:text-gray-400">
         {errorMessage}
       </p>
       <div className="mt-4 flex justify-center">
         <Button color="gray" onClick={() => setShowErrorModal(false)}>
           Close
         </Button>
       </div>
     </div>
   </Modal.Body>
 </Modal>

Ensure that the errorMessageTitle state is managed appropriately in your component:

 const [errorMessage, setErrorMessage] = useState("");
+const [errorMessageTitle, setErrorMessageTitle] = useState("");

Set the errorMessageTitle in your error handling code. For example, in the confirmUpdate function:

 } catch (error) {
   setShowUpdateModal(false);
   setErrorMessage(
     error instanceof Error ? error.message : "An error occurred"
   );
+  setErrorMessageTitle("Error updating task");
   setShowErrorModal(true);
 }

And in the handleDelete function:

 } catch (error) {
   console.error("Error deleting task:", error);
   setErrorMessage(
     error instanceof Error ? error.message : "An error occurred"
   );
+  setErrorMessageTitle("Error deleting task");
   setShowErrorModal(true);
 }
nextjs-task-system/app/user/tasks/page.tsx (2)

127-136: Refactor getStatusColor to use an object mapping

Using an object mapping improves readability and maintainability.

Apply this diff:

- const getStatusColor = (status: string) => {
-   switch (status.toLowerCase()) {
-     case "completed":
-       return "bg-green-100 text-green-800";
-     case "in progress":
-       return "bg-blue-100 text-blue-800";
-     default:
-       return "bg-yellow-100 text-yellow-800";
-   }
- };

+ const statusColors: { [key: string]: string } = {
+   'completed': 'bg-green-100 text-green-800',
+   'in progress': 'bg-blue-100 text-blue-800',
+   'pending': 'bg-yellow-100 text-yellow-800',
+ };

+ const getStatusColor = (status: string) =>
+   statusColors[status.toLowerCase()] || 'bg-gray-100 text-gray-800';

138-149: Refactor getPriorityColor to use an object mapping

Using an object mapping improves readability and maintainability.

Apply this diff:

- const getPriorityColor = (priority: string) => {
-   switch (priority.toLowerCase()) {
-     case "high":
-       return "bg-red-100 text-red-800";
-     case "medium":
-       return "bg-orange-100 text-orange-800";
-     case "low":
-       return "bg-green-100 text-green-800";
-     default:
-       return "bg-gray-100 text-gray-800";
-   }
- };

+ const priorityColors: { [key: string]: string } = {
+   'high': 'bg-red-100 text-red-800',
+   'medium': 'bg-orange-100 text-orange-800',
+   'low': 'bg-green-100 text-green-800',
+ };

+ const getPriorityColor = (priority: string) =>
+   priorityColors[priority.toLowerCase()] || 'bg-gray-100 text-gray-800';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between af1b081 and bcc1828.

📒 Files selected for processing (5)
  • nextjs-task-system/app/admin/layout.tsx (1 hunks)
  • nextjs-task-system/app/admin/tasks/[id]/page.tsx (1 hunks)
  • nextjs-task-system/app/api/tasks/[id]/route.ts (1 hunks)
  • nextjs-task-system/app/user/tasks/page.tsx (1 hunks)
  • nextjs-task-system/components/TaskComponents/TaskCard.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • nextjs-task-system/app/api/tasks/[id]/route.ts
🔇 Additional comments (10)
nextjs-task-system/components/TaskComponents/TaskCard.tsx (4)

1-16: LGTM! Well-structured interfaces and imports.

The interfaces are well-defined with proper TypeScript types, and imports are appropriately organized.


44-46: LGTM! Clean component setup.

The component is well-typed and uses appropriate state management.


47-63: 🛠️ Refactor suggestion

Enhance error handling and add loading state.

The current implementation could be improved by:

  1. Adding proper response parsing
  2. Including a loading state during deletion
  3. Providing more specific error messages
 const TaskCard: React.FC<TaskCardProps> = ({ task, onEdit, onDelete }) => {
   const [showDeleteModal, setShowDeleteModal] = useState(false);
+  const [isDeleting, setIsDeleting] = useState(false);
+  const [error, setError] = useState<string | null>(null);

   const handleDelete = async () => {
+    setIsDeleting(true);
+    setError(null);
     try {
       const response = await fetch(`/api/tasks/${task.id}`, {
         method: "DELETE",
       });

+      const data = await response.json();
       if (!response.ok) {
-        throw new Error("Failed to delete task");
+        throw new Error(data.message || "Failed to delete task");
       }

       setShowDeleteModal(false);
       onDelete(task);
     } catch (error) {
       console.error("Error deleting task:", error);
-      alert("Error deleting task");
+      setError(error instanceof Error ? error.message : "Failed to delete task");
+    } finally {
+      setIsDeleting(false);
     }
   };

49-49: Verify API endpoint structure.

Let's ensure the API endpoint structure is consistent across the application.

✅ Verification successful

API endpoint structure is consistent with Next.js API routes

The API endpoint structure /api/tasks/${task.id} is correctly implemented and matches the Next.js API route handlers found in the codebase. The following evidence confirms this:

  • The endpoint /api/tasks/${task.id} corresponds to the route handler at nextjs-task-system/app/api/tasks/[id]/route.ts
  • The route handler implements all necessary HTTP methods (GET, PUT, DELETE)
  • Other task-related endpoints follow the same pattern:
    • /api/tasks/user/[id] for user-specific tasks
    • /api/tasks for general task operations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent API endpoint structure

# Search for API endpoint patterns
echo "Checking API endpoint patterns..."
rg -g '*.{ts,tsx,js,jsx}' '/api/tasks/[^"'\'']*' --no-filename

# Search for route handlers
echo "Checking route handlers..."
fd -e ts -e js 'route.ts' app/api/tasks

Length of output: 874


Script:

#!/bin/bash
# Let's check for API route handlers in the Next.js app directory structure
echo "Checking for API route handlers..."
fd -e ts -e js 'route' app

# Let's also check the entire codebase for any API-related files
echo "Checking for API-related files..."
fd -e ts -e js -e tsx 'api'

# Let's examine the content of any route handlers we find
echo "Examining route handler implementations..."
rg -g '*.{ts,tsx,js,jsx}' -A 5 'export async function' app

Length of output: 501


Script:

#!/bin/bash
# Let's first check the directory structure to understand the project layout
echo "Checking directory structure..."
ls -R

# Then let's look for any Next.js API routes or handlers across the project
echo "Searching for API route handlers..."
rg -g '*.{ts,tsx,js,jsx}' -A 5 'export.*function.*handler|export.*async function' 

# Let's also check for any route configuration files
echo "Checking for route configuration..."
fd -e ts -e js -e tsx route

Length of output: 11422

nextjs-task-system/app/admin/tasks/[id]/page.tsx (1)

2-2: ⚠️ Potential issue

Correct the usage of params and remove unnecessary import of use

  • The use function is not a standard export from React and is unnecessary here.
  • The params in PageProps should not be a Promise. In Next.js, params are passed as a plain object to the page component.

Apply this diff to fix the issue:

-import { useState, useEffect, use } from "react";
+import { useState, useEffect } from "react";

 interface PageProps {
-  params: Promise<{
+  params: {
     id: string;
-  }>;
+  };
 }

 export default function EditTask({ params }: PageProps) {
-  const resolvedParams = use(params);
-  const taskId = resolvedParams.id;
+  const taskId = params.id;

Also applies to: 21-25, 27-29

nextjs-task-system/app/user/tasks/page.tsx (5)

15-27: Maintain consistent naming conventions in the Task interface

The interface mixes snake_case and camelCase naming conventions. Consider using consistent camelCase for better TypeScript conventions.

Apply this diff to standardize the property names:

interface Task {
  id: number;
  title: string;
  description: string;
  status: string;
  comments: string | null;
  priority: string;
- due_date: string;
+ dueDate: string;
- creator_name: string;
+ creatorName: string;
- assignee_name: string;
+ assigneeName: string;
- created_at: string;
+ createdAt: string;
- updated_at: string;
+ updatedAt: string;
}

48-61: Improve error handling in getCurrentUser

The error handling could be enhanced to provide better user feedback and distinguish between different types of errors.

Consider applying this diff:

const getCurrentUser = async () => {
  try {
    const response = await fetch("/api/auth/me");
    if (response.ok) {
      const userData = await response.json();
      setCurrentUser(userData);
    } else {
+     const errorData = await response.json();
+     if (response.status === 401) {
        router.push("/login");
+     } else {
+       throw new Error(errorData.message || 'Failed to fetch user data');
+     }
    }
  } catch (error) {
-   console.error("Error fetching current user:", error);
+   const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
+   // Consider using a toast notification system to notify the user
+   console.error(errorMessage);
    router.push("/login");
  }
};

63-73: Enhance fetchTasks with loading states and error handling

Consider adding loading states and proper error handling for a better user experience.

Apply this diff:

+ const [isLoading, setIsLoading] = useState<boolean>(false);

const fetchTasks = async () => {
  if (!currentUser) return;

+ setIsLoading(true);
  try {
    const response = await fetch(`/api/tasks/user/${currentUser.id}`);
+   if (!response.ok) {
+     const errorData = await response.json();
+     throw new Error(errorData.message || 'Failed to fetch tasks');
+   }
    const data = await response.json();
    setTasks(data);
  } catch (error) {
-   console.error("Error fetching tasks:", error);
+   const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
+   // Consider using a toast notification system to notify the user
+   console.error(errorMessage);
  } finally {
+   setIsLoading(false);
  }
};

90-125: Enhance updateStatus with optimistic updates and validation

Consider implementing optimistic updates and status validation for better UX and data integrity.

Apply this diff:

+ const VALID_STATUSES = ['pending', 'in progress', 'completed'] as const;
+ type TaskStatus = typeof VALID_STATUSES[number];

const updateStatus = async () => {
  if (!selectedTask) return;
+ if (!VALID_STATUSES.includes(newStatus as TaskStatus)) {
+   alert('Invalid status value');
+   return;
+ }

+ // Optimistic update
+ const previousTasks = [...tasks];
+ setTasks((prevTasks) =>
+   prevTasks.map((task) =>
+     task.id === selectedTask.id
+       ? { ...task, status: newStatus, comments: newComment }
+       : task
+   )
+ );

  try {
    const response = await fetch(`/api/tasks/${selectedTask.id}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        status: newStatus,
        comments: newComment,
      }),
    });

    if (response.ok) {
      const updatedTask = await response.json();
      setTasks((prevTasks) =>
        prevTasks.map((task) =>
          task.id === selectedTask.id
            ? { ...task, status: updatedTask.status, comments: updatedTask.comments }
            : task
        )
      );
      setShowModal(false);
      setNewComment("");
    } else {
      const errorData = await response.json();
+     // Revert optimistic update on error
+     setTasks(previousTasks);
      alert(`Error: ${errorData.error}`);
    }
  } catch (error) {
    console.error("Error updating task:", error);
+   // Revert optimistic update on error
+   setTasks(previousTasks);
+   alert('Failed to update task. Please try again.');
  }
};

205-261: Add accessibility attributes to the modal

The modal component should have proper accessibility attributes for better user experience.

Consider applying this diff:

{selectedTask && (
  <Modal 
    show={showModal} 
    onClose={() => setShowModal(false)} 
    size="xl"
+   aria-labelledby="task-modal-title"
+   aria-describedby="task-modal-description"
  >
    <Modal.Header>
-     Edit Task
+     <h2 id="task-modal-title">Edit Task</h2>
    </Modal.Header>
    <Modal.Body>
-     <div className="space-y-4">
+     <div id="task-modal-description" className="space-y-4">
        {/* ... modal content ... */}
      </div>
    </Modal.Body>
    <Modal.Footer>
-     <Button onClick={updateStatus}>Update Task</Button>
+     <Button onClick={updateStatus} aria-label="Update task">
+       Update Task
+     </Button>
-     <Button color="gray" onClick={() => setShowModal(false)}>
+     <Button 
+       color="gray" 
+       onClick={() => setShowModal(false)}
+       aria-label="Cancel"
+     >
        Cancel
      </Button>
    </Modal.Footer>
  </Modal>
)}

Comment on lines +129 to +131
console.error("Error deleting task:", error);
alert("Error deleting task");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using alert for error messages

Using alert to display error messages can disrupt the user experience and is generally discouraged in modern web applications. Consider using the existing error modal to display the error message instead.

Apply this diff to replace the alert with the error modal:

 try {
   // ...
 } catch (error) {
   console.error("Error deleting task:", error);
-  alert("Error deleting task");
+  setErrorMessage(
+    error instanceof Error ? error.message : "An error occurred"
+  );
+  setErrorMessageTitle("Error deleting task");
+  setShowErrorModal(true);
 }

Ensure that you have the errorMessageTitle state defined:

 const [errorMessage, setErrorMessage] = useState("");
+const [errorMessageTitle, setErrorMessageTitle] = useState("");

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +201 to +203
{tasks.length === 0 && (
<div className="mt-8 text-center text-gray-500">No tasks found.</div>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant 'No tasks found' message

The check for tasks.length === 0 at line 152 already handles the case where no tasks are found. The additional check here is redundant and will never be reached.

Apply this diff to remove the redundant code:

- {tasks.length === 0 && (
-   <div className="mt-8 text-center text-gray-500">No tasks found.</div>
- )}

Committable suggestion skipped: line range outside the PR's diff.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (2)
nextjs-task-system/app/admin/tasks/page.tsx (2)

42-54: Optimize filter performance with useMemo.

The filterTasks function recreates the filtered array on every render. Using useMemo would optimize performance.

Replace the filterTasks function with a memoized version:

- const filterTasks = () => {
-   let filtered = tasks;
-   if (priorityFilter) {
-     filtered = filtered.filter((task) => task.priority === priorityFilter);
-   }
-   if (statusFilter) {
-     filtered = filtered.filter((task) => task.status === statusFilter);
-   }
-   setFilteredTasks(filtered);
- };

+ const filteredTasks = useMemo(() => {
+   let filtered = tasks;
+   if (priorityFilter) {
+     filtered = filtered.filter((task) => task.priority === priorityFilter);
+   }
+   if (statusFilter) {
+     filtered = filtered.filter((task) => task.status === statusFilter);
+   }
+   return filtered;
+ }, [tasks, priorityFilter, statusFilter]);

131-132: Translate comment to English and add aria-label.

The code contains a Spanish comment and lacks accessibility attributes.

- // Mostrar skeletons mientras carga
+ // Show skeletons while loading
  Array.from({ length: 6 }).map((_, index) => (
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bcc1828 and 3caccd1.

📒 Files selected for processing (1)
  • nextjs-task-system/app/admin/tasks/page.tsx (1 hunks)
🔇 Additional comments (1)
nextjs-task-system/app/admin/tasks/page.tsx (1)

1-9: ⚠️ Potential issue

Verify the component import path and add authentication check.

The import path for TaskCard (@/components/TaskComponents/TaskCard) differs from the path mentioned in the AI summary (nextjs-task-system/componentes/AdminComponents/TaskCard.tsx).

Additionally, this admin page requires authentication but lacks the necessary auth check.

Add the following authentication check:

import { useRouter } from "next/navigation";
+import { getCurrentUser } from "@/Utils/auth";
+import { useEffect } from "react";

export default function TasksPage() {
+ const router = useRouter();
+
+ useEffect(() => {
+   const checkAuth = async () => {
+     const user = await getCurrentUser();
+     if (!user || user.role !== 'admin') {
+       router.push('/login');
+     }
+   };
+   checkAuth();
+ }, [router]);

Comment on lines +10 to +19
export default function TasksPage() {
const [tasks, setTasks] = useState<Task[]>([]);
const [filteredTasks, setFilteredTasks] = useState<Task[]>([]);
const [priorityFilter, setPriorityFilter] = useState<string>("");
const [statusFilter, setStatusFilter] = useState<string>("");
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [taskToDelete, setTaskToDelete] = useState<Task | null>(null);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error state management for better user experience.

While loading state is handled, there's no error state management for API failures. Users won't see any feedback when errors occur.

Add error state management:

export default function TasksPage() {
  const [tasks, setTasks] = useState<Task[]>([]);
  const [filteredTasks, setFilteredTasks] = useState<Task[]>([]);
  const [priorityFilter, setPriorityFilter] = useState<string>("");
  const [statusFilter, setStatusFilter] = useState<string>("");
  const [showDeleteModal, setShowDeleteModal] = useState(false);
  const [taskToDelete, setTaskToDelete] = useState<Task | null>(null);
  const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState<string | null>(null);
  const router = useRouter();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +28 to +40
const fetchTasks = async () => {
try {
setIsLoading(true);
const response = await fetch("/api/tasks");
const data = await response.json();
setTasks(data);
setFilteredTasks(data);
} catch (error) {
console.error("Error:", error);
} finally {
setIsLoading(false);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling in fetchTasks function.

The current error handling only logs to console. Users should be notified of API failures.

Update the error handling:

  const fetchTasks = async () => {
    try {
      setIsLoading(true);
+     setError(null);
      const response = await fetch("/api/tasks");
+     if (!response.ok) {
+       throw new Error(`Failed to fetch tasks: ${response.statusText}`);
+     }
      const data = await response.json();
      setTasks(data);
      setFilteredTasks(data);
    } catch (error) {
      console.error("Error:", error);
+     setError(error instanceof Error ? error.message : 'Failed to fetch tasks');
    } finally {
      setIsLoading(false);
    }
  };

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +65 to +84
const handleDeleteConfirm = async () => {
if (!taskToDelete) return;

try {
const response = await fetch(`/api/tasks/${taskToDelete.id}`, {
method: "DELETE",
});

if (!response.ok) {
throw new Error("Failed to delete task");
}

// Actualizar la lista de tareas
setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
setShowDeleteModal(false);
setTaskToDelete(null);
} catch (error) {
console.error("Error deleting task:", error);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance delete operation with better error handling and optimistic updates.

The current implementation could be improved with proper error handling and optimistic updates for better UX.

Implement optimistic updates and better error handling:

  const handleDeleteConfirm = async () => {
    if (!taskToDelete) return;

+   // Optimistic update
+   const previousTasks = tasks;
+   setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
+   setShowDeleteModal(false);
+   setTaskToDelete(null);

    try {
      const response = await fetch(`/api/tasks/${taskToDelete.id}`, {
        method: "DELETE",
      });

      if (!response.ok) {
+       const errorData = await response.json();
+       throw new Error(errorData.message || 'Failed to delete task');
      }
-
-      // Actualizar la lista de tareas
-      setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
-      setShowDeleteModal(false);
-      setTaskToDelete(null);
    } catch (error) {
      console.error("Error deleting task:", error);
+     // Revert optimistic update
+     setTasks(previousTasks);
+     setError(error instanceof Error ? error.message : 'Failed to delete task');
    }
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleDeleteConfirm = async () => {
if (!taskToDelete) return;
try {
const response = await fetch(`/api/tasks/${taskToDelete.id}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error("Failed to delete task");
}
// Actualizar la lista de tareas
setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
setShowDeleteModal(false);
setTaskToDelete(null);
} catch (error) {
console.error("Error deleting task:", error);
}
};
const handleDeleteConfirm = async () => {
if (!taskToDelete) return;
// Optimistic update
const previousTasks = tasks;
setTasks(tasks.filter((task) => task.id !== taskToDelete.id));
setShowDeleteModal(false);
setTaskToDelete(null);
try {
const response = await fetch(`/api/tasks/${taskToDelete.id}`, {
method: "DELETE",
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Failed to delete task');
}
} catch (error) {
console.error("Error deleting task:", error);
// Revert optimistic update
setTasks(previousTasks);
setError(error instanceof Error ? error.message : 'Failed to delete task');
}
};

Comment on lines +86 to +178
return (
<div className="container mx-auto p-4">
<h1 className="mb-6 mt-12 text-3xl font-bold md:mt-0">Tasks List</h1>

{/* Filters and Create Button */}
<div className="mb-6 flex flex-col space-y-4 sm:flex-row sm:items-center sm:justify-between sm:space-x-4 sm:space-y-0">
<div className="flex flex-1 space-x-4">
<Select
id="priorityFilter"
name="priorityFilter"
value={priorityFilter}
onChange={(e) => setPriorityFilter(e.target.value)}
className="flex-1"
>
<option value="">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</Select>

<Select
id="statusFilter"
name="statusFilter"
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
className="flex-1"
>
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="in progress">In Progress</option>
<option value="completed">Completed</option>
</Select>
</div>

<Button
onClick={() => router.push("/admin/create")}
className="w-full sm:w-auto"
color="blue"
>
Create New Task
</Button>
</div>

<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{isLoading
? // Mostrar skeletons mientras carga
Array.from({ length: 6 }).map((_, index) => (
<TaskCardSkeleton key={index} />
))
: filteredTasks.map((task) => (
<div key={task.id}>
<TaskCard
task={task}
onEdit={() => handleTaskClick(task, "edit")}
onDelete={() => handleTaskClick(task, "delete")}
/>
</div>
))}
</div>

{/* Delete Confirmation Modal */}
<Modal
show={showDeleteModal}
onClose={() => setShowDeleteModal(false)}
popup
size="md"
>
<Modal.Header />
<Modal.Body>
<div className="text-center">
<HiOutlineExclamationCircle className="mx-auto mb-4 h-14 w-14 text-gray-400 dark:text-gray-200" />
<h3 className="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">
Are you sure you want to delete this task?
</h3>
<div className="flex justify-center gap-4">
<Button color="failure" onClick={handleDeleteConfirm}>
Yes, delete
</Button>
<Button color="gray" onClick={() => setShowDeleteModal(false)}>
No, cancel
</Button>
</div>
</div>
</Modal.Body>
</Modal>

{filteredTasks.length === 0 && (
<div className="mt-8 text-center text-gray-500">
No tasks found matching the selected filters.
</div>
)}
</div>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error state display and improve accessibility.

The UI needs to handle error states and improve accessibility for screen readers.

Add error state display and accessibility improvements:

  return (
    <div className="container mx-auto p-4">
      <h1 className="mb-6 mt-12 text-3xl font-bold md:mt-0">Tasks List</h1>

+     {error && (
+       <div role="alert" className="mb-4 rounded-lg bg-red-100 p-4 text-red-700">
+         {error}
+       </div>
+     )}

      {/* Filters and Create Button */}
      <div className="mb-6 flex flex-col space-y-4 sm:flex-row sm:items-center sm:justify-between sm:space-x-4 sm:space-y-0">
        <div className="flex flex-1 space-x-4">
          <Select
            id="priorityFilter"
            name="priorityFilter"
            value={priorityFilter}
            onChange={(e) => setPriorityFilter(e.target.value)}
            className="flex-1"
+           aria-label="Filter tasks by priority"
          >

          <Select
            id="statusFilter"
            name="statusFilter"
            value={statusFilter}
            onChange={(e) => setStatusFilter(e.target.value)}
            className="flex-1"
+           aria-label="Filter tasks by status"
          >

        <Button
          onClick={() => router.push("/admin/create")}
          className="w-full sm:w-auto"
          color="blue"
+         aria-label="Create new task"
        >

Committable suggestion skipped: line range outside the PR's diff.

@korutx korutx requested review from korutx and odtorres December 10, 2024 18:52

@korutx korutx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final Review - Breyeszx

Strengths 🌟

Technical Implementation

  • Excellent Backend Architecture

    • Very good use of NextJS Backend Route API
    • Well-structured API endpoints
    • Clean separation of concerns
  • Strong Security Implementation

    • Robust session management with UUID
    • Proper cookie security settings (httpOnly, secure, sameSite)
    • Good password handling with bcrypt
    • Database-backed session validation with expiration
  • Frontend Development

    • Very good implementation of Tailwind CSS
    • Clean and responsive UI using Flowbite components
    • Mobile-friendly layouts
    • Intuitive user interface

Completed Features

  • ✅ Comprehensive user role system (Admin/Regular)
  • ✅ Task management functionality
  • ✅ Comments and status tracking
  • ✅ Basic filtering capabilities
  • ✅ Responsive design
  • ✅ Database integration with SQLite

Areas for Improvement 🎯

Missing Features

  • Group tasks functionality not implemented
  • Sorting capabilities pending (due date, priority, creation date)

Technical Considerations

  • No Server-Side Rendering (SSR) implementation
    • Could improve initial page load performance
    • Better SEO potential with SSR
    • Consider implementing for future iterations

Overall Assessment 🏆

This is a solid implementation of a task management system that demonstrates strong understanding of:

  • Modern web development practices
  • Security considerations
  • UI/UX design principles
  • Database integration

The core functionality is well-implemented, and while some features are missing, the existing codebase provides a strong foundation for future enhancements.

Recommendations for Next Steps

  1. Implement missing group tasks functionality
  2. Add sorting capabilities
  3. Consider implementing SSR for performance optimization
  4. Add session cleanup routine for expired sessions
  5. Consider adding rate limiting for login attempts

Conclusion

Congratulations on delivering a well-structured and secure task management system! The attention to security details and clean implementation of the core features is particularly commendable. While there's room for some additional features, the current implementation provides a solid foundation for future development.

Keep up the excellent work! 🎉

);
}

const sessionId = crypto.randomUUID();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good: Using UUID for session ID provides strong randomness and uniqueness

const sessionId = crypto.randomUUID();
db.prepare(
"INSERT INTO sessions (id, user_id, created_at) VALUES (?, ?, datetime('now'))",
).run(sessionId, user.id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good: Storing session with timestamp in database

Comment on lines +55 to +60
response.cookies.set("sessionId", sessionId, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Excellent cookie security settings:

response.cookies.set("sessionId", sessionId, {
      httpOnly: true,                                    // Prevents JavaScript access
      secure: process.env.NODE_ENV === "production",     // Forces HTTPS in production
      sameSite: "lax",                                  // Protects against CSRF
      path: "/",                                        // Proper scope setting
    });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential improvement: Consider adding maxAge or expires option
to match your 24-hour session expiration logic in the GET route

return NextResponse.json({ error: "No session found" }, { status: 401 });
}

// Obtener usuario de la sesión

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good: Session validation includes timestamp check

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Key positive points to highlight:

  1. Strong security practices with cookie settings
  2. Use of UUID for session IDs
  3. Database-backed sessions with timestamps
  4. Proper session validation including expiration checks

Suggestions for improvement:

  1. Consider adding cookie expiration to match database session expiration
  2. Could add session cleanup routine for expired sessions
  3. Might want to add session invalidation on logout (if not already implemented elsewhere)
  4. Consider adding rate limiting for login attempts

Overall, this is a well-implemented authentication system following security best practices! 🎉

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