diff --git a/backend/boards/signals.py b/backend/boards/signals.py index c416de9..2c44daa 100644 --- a/backend/boards/signals.py +++ b/backend/boards/signals.py @@ -8,8 +8,10 @@ from users.models import CustomUser def create_default_board(sender, instance, created, **kwargs): """Signal handler to automatically create a default Board for a user upon creation.""" if created: - board = Board.objects.create(user=instance, name="My Default Board") - - ListBoard.objects.create(board=board, name="Todo", position=1) - ListBoard.objects.create(board=board, name="In Progress", position=2) - ListBoard.objects.create(board=board, name="Done", position=3) \ No newline at end of file + # Create unique board by user id + user_id = instance.id + board = Board.objects.create(user=instance, name=f"Board of #{user_id}") + ListBoard.objects.create(board=board, name="Backlog", position=1) + ListBoard.objects.create(board=board, name="Doing", position=2) + ListBoard.objects.create(board=board, name="Review", position=3) + ListBoard.objects.create(board=board, name="Done", position=4) \ No newline at end of file diff --git a/backend/boards/views.py b/backend/boards/views.py index 8b3fd47..d405c87 100644 --- a/backend/boards/views.py +++ b/backend/boards/views.py @@ -1,11 +1,13 @@ from rest_framework import viewsets from rest_framework.response import Response from rest_framework import status +from rest_framework.permissions import IsAuthenticated from boards.models import Board, ListBoard from boards.serializers import BoardSerializer, ListBoardSerializer class BoardViewSet(viewsets.ModelViewSet): + permission_classes = (IsAuthenticated,) queryset = Board.objects.all() serializer_class = BoardSerializer http_method_names = ['get'] @@ -16,6 +18,7 @@ class BoardViewSet(viewsets.ModelViewSet): class ListBoardViewSet(viewsets.ModelViewSet): + permission_classes = (IsAuthenticated,) serializer_class = ListBoardSerializer def get_queryset(self): diff --git a/backend/tasks/signals.py b/backend/tasks/signals.py index 063292e..4c9e6f2 100644 --- a/backend/tasks/signals.py +++ b/backend/tasks/signals.py @@ -27,15 +27,63 @@ def update_priority(sender, instance, **kwargs): instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT -@receiver(post_save, sender=Todo) -def assign_todo_to_listboard(sender, instance, created, **kwargs): - """Signal handler to automatically assign a Todo to the first ListBoard in the user's Board upon creation.""" +# @receiver(post_save, sender=Todo) +# def assign_todo_to_listboard(sender, instance, created, **kwargs): +# """Signal handler to automatically assign a Todo to the first ListBoard in the user's Board upon creation.""" +# if created: +# user_board = instance.user.board_set.first() + +# if user_board: +# first_list_board = user_board.listboard_set.order_by('position').first() + +# if first_list_board: +# instance.list_board = first_list_board +# instance.save() + + +@receiver(post_save, sender=ListBoard) +def create_placeholder_tasks(sender, instance, created, **kwargs): + """ + Signal handler to create placeholder tasks for each ListBoard. + """ if created: - user_board = instance.user.board_set.first() + list_board_position = instance.position - if user_board: - first_list_board = user_board.listboard_set.order_by('position').first() + if list_board_position == 1: + placeholder_tasks = [ + {"title": "Normal Task Example"}, + {"title": "Task with Extra Information Example", "description": "Description for Task 2"}, + ] + elif list_board_position == 2: + placeholder_tasks = [ + {"title": "Time Task Example #1", "description": "Description for Task 2", + "start_event": timezone.now(), "end_event": timezone.now() + timezone.timedelta(days=5)}, + ] + elif list_board_position == 3: + placeholder_tasks = [ + {"title": "Time Task Example #2", "description": "Description for Task 2", + "start_event": timezone.now(), "end_event": timezone.now() + timezone.timedelta(days=30)}, + ] + elif list_board_position == 4: + placeholder_tasks = [ + {"title": "Completed Task Example", "description": "Description for Task 2", + "start_event": timezone.now(), "completed": True}, + ] + else: + placeholder_tasks = [ + {"title": "Default Task Example"}, + ] - if first_list_board: - instance.list_board = first_list_board - instance.save() \ No newline at end of file + for task_data in placeholder_tasks: + Todo.objects.create( + list_board=instance, + user=instance.board.user, + title=task_data["title"], + notes=task_data.get("description", ""), + is_active=True, + start_event=task_data.get("start_event"), + end_event=task_data.get("end_event"), + completed=task_data.get("completed", False), + creation_date=timezone.now(), + last_update=timezone.now(), + ) \ No newline at end of file diff --git a/frontend/src/components/authentication/SignUpPage.jsx b/frontend/src/components/authentication/SignUpPage.jsx index 0492412..6fcd086 100644 --- a/frontend/src/components/authentication/SignUpPage.jsx +++ b/frontend/src/components/authentication/SignUpPage.jsx @@ -56,6 +56,8 @@ export function SignUp() { const googleLoginImplicit = useGoogleLogin({ flow: "auth-code", redirect_uri: "postmessage", + scope: + "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/calendar.acls.readonly https://www.googleapis.com/auth/calendar.events.readonly", onSuccess: async (response) => { try { const loginResponse = await googleLogin(response.code); diff --git a/frontend/src/components/calendar/calendar.jsx b/frontend/src/components/calendar/calendar.jsx index a85ea2d..79dae97 100644 --- a/frontend/src/components/calendar/calendar.jsx +++ b/frontend/src/components/calendar/calendar.jsx @@ -1,11 +1,10 @@ -import React from "react"; +import React, { useState } from "react"; import { formatDate } from "@fullcalendar/core"; import FullCalendar from "@fullcalendar/react"; import dayGridPlugin from "@fullcalendar/daygrid"; import timeGridPlugin from "@fullcalendar/timegrid"; import interactionPlugin from "@fullcalendar/interaction"; import { getEvents, createEventId } from "./TaskDataHandler"; -import { axiosInstance } from "src/api/AxiosConfig"; export class Calendar extends React.Component { state = { @@ -62,10 +61,13 @@ export class Calendar extends React.Component { type="checkbox" checked={this.state.weekendsVisible} onChange={this.handleWeekendsToggle} - className="mr-2" + className="mr-2 mb-4" /> Toggle weekends + {/* Show all task */} diff --git a/frontend/src/components/kanbanBoard/columnContainer.jsx b/frontend/src/components/kanbanBoard/columnContainer.jsx index 819a980..d6f4565 100644 --- a/frontend/src/components/kanbanBoard/columnContainer.jsx +++ b/frontend/src/components/kanbanBoard/columnContainer.jsx @@ -1,53 +1,16 @@ import { SortableContext, useSortable } from "@dnd-kit/sortable"; -import { BsFillTrashFill } from "react-icons/bs"; import { AiOutlinePlusCircle } from "react-icons/ai"; -import { CSS } from "@dnd-kit/utilities"; -import { useMemo, useState } from "react"; +import { useMemo } from "react"; import { TaskCard } from "./taskCard"; -export function ColumnContainer({ column, deleteColumn, updateColumn, createTask, tasks, deleteTask, updateTask }) { - const [editMode, setEditMode] = useState(false); - +export function ColumnContainer({ column, createTask, tasks, deleteTask, updateTask }) { + // Memoize task IDs to prevent unnecessary recalculations const tasksIds = useMemo(() => { return tasks.map((task) => task.id); }, [tasks]); - const { setNodeRef, attributes, listeners, transform, transition, isDragging } = useSortable({ - id: column.id, - data: { - type: "Column", - column, - }, - disabled: editMode, - }); - - const style = { - transition, - transform: CSS.Transform.toString(transform), - }; - - if (isDragging) { - return ( -
- ); - } - return (
{/* Column title */}
{ - setEditMode(true); - }} className=" ml-3 text-md - cursor-grab font-bold flex items-center justify-between "> -
- {!editMode && column.title} - {editMode && ( - updateColumn(column.id, e.target.value)} - autoFocus - onBlur={() => { - setEditMode(false); - }} - onKeyDown={(e) => { - if (e.key !== "Enter") return; - setEditMode(false); - }} - /> - )} -
- +
{column.title}
{/* Column task container */}
+ {/* Provide a SortableContext for the tasks within the column */} + {/* Render TaskCard for each task in the column */} {tasks.map((task) => ( - + useSortable({ ...props, disabled: false })} + /> ))}
+ {/* Column footer */}
-
); }; diff --git a/frontend/src/components/navigations/IconSideNav.jsx b/frontend/src/components/navigations/IconSideNav.jsx index 02cc1a0..9f9ad4b 100644 --- a/frontend/src/components/navigations/IconSideNav.jsx +++ b/frontend/src/components/navigations/IconSideNav.jsx @@ -9,9 +9,9 @@ const menuItems = [ { id: 0, path: "/", icon: }, { id: 1, path: "/tasks", icon: }, { id: 2, path: "/calendar", icon: }, - { id: 3, path: "/settings", icon: }, - { id: 4, path: "/priority", icon: }, + { id: 3, path: "/priority", icon: }, ]; +// { id: 3, path: "/settings", icon: }, export const SideNav = () => { const [selected, setSelected] = useState(0); @@ -32,12 +32,12 @@ export const SideNav = () => { ); }; -const NavItem = ({ icon, selected, id, setSelected, logo, path }) => { +const NavItem = ({ icon, selected, id, setSelected, path }) => { const navigate = useNavigate(); return ( { setSelected(id); navigate(path); diff --git a/frontend/src/components/navigations/Navbar.jsx b/frontend/src/components/navigations/Navbar.jsx index 46dd45e..22120b3 100644 --- a/frontend/src/components/navigations/Navbar.jsx +++ b/frontend/src/components/navigations/Navbar.jsx @@ -25,14 +25,14 @@ export function NavBar() {
-
- -
+ {/*
+ +
*/} {isAuthenticated ? (