Skip to content

Commit 4b65fca

Browse files
committed
add query and filter to list questions
1 parent 75c0147 commit 4b65fca

16 files changed

+341
-95
lines changed

jupyterlab_leetcode/handlers/leetcode_handler.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,28 @@ async def post(self):
254254
"categorySlug": "algorithms",
255255
"filters": {
256256
"filterCombineType": "ALL",
257-
"statusFilter": {"questionStatuses": [], "operator": "IS"},
258-
"difficultyFilter": {"difficulties": [], "operator": "IS"},
257+
"statusFilter": {
258+
"questionStatuses": query["statuses"],
259+
"operator": "IS",
260+
},
261+
"difficultyFilter": {
262+
"difficulties": query["difficulties"],
263+
"operator": "IS",
264+
},
259265
"languageFilter": {"languageSlugs": [], "operator": "IS"},
260-
"topicFilter": {"topicSlugs": [], "operator": "IS"},
266+
"topicFilter": {
267+
"topicSlugs": query["topics"],
268+
"operator": "IS",
269+
},
261270
"acceptanceFilter": {},
262271
"frequencyFilter": {},
263272
"frontendIdFilter": {},
264273
"lastSubmittedFilter": {},
265274
"publishedFilter": {},
266-
"companyFilter": {"companySlugs": [], "operator": "IS"},
275+
"companyFilter": {
276+
"companySlugs": query["companies"],
277+
"operator": "IS",
278+
},
267279
"positionFilter": {"positionSlugs": [], "operator": "IS"},
268280
"premiumFilter": {"premiumStatus": [], "operator": "IS"},
269281
},

