Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ab476d2
[Issue-26]: Update roles and permission 'QuickDo User' can only see h…
karan-sanskar Jul 20, 2025
0ca691f
Merge pull request #43 from karanmistry007/karan
karanmistry007 Jul 20, 2025
3c85f9d
[Issue-27]: Sign Up page show password button shows both the fields
karan-sanskar Jul 20, 2025
fb30aad
Merge pull request #44 from karanmistry007/karan
karanmistry007 Jul 20, 2025
bb5a079
[Issue-40]: Remove the app_logo_url from the hooks for stop auto repl…
karan-sanskar Jul 20, 2025
4382f43
Merge pull request #46 from karanmistry007/karan
karanmistry007 Jul 20, 2025
0d5a981
[Issue-45]: Check permission for QuickDo, only show the app if QuickD…
karan-sanskar Jul 20, 2025
4f19e39
Merge pull request #47 from karanmistry007/karan
karanmistry007 Jul 20, 2025
be7869d
[Issue-41]: Make the my settings rediret to the user settings /me page
karan-sanskar Jul 20, 2025
2447d30
Merge pull request #48 from karanmistry007/karan
karanmistry007 Jul 20, 2025
647bd1e
[Issue-36]: Make the Due Date filter workable and Many more changes t…
karan-sanskar Jul 20, 2025
97e0628
Merge pull request #49 from karanmistry007/karan
karanmistry007 Jul 20, 2025
9532d89
[Fix]: Fixed the hover effect on the datepicker inside the filters dr…
karan-sanskar Jul 20, 2025
ad57aee
Merge pull request #50 from karanmistry007/karan
karanmistry007 Jul 20, 2025
6508871
[Fix]: Updated filters for my day
karan-sanskar Jul 20, 2025
63e3298
[Feat]: New feature to auto close drawer on delete on all the view an…
karan-sanskar Jul 20, 2025
7b23cac
Merge pull request #51 from karanmistry007/karan
karanmistry007 Jul 20, 2025
d638daa
[v1.1.0]
karan-sanskar Jul 20, 2025
f3dc538
Merge pull request #52 from karanmistry007/karan
karanmistry007 Jul 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/src/components/charts/StatusDonutChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
247 changes: 184 additions & 63 deletions frontend/src/components/layout/filters.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,244 @@
import React from "react";
"use client"

import type React from "react"
import { useState } from "react"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuSub,
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<FiltersProps> = ({ filters, useStatusFilterData, getAllCategories, handleFilters, handleClearFilters, setRefreshState, defaultFilter = null }) => {
const Filters: React.FC<FiltersProps> = ({
filters,
useStatusFilterData,
getAllCategories,
handleFilters,
handleClearFilters,
setRefreshState,
defaultFilter = [],
}) => {
const [dropdownOpen, setDropdownOpen] = useState<boolean>(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
setDropdownOpen(false)
}

// ? HANDLE IMPORTANCE FILTER WITH DROPDOWN CLOSE
const handleImportanceFilter = () => {
handleFilters("is_important", "1", "single")
setDropdownOpen(false)
}

// ? HANDLE REMINDER FILTER WITH DROPDOWN CLOSE
const handleReminderFilter = () => {
handleFilters("send_reminder", "1", "single")
setDropdownOpen(false)
}

return (
<div className="filters-quickdo flex border-neutral-200 border rounded-md w-fit shadow-sm sm:order-2">
<DropdownMenu>
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="font-normal">Filters</Button>
<Button variant="outline" className="font-normal bg-transparent">
Filters
{/* // ? ALWAYS SHOW COUNTER BADGE - EMPTY SPACE WHEN NO FILTERS */}
<span
className={`ml-2 rounded-full px-2 py-0.5 text-xs min-w-[20px] text-center ${filters.length > 0 ? "bg-primary text-primary-foreground" : "bg-transparent text-transparent"
}`}
>
{filters.length > 0 ? filters.length : "0"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel>Filters</DropdownMenuLabel>
<DropdownMenuSeparator />

<DropdownMenuGroup>
{/* STATUS FILTERS */}
{/* // ? STATUS FILTERS - MULTI-VALUE WITH RIGHT-SIDE CHECKBOX */}
<DropdownMenuSub>
<DropdownMenuSubTrigger>Status</DropdownMenuSubTrigger>
<DropdownMenuSubTrigger className={isStatusFilterActive(filters) ? "bg-accent" : ""}>
<div className="flex items-center justify-between w-full">
<span>Status</span>
<div className="flex items-center gap-1">
{isStatusFilterActive(filters) && <Check className="h-4 w-4 text-primary" />}
</div>
</div>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
{useStatusFilterData.length > 0 && useStatusFilterData.map((data, index) => (
<DropdownMenuCheckboxItem
key={index}
checked={filters.some(filter => filter[0] === "status" && filter[2]?.includes(data.name))}
onCheckedChange={() => handleFilters("status", data.name)}
>
{data.name}
</DropdownMenuCheckboxItem>
))}
{useStatusFilterData.length > 0 &&
useStatusFilterData.map((data, index) => (
<DropdownMenuCheckboxItem
key={index}
checked={filters.some((filter) => filter[0] === "status" && filter[2]?.includes(data.name))}
onCheckedChange={() => handleFilters("status", data.name, "multi")}
>
{data.name}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>

{/* CATEGORY FILTERS */}
{/* // ? CATEGORY FILTERS - MULTI-VALUE WITH RIGHT-SIDE CHECKBOX */}
<DropdownMenuSub>
<DropdownMenuSubTrigger>
Category
<DropdownMenuSubTrigger className={isCategoryFilterActive(filters) ? "bg-accent" : ""}>
<div className="flex items-center justify-between w-full">
<span>Category</span>
<div className="flex items-center gap-1">
{isCategoryFilterActive(filters) && <Check className="h-4 w-4 text-primary" />}
</div>
</div>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent className="categories-items max-h-[240px] w-[200px] overflow-x-hidden whitespace-nowrap text-ellipsis overflow-y-auto">
{getAllCategories.length > 0 && getAllCategories.map((data, index) => (
<DropdownMenuCheckboxItem
key={index}
title={data.category}
checked={filters.some(filter => filter[0] === "QuickDo Categories" && filter[3]?.includes(data.category))}
onCheckedChange={() => handleFilters("category", data.category, "QuickDo Categories")}
>
{data.category}
</DropdownMenuCheckboxItem>
))}
{getAllCategories.length > 0 &&
getAllCategories.map((data, index) => (
<DropdownMenuCheckboxItem
key={index}
title={data.category}
checked={filters.some(
(filter) => filter[0] === "QuickDo Categories" && filter[3]?.includes(data.category),
)}
onCheckedChange={() => handleFilters("category", data.category, "multi", "QuickDo Categories")}
>
{data.category}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>

{/* DUE DATE FILTER */}
<DropdownMenuItem>
Due Date
</DropdownMenuItem>
{/* // ? DUE DATE FILTER - SINGLE DATE SELECTION WITH RIGHT-SIDE CHECKBOX */}
<DropdownMenuSub>
<DropdownMenuSubTrigger className={isDateFilterActive(filters) ? "bg-accent" : ""}>
<div className="flex items-center justify-between w-full">
<span>Due Date</span>
<div className="flex items-center gap-1">
{isDateFilterActive(filters) && <Check className="h-4 w-4 text-primary" />}
</div>
</div>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<Calendar
mode="single"
selected={getSelectedDate(filters)}
onSelect={handleDateSelect}
initialFocus
/>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>

<DropdownMenuSeparator />

<DropdownMenuGroup>
{/* IMPORTANCE FILTER */}
<DropdownMenuCheckboxItem
checked={filters.some(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 */}
<DropdownMenuItem
className={`flex items-center justify-between w-full ${isImportanceFilterActive(filters) ? "bg-accent" : ""}`}
onClick={handleImportanceFilter}
>
Importance
</DropdownMenuCheckboxItem>
<span>Importance</span>
<div className="flex items-center gap-1">
{isImportanceFilterActive(filters) && <Check className="h-4 w-4 text-primary" />}
</div>
</DropdownMenuItem>

{/* REMINDER FILTER */}
<DropdownMenuCheckboxItem
checked={filters.some(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 */}
<DropdownMenuItem
className={`flex items-center justify-between w-full ${isReminderFilterActive(filters) ? "bg-accent" : ""}`}
onClick={handleReminderFilter}
>
Reminder
</DropdownMenuCheckboxItem>
<span>Reminder</span>
<div className="flex items-center gap-1">
{isReminderFilterActive(filters) && <Check className="h-4 w-4 text-primary" />}
</div>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>

{/* CLEAR FILTERS */}
{/* // ? CLEAR FILTERS */}
<button
className="clear-filters px-3 py-2 text-[20px]"
className="clear-filters px-3 py-2 text-[20px] hover:bg-accent rounded-r-md transition-colors"
onClick={() => {
handleClearFilters();
setRefreshState(true);
handleClearFilters()
setRefreshState(true)
}}
title="Clear Filters"
>
<IoClose title="Clear Filters" className={`text-xl transition-transform duration-300 ${defaultFilter ? filters.length == defaultFilter.length ? "rotate-45" : "rotate-180" : filters.length === 0 ? "rotate-45" : "rotate-180"}`} />
<X
className={`h-5 w-5 transition-transform duration-300 ${defaultFilter
? filters.length === defaultFilter.length
? "rotate-45"
: "rotate-180"
: filters.length === 0
? "rotate-45"
: "rotate-180"
}`}
/>
</button>
</div>
);
};
)
}

export default Filters;
export default Filters
6 changes: 4 additions & 2 deletions frontend/src/components/layout/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
];

Expand Down
Loading