Skip to content

Commit 81ac810

Browse files
authored
Merge pull request #25 from prathamdby/16-sorting-and-multi-select
Add support for multi-select in filters and improve sorting
2 parents a9f20e1 + 3f6635d commit 81ac810

File tree

2 files changed

+64
-28
lines changed

2 files changed

+64
-28
lines changed

components/LeetCodeDashboard.tsx

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
SelectTrigger,
1919
SelectValue,
2020
} from "@/components/ui/select";
21-
import { Check, X, ChevronLeft, ChevronRight } from "lucide-react";
21+
import { Check, X, ChevronLeft, ChevronRight, ChevronDown, ChevronUp } from "lucide-react";
2222
import { Checkbox } from "@/components/ui/checkbox";
2323
import { Button } from "@/components/ui/button";
2424
import { Progress } from "@/components/ui/progress";
@@ -75,14 +75,15 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
7575

7676
const [isClient, setIsClient] = useState(false);
7777
const [searchQuery, setSearchQuery] = useState("");
78-
const [difficultyFilter, setDifficultyFilter] = useState("all");
78+
const [difficultyFilter, setDifficultyFilter] = useState<string[]>([]);
7979
const [selectedCompany] = useState("");
8080
const [checkedItems, setCheckedItems] = useState<{ [key: string]: boolean }>({});
8181
const [currentPage, setCurrentPage] = useState(1);
8282
const [itemsPerPage, setItemsPerPage] = useState(10);
8383
const [goToPage, setGoToPage] = useState("");
8484
const [selectedTopics, setSelectedTopics] = useState<string[]>([]);
85-
const [sortDirection, setSortDirection] = useState<"asc" | "desc" | null>(null);
85+
const [frequencySort, setFrequencySort] = useState<"asc" | "desc" | null>(null);
86+
const [acceptanceSort, setAcceptanceSort] = useState<"asc" | "desc" | null>(null);
8687
const [timeframeFilter, setTimeframeFilter] = useState("all");
8788
const [progressLoading, setProgressLoading] = useState(false);
8889
const { userId } = useAuth();
@@ -236,7 +237,7 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
236237
.some((topic) => topic.trim().includes(word))
237238
);
238239
const matchesDifficulty =
239-
difficultyFilter === "all" || question.Difficulty === difficultyFilter;
240+
difficultyFilter.length === 0 || difficultyFilter.includes(question.Difficulty);
240241
const matchesCompany = !selectedCompany || question.company === selectedCompany;
241242
const matchesTopic =
242243
selectedTopics.length === 0 ||
@@ -254,21 +255,41 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
254255
}, [questions, searchQuery, difficultyFilter, selectedCompany, selectedTopics, timeframeFilter]);
255256

256257
const filteredAndSortedQuestions = useMemo(() => {
257-
let result = filteredQuestions;
258+
let result = [...filteredQuestions];
258259

259-
if (sortDirection) {
260-
result = [...result].sort((a, b) => {
260+
result.sort((a, b) => {
261+
// First sort by frequency if it's set
262+
if (frequencySort) {
261263
const freqA = parseFloat(a["Frequency %"]);
262264
const freqB = parseFloat(b["Frequency %"]);
263-
return sortDirection === "asc" ? freqA - freqB : freqB - freqA;
264-
});
265-
}
265+
const freqResult = frequencySort === "asc" ? freqA - freqB : freqB - freqA;
266+
if (freqResult !== 0) return freqResult;
267+
}
268+
269+
// Then sort by acceptance if it's set
270+
if (acceptanceSort) {
271+
const accA = parseFloat(a["Acceptance %"]);
272+
const accB = parseFloat(b["Acceptance %"]);
273+
const accResult = acceptanceSort === "asc" ? accA - accB : accB - accA;
274+
if (accResult !== 0) return accResult;
275+
}
276+
277+
return 0;
278+
});
266279

267280
return result;
268-
}, [filteredQuestions, sortDirection]);
281+
}, [filteredQuestions, frequencySort, acceptanceSort]);
269282