src/components/BrowserMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ const BrowserMenu: React.FC<{
125125
>
126126
<Menu.Target>
127127
<Button
128-
rightSection={<IconChevronDown size={18} stroke={1.5} />}
128+
rightSection=<IconChevronDown size={18} stroke={1.5} />
129129
pr={12}
130130
radius="md"
131131
size="md"

src/components/DifficultyStatistics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const DifficultyStatistics: React.FC<{ text: string; color: string }> = ({
77
}) => {
88
return (
99
<Stack>
10-
<Text>{text.charAt(0).toUpperCase() + text.slice(1)}</Text>
10+
<Text tt="capitalize">{text}</Text>
1111
</Stack>
1212
);
1313
};

src/components/LeetCodeMain.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ const LeetCodeMain: React.FC<{ docManager: IDocumentManager }> = ({
4444
};
4545

4646
return (
47-
<Container fluid={true} h={'100%'} p="lg" id="jll-main">
47+
<Container fluid={true} h="100%" p="lg" id="jll-main">
4848
<Stack>
49-
<Group id="jll-profile">
49+
<Group id="jll-profile" h={146}>
5050
{profile && <Profile profile={profile} />}
5151
{profile && <Statistics username={profile.username} />}
5252
</Group>

src/components/Profile.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ const Profile: React.FC<{
1111
radius="md"
1212
withBorder
1313
p="sm"
14-
miw={'20%'}
15-
maw={'40%'}
14+
miw="20%"
15+
maw="40%"
1616
bg="var(--mantine-color-body)"
1717
>
1818
<Avatar src={profile.avatar} size={60} radius={60} mx="auto" />
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React, { useState } from 'react';
2+
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
3+
import { IconBuildings, IconCheck } from '@tabler/icons-react';
4+
import classes from '../styles/Filter.module.css';
5+
6+
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
7+
8+
const Data = ['facebook', 'google'];
9+
10+
const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
11+
option,
12+
checked
13+
}) => (
14+
<Badge
15+
leftSection={checked ? CheckedIcon : null}
16+
color="blue"
17+
variant="light"
18+
tt="capitalize"
19+
>
20+
{option.value}
21+
</Badge>
22+
);
23+
24+
const QuestionCompanyFilter: React.FC<{
25+
updateCompanies: (companies: string[]) => void;
26+
}> = ({ updateCompanies }) => {
27+
const [selected, setSelected] = useState(false);
28+
29+
return (
30+
<MultiSelect
31+
tt="capitalize"
32+
data={Data}
33+
renderOption={renderMultiSelectOption}
34+
maxDropdownHeight={300}
35+
placeholder="Company"
36+
checkIconPosition="left"
37+
leftSection={<IconBuildings size={16} stroke={1.5} />}
38+
leftSectionPointerEvents="none"
39+
clearable
40+
searchable
41+
onChange={v => {
42+
setSelected(v.length > 0);
43+
updateCompanies(v);
44+
}}
45+
className={selected ? classes.filter_selected : classes.filter_empty}
46+
/>
47+
);
48+
};
49+
50+
export default QuestionCompanyFilter;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React, { useState } from 'react';
2+
import { Badge, MultiSelect, MultiSelectProps } from '@mantine/core';
3+
import { IconCheck, IconGauge } from '@tabler/icons-react';
4+
import { DifficultyColors } from './Statistics';
5+
import classes from '../styles/Filter.module.css';
6+
7+
const CheckedIcon = <IconCheck size={12} stroke={1.5} />;
8+
9+
const Data = Object.keys(DifficultyColors);
10+
11+
const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({
12+
option,
13+
checked
14+
}) => (
15+
<Badge
16+
leftSection={checked ? CheckedIcon : null}
17+
color={DifficultyColors[option.value.toLowerCase()] || 'blue'}
18+
variant="light"
19+
tt="capitalize"
20+
>
21+
{option.value}
22+
</Badge>
23+
);
24+
25+
const QuestionDifficultyFilter: React.FC<{
26+
updateDifficulties: (ds: string[]) => void;
27+
}> = ({ updateDifficulties }) => {
28+
const [selected, setSelected] = useState(false);
29+
30+
return (
31+
<MultiSelect
32+
tt="capitalize"
33+
data={Data}
34+
renderOption={renderMultiSelectOption}
35+
maxDropdownHeight={300}
36+
placeholder="Difficulty"
37+
checkIconPosition="left"
38+
leftSection={<IconGauge size={16} stroke={1.5} />}
39+
leftSectionPointerEvents="none"
40+
clearable
41+
searchable
42+
onChange={v => {
43+
setSelected(v.length > 0);
44+
updateDifficulties(v.map(v => v.toUpperCase()));
45+
}}
46+
className={selected ? classes.filter_selected : classes.filter_empty}
47+
/>
48+
);
49+
};
50+
51+
export default QuestionDifficultyFilter;

src/components/QuestionItem.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,44 +29,51 @@ const DifficultyAbbreviations: Record<string, string> = {
2929
hard: 'Hard'
3030
};
3131

32+
export const StatusColors: Record<string, string> = {
33+
SOLVED: 'green',
34+
ATTEMPTED: 'violet',
35+
TO_DO: 'gray'
36+
};
37+
38+
const IconProps = { size: 16, stroke: 1.5 };
39+
3240
const QuestionItem: React.FC<{
3341
question: LeetCodeQuestion;
3442
onGenerateSuccess: (p: string) => void;
3543
}> = ({ question, onGenerateSuccess }) => {
3644
const [showGenerateIcon, setShowGenerateIcon] = React.useState(false);
3745

3846
const statusIcon = () => {
39-
const size = 16;
4047
if (!question.status) {
4148
return null;
4249
}
4350
switch (question.status) {
4451
case 'SOLVED':
4552
return (
4653
<Tooltip fz="xs" label="Solved">
47-
<IconCheck size={size} color="green" stroke={1.5} />
54+
<IconCheck color={StatusColors[question.status]} {...IconProps} />
4855
</Tooltip>
4956
);
5057
case 'ATTEMPTED':
5158
return (
5259
<Tooltip fz="xs" label="Attempted">
53-
<IconCircle size={size} color="gray" stroke={1.5} />
60+
<IconCircle color={StatusColors[question.status]} {...IconProps} />
5461
</Tooltip>
5562
);
5663
case 'TO_DO':
5764
default:
5865
return question.paidOnly ? (
5966
<Tooltip fz="xs" label="Paid Only">
60-
<IconLock size={size} color={LeetCodeMainColor} stroke={1.5} />
67+
<IconLock color={LeetCodeMainColor} {...IconProps} />
6168
</Tooltip>
6269
) : (
63-
<div style={{ width: size, height: size }}></div>
70+
<div style={{ width: IconProps.size, height: IconProps.size }}></div>
6471
);
6572
}
6673
};
6774

68-
const generate = (slug: string) => {
69-
generateNotebook(slug)
75+
const generate = () => {
76+
generateNotebook(question.titleSlug)
7077
.then(({ filePath }) => {
7178
onGenerateSuccess(filePath);
7279
})
@@ -88,13 +95,15 @@ const QuestionItem: React.FC<{
8895
fz="sm"
8996
fw={600}
9097
>
98+
{question.questionFrontendId}
99+
{'. '}
91100
{question.title}
92101
</Anchor>
93102
</Group>
94103
</Table.Td>
95104

96105
<Table.Td className={classes.ac_column}>
97-
<Tooltip fz="xs" label="Acceptance Rate" position={'top-start'}>
106+
<Tooltip fz="xs" label="Acceptance Rate" position="top-start">
98107
<Text fz="sm" c="gray">
99108
{(question.acRate * 100).toFixed(2)}%
100109
</Text>
@@ -122,7 +131,7 @@ const QuestionItem: React.FC<{
122131
size="sm"
123132
variant="transparent"
124133
color={LeetCodeMainColor}
125-
onClick={() => generate(question.titleSlug)}
134+
onClick={() => generate()}
126135
>
127136
<IconBrandLeetcode stroke={1.5} />
128137
</ActionIcon>

src/components/QuestionQueryBar.tsx

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { Group, TextInput } from '@mantine/core';
3+
import { IconSearch } from '@tabler/icons-react';
4+
import { useDebouncedCallback } from '@mantine/hooks';
5+
6+
const QuestionQueryBar: React.FC<{
7+
updateKeyword: (keyword: string) => void;
8+
}> = ({ updateKeyword }) => {
9+
const debounced = useDebouncedCallback(updateKeyword, 200);
10+
11+
return (
12+
<Group>
13+
<TextInput
14+
placeholder="Search questions"
15+
leftSection=<IconSearch size={16} stroke={1.5} />
16+
onChange={e => debounced(e.target.value)}
17+
/>
18+
</Group>
19+
);
20+
};
21+
22+
export default QuestionQueryBar;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy