From ab476d2ac83c24b366a4e4b314f8c5f9dc696b23 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 15:22:20 +0530 Subject: [PATCH 01/10] [Issue-26]: Update roles and permission 'QuickDo User' can only see his created tasks --- quickdo/fixtures/role.json | 2 +- quickdo/quickdo/doctype/quickdo/quickdo.json | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/quickdo/fixtures/role.json b/quickdo/fixtures/role.json index 32023a9..16b2bcb 100644 --- a/quickdo/fixtures/role.json +++ b/quickdo/fixtures/role.json @@ -2,7 +2,7 @@ { "bulk_actions": 1, "dashboard": 1, - "desk_access": 1, + "desk_access": 0, "disabled": 0, "docstatus": 0, "doctype": "Role", diff --git a/quickdo/quickdo/doctype/quickdo/quickdo.json b/quickdo/quickdo/doctype/quickdo/quickdo.json index db30776..1742f5a 100644 --- a/quickdo/quickdo/doctype/quickdo/quickdo.json +++ b/quickdo/quickdo/doctype/quickdo/quickdo.json @@ -202,7 +202,7 @@ "is_calendar_and_gantt": 1, "links": [], "make_attachments_public": 1, - "modified": "2025-07-18 01:46:39.790238", + "modified": "2025-07-20 15:20:01.600703", "modified_by": "Administrator", "module": "QuickDo", "name": "QuickDo", @@ -226,10 +226,13 @@ "delete": 1, "email": 1, "export": 1, + "if_owner": 1, + "import": 1, "print": 1, "read": 1, "report": 1, "role": "QuickDo User", + "select": 1, "share": 1, "write": 1 } From 3c85f9d08c8a2bb39041c716f5a978a41f993693 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 15:28:14 +0530 Subject: [PATCH 02/10] [Issue-27]: Sign Up page show password button shows both the fields --- frontend/src/pages/auth/sign-up.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/auth/sign-up.tsx b/frontend/src/pages/auth/sign-up.tsx index 9b6b83c..3bfa7f5 100644 --- a/frontend/src/pages/auth/sign-up.tsx +++ b/frontend/src/pages/auth/sign-up.tsx @@ -59,6 +59,7 @@ const SignUp = () => { // ? HOOKS const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < 1024) const [showPassword, setShowPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) const navigate = useNavigate() // ? FRAPPE LOGIN HOOK @@ -255,16 +256,16 @@ const SignUp = () => {
{/* Eye Icon */} setShowPassword((prev) => !prev)} + onClick={() => setShowConfirmPassword((prev) => !prev)} > - {showPassword ? : } + {showConfirmPassword ? : }
From bb5a07905ba045dec2a309a4fa65723bb860653b Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 15:51:30 +0530 Subject: [PATCH 03/10] [Issue-40]: Remove the app_logo_url from the hooks for stop auto replace the logo of frappe and erpnext --- quickdo/hooks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quickdo/hooks.py b/quickdo/hooks.py index 6be8ec2..263d3bd 100644 --- a/quickdo/hooks.py +++ b/quickdo/hooks.py @@ -7,9 +7,11 @@ app_icon = "fa fa-th" app_color = "#e74c3c" source_link = "https://github.com/karanmistry007/QuickDo.git" -app_logo_url = "/assets/quickdo/logo.png" app_home = "/app/quickdos" +# ? IT ADDS THE DEFAULT LOGO AS QUICKDO LOGO +# app_logo_url = "/assets/quickdo/logo.png" + # Apps # ------------------ From 0d5a981f4f576587aeb4b7fbd496db1a9f0189f8 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 15:57:43 +0530 Subject: [PATCH 04/10] [Issue-45]: Check permission for QuickDo, only show the app if QuickDo User role is assigned --- quickdo/api.py | 15 +++++++++++++++ quickdo/hooks.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/quickdo/api.py b/quickdo/api.py index 5beed32..adf909a 100644 --- a/quickdo/api.py +++ b/quickdo/api.py @@ -699,3 +699,18 @@ def register_user(full_name, email, password, redirect_to="/quickdo"): "success": True, "message": f"User has been registered successfully {full_name}", } + + +# ! api/method/quickdo.api.has_app_permission +# ? CHECK IF THE USER HAS PERMISSION TO ACCESS QUICKDO APP +def has_app_permission(): + """Check if the user has permission to access the app.""" + if frappe.session.user == "Administrator": + return True + + roles = frappe.get_roles() + quickdo_roles = ["QuickDo User", "System Manager"] + if any(role in roles for role in quickdo_roles): + return True + + return False diff --git a/quickdo/hooks.py b/quickdo/hooks.py index 263d3bd..bf34280 100644 --- a/quickdo/hooks.py +++ b/quickdo/hooks.py @@ -25,7 +25,7 @@ "logo": "/assets/quickdo/favicon.png", "title": "QuickDo", "route": "/app/quickdos", - # "has_permission": "quickdo.api.permission.has_app_permission", + "has_permission": "quickdo.api.has_app_permission", } ] From be7869dd6c9a3fb2c70b19fed0427bae8d4f7284 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 16:03:47 +0530 Subject: [PATCH 05/10] [Issue-41]: Make the my settings rediret to the user settings /me page --- frontend/src/components/layout/navbar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/layout/navbar.tsx b/frontend/src/components/layout/navbar.tsx index f75d604..7d92bb9 100644 --- a/frontend/src/components/layout/navbar.tsx +++ b/frontend/src/components/layout/navbar.tsx @@ -18,8 +18,10 @@ import { const userProfileItems: UserProfileItem[] = [ { name: "Frappe Dashboard", link: "/app/quickdos" }, { name: "Apps", link: "/apps/" }, - { name: "My Profile", link: "/app/user-profile" }, - { name: "My Settings", link: "/app/user" }, + // TODO : CREATE A USER PROFILE PAGE IN REACT + { name: "My Profile", link: "/me" }, + // TODO : CREATE A USER SETTINGS PAGE IN REACT WITH BACKEND + // { name: "My Settings", link: "/app/user" }, { name: "Logout", link: "/quickdo/auth/logout" }, ]; From 647bd1e77545d165df80d3097750cd14fc9aa371 Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Sun, 20 Jul 2025 18:14:51 +0530 Subject: [PATCH 06/10] [Issue-36]: Make the Due Date filter workable and Many more changes to the struture as making the filters and sort onjet more dynamic --- .../components/charts/StatusDonutChart.tsx | 1 - frontend/src/components/layout/filters.tsx | 254 ++++++++++---- .../src/components/layout/quickdo-item.tsx | 8 +- frontend/src/components/layout/sort.tsx | 16 +- frontend/src/pages/quickdo/group-by-view.tsx | 70 +--- frontend/src/pages/quickdo/inbox-view.tsx | 74 +---- frontend/src/pages/quickdo/my-day-view.tsx | 199 ++++------- frontend/src/utils/filter-utils.ts | 310 ++++++++++++++++++ 8 files changed, 605 insertions(+), 327 deletions(-) create mode 100644 frontend/src/utils/filter-utils.ts diff --git a/frontend/src/components/charts/StatusDonutChart.tsx b/frontend/src/components/charts/StatusDonutChart.tsx index fe431de..b53c8dd 100644 --- a/frontend/src/components/charts/StatusDonutChart.tsx +++ b/frontend/src/components/charts/StatusDonutChart.tsx @@ -39,7 +39,6 @@ const chartConfig = { } satisfies ChartConfig export const StatusDonutChart = ({ data }: any) => { - console.log(data) const totalQuickdos = data.reduce((acc: any, curr: any) => acc + curr.quickdo, 0) return ( diff --git a/frontend/src/components/layout/filters.tsx b/frontend/src/components/layout/filters.tsx index 3a33755..fc7b136 100644 --- a/frontend/src/components/layout/filters.tsx +++ b/frontend/src/components/layout/filters.tsx @@ -1,9 +1,13 @@ -import React from "react"; +"use client" + +import type React from "react" +import { useState } from "react" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, + DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, @@ -11,113 +15,237 @@ import { DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, - DropdownMenuItem } from "@/components/ui/dropdown-menu" -import { IoClose } from "react-icons/io5"; -import { Button } from "../ui/button"; +import { Calendar } from "@/components/ui/calendar" +import { X, Check } from "lucide-react" +import { Button } from "@/components/ui/button" + +// ? IMPORT ALL UTILITY FUNCTIONS FROM SINGLE FILE +import { + getSelectedDate, + getFormattedDate, + isSameDay, + isDateFilterActive, + isStatusFilterActive, + isCategoryFilterActive, + isImportanceFilterActive, + isReminderFilterActive, + type useStatusFiltersItems, +} from "@/utils/filter-utils" interface FiltersProps { - filters: any[]; - useStatusFilterData: any[]; - getAllCategories: any[]; - defaultFilter: any[]; - handleFilters: (type: string, value: string, label?: string) => void; - handleClearFilters: () => void; - setRefreshState: (state: boolean) => void; + filters: any[] + useStatusFilterData: useStatusFiltersItems[] + getAllCategories: any[] + defaultFilter?: any[] + handleFilters: (key: string, value: string, filter_type?: "single" | "multi", child_table?: string) => void + handleClearFilters: () => void + setRefreshState: (state: boolean) => void } -const Filters: React.FC = ({ filters, useStatusFilterData, getAllCategories, handleFilters, handleClearFilters, setRefreshState, defaultFilter = null }) => { +const Filters: React.FC = ({ + filters, + useStatusFilterData, + getAllCategories, + handleFilters, + handleClearFilters, + setRefreshState, + defaultFilter = [], +}) => { + const [dropdownOpen, setDropdownOpen] = useState(false) + + // ? HANDLE DATE SELECTION - SINGLE DATE ONLY WITH PROPER HIGHLIGHTING + const handleDateSelect = (selectedDate: Date | undefined) => { + const currentSelectedDate = getSelectedDate(filters) + + // ? If clicking on the same date that's already selected, remove the filter + if (selectedDate && currentSelectedDate && isSameDay(selectedDate, currentSelectedDate)) { + handleFilters("date", "", "single") + setDropdownOpen(false) + return + } + + // ? If no date selected, remove filter + if (!selectedDate) { + handleFilters("date", "", "single") + setDropdownOpen(false) + return + } + + // ? Apply filter for the selected date only + const formattedDate = getFormattedDate(selectedDate) + handleFilters("date", formattedDate, "single") + + // ? Close dropdown after selection + setTimeout(() => { + setDropdownOpen(false) + }, 150) + } + + // ? HANDLE IMPORTANCE FILTER WITH DROPDOWN CLOSE + const handleImportanceFilter = () => { + handleFilters("is_important", "1", "single") + setTimeout(() => { + setDropdownOpen(false) + }, 150) + } + + // ? HANDLE REMINDER FILTER WITH DROPDOWN CLOSE + const handleReminderFilter = () => { + handleFilters("send_reminder", "1", "single") + setTimeout(() => { + setDropdownOpen(false) + }, 150) + } return (
- + - + Filters - - {/* STATUS FILTERS */} + {/* // ? STATUS FILTERS - MULTI-VALUE WITH RIGHT-SIDE CHECKBOX */} - Status + +
+ Status +
+ {isStatusFilterActive(filters) && } +
+
+
- {useStatusFilterData.length > 0 && useStatusFilterData.map((data, index) => ( - filter[0] === "status" && filter[2]?.includes(data.name))} - onCheckedChange={() => handleFilters("status", data.name)} - > - {data.name} - - ))} + {useStatusFilterData.length > 0 && + useStatusFilterData.map((data, index) => ( + filter[0] === "status" && filter[2]?.includes(data.name))} + onCheckedChange={() => handleFilters("status", data.name, "multi")} + > + {data.name} + + ))}
- {/* CATEGORY FILTERS */} + {/* // ? CATEGORY FILTERS - MULTI-VALUE WITH RIGHT-SIDE CHECKBOX */} - - Category + +
+ Category +
+ {isCategoryFilterActive(filters) && } +
+
- {getAllCategories.length > 0 && getAllCategories.map((data, index) => ( - filter[0] === "QuickDo Categories" && filter[3]?.includes(data.category))} - onCheckedChange={() => handleFilters("category", data.category, "QuickDo Categories")} - > - {data.category} - - ))} + {getAllCategories.length > 0 && + getAllCategories.map((data, index) => ( + filter[0] === "QuickDo Categories" && filter[3]?.includes(data.category), + )} + onCheckedChange={() => handleFilters("category", data.category, "multi", "QuickDo Categories")} + > + {data.category} + + ))}
- {/* DUE DATE FILTER */} - - Due Date - + {/* // ? DUE DATE FILTER - SINGLE DATE SELECTION WITH RIGHT-SIDE CHECKBOX */} + + +
+ Due Date +
+ {isDateFilterActive(filters) && } +
+
+
+ + + + + +
- {/* IMPORTANCE FILTER */} - filter[0] === "is_important" && filter[2]?.includes("1"))} - onCheckedChange={() => handleFilters("is_important", "1")} + {/* // ? IMPORTANCE FILTER - SINGLE-VALUE WITH RIGHT-SIDE CHECKBOX AND DROPDOWN CLOSE */} + - Importance - + Importance +
+ {isImportanceFilterActive(filters) && } +
+ - {/* REMINDER FILTER */} - filter[0] === "send_reminder" && filter[2]?.includes("1"))} - onCheckedChange={() => handleFilters("send_reminder", "1")} + {/* // ? REMINDER FILTER - SINGLE-VALUE WITH RIGHT-SIDE CHECKBOX AND DROPDOWN CLOSE */} + - Reminder - + Reminder +
+ {isReminderFilterActive(filters) && } +
+
- {/* CLEAR FILTERS */} + {/* // ? CLEAR FILTERS */}
- ); -}; + ) +} -export default Filters; +export default Filters diff --git a/frontend/src/components/layout/quickdo-item.tsx b/frontend/src/components/layout/quickdo-item.tsx index cbff222..2323e9b 100644 --- a/frontend/src/components/layout/quickdo-item.tsx +++ b/frontend/src/components/layout/quickdo-item.tsx @@ -29,6 +29,7 @@ const QuickDoItem = (props: ListItemProps) => { const [categories, setCategories] = useState(props.todoData.categories); const [showCategories, setShowCategories] = useState(false); const [allCategories, setAllCategories] = useState(props.allCategories); + const [datePopupOpen, setDatePopupOpen] = useState(false); //? STATUS HANDLER @@ -170,7 +171,7 @@ const QuickDoItem = (props: ListItemProps) => { {/* DUE DATE */}
- +
- {/* STATUS TODO */} + {/* ? TODO STATUS CYCLE */}
{ - handleStatus(status); - setIsDataChanged(true); - }} + onClick={handleStatus} title="Status" > - -
-

Status QuickDo

-
+

Status QuickDo

- {/* TODO IMPORTANCE */} + {/* ? TODO IMPORTANCE BUTTON */}
{ - setIsImportant(!isImportant); - setIsDataChanged(true); - }} + onClick={handleImportance} title="Importance" >

Importance

- {/* TODO REMINDER */} + {/* ? TODO REMINDER BUTTON */}
{ - setSendReminder(!sendReminder); - setIsDataChanged(true); - }} + onClick={handleReminder} title="Reminder" >

Reminder

- {/* TODO CATEGORIES */} + {/* ? TODO CATEGORIES SECTION */}
- - {/* CATEGORIES TOOLBAR */}
{ - setShowCategories(!showCategories); - }} + className="categories-toolbar pb-2 flex justify-between border-b border-gray-200 " + onClick={() => setShowCategories(!showCategories)} title="Categories" > - - {/* CATEGORIES ICON */}

Categories

- - {/* CLEAR CATEGORIES */} -
- - {/* CATEGORIES ITEMS DROPDOWN*/} -
+
{allCategories && allCategories.map((data, index) => (
{ id="ToDo" value={description} onChange={(e) => { - setDescription(e.target.value); + handleDescriptionChange(e.target.value) }} onKeyUp={(e) => { if (e.key === "Enter") { - e.currentTarget.blur(); + e.currentTarget.blur() } }} - onBlur={() => { - description.trim() !== props.todoData.description.trim() && handleSaveToDo(); - }} + onBlur={handleDescriptionBlur} />
- {/* EDIT AND CLOSE TASK */} + {/* ? END EDIT AND CLOSE TASK */} - {/* DUE DATE */} + {/* ? DUE DATE */}
- - { - handleSetDate(e); - setDatePopupOpen(false); + handleSetDate(e) + setDatePopupOpen(false) }} initialFocus />
- {/* END DUE DATE */} + {/* ? END DUE DATE */} - {/* IMPORTANCE */} + {/* ? IMPORTANCE */}
- {/* END IMPORTANCE */} + {/* ? END IMPORTANCE */} - {/* CATEGORIES */} + {/* ? CATEGORIES */}
- - {/* CATEGORIES MULTISELECT */} { categories={categories} handleCategories={handleCategories} /> - {/* END CATEGORIES MULTISELECT */} -
- {/* END CATEGORIES */} - - {/* MORE */} - - {/* END MORE */} - + {/* ? END CATEGORIES */} + + {/* ? MORE / DRAWER TRIGGER BUTTON */} + + + {/* ? QUICKDODRAWER - CONDITIONALLY RENDERED */} + {isDrawerOpen && ( + + )}
- {/* END LIST ITEMS */} + {/* ? END LIST ITEMS */} - ); -}; + ) +} -export default QuickDoItem; +export default QuickDoItem diff --git a/frontend/src/pages/quickdo/calendar-view.tsx b/frontend/src/pages/quickdo/calendar-view.tsx index 03a53f1..5b6c7bc 100644 --- a/frontend/src/pages/quickdo/calendar-view.tsx +++ b/frontend/src/pages/quickdo/calendar-view.tsx @@ -1,45 +1,43 @@ -import { CalendarEvents, DashboardProps, useAllQuickDoData, useAllCategories } from "@/types/Common"; -import { Calendar, dayjsLocalizer } from "react-big-calendar"; -import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop"; -import dayjs from "dayjs"; -import "react-big-calendar/lib/addons/dragAndDrop/styles.css"; -import "react-big-calendar/lib/css/react-big-calendar.css"; -import { useEffect, useState } from "react"; -import axios from "axios"; -import { toast } from 'sonner' -import QuickDoDrawer from "@/components/layout/quickdo-drawer"; -import Navbar from "@/components/layout/navbar"; -import Sidebar from "@/components/layout/sidebar"; - -const localizer = dayjsLocalizer(dayjs); -const DnDCalendar = withDragAndDrop(Calendar); +"use client" + +import type { CalendarEvents, DashboardProps, useAllQuickDoData, useAllCategories } from "@/types/Common" +import { Calendar, dayjsLocalizer } from "react-big-calendar" +import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop" +import dayjs from "dayjs" +import "react-big-calendar/lib/addons/dragAndDrop/styles.css" +import "react-big-calendar/lib/css/react-big-calendar.css" +import { useEffect, useState } from "react" +import axios from "axios" +import { toast } from "sonner" +import QuickDoDrawer from "@/components/layout/quickdo-drawer" +import Navbar from "@/components/layout/navbar" +import Sidebar from "@/components/layout/sidebar" + +const localizer = dayjsLocalizer(dayjs) +const DnDCalendar = withDragAndDrop(Calendar) const CalendarView = (props: DashboardProps) => { - - //? HOOKS - const [events, setEvents] = useState([]); - const BASE_URL = import.meta.env.VITE_BASE_URL || window.location.origin; - const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null; - - - // TODO WILL REPLACE WITH THE SOCKET - //? UPDATE STATE - const [refreshState, setRefreshState] = useState(true); + // ? HOOKS + const [events, setEvents] = useState([]) + const BASE_URL = import.meta.env.VITE_BASE_URL || window.location.origin + const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN || null + + // ? TODO WILL REPLACE WITH THE SOCKET + // ? UPDATE STATE + const [refreshState, setRefreshState] = useState(true) const handleRefreshState = (state: boolean) => { - setRefreshState(state); - }; - - //? CATEGORY API DATA - const [getAllCategories, setGetAllCategories] = useState([]); - + setRefreshState(state) + } - //? TODO DATA - const [todoData, setTodoData] = useState(); + // ? CATEGORY API DATA + const [getAllCategories, setGetAllCategories] = useState([]) + // ? TODO DATA + const [todoData, setTodoData] = useState() // ? EXPLICITLY SET TO UNDEFINED - //? CATEGORIES LIST API + // ? CATEGORIES LIST API useEffect(() => { - //? FETCH CATEGORY LIST API FUNCTION + // ? FETCH CATEGORY LIST API FUNCTION const fetchAPI = async () => { try { const response = await axios.get( @@ -48,33 +46,29 @@ const CalendarView = (props: DashboardProps) => { headers: { Authorization: AUTH_TOKEN, }, - } - ); - - //? IF THE API RETURNS DATA + }, + ) + // ? IF THE API RETURNS DATA if (response.data.message) { - //? SET ALL CATEGORIES STATE - setGetAllCategories(response.data.message); - - //? REFRESH STATE - handleRefreshState(false); + // ? SET ALL CATEGORIES STATE + setGetAllCategories(response.data.message) + // ? REFRESH STATE + handleRefreshState(false) } } catch (error) { - console.log(error); - toast.error("There was a problem while loading Categories!"); + console.log(error) + toast.error("There was a problem while loading Categories!") } - }; - - //? CALL THE FETCH API FUNCTION + } + // ? CALL THE FETCH API FUNCTION if (refreshState) { - fetchAPI(); + fetchAPI() } - }, [refreshState]); + }, [refreshState, BASE_URL, AUTH_TOKEN]) - //? SAVE TODO HANDLER + // ? SAVE TODO HANDLER const handleSaveToDo = (data: useAllQuickDoData) => { - - //? MAP THE OBJECT TO FRAPPE'S DATA + // ? MAP THE OBJECT TO FRAPPE'S DATA const finalData: useAllQuickDoData = { name: data?.name, owner: data?.owner, @@ -82,15 +76,14 @@ const CalendarView = (props: DashboardProps) => { modified: data?.modified, modified_by: data?.modified_by, doctype: "QuickDo", - status: data.status ? "Completed" : "Open", + status: data.status === "Completed" ? "Completed" : "Open", is_important: data.is_important, send_reminder: data.send_reminder, description: data.description, date: data.date, categories: data.categories, - }; - - //? FETCH SAVE TODO API FUNCTION + } + // ? FETCH SAVE TODO API FUNCTION const fetchAPI = async (finalData: useAllQuickDoData) => { try { const response = await axios.post( @@ -103,39 +96,34 @@ const CalendarView = (props: DashboardProps) => { headers: { Authorization: AUTH_TOKEN, }, - } - ); - - //? REFRESH THE STATE + }, + ) + // ? REFRESH THE STATE if (response.status === 200) { - handleRefreshState(true); - + handleRefreshState(true) // ? IF NEW CREATED if (!finalData.name) { - toast.success("The QuickDo has been created!"); + toast.success("The QuickDo has been created!") } - // ? IF UPDATED else { - toast.success("The QuickDo has been updated!"); + toast.success("The QuickDo has been updated!") } - - setTodoData(undefined); + setTodoData(undefined) // ? CLEAR TODODATA TO CLOSE THE DRAWER } } catch (error) { - console.log(error); - toast.error("There was a problem while saving QuickDo!"); - setTodoData(undefined); + console.log(error) + toast.error("There was a problem while saving QuickDo!") + setTodoData(undefined) // ? CLEAR TODODATA EVEN ON ERROR TO CLOSE THE DRAWER } - }; - - //? FETCH POST API CALL - fetchAPI(finalData); - }; + } + // ? FETCH POST API CALL + fetchAPI(finalData) + } - //? DELETE TODO HANDLER + // ? DELETE TODO HANDLER const handleDeleteTodo = (data: string) => { - //? FETCH DELETE TODO API FUNCTION + // ? FETCH DELETE TODO API FUNCTION const fetchAPI = async (data: string) => { try { const response = await axios.post( @@ -148,27 +136,26 @@ const CalendarView = (props: DashboardProps) => { headers: { Authorization: AUTH_TOKEN, }, - } - ); - - //? REFRESH THE STATE + }, + ) + // ? REFRESH THE STATE if (response.status === 200) { - handleRefreshState(true); - toast.success("The QuickDo has been deleted!"); + handleRefreshState(true) + toast.success("The QuickDo has been deleted!") + setTodoData(undefined) // ? CLEAR TODODATA TO CLOSE THE DRAWER } } catch (error) { - console.log(error); - toast.error("There was a problem while deleting QuickDo!"); + console.log(error) + toast.error("There was a problem while deleting QuickDo!") } - }; - - //? CALL FETCH API - fetchAPI(data); - }; + } + // ? CALL FETCH API + fetchAPI(data) + } - //? DELETE TODO HANDLER + // ? GET TODO HANDLER const handleGetTodo = (data: string) => { - //? FETCH DELETE TODO API FUNCTION + // ? FETCH GET TODO API FUNCTION const fetchAPI = async (data: string) => { try { const response = await axios.post( @@ -181,29 +168,19 @@ const CalendarView = (props: DashboardProps) => { headers: { Authorization: AUTH_TOKEN, }, - } - ); - - //? REFRESH THE STATE + }, + ) + // ? REFRESH THE STATE if (response.data.message) { - handleRefreshState(true); - - const todoDoc = response.data.message; - - //? PARSE THE TODO HTML - const parser = new DOMParser(); - const description_doc = parser.parseFromString( - todoDoc.description, - "text/html" - ); - const description: any = description_doc.querySelector( - ".ql-editor.read-mode p" - )?.textContent - ? description_doc.querySelector(".ql-editor.read-mode p") - ?.textContent - : todoDoc.description; - - //? UPDATE THE FINAL DATA + handleRefreshState(true) + const todoDoc = response.data.message + // ? PARSE THE TODO HTML + const parser = new DOMParser() + const description_doc = parser.parseFromString(todoDoc.description, "text/html") + const description: any = description_doc.querySelector(".ql-editor.read-mode p")?.textContent + ? description_doc.querySelector(".ql-editor.read-mode p")?.textContent + : todoDoc.description + // ? UPDATE THE FINAL DATA const finalData: useAllQuickDoData = { name: todoDoc.name, owner: todoDoc.owner, @@ -216,19 +193,17 @@ const CalendarView = (props: DashboardProps) => { description: description || "", date: todoDoc.date || "", categories: todoDoc.categories || [], - }; - - setTodoData(finalData); + } + setTodoData(finalData) } } catch (error) { - console.log(error); - toast.error("There was a problem while getting QuickDo!"); + console.log(error) + toast.error("There was a problem while getting QuickDo!") } - }; - - //? CALL FETCH API - fetchAPI(data); - }; + } + // ? CALL FETCH API + fetchAPI(data) + } // ? UPDATE QUICKDO FUNCTION const updateQuickDo = async (name: string, value: string) => { @@ -239,29 +214,25 @@ const CalendarView = (props: DashboardProps) => { doctype: "QuickDo", name: name, fieldname: "date", - value: value + value: value, }, { headers: { Authorization: AUTH_TOKEN, }, - } - ); - - //? IF QUICKDO UPDATED SUCCESSFULLY + }, + ) + // ? IF QUICKDO UPDATED SUCCESSFULLY if (response.status === 200) { - toast.success("The due date of the QuickDo has been updated!"); + toast.success("The due date of the QuickDo has been updated!") } } catch (error) { - console.log(error); - toast.error("There was a problem while updating QuickDo!"); + console.log(error) + toast.error("There was a problem while updating QuickDo!") } - }; - + } // ? LOAD QUICKDO DATA API FUNCTION - - useEffect(() => { const loadQuickDoDataAPI = async () => { try { @@ -269,119 +240,93 @@ const CalendarView = (props: DashboardProps) => { headers: { Authorization: AUTH_TOKEN, }, - }); - - //? IF NO ERROR + }) + // ? IF NO ERROR if (response.data.message) { - // ? DEFINE VARIABLES - const QuickDoData = response.data.message; - + const QuickDoData = response.data.message QuickDoData.map((item: any) => { - item.start = dateTimeConverter("start", item.start); - item.end = dateTimeConverter("end", item.end); - }); - + item.start = dateTimeConverter("start", item.start) + item.end = dateTimeConverter("end", item.end) + }) // ? SET EVENT DATA - setEvents(QuickDoData); + setEvents(QuickDoData) } - } - catch (error) { - console.log(error); - toast.error("There was a problem while loading QuickDos!"); + } catch (error) { + console.log(error) + toast.error("There was a problem while loading QuickDos!") } } - - loadQuickDoDataAPI(); - }, [refreshState]) - + loadQuickDoDataAPI() + }, [refreshState, BASE_URL, AUTH_TOKEN]) // ? DATE TIME CONVERTER - function dateTimeConverter(field: string = "start", date: string = "00-00-00") { + function dateTimeConverter(field = "start", date = "00-00-00") { if (field === "start") { return new Date(`${date} 00:00:00`) - } - else if (field === "end") { + } else if (field === "end") { return new Date(`${date} 23:59:59`) } } - //? SELECT EVENT HANDLER + // ? SELECT EVENT HANDLER const handleSelectEvent = (data: any) => { - handleGetTodo(data.name); + handleGetTodo(data.name) } - //? DROP EVENT HANDLER + // ? DROP EVENT HANDLER const handleDropEvent = (data: any) => { - - //? CHECK IF THE EVENT IS ONE DAY EVENT MAKE THE END DATE PRIMARY - const isSameDay = dayjs(data.start).isSame(dayjs(data.end), "day"); - - //? IF THE EVENT IS IN THE SAME DAY + // ? CHECK IF THE EVENT IS ONE DAY EVENT MAKE THE END DATE PRIMARY + const isSameDay = dayjs(data.start).isSame(dayjs(data.end), "day") + // ? IF THE EVENT IS IN THE SAME DAY if (isSameDay) { - // ? UPDATE THE EVENTS DATA setEvents((prevEvents) => - prevEvents.map((ev) => - ev.title === data.event.title ? { ...ev, start: data.start, end: data.end } : ev - ) - ); - + prevEvents.map((ev) => (ev.title === data.event.title ? { ...ev, start: data.start, end: data.end } : ev)), + ) // ? UPDATE THE DATA API - updateQuickDo(data.event.name, data.end.toISOString().split('T')[0]); + updateQuickDo(data.event.name, data.end.toISOString().split("T")[0]) } - // ? IF THE EVENT IS NOT IN SAME DAY MAKE IT AS START DATE ONE DAY EVENT else { - const startDate = dateTimeConverter("start", data.start.toISOString().split('T')[0]); - const endDate = dateTimeConverter("end", data.start.toISOString().split('T')[0]); - + const startDate = dateTimeConverter("start", data.start.toISOString().split("T")[0]) + const endDate = dateTimeConverter("end", data.start.toISOString().split("T")[0]) // ? UPDATE THE EVENTS DATA // @ts-ignore setEvents((prevEvents) => - prevEvents.map((ev) => - ev.title === data.event.title ? { ...ev, start: startDate, end: endDate } : ev - ) - ); - + prevEvents.map((ev) => (ev.title === data.event.title ? { ...ev, start: startDate, end: endDate } : ev)), + ) // ? UPDATE THE DATA API - updateQuickDo(data.event.name, data.start.toISOString().split('T')[0]); + updateQuickDo(data.event.name, data.start.toISOString().split("T")[0]) } - }; - - + } // ? EVENT COLOR STYLING USING THE EVENT PROP GETTER const eventPropGetter = (event: CalendarEvents) => { - // ? DEFINE VARIABLES - let backgroundColor = ""; - + let backgroundColor = "" // ? SET COLORS AS PER NEEDS if (event.color) { - backgroundColor = event.color; + backgroundColor = event.color } else if (event.is_important) { - backgroundColor = "#CB2929"; + backgroundColor = "#CB2929" } else if (event.sent_reminder) { - backgroundColor = "#EC864B"; + backgroundColor = "#EC864B" } else { - backgroundColor = "#3c50e0"; + backgroundColor = "#3c50e0" } - return { style: { backgroundColor }, - }; - }; + } + } + return ( <> - - {/* NAVBAR */} + {/* ? NAVBAR */} - - {/* SIDEBAR */} + {/* ? SIDEBAR */} - - {/* DASHBOARD CONTAINER */} + {/* ? DASHBOARD CONTAINER */}
{ onSelectEvent={handleSelectEvent} resizable={false} /> - {todoData && + {todoData && ( setTodoData(undefined)} // ? CALLBACK TO CLOSE THE DRAWER /> - } - + )}
- {/* END DASHBOARD CONTAINER */} + {/* ? END DASHBOARD CONTAINER */} - ); -}; + ) +} -export default CalendarView; +export default CalendarView diff --git a/frontend/src/types/Common.ts b/frontend/src/types/Common.ts index 0856bfc..b6064ae 100644 --- a/frontend/src/types/Common.ts +++ b/frontend/src/types/Common.ts @@ -158,6 +158,7 @@ export type DrawerProps = { handleSaveToDo: (data: useAllQuickDoData) => void; handleDeleteTodo: (data: string) => void; autoOpenDrawer?: boolean; + onCloseRequest?: () => void; }; //? CONFIRM BOX PROPS From d638daa187c47c48154e9cc5fcd0d393c68c006c Mon Sep 17 00:00:00 2001 From: karan-sanskar Date: Mon, 21 Jul 2025 01:45:32 +0530 Subject: [PATCH 10/10] [v1.1.0] --- quickdo/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickdo/__init__.py b/quickdo/__init__.py index 5c4105c..6849410 100644 --- a/quickdo/__init__.py +++ b/quickdo/__init__.py @@ -1 +1 @@ -__version__ = "1.0.1" +__version__ = "1.1.0"