270283
const handleFrequencySort = () => {
271-
setSortDirection((prev) => {
284+
setFrequencySort((prev) => {
285+
if (prev === null) return "desc";
286+
if (prev === "desc") return "asc";
287+
return null;
288+
});
289+
};
290+
291+
const handleAcceptanceSort = () => {
292+
setAcceptanceSort((prev) => {
272293
if (prev === null) return "desc";
273294
if (prev === "desc") return "asc";
274295
return null;
@@ -471,17 +492,12 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
471492
/>
472493
</div>
473494

474-
<Select value={difficultyFilter} onValueChange={setDifficultyFilter}>
475-
<SelectTrigger className="w-full md:w-52">
476-
<SelectValue placeholder="Difficulty" />
477-
</SelectTrigger>
478-
<SelectContent>
479-
<SelectItem value="all">All Difficulties</SelectItem>
480-
<SelectItem value="Easy">Easy</SelectItem>
481-
<SelectItem value="Medium">Medium</SelectItem>
482-
<SelectItem value="Hard">Hard</SelectItem>
483-
</SelectContent>
484-
</Select>
495+
<TopicDropdown
496+
options={["Easy", "Medium", "Hard"]}
497+
selectedOptions={difficultyFilter}
498+
setSelectedOptions={setDifficultyFilter}
499+
placeholder="Difficulty"
500+
/>
485501

486502
<Select value={timeframeFilter} onValueChange={setTimeframeFilter}>
487503
<SelectTrigger className="w-full md:w-56">
@@ -500,6 +516,7 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
500516
options={uniqueTopics}
501517
selectedOptions={selectedTopics}
502518
setSelectedOptions={setSelectedTopics}
519+
placeholder="Topics"
503520
/>
504521
</div>
505522

@@ -518,12 +535,31 @@ const LeetCodeDashboard: React.FC<LeetCodeDashboardProps> = ({
518535
<TableHead>Company</TableHead>
519536
<TableHead>Difficulty</TableHead>
520537
<TableHead>Topics</TableHead>
521-
<TableHead className="text-right">Acceptance</TableHead>
538+
<TableHead
539+
className="text-right cursor-pointer hover:text-primary transition-colors"
540+
onClick={handleAcceptanceSort}
541+
>
542+
<div className="flex items-center justify-end gap-1">
543+
Acceptance
544+
{acceptanceSort === "desc" && <ChevronDown className="h-4 w-4" />}
545+
{acceptanceSort === "asc" && <ChevronUp className="h-4 w-4" />}
546+
{acceptanceSort === null && (
547+
<ChevronDown className="h-4 w-4 opacity-30" />
548+
)}
549+
</div>
550+
</TableHead>
522551
<TableHead
523552
className="text-right cursor-pointer hover:text-primary transition-colors"
524553
onClick={handleFrequencySort}
525554
>
526-
Frequency
555+
<div className="flex items-center justify-end gap-1">
556+
Frequency
557+
{frequencySort === "desc" && <ChevronDown className="h-4 w-4" />}
558+
{frequencySort === "asc" && <ChevronUp className="h-4 w-4" />}
559+
{frequencySort === null && (
560+
<ChevronDown className="h-4 w-4 opacity-30" />
561+
)}
562+
</div>
527563
</TableHead>
528564
<TableHead className="text-center">Premium</TableHead>
529565
<TableHead className="text-left">Solution</TableHead>

components/TopicDropdown.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ interface TopicDropdownProps {
1818
options: string[];
1919
selectedOptions: string[];
2020
setSelectedOptions: (options: string[]) => void;
21+
placeholder?: string;
2122
}
2223

2324
export default function TopicDropdown({
2425
options,
2526
selectedOptions,
2627
setSelectedOptions,
28+
placeholder,
2729
}: TopicDropdownProps) {
2830
const [open, setOpen] = React.useState(false);
2931

@@ -46,9 +48,7 @@ export default function TopicDropdown({
4648
className="justify-between min-w-[200px] bg-transparent [&>span]:line-clamp-1"
4749
>
4850
<span className="line-clamp-1">
49-
{selectedOptions.length === 0
50-
? "Select topics..."
51-
: `${selectedOptions.length} selected`}
51+
{selectedOptions.length === 0 ? placeholder : `${selectedOptions.length} selected`}
5252
</span>
5353
<ChevronDown className="h-4 w-4 opacity-50" />
5454
</Button>

0 commit comments

Comments
 (0)