From 8ba74d846dc7184ba17b9396a838b458d951db47 Mon Sep 17 00:00:00 2001 From: sosokker Date: Wed, 22 Nov 2023 17:32:01 +0700 Subject: [PATCH 1/3] Add absolute import part and move AxiosInstance --- frontend/jsconfig.json | 8 + frontend/src/api/AuthenticationApi.jsx | 28 +-- .../src/api/{configs => }/AxiosConfig.jsx | 9 +- frontend/src/api/TaskApi.jsx | 42 ++-- .../EisenhowerMatrix/Eisenhower.jsx | 18 +- frontend/src/components/calendar/calendar.jsx | 24 +-- .../src/components/dashboard/Areachart.jsx | 192 +++++++++--------- .../src/components/dashboard/Barchart.jsx | 2 +- .../src/components/dashboard/DonutChart.jsx | 2 +- frontend/src/components/dashboard/KpiCard.jsx | 15 +- .../components/dashboard/ProgressCircle.jsx | 11 +- .../components/kanbanBoard/kanbanBoard.jsx | 91 ++++----- frontend/vite.config.js | 11 +- 13 files changed, 225 insertions(+), 228 deletions(-) create mode 100644 frontend/jsconfig.json rename frontend/src/api/{configs => }/AxiosConfig.jsx (90%) diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..af4aef6 --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"] + } + } +} diff --git a/frontend/src/api/AuthenticationApi.jsx b/frontend/src/api/AuthenticationApi.jsx index d4b6486..1913922 100644 --- a/frontend/src/api/AuthenticationApi.jsx +++ b/frontend/src/api/AuthenticationApi.jsx @@ -1,15 +1,15 @@ import axios from "axios"; -import axiosInstance from "./configs/AxiosConfig"; +import axiosInstance from "./AxiosConfig"; // Function for user login -const apiUserLogin = data => { +const apiUserLogin = (data) => { return axiosInstance .post("token/obtain/", data) - .then(response => { + .then((response) => { console.log(response.statusText); return response; }) - .catch(error => { + .catch((error) => { console.log("apiUserLogin error: ", error); return error; }); @@ -23,7 +23,7 @@ const apiUserLogout = () => { }; // Function for Google login -const googleLogin = async token => { +const googleLogin = async (token) => { axios.defaults.withCredentials = true; let res = await axios.post("http://localhost:8000/api/auth/google/", { code: token, @@ -36,29 +36,23 @@ const googleLogin = async token => { const getGreeting = () => { return axiosInstance .get("hello") - .then(response => { + .then((response) => { return response; }) - .catch(error => { + .catch((error) => { return error; }); }; -const config = { - headers: { - "Content-Type": "application/json", - }, -}; - // Function to register -const createUser = async formData => { +const createUser = async (formData) => { try { axios.defaults.withCredentials = true; - const resposne = axios.post("http://localhost:8000/api/user/create/", formData); + const response = axios.post("http://localhost:8000/api/user/create/", formData); // const response = await axiosInstance.post('/user/create/', formData); return response.data; - } catch (error) { - throw error; + } catch (e) { + console.log(e); } }; diff --git a/frontend/src/api/configs/AxiosConfig.jsx b/frontend/src/api/AxiosConfig.jsx similarity index 90% rename from frontend/src/api/configs/AxiosConfig.jsx rename to frontend/src/api/AxiosConfig.jsx index b0410d1..336d18d 100644 --- a/frontend/src/api/configs/AxiosConfig.jsx +++ b/frontend/src/api/AxiosConfig.jsx @@ -1,5 +1,4 @@ import axios from "axios"; -import { redirect } from "react-router-dom"; const axiosInstance = axios.create({ baseURL: "http://127.0.0.1:8000/api/", @@ -13,8 +12,8 @@ const axiosInstance = axios.create({ // handling token refresh on 401 Unauthorized errors axiosInstance.interceptors.response.use( - response => response, - error => { + (response) => response, + (error) => { const originalRequest = error.config; const refresh_token = localStorage.getItem("refresh_token"); @@ -26,7 +25,7 @@ axiosInstance.interceptors.response.use( ) { return axiosInstance .post("/token/refresh/", { refresh: refresh_token }) - .then(response => { + .then((response) => { localStorage.setItem("access_token", response.data.access); axiosInstance.defaults.headers["Authorization"] = "Bearer " + response.data.access; @@ -34,7 +33,7 @@ axiosInstance.interceptors.response.use( return axiosInstance(originalRequest); }) - .catch(err => { + .catch((err) => { console.log("Interceptors error: ", err); }); } diff --git a/frontend/src/api/TaskApi.jsx b/frontend/src/api/TaskApi.jsx index eee70d0..45737f9 100644 --- a/frontend/src/api/TaskApi.jsx +++ b/frontend/src/api/TaskApi.jsx @@ -1,21 +1,21 @@ -import axiosInstance from "./configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const baseURL = ""; export const createTask = (endpoint, data) => { return axiosInstance .post(`${baseURL}${endpoint}/`, data) - .then(response => response.data) - .catch(error => { + .then((response) => response.data) + .catch((error) => { throw error; }); }; -export const readTasks = endpoint => { +export const readTasks = (endpoint) => { return axiosInstance .get(`${baseURL}${endpoint}/`) - .then(response => response.data) - .catch(error => { + .then((response) => response.data) + .catch((error) => { throw error; }); }; @@ -23,8 +23,8 @@ export const readTasks = endpoint => { export const readTaskByID = (endpoint, id) => { return axiosInstance .get(`${baseURL}${endpoint}/${id}/`) - .then(response => response.data) - .catch(error => { + .then((response) => response.data) + .catch((error) => { throw error; }); }; @@ -32,8 +32,8 @@ export const readTaskByID = (endpoint, id) => { export const updateTask = (endpoint, id, data) => { return axiosInstance .put(`${baseURL}${endpoint}/${id}/`, data) - .then(response => response.data) - .catch(error => { + .then((response) => response.data) + .catch((error) => { throw error; }); }; @@ -41,16 +41,16 @@ export const updateTask = (endpoint, id, data) => { export const deleteTask = (endpoint, id) => { return axiosInstance .delete(`${baseURL}${endpoint}/${id}/`) - .then(response => response.data) - .catch(error => { + .then((response) => response.data) + .catch((error) => { throw error; }); }; // Create -export const createTodoTask = data => createTask("todo", data); -export const createRecurrenceTask = data => createTask("daily", data); -export const createHabitTask = data => createTask("habit", data); +export const createTodoTask = (data) => createTask("todo", data); +export const createRecurrenceTask = (data) => createTask("daily", data); +export const createHabitTask = (data) => createTask("habit", data); // Read export const readTodoTasks = () => readTasks("todo"); @@ -58,9 +58,9 @@ export const readRecurrenceTasks = () => readTasks("daily"); export const readHabitTasks = () => readTasks("habit"); // Read by ID -export const readTodoTaskByID = id => readTaskByID("todo", id); -export const readRecurrenceTaskByID = id => readTaskByID("daily", id); -export const readHabitTaskByID = id => readTaskByID("habit", id); +export const readTodoTaskByID = (id) => readTaskByID("todo", id); +export const readRecurrenceTaskByID = (id) => readTaskByID("daily", id); +export const readHabitTaskByID = (id) => readTaskByID("habit", id); // Update export const updateTodoTask = (id, data) => updateTask("todo", id, data); @@ -68,6 +68,6 @@ export const updateRecurrenceTask = (id, data) => updateTask("daily", id, data); export const updateHabitTask = (id, data) => updateTask("habit", id, data); // Delete -export const deleteTodoTask = id => deleteTask("todo", id); -export const deleteRecurrenceTask = id => deleteTask("daily", id); -export const deleteHabitTask = id => deleteTask("habit", id); +export const deleteTodoTask = (id) => deleteTask("todo", id); +export const deleteRecurrenceTask = (id) => deleteTask("daily", id); +export const deleteHabitTask = (id) => deleteTask("habit", id); diff --git a/frontend/src/components/EisenhowerMatrix/Eisenhower.jsx b/frontend/src/components/EisenhowerMatrix/Eisenhower.jsx index 374ac66..619dca9 100644 --- a/frontend/src/components/EisenhowerMatrix/Eisenhower.jsx +++ b/frontend/src/components/EisenhowerMatrix/Eisenhower.jsx @@ -1,14 +1,14 @@ import React, { useState, useEffect } from "react"; import { FiAlertCircle, FiClock, FiXCircle, FiCheckCircle } from "react-icons/fi"; import { readTodoTasks } from "../../api/TaskApi"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; function EachBlog({ name, colorCode, contentList, icon }) { const [tasks, setTasks] = useState(contentList); - const handleCheckboxChange = async index => { + const handleCheckboxChange = async (index) => { try { - setTasks(contentList) + setTasks(contentList); const updatedTasks = [...tasks]; const taskId = updatedTasks[index].id; @@ -60,12 +60,12 @@ function Eisenhower() { useEffect(() => { readTodoTasks() - .then(data => { + .then((data) => { console.log(data); - const contentList_ui = data.filter(task => task.priority === 1); - const contentList_uni = data.filter(task => task.priority === 2); - const contentList_nui = data.filter(task => task.priority === 3); - const contentList_nuni = data.filter(task => task.priority === 4); + const contentList_ui = data.filter((task) => task.priority === 1); + const contentList_uni = data.filter((task) => task.priority === 2); + const contentList_nui = data.filter((task) => task.priority === 3); + const contentList_nuni = data.filter((task) => task.priority === 4); setTasks({ contentList_ui, @@ -74,7 +74,7 @@ function Eisenhower() { contentList_nuni, }); }) - .catch(error => console.error("Error fetching tasks:", error)); + .catch((error) => console.error("Error fetching tasks:", error)); }, []); return ( diff --git a/frontend/src/components/calendar/calendar.jsx b/frontend/src/components/calendar/calendar.jsx index b4f8430..e4f1bdd 100644 --- a/frontend/src/components/calendar/calendar.jsx +++ b/frontend/src/components/calendar/calendar.jsx @@ -1,11 +1,11 @@ -import React, { useState } from "react"; +import React 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 "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; export default class Calendar extends React.Component { state = { @@ -83,7 +83,7 @@ export default class Calendar extends React.Component { }); }; - handleDateSelect = selectInfo => { + handleDateSelect = (selectInfo) => { let title = prompt("Please enter a new title for your event"); let calendarApi = selectInfo.view.calendar; @@ -100,20 +100,20 @@ export default class Calendar extends React.Component { } }; - handleEventClick = clickInfo => { + handleEventClick = (clickInfo) => { if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) { axiosInstance - .delete(`todo/${clickInfo.event.id}/`) - .then(response => { - clickInfo.event.remove(); - }) - .catch(error => { - console.error("Error deleting Task:", error); - }); + .delete(`todo/${clickInfo.event.id}/`) + .then((response) => { + clickInfo.event.remove(); + }) + .catch((error) => { + console.error("Error deleting Task:", error); + }); } }; - handleEvents = events => { + handleEvents = (events) => { this.setState({ currentEvents: events, }); diff --git a/frontend/src/components/dashboard/Areachart.jsx b/frontend/src/components/dashboard/Areachart.jsx index 8ede4d8..3dde527 100644 --- a/frontend/src/components/dashboard/Areachart.jsx +++ b/frontend/src/components/dashboard/Areachart.jsx @@ -1,103 +1,103 @@ import { AreaChart, Title } from "@tremor/react"; import React from "react"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const fetchAreaChartData = async () => { - let res = await axiosInstance.get("/dashboard/weekly/"); - console.log(res.data); - // const areaChartData = [ - // { - // date: "Mon", - // "This Week": res.data[0]["This Week"], - // "Last Week": res.data[0]["Last Week"], - // }, - // { - // date: "Tue", - // "This Week": res.data[1]["This Week"], - // "Last Week": res.data[1]["Last Week"], - // }, - // { - // date: "Wed", - // "This Week": res.data[2]["This Week"], - // "Last Week": res.data[2]["Last Week"], - // }, - // { - // date: "Th", - // "This Week": res.data[3]["This Week"], - // "Last Week": res.data[3]["Last Week"], - // }, - // { - // date: "Fri", - // "This Week": res.data[4]["This Week"], - // "Last Week": res.data[4]["Last Week"], - // }, - // { - // date: "Sat", - // "This Week": res.data[5]["This Week"], - // "Last Week": res.data[5]["Last Week"], - // }, - // { - // date: "Sun", - // "This Week": res.data[6]["This Week"], - // "Last Week": res.data[6]["Last Week"], - // }, - // ]; - const areaChartData = [ - { - date: "Mon", - "This Week": 1, - "Last Week": 2, - }, - { - date: "Tue", - "This Week": 5, - "Last Week": 2, - }, - { - date: "Wed", - "This Week": 7, - "Last Week": 9, - }, - { - date: "Th", - "This Week": 10, - "Last Week": 3, - }, - { - date: "Fri", - "This Week": 5, - "Last Week": 1, - }, - { - date: "Sat", - "This Week": 7, - "Last Week": 8, - }, - { - date: "Sun", - "This Week": 3, - "Last Week": 8, - }, - ]; - return areaChartData; -} + let res = await axiosInstance.get("/dashboard/weekly/"); + console.log(res.data); + // const areaChartData = [ + // { + // date: "Mon", + // "This Week": res.data[0]["This Week"], + // "Last Week": res.data[0]["Last Week"], + // }, + // { + // date: "Tue", + // "This Week": res.data[1]["This Week"], + // "Last Week": res.data[1]["Last Week"], + // }, + // { + // date: "Wed", + // "This Week": res.data[2]["This Week"], + // "Last Week": res.data[2]["Last Week"], + // }, + // { + // date: "Th", + // "This Week": res.data[3]["This Week"], + // "Last Week": res.data[3]["Last Week"], + // }, + // { + // date: "Fri", + // "This Week": res.data[4]["This Week"], + // "Last Week": res.data[4]["Last Week"], + // }, + // { + // date: "Sat", + // "This Week": res.data[5]["This Week"], + // "Last Week": res.data[5]["Last Week"], + // }, + // { + // date: "Sun", + // "This Week": res.data[6]["This Week"], + // "Last Week": res.data[6]["Last Week"], + // }, + // ]; + const areaChartData = [ + { + date: "Mon", + "This Week": 1, + "Last Week": 2, + }, + { + date: "Tue", + "This Week": 5, + "Last Week": 2, + }, + { + date: "Wed", + "This Week": 7, + "Last Week": 9, + }, + { + date: "Th", + "This Week": 10, + "Last Week": 3, + }, + { + date: "Fri", + "This Week": 5, + "Last Week": 1, + }, + { + date: "Sat", + "This Week": 7, + "Last Week": 8, + }, + { + date: "Sun", + "This Week": 3, + "Last Week": 8, + }, + ]; + return areaChartData; +}; const areaChartDataArray = await fetchAreaChartData(); export const AreaChartGraph = () => { - const [value, setValue] = React.useState(null); - return ( - <> - Number of tasks statistics vs. last week - setValue(v)} - showAnimation - /> - - ); -}; \ No newline at end of file + const [value, setValue] = React.useState(null); + return ( + <> + Number of tasks statistics vs. last week + setValue(v)} + showAnimation + /> + + ); +}; diff --git a/frontend/src/components/dashboard/Barchart.jsx b/frontend/src/components/dashboard/Barchart.jsx index 18c53fe..dbc5650 100644 --- a/frontend/src/components/dashboard/Barchart.jsx +++ b/frontend/src/components/dashboard/Barchart.jsx @@ -1,6 +1,6 @@ import { BarChart, Title } from "@tremor/react"; import React from "react"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const fetchBarChartData = async () => { let res = await axiosInstance.get("/dashboard/weekly/"); diff --git a/frontend/src/components/dashboard/DonutChart.jsx b/frontend/src/components/dashboard/DonutChart.jsx index c71b81d..4db0ceb 100644 --- a/frontend/src/components/dashboard/DonutChart.jsx +++ b/frontend/src/components/dashboard/DonutChart.jsx @@ -1,5 +1,5 @@ import { DonutChart } from "@tremor/react"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const fetchDonutData = async () => { try { diff --git a/frontend/src/components/dashboard/KpiCard.jsx b/frontend/src/components/dashboard/KpiCard.jsx index 7ebb841..4af8a22 100644 --- a/frontend/src/components/dashboard/KpiCard.jsx +++ b/frontend/src/components/dashboard/KpiCard.jsx @@ -1,7 +1,6 @@ - import { BadgeDelta, Card, Flex, Metric, ProgressBar, Text } from "@tremor/react"; import React from "react"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const fetchKpiCardData = async () => { let res = await axiosInstance.get("/dashboard/stats/"); @@ -9,7 +8,7 @@ const fetchKpiCardData = async () => { // let completedLastWeek = res.data["completed_last_week"]; let completedThisWeek = 4; let completedLastWeek = 23; - let percentage = (completedThisWeek / completedLastWeek)*100; + let percentage = (completedThisWeek / completedLastWeek) * 100; let incOrdec = undefined; if (completedThisWeek <= completedLastWeek) { incOrdec = "moderateDecrease"; @@ -17,15 +16,13 @@ const fetchKpiCardData = async () => { if (completedThisWeek > completedLastWeek) { incOrdec = "moderateIncrease"; } - return {completedThisWeek, completedLastWeek, incOrdec, percentage}; -} - -const {kpiCardDataArray, completedThisWeek ,completedLastWeek, incOrdec, percentage} = await fetchKpiCardData(); + return { completedThisWeek, completedLastWeek, incOrdec, percentage }; +}; +const { kpiCardDataArray, completedThisWeek, completedLastWeek, incOrdec, percentage } = await fetchKpiCardData(); export default function KpiCard() { return ( -
@@ -39,4 +36,4 @@ export default function KpiCard() { ); -} \ No newline at end of file +} diff --git a/frontend/src/components/dashboard/ProgressCircle.jsx b/frontend/src/components/dashboard/ProgressCircle.jsx index 4f8382b..40b8d0b 100644 --- a/frontend/src/components/dashboard/ProgressCircle.jsx +++ b/frontend/src/components/dashboard/ProgressCircle.jsx @@ -1,6 +1,6 @@ -import { Card, Flex, ProgressCircle, Text, } from "@tremor/react"; +import { Card, Flex, ProgressCircle, Text } from "@tremor/react"; import React from "react"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; const fetchProgressData = async () => { try { @@ -27,12 +27,7 @@ export default function ProgressCircleChart() { return ( - + {progressData.toFixed(0)} % diff --git a/frontend/src/components/kanbanBoard/kanbanBoard.jsx b/frontend/src/components/kanbanBoard/kanbanBoard.jsx index 2272444..a4ecd79 100644 --- a/frontend/src/components/kanbanBoard/kanbanBoard.jsx +++ b/frontend/src/components/kanbanBoard/kanbanBoard.jsx @@ -5,11 +5,11 @@ import { SortableContext, arrayMove } from "@dnd-kit/sortable"; import { createPortal } from "react-dom"; import TaskCard from "./taskCard"; import { AiOutlinePlusCircle } from "react-icons/ai"; -import axiosInstance from "../../api/configs/AxiosConfig"; +import axiosInstance from "src/api/AxiosConfig"; function KanbanBoard() { const [columns, setColumns] = useState([]); - const columnsId = useMemo(() => columns.map(col => col.id), [columns]); + const columnsId = useMemo(() => columns.map((col) => col.id), [columns]); const [boardId, setBoardData] = useState(); const [tasks, setTasks] = useState([]); @@ -66,7 +66,7 @@ function KanbanBoard() { const tasksResponse = await axiosInstance.get("/todo"); // Transform - const transformedTasks = tasksResponse.data.map(task => ({ + const transformedTasks = tasksResponse.data.map((task) => ({ id: task.id, columnId: task.list_board, content: task.title, @@ -95,7 +95,7 @@ function KanbanBoard() { const columnsResponse = await axiosInstance.get("/lists"); // Transform - const transformedColumns = columnsResponse.data.map(column => ({ + const transformedColumns = columnsResponse.data.map((column) => ({ id: column.id, title: column.name, })); @@ -135,7 +135,7 @@ function KanbanBoard() {
- {columns.map(col => ( + {columns.map((col) => ( task.columnId === col.id)} + tasks={tasks.filter((task) => task.columnId === col.id)} /> ))} @@ -186,7 +186,7 @@ function KanbanBoard() { createTask={createTask} deleteTask={deleteTask} updateTask={updateTask} - tasks={tasks.filter(task => task.columnId === activeColumn.id)} + tasks={tasks.filter((task) => task.columnId === activeColumn.id)} /> )} {activeTask && } @@ -213,35 +213,34 @@ function KanbanBoard() { axiosInstance .post("todo/", newTaskData) - .then(response => { + .then((response) => { const newTask = { id: response.data.id, columnId, content: response.data.title, }; - }) - .catch(error => { + .catch((error) => { console.error("Error creating task:", error); }); - setTasks(tasks => [...tasks, newTask]); - } + setTasks((tasks) => [...tasks, newTask]); + } function deleteTask(id) { - const newTasks = tasks.filter(task => task.id !== id); + const newTasks = tasks.filter((task) => task.id !== id); axiosInstance .delete(`todo/${id}/`) - .then(response => { + .then((response) => { setTasks(newTasks); }) - .catch(error => { + .catch((error) => { console.error("Error deleting Task:", error); }); - setTasks(newTasks); + setTasks(newTasks); } function updateTask(id, content) { - const newTasks = tasks.map(task => { + const newTasks = tasks.map((task) => { if (task.id !== id) return task; return { ...task, content }; }); @@ -252,15 +251,15 @@ function KanbanBoard() { function createNewColumn() { axiosInstance .post("lists/", { name: `Column ${columns.length + 1}`, position: 1, board: boardId.id }) - .then(response => { + .then((response) => { const newColumn = { id: response.data.id, title: response.data.name, }; - setColumns(prevColumns => [...prevColumns, newColumn]); + setColumns((prevColumns) => [...prevColumns, newColumn]); }) - .catch(error => { + .catch((error) => { console.error("Error creating ListBoard:", error); }); } @@ -268,22 +267,22 @@ function KanbanBoard() { function deleteColumn(id) { axiosInstance .delete(`lists/${id}/`) - .then(response => { - setColumns(prevColumns => prevColumns.filter(col => col.id !== id)); + .then((response) => { + setColumns((prevColumns) => prevColumns.filter((col) => col.id !== id)); }) - .catch(error => { + .catch((error) => { console.error("Error deleting ListBoard:", error); }); - const tasksToDelete = tasks.filter(t => t.columnId === id); + const tasksToDelete = tasks.filter((t) => t.columnId === id); - tasksToDelete.forEach(task => { + tasksToDelete.forEach((task) => { axiosInstance .delete(`todo/${task.id}/`) - .then(response => { - setTasks(prevTasks => prevTasks.filter(t => t.id !== task.id)); + .then((response) => { + setTasks((prevTasks) => prevTasks.filter((t) => t.id !== task.id)); }) - .catch(error => { + .catch((error) => { console.error("Error deleting Task:", error); }); }); @@ -293,10 +292,10 @@ function KanbanBoard() { // Update the column axiosInstance .patch(`lists/${id}/`, { name: title }) // Adjust the payload based on your API requirements - .then(response => { - setColumns(prevColumns => prevColumns.map(col => (col.id === id ? { ...col, title } : col))); + .then((response) => { + setColumns((prevColumns) => prevColumns.map((col) => (col.id === id ? { ...col, title } : col))); }) - .catch(error => { + .catch((error) => { console.error("Error updating ListBoard:", error); }); } @@ -330,9 +329,9 @@ function KanbanBoard() { // Reorder columns if the dragged item is a column if (isActiveAColumn && isOverAColumn) { - setColumns(columns => { - const activeColumnIndex = columns.findIndex(col => col.id === activeId); - const overColumnIndex = columns.findIndex(col => col.id === overId); + setColumns((columns) => { + const activeColumnIndex = columns.findIndex((col) => col.id === activeId); + const overColumnIndex = columns.findIndex((col) => col.id === overId); const reorderedColumns = arrayMove(columns, activeColumnIndex, overColumnIndex); @@ -342,9 +341,9 @@ function KanbanBoard() { // Reorder tasks within the same column if (isActiveATask && isOverATask) { - setTasks(tasks => { - const activeIndex = tasks.findIndex(t => t.id === activeId); - const overIndex = tasks.findIndex(t => t.id === overId); + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); + const overIndex = tasks.findIndex((t) => t.id === overId); const reorderedTasks = arrayMove(tasks, activeIndex, overIndex); @@ -354,15 +353,15 @@ function KanbanBoard() { // Move tasks between columns and update columnId if (isActiveATask && isOverAColumn) { - setTasks(tasks => { - const activeIndex = tasks.findIndex(t => t.id === activeId); + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); tasks[activeIndex].columnId = overId; axiosInstance .put(`todo/change_task_list_board/`, { todo_id: activeId, new_list_board_id: overId, new_index: 0 }) - .then(response => {}) - .catch(error => { + .then((response) => {}) + .catch((error) => { console.error("Error updating task columnId:", error); }); @@ -386,9 +385,9 @@ function KanbanBoard() { if (!isActiveATask) return; if (isActiveATask && isOverATask) { - setTasks(tasks => { - const activeIndex = tasks.findIndex(t => t.id === activeId); - const overIndex = tasks.findIndex(t => t.id === overId); + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); + const overIndex = tasks.findIndex((t) => t.id === overId); if (tasks[activeIndex].columnId !== tasks[overIndex].columnId) { tasks[activeIndex].columnId = tasks[overIndex].columnId; @@ -402,8 +401,8 @@ function KanbanBoard() { const isOverAColumn = over.data.current?.type === "Column"; if (isActiveATask && isOverAColumn) { - setTasks(tasks => { - const activeIndex = tasks.findIndex(t => t.id === activeId); + setTasks((tasks) => { + const activeIndex = tasks.findIndex((t) => t.id === activeId); tasks[activeIndex].columnId = overId; return arrayMove(tasks, activeIndex, activeIndex); diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 5a33944..3dcabd8 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,7 +1,12 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], -}) + resolve: { + alias: { + src: "/src", + }, + }, +}); From 107a3d23f27a049e5959e95b4d1cd64b7c4bd08f Mon Sep 17 00:00:00 2001 From: sosokker Date: Wed, 22 Nov 2023 20:00:02 +0700 Subject: [PATCH 2/3] Seperate authentication context and hook --- backend/authentications/urls.py | 3 +- backend/authentications/views.py | 32 +++++++-- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 49 ++++++++------ frontend/src/App.jsx | 67 ++++++++++++++++--- frontend/src/PrivateRoute.jsx | 10 ++- frontend/src/api/AuthenticationApi.jsx | 1 + frontend/src/api/AxiosConfig.jsx | 33 ++++----- frontend/src/contexts/AuthContextProvider.jsx | 29 ++++++++ frontend/src/hooks/AuthHooks.jsx | 36 ++++++++++ .../hooks/authentication/IsAuthenticated.jsx | 39 ----------- frontend/src/main.jsx | 4 +- 12 files changed, 205 insertions(+), 99 deletions(-) create mode 100644 frontend/src/contexts/AuthContextProvider.jsx create mode 100644 frontend/src/hooks/AuthHooks.jsx delete mode 100644 frontend/src/hooks/authentication/IsAuthenticated.jsx diff --git a/backend/authentications/urls.py b/backend/authentications/urls.py index 32965a4..75c5914 100644 --- a/backend/authentications/urls.py +++ b/backend/authentications/urls.py @@ -1,6 +1,6 @@ from django.urls import path from rest_framework_simplejwt import views as jwt_views -from authentications.views import ObtainTokenPairWithCustomView, GreetingView, GoogleLogin, GoogleRetrieveUserInfo +from authentications.views import ObtainTokenPairWithCustomView, GreetingView, GoogleLogin, GoogleRetrieveUserInfo, CheckAccessTokenAndRefreshToken urlpatterns = [ path('token/obtain/', jwt_views.TokenObtainPairView.as_view(), name='token_create'), @@ -9,4 +9,5 @@ urlpatterns = [ path('hello/', GreetingView.as_view(), name='hello_world'), path('dj-rest-auth/google/', GoogleLogin.as_view(), name="google_login"), path('auth/google/', GoogleRetrieveUserInfo.as_view()), + path('auth/status/', CheckAccessTokenAndRefreshToken.as_view(), name='check_token_status') ] \ No newline at end of file diff --git a/backend/authentications/views.py b/backend/authentications/views.py index 9b5f249..167581b 100644 --- a/backend/authentications/views.py +++ b/backend/authentications/views.py @@ -1,6 +1,3 @@ -from django.shortcuts import render - -# Create your views here. """This module defines API views for authentication, user creation, and a simple hello message.""" import json @@ -14,6 +11,8 @@ from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.response import Response from rest_framework.views import APIView from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework_simplejwt.authentication import JWTAuthentication + from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter @@ -27,6 +26,31 @@ from users.managers import CustomAccountManager from users.models import CustomUser +class CheckAccessTokenAndRefreshToken(APIView): + permission_classes = (AllowAny,) + JWT_authenticator = JWTAuthentication() + + def post(self, request, *args, **kwargs): + access_token = request.data.get('access_token') + refresh_token = request.data.get('refresh_token') + # Check if the access token is valid + if access_token: + response = self.JWT_authenticator.authenticate(request) + if response is not None: + return Response({'status': 'true'}, status=status.HTTP_200_OK) + + # Check if the refresh token is valid + if refresh_token: + try: + refresh = RefreshToken(refresh_token) + access_token = str(refresh.access_token) + return Response({'access_token': access_token}, status=status.HTTP_200_OK) + except Exception as e: + return Response({'status': 'false'}, status=status.HTTP_401_UNAUTHORIZED) + + return Response({'status': 'false'}, status=status.HTTP_400_BAD_REQUEST) + + class ObtainTokenPairWithCustomView(APIView): """ Custom Token Obtain Pair View. @@ -165,4 +189,4 @@ class GoogleRetrieveUserInfo(APIView): response = requests.get(api_url, headers=headers) if response.status_code == 200: return response.json() - raise Exception('Google API Error', response) + raise Exception('Google API Error', response) \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 714e1c7..70698dc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,7 @@ "framer-motion": "^10.16.4", "gapi-script": "^1.2.0", "jwt-decode": "^4.0.0", + "prop-types": "^15.8.1", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.9.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 26c8422..143df2b 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -86,6 +86,9 @@ dependencies: jwt-decode: specifier: ^4.0.0 version: 4.0.0 + prop-types: + specifier: ^15.8.1 + version: 15.8.1 react: specifier: ^18.2.0 version: 18.2.0 @@ -3380,14 +3383,6 @@ packages: warning: 4.0.3 dev: false - /react-day-picker@8.9.1(date-fns@2.30.0)(react@18.2.0): - resolution: {integrity: sha512-W0SPApKIsYq+XCtfGeMYDoU0KbsG3wfkYtlw8l+vZp6KoBXGOlhzBUp4tNx1XiwiOZwhfdGOlj7NGSCKGSlg5Q==} - peerDependencies: - date-fns: ^2.28.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - date-fns: 2.30.0 - react: 18.2.0 /react-calendar@4.6.1(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-MvCPdvxEvq7wICBhFxlYwxS2+IsVvSjTcmlr0Kl3yDRVhoX7btNg0ySJx5hy9rb1eaM4nDpzQcW5c87nfQ8n8w==} peerDependencies: @@ -3479,6 +3474,16 @@ packages: - '@types/react-dom' dev: false + /react-day-picker@8.9.1(date-fns@2.30.0)(react@18.2.0): + resolution: {integrity: sha512-W0SPApKIsYq+XCtfGeMYDoU0KbsG3wfkYtlw8l+vZp6KoBXGOlhzBUp4tNx1XiwiOZwhfdGOlj7NGSCKGSlg5Q==} + peerDependencies: + date-fns: ^2.28.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + date-fns: 2.30.0 + react: 18.2.0 + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -3511,8 +3516,6 @@ packages: tiny-warning: 1.0.3 dev: false - /react-icons@4.11.0(react@18.2.0): - resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==} /react-icons@4.12.0(react@18.2.0): resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} peerDependencies: @@ -3611,18 +3614,6 @@ packages: react-transition-group: 2.9.0(react-dom@18.2.0)(react@18.2.0) dev: false - /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} - peerDependencies: - react: '>=15.0.0' - react-dom: '>=15.0.0' - dependencies: - dom-helpers: 3.4.0 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-lifecycles-compat: 3.0.4 /react-time-picker@6.5.2(@types/react-dom@18.2.15)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xRamxjndpq3HfnEL+6T3VyirLMEn4D974OJgs9sTP8iJX/RB02rmwy09C9oBThTGuN3ycbozn06iYLn148vcdw==} peerDependencies: @@ -3648,6 +3639,20 @@ packages: - '@types/react-dom' dev: false + /react-transition-group@2.9.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + peerDependencies: + react: '>=15.0.0' + react-dom: '>=15.0.0' + dependencies: + dom-helpers: 3.4.0 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-lifecycles-compat: 3.0.4 + dev: false + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index b6d42b2..83ebef6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,7 @@ +import { useEffect } from "react"; import "./App.css"; -import { Route, Routes, useLocation } from "react-router-dom"; - +import { Route, Routes } from "react-router-dom"; +import axios from "axios"; import TestAuth from "./components/testAuth"; import LoginPage from "./components/authentication/LoginPage"; import SignUpPage from "./components/authentication/SignUpPage"; @@ -13,18 +14,66 @@ import PrivateRoute from "./PrivateRoute"; import ProfileUpdatePage from "./components/profilePage"; import Dashboard from "./components/dashboard/dashboard"; +import { useAuth } from "./hooks/AuthHooks"; const App = () => { - const location = useLocation(); - const prevention = ["/login", "/signup"]; - const isLoginPageOrSignUpPage = prevention.some(_ => location.pathname.includes(_)); + const { isAuthenticated, setIsAuthenticated } = useAuth(); + useEffect(() => { + const checkLoginStatus = async () => { + const data = { + access_token: localStorage.getItem("access_token"), + refresh_token: localStorage.getItem("refresh_token"), + }; + + await axios + .post("http://127.0.0.1:8000/api/auth/status/", data, { + headers: { + Authorization: "Bearer " + localStorage.getItem("access_token"), + }, + }) + .then((response) => { + if (response.status === 200) { + if (response.data.access_token) { + localStorage.setItem("access_token", response.data.access_token); + setIsAuthenticated(true); + } else { + setIsAuthenticated(true); + } + } else { + setIsAuthenticated(false); + } + }) + .catch((error) => { + console.error("Error checking login status:", error.message); + }); + }; + + checkLoginStatus(); + }, [setIsAuthenticated]); + + return
{isAuthenticated ? : }
; +}; + +const NonAuthenticatedComponents = () => { return ( -
- {!isLoginPageOrSignUpPage && } -
+
+ + + } /> + } /> + +
+ ); +}; + +const AuthenticatedComponents = () => { + return ( +
+ +
-
+
} /> }> diff --git a/frontend/src/PrivateRoute.jsx b/frontend/src/PrivateRoute.jsx index defeaea..b9784f6 100644 --- a/frontend/src/PrivateRoute.jsx +++ b/frontend/src/PrivateRoute.jsx @@ -1,11 +1,9 @@ -import React from "react"; import { Navigate, Outlet } from "react-router-dom"; -import { useAuth } from "./hooks/authentication/IsAuthenticated"; +import { useAuth } from "src/hooks/AuthHooks"; const PrivateRoute = () => { - const { isAuthenticated, setIsAuthenticated } = useAuth(); - const auth = isAuthenticated; - return auth ? : ; + const { isAuthenticated } = useAuth(); + return isAuthenticated ? : ; }; -export default PrivateRoute; \ No newline at end of file +export default PrivateRoute; diff --git a/frontend/src/api/AuthenticationApi.jsx b/frontend/src/api/AuthenticationApi.jsx index 1913922..7f88744 100644 --- a/frontend/src/api/AuthenticationApi.jsx +++ b/frontend/src/api/AuthenticationApi.jsx @@ -7,6 +7,7 @@ const apiUserLogin = (data) => { .post("token/obtain/", data) .then((response) => { console.log(response.statusText); + return response; }) .catch((error) => { diff --git a/frontend/src/api/AxiosConfig.jsx b/frontend/src/api/AxiosConfig.jsx index 336d18d..7b701bd 100644 --- a/frontend/src/api/AxiosConfig.jsx +++ b/frontend/src/api/AxiosConfig.jsx @@ -1,4 +1,5 @@ import axios from "axios"; +import { redirect } from "react-router-dom"; const axiosInstance = axios.create({ baseURL: "http://127.0.0.1:8000/api/", @@ -18,24 +19,24 @@ axiosInstance.interceptors.response.use( const refresh_token = localStorage.getItem("refresh_token"); // Check if the error is due to 401 and a refresh token is available - if ( - error.response.status === 401 && - error.response.statusText === "Unauthorized" && - refresh_token !== "undefined" - ) { - return axiosInstance - .post("/token/refresh/", { refresh: refresh_token }) - .then((response) => { - localStorage.setItem("access_token", response.data.access); + if (error.response && error.response.status === 401) { + if (refresh_token) { + return axiosInstance + .post("/token/refresh/", { refresh: refresh_token }) + .then((response) => { + localStorage.setItem("access_token", response.data.access); - axiosInstance.defaults.headers["Authorization"] = "Bearer " + response.data.access; - originalRequest.headers["Authorization"] = "Bearer " + response.data.access; + axiosInstance.defaults.headers["Authorization"] = "Bearer " + response.data.access; + originalRequest.headers["Authorization"] = "Bearer " + response.data.access; - return axiosInstance(originalRequest); - }) - .catch((err) => { - console.log("Interceptors error: ", err); - }); + return axiosInstance(originalRequest); + }) + .catch((err) => { + console.log("Interceptors error: ", err); + }); + } else { + redirect("/login"); + } } return Promise.reject(error); } diff --git a/frontend/src/contexts/AuthContextProvider.jsx b/frontend/src/contexts/AuthContextProvider.jsx new file mode 100644 index 0000000..fea0243 --- /dev/null +++ b/frontend/src/contexts/AuthContextProvider.jsx @@ -0,0 +1,29 @@ +import { useEffect } from "react"; +import PropTypes from "prop-types"; +import { createContext, useState } from "react"; + +const AuthContext = createContext(); + +export const AuthProvider = ({ children }) => { + const [isAuthenticated, setIsAuthenticated] = useState(false); + + useEffect(() => { + const accessToken = localStorage.getItem("access_token"); + if (accessToken) { + setIsAuthenticated(true); + } + }, []); + + const contextValue = { + isAuthenticated, + setIsAuthenticated, + }; + + return {children}; +}; + +AuthProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + +export default AuthContext; diff --git a/frontend/src/hooks/AuthHooks.jsx b/frontend/src/hooks/AuthHooks.jsx new file mode 100644 index 0000000..27d3afb --- /dev/null +++ b/frontend/src/hooks/AuthHooks.jsx @@ -0,0 +1,36 @@ +import { useContext } from "react"; +import AuthContext from "src/contexts/AuthContextProvider"; + +/** + * useAuth - Custom React Hook for Accessing Authentication Context + * + * @returns {Object} An object containing: + * - {boolean} isAuthenticated: A boolean indicating whether the user is authenticated. + * - {function} setIsAuthenticated: A function to set the authentication status manually. + * + * @throws {Error} If used outside the context of an AuthProvider. + * + * @example + * // Import the hook + * import useAuth from './AuthHooks'; + * + * // Inside a functional component + * const { isAuthenticated, setIsAuthenticated } = useAuth(); + * + * // Check authentication status + * if (isAuthenticated) { + * // User is authenticated + * } else { + * // User is not authenticated + * } + * + * // Manually set authentication status + * setIsAuthenticated(true); + */ +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; diff --git a/frontend/src/hooks/authentication/IsAuthenticated.jsx b/frontend/src/hooks/authentication/IsAuthenticated.jsx deleted file mode 100644 index 8874645..0000000 --- a/frontend/src/hooks/authentication/IsAuthenticated.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { createContext, useContext, useState, useEffect } from "react"; - -const AuthContext = createContext(); - -export const AuthProvider = ({ children }) => { - const [isAuthenticated, setIsAuthenticated] = useState(() => { - const access_token = localStorage.getItem("access_token"); - return !!access_token; - }); - - useEffect(() => { - const handleTokenChange = () => { - const newAccessToken = localStorage.getItem("access_token"); - setIsAuthenticated(!!newAccessToken); - }; - - handleTokenChange(); - - window.addEventListener("storage", handleTokenChange); - - return () => { - window.removeEventListener("storage", handleTokenChange); - }; - }, []); - - return ( - - {children} - - ); -}; - -export const useAuth = () => { - const context = useContext(AuthContext); - if (!context) { - throw new Error("useAuth must be used within an AuthProvider"); - } - return context; -}; \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 38cca17..8ca8296 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"; import App from "./App"; import { GoogleOAuthProvider } from "@react-oauth/google"; import { BrowserRouter } from "react-router-dom"; -import { AuthProvider } from "./hooks/authentication/IsAuthenticated"; +import { AuthProvider } from "./contexts/AuthContextProvider"; const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID; @@ -12,7 +12,7 @@ ReactDOM.createRoot(document.getElementById("root")).render( - + From da2dc5eff8af28b51be5f95c03a26b8e9bd9045b Mon Sep 17 00:00:00 2001 From: sosokker Date: Wed, 22 Nov 2023 20:00:20 +0700 Subject: [PATCH 3/3] Fix fetch data before component is mouted --- .../components/authentication/LoginPage.jsx | 29 ++--- .../src/components/dashboard/Areachart.jsx | 101 +++--------------- .../src/components/dashboard/Barchart.jsx | 100 +++-------------- .../src/components/dashboard/DonutChart.jsx | 51 +++++---- frontend/src/components/dashboard/KpiCard.jsx | 66 +++++++----- .../components/dashboard/ProgressCircle.jsx | 52 +++++---- .../src/components/navigations/Navbar.jsx | 9 +- 7 files changed, 143 insertions(+), 265 deletions(-) diff --git a/frontend/src/components/authentication/LoginPage.jsx b/frontend/src/components/authentication/LoginPage.jsx index 93f8f0b..c76ff68 100644 --- a/frontend/src/components/authentication/LoginPage.jsx +++ b/frontend/src/components/authentication/LoginPage.jsx @@ -1,18 +1,17 @@ -import React, { useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { useNavigate, redirect } from "react-router-dom"; import { useGoogleLogin } from "@react-oauth/google"; import { useCallback } from "react"; import Particles from "react-tsparticles"; import { loadFull } from "tsparticles"; import refreshAccessToken from "./refreshAcesstoken"; import axiosapi from "../../api/AuthenticationApi"; -import { useAuth } from "../../hooks/authentication/IsAuthenticated"; import { FcGoogle } from "react-icons/fc"; - +import { useAuth } from "src/hooks/AuthHooks"; function LoginPage() { + const { setIsAuthenticated } = useAuth(); const Navigate = useNavigate(); - const { isAuthenticated, setIsAuthenticated } = useAuth(); useEffect(() => { if (!refreshAccessToken()) { @@ -43,15 +42,13 @@ function LoginPage() { // On successful login, store tokens and set the authorization header localStorage.setItem("access_token", res.data.access); localStorage.setItem("refresh_token", res.data.refresh); - axiosapi.axiosInstance.defaults.headers["Authorization"] = - "Bearer " + res.data.access; + axiosapi.axiosInstance.defaults.headers["Authorization"] = "Bearer " + res.data.access; setIsAuthenticated(true); - Navigate("/"); + redirect("/"); }) .catch((err) => { console.log("Login failed"); console.log(err); - setIsAuthenticated(false); }); }; @@ -71,7 +68,6 @@ function LoginPage() { } } catch (error) { console.error("Error with the POST request:", error); - setIsAuthenticated(false); } }, onError: (errorResponse) => console.log(errorResponse), @@ -89,10 +85,7 @@ function LoginPage() { }, []); return ( -
+
{/* Particles Container */}
OR
{/* Login with Google Button */} - {/* Forgot Password Link */}
diff --git a/frontend/src/components/dashboard/Areachart.jsx b/frontend/src/components/dashboard/Areachart.jsx index 3dde527..862fa21 100644 --- a/frontend/src/components/dashboard/Areachart.jsx +++ b/frontend/src/components/dashboard/Areachart.jsx @@ -1,90 +1,24 @@ import { AreaChart, Title } from "@tremor/react"; -import React from "react"; +import { useState, useEffect } from "react"; import axiosInstance from "src/api/AxiosConfig"; -const fetchAreaChartData = async () => { - let res = await axiosInstance.get("/dashboard/weekly/"); - console.log(res.data); - // const areaChartData = [ - // { - // date: "Mon", - // "This Week": res.data[0]["This Week"], - // "Last Week": res.data[0]["Last Week"], - // }, - // { - // date: "Tue", - // "This Week": res.data[1]["This Week"], - // "Last Week": res.data[1]["Last Week"], - // }, - // { - // date: "Wed", - // "This Week": res.data[2]["This Week"], - // "Last Week": res.data[2]["Last Week"], - // }, - // { - // date: "Th", - // "This Week": res.data[3]["This Week"], - // "Last Week": res.data[3]["Last Week"], - // }, - // { - // date: "Fri", - // "This Week": res.data[4]["This Week"], - // "Last Week": res.data[4]["Last Week"], - // }, - // { - // date: "Sat", - // "This Week": res.data[5]["This Week"], - // "Last Week": res.data[5]["Last Week"], - // }, - // { - // date: "Sun", - // "This Week": res.data[6]["This Week"], - // "Last Week": res.data[6]["Last Week"], - // }, - // ]; - const areaChartData = [ - { - date: "Mon", - "This Week": 1, - "Last Week": 2, - }, - { - date: "Tue", - "This Week": 5, - "Last Week": 2, - }, - { - date: "Wed", - "This Week": 7, - "Last Week": 9, - }, - { - date: "Th", - "This Week": 10, - "Last Week": 3, - }, - { - date: "Fri", - "This Week": 5, - "Last Week": 1, - }, - { - date: "Sat", - "This Week": 7, - "Last Week": 8, - }, - { - date: "Sun", - "This Week": 3, - "Last Week": 8, - }, - ]; - return areaChartData; -}; - -const areaChartDataArray = await fetchAreaChartData(); export const AreaChartGraph = () => { - const [value, setValue] = React.useState(null); + const [areaChartDataArray, setAreaChartDataArray] = useState([]); + + useEffect(() => { + const fetchAreaChartData = async () => { + try { + const response = await axiosInstance.get("/dashboard/weekly/"); + const areaChartData = response.data; + setAreaChartDataArray(areaChartData); + } catch (error) { + console.error("Error fetching area chart data:", error); + } + }; + + fetchAreaChartData(); + }, []); + return ( <> Number of tasks statistics vs. last week @@ -95,7 +29,6 @@ export const AreaChartGraph = () => { categories={["This Week", "Last Week"]} colors={["neutral", "indigo"]} yAxisWidth={30} - onValueChange={(v) => setValue(v)} showAnimation /> diff --git a/frontend/src/components/dashboard/Barchart.jsx b/frontend/src/components/dashboard/Barchart.jsx index dbc5650..63a2c4f 100644 --- a/frontend/src/components/dashboard/Barchart.jsx +++ b/frontend/src/components/dashboard/Barchart.jsx @@ -1,89 +1,24 @@ import { BarChart, Title } from "@tremor/react"; -import React from "react"; +import { useState, useEffect } from "react"; import axiosInstance from "src/api/AxiosConfig"; -const fetchBarChartData = async () => { - let res = await axiosInstance.get("/dashboard/weekly/"); - console.log(res.data); - // const barchartData = [ - // { - // date: "Mon", - // "This Week": res.data[0]["Completed This Week"], - // "Last Week": res.data[0]["Completed Last Week"], - // }, - // { - // date: "Tue", - // "This Week": res.data[1]["Completed This Week"], - // "Last Week": res.data[1]["Completed Last Week"], - // }, - // { - // date: "Wed", - // "This Week": res.data[2]["Completed This Week"], - // "Last Week": res.data[2]["Completed Last Week"], - // }, - // { - // date: "Th", - // "This Week": res.data[3]["Completed This Week"], - // "Last Week": res.data[3]["Completed Last Week"], - // }, - // { - // date: "Fri", - // "This Week": res.data[4]["Completed This Week"], - // "Last Week": res.data[4]["Completed Last Week"], - // }, - // { - // date: "Sat", - // "This Week": res.data[5]["Completed This Week"], - // "Last Week": res.data[5]["Completed Last Week"], - // }, - // { - // date: "Sun", - // "This Week": res.data[6]["Completed This Week"], - // "Last Week": res.data[6]["Completed Last Week"], - // }, - // ]; - const barchartData = [ - { - date: "Mon", - "This Week": 1, - "Last Week": 2, - }, - { - date: "Tue", - "This Week": 5, - "Last Week": 2, - }, - { - date: "Wed", - "This Week": 7, - "Last Week": 9, - }, - { - date: "Th", - "This Week": 10, - "Last Week": 3, - }, - { - date: "Fri", - "This Week": 5, - "Last Week": 1, - }, - { - date: "Sat", - "This Week": 7, - "Last Week": 8, - }, - { - date: "Sun", - "This Week": 3, - "Last Week": 8, - }, - ]; - return barchartData; -}; -const barchartDataArray = await fetchBarChartData(); export const BarChartGraph = () => { - const [value, setValue] = React.useState(null); + const [barchartDataArray, setBarChartDataArray] = useState([]); + + useEffect(() => { + const fetchAreaChartData = async () => { + try { + const response = await axiosInstance.get("/dashboard/weekly/"); + const barchartDataArray = response.data; + setBarChartDataArray(barchartDataArray); + } catch (error) { + console.error("Error fetching area chart data:", error); + } + }; + + fetchAreaChartData(); + }, []); + return ( <> Task completed statistics vs. last week @@ -94,7 +29,6 @@ export const BarChartGraph = () => { categories={["This Week", "Last Week"]} colors={["neutral", "indigo"]} yAxisWidth={30} - onValueChange={(v) => setValue(v)} showAnimation /> diff --git a/frontend/src/components/dashboard/DonutChart.jsx b/frontend/src/components/dashboard/DonutChart.jsx index 4db0ceb..ccab32f 100644 --- a/frontend/src/components/dashboard/DonutChart.jsx +++ b/frontend/src/components/dashboard/DonutChart.jsx @@ -1,40 +1,37 @@ import { DonutChart } from "@tremor/react"; import axiosInstance from "src/api/AxiosConfig"; +import { useState, useEffect } from "react"; -const fetchDonutData = async () => { - try { - let res = await axiosInstance.get("/dashboard/stats/"); - // let todoCount = res.data.todo_count; - // let recurrenceCount = res.data.recurrence_count; - let todoCount = 10; - let recurrenceCount = 15; - if (todoCount === undefined) { - todoCount = 0; - } - if (recurrenceCount === undefined) { - recurrenceCount = 0; - } - const donutData = [ - { name: "Todo", count: todoCount }, - { name: "Recurrence", count: recurrenceCount }, - ]; - return donutData; - } catch (error) { - console.error("Error fetching donut data:", error); - return []; - } -}; - -const donutDataArray = await fetchDonutData(); export default function DonutChartGraph() { + const [donutData, setDonutData] = useState([]); + + useEffect(() => { + const fetchDonutData = async () => { + try { + const response = await axiosInstance.get("/dashboard/stats/"); + const todoCount = response.data.todo_count || 0; + const recurrenceCount = response.data.recurrence_count || 0; + + const donutData = [ + { name: "Todo", count: todoCount }, + { name: "Recurrence", count: recurrenceCount }, + ]; + + setDonutData(donutData); + } catch (error) { + console.error("Error fetching donut data:", error); + } + }; + fetchDonutData(); + }, []); + return ( setValue(v)} showAnimation radius={25} /> diff --git a/frontend/src/components/dashboard/KpiCard.jsx b/frontend/src/components/dashboard/KpiCard.jsx index 4af8a22..47c7162 100644 --- a/frontend/src/components/dashboard/KpiCard.jsx +++ b/frontend/src/components/dashboard/KpiCard.jsx @@ -1,39 +1,57 @@ import { BadgeDelta, Card, Flex, Metric, ProgressBar, Text } from "@tremor/react"; -import React from "react"; +import { useEffect, useState } from "react"; import axiosInstance from "src/api/AxiosConfig"; -const fetchKpiCardData = async () => { - let res = await axiosInstance.get("/dashboard/stats/"); - // let completedThisWeek = res.data["completed_this_week"]; - // let completedLastWeek = res.data["completed_last_week"]; - let completedThisWeek = 4; - let completedLastWeek = 23; - let percentage = (completedThisWeek / completedLastWeek) * 100; - let incOrdec = undefined; - if (completedThisWeek <= completedLastWeek) { - incOrdec = "moderateDecrease"; - } - if (completedThisWeek > completedLastWeek) { - incOrdec = "moderateIncrease"; - } - return { completedThisWeek, completedLastWeek, incOrdec, percentage }; -}; - -const { kpiCardDataArray, completedThisWeek, completedLastWeek, incOrdec, percentage } = await fetchKpiCardData(); - export default function KpiCard() { + const [kpiCardData, setKpiCardData] = useState({ + completedThisWeek: 0, + completedLastWeek: 0, + incOrdec: undefined, + percentage: 0, + }); + + useEffect(() => { + const fetchKpiCardData = async () => { + try { + const response = await axiosInstance.get("/dashboard/stats/"); + const completedThisWeek = response.data.completed_this_week || 0; + const completedLastWeek = response.data.completed_last_week || 0; + const percentage = (completedThisWeek / completedLastWeek) * 100; + let incOrdec = undefined; + + if (completedThisWeek <= completedLastWeek) { + incOrdec = "moderateDecrease"; + } + if (completedThisWeek > completedLastWeek) { + incOrdec = "moderateIncrease"; + } + + setKpiCardData({ + completedThisWeek, + completedLastWeek, + incOrdec, + percentage, + }); + } catch (error) { + console.error("Error fetching KPI card data:", error); + } + }; + + fetchKpiCardData(); + }, []); + return (
- {completedThisWeek} + {kpiCardData.completedThisWeek}
- {percentage.toFixed(0)}% + {kpiCardData.percentage.toFixed(0)}%
- vs. {completedLastWeek} (last week) + vs. {kpiCardData.completedLastWeek} (last week) - +
); } diff --git a/frontend/src/components/dashboard/ProgressCircle.jsx b/frontend/src/components/dashboard/ProgressCircle.jsx index 40b8d0b..c6d09b3 100644 --- a/frontend/src/components/dashboard/ProgressCircle.jsx +++ b/frontend/src/components/dashboard/ProgressCircle.jsx @@ -1,29 +1,35 @@ -import { Card, Flex, ProgressCircle, Text } from "@tremor/react"; -import React from "react"; +import { Card, Flex, ProgressCircle } from "@tremor/react"; +import { useState, useEffect } from "react"; import axiosInstance from "src/api/AxiosConfig"; -const fetchProgressData = async () => { - try { - let res = await axiosInstance.get("/dashboard/stats/"); - // let completedLastWeek = res.data.completed_last_week; - // let assignLastWeek = res.data.tasks_assigned_last_week; - let completedLastWeek = 15; - let assignLastWeek = 35; - if (completedLastWeek === undefined) { - completedLastWeek = 0; - } - if (assignLastWeek === undefined) { - assignLastWeek = 0; - } - return (completedLastWeek / assignLastWeek) * 100; - } catch (error) { - console.error("Error fetching progress data:", error); - return 0; - } -}; - -const progressData = await fetchProgressData(); export default function ProgressCircleChart() { + const [progressData, setProgressData] = useState(0); + + useEffect(() => { + const fetchProgressData = async () => { + try { + const response = await axiosInstance.get("/dashboard/stats/"); + let completedLastWeek = response.data.completed_last_week || 0; + let assignLastWeek = response.data.tasks_assigned_last_week || 0; + + if (completedLastWeek === undefined) { + completedLastWeek = 0; + } + if (assignLastWeek === undefined) { + assignLastWeek = 0; + } + + const progress = (completedLastWeek / assignLastWeek) * 100; + + setProgressData(progress); + } catch (error) { + console.error("Error fetching progress data:", error); + } + }; + + fetchProgressData(); + }, []); + return ( diff --git a/frontend/src/components/navigations/Navbar.jsx b/frontend/src/components/navigations/Navbar.jsx index aeb0065..5a024af 100644 --- a/frontend/src/components/navigations/Navbar.jsx +++ b/frontend/src/components/navigations/Navbar.jsx @@ -1,18 +1,17 @@ -import * as React from "react"; import { useNavigate } from "react-router-dom"; import axiosapi from "../../api/AuthenticationApi"; -import { useAuth } from "../../hooks/authentication/IsAuthenticated"; +import { useAuth } from "src/hooks/AuthHooks"; const settings = { - Profile: '/profile', - Account: '/account', + Profile: "/profile", + Account: "/account", }; function NavBar() { const Navigate = useNavigate(); - const { isAuthenticated, setIsAuthenticated } = useAuth(); + console.log(isAuthenticated); const logout = () => { axiosapi.apiUserLogout(); setIsAuthenticated(false);