[web] projects(challenge): integrate skills follow up (#335)

This commit is contained in:
Nitesh Seram 2024-02-19 05:39:23 +05:30 committed by GitHub
parent 672f26accc
commit c3c036f1f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 191 additions and 96 deletions

View File

@ -3,5 +3,6 @@
"printWidth": 80,
"proseWrap": "never",
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "all",
"plugins": ["prettier-plugin-tailwindcss"]
}

View File

@ -10,6 +10,8 @@ import { useIntl } from 'react-intl';
import useFilterSearchParams from '~/hooks/useFilterSearchParams';
import type { PopoverContentWidth } from '~/components/ui/Popover';
export type ProjectsChallengeFilterType = 'checkbox' | 'skill-selection';
export type ProjectsChallengeFilter = {
id: ProjectsChallengeFilterKey;
@ -21,6 +23,7 @@ export type ProjectsChallengeFilter = {
}>;
tooltip: string;
type: ProjectsChallengeFilterType;
width?: PopoverContentWidth;
};
export type ProjectsChallengeFilterKey =
@ -51,6 +54,7 @@ function useFilters() {
id: '03TVln',
}),
type: 'checkbox',
width: 'md',
},
{
id: 'skills',

View File

@ -31,7 +31,7 @@ import ProjectsChallengeFilterContextProvider, {
useProjectsChallengeFilterContext,
} from './ProjectsChallengeFilterContext';
import ProjectsChallengeFilterSlideOut from './ProjectsChallengeFilterSlideOut';
import ProjectsListFilterDropdown from './ProjectsListFilterDropdown';
import ProjectsChallengeListFilter from './ProjectsChallengeListFilter';
import type { ProjectsChallengeItem } from '../types';
type Props = Readonly<{
@ -220,8 +220,8 @@ function ProjectsChallengeGridListWithFiltersImpl({ challenges }: Props) {
}}
/>
<div className="flex flex-col gap-6">
<div className="flex gap-3 flex-wrap lg:flex-row md:flex-col flex-row">
<div className="flex-1 w-full lg:w-auto">
<div className="flex flex-row flex-wrap gap-3 md:flex-col lg:flex-row">
<div className="w-full flex-1 lg:w-auto">
<TextInput
isLabelHidden={true}
label="Search"
@ -232,13 +232,13 @@ function ProjectsChallengeGridListWithFiltersImpl({ challenges }: Props) {
onChange={onChangeQuery}
/>
</div>
<div className="md:flex hidden gap-3 flex-wrap">
<div className="hidden flex-wrap gap-3 md:flex">
{filters.map((filter) => (
<ProjectsListFilterDropdown key={filter.id} filter={filter} />
<ProjectsChallengeListFilter key={filter.id} filter={filter} />
))}
{sortAndFilterButton}
</div>
<div className="md:hidden flex gap-3">{sortAndFilterButton}</div>
<div className="flex gap-3 md:hidden">{sortAndFilterButton}</div>
</div>
{currentPageChallenges.length === 0 ? (
<div className="p-24">
@ -282,7 +282,7 @@ function ProjectsChallengeGridListWithFiltersImpl({ challenges }: Props) {
</div>
)}
{totalPages > 1 && (
<div className="flex justify-between items-center">
<div className="flex items-center justify-between">
<Text color="secondary" size="body3">
<FormattedMessage
defaultMessage="Showing {currentPageCount} out of {totalCount} projects"

View File

@ -0,0 +1,16 @@
import { type ProjectsChallengeFilter } from '~/components/projects/challenges/lists/ProjectsChallengeFilterContext';
import ProjectsChallengePopoverFilterInput from './filters/ProjectsChallengePopoverFilterInput';
import ProjectsChallengeSkillsFilterInput from './filters/ProjectsChallengeSkillsFilterInput';
type Props = Readonly<{
filter: ProjectsChallengeFilter;
}>;
export default function ProjectsChallengeListFilter({ filter }: Props) {
return filter.type === 'skill-selection' ? (
<ProjectsChallengeSkillsFilterInput filter={filter} />
) : (
<ProjectsChallengePopoverFilterInput filter={filter} />
);
}

View File

@ -1,83 +0,0 @@
import { useState } from 'react';
import { RiArrowDownSLine } from 'react-icons/ri';
import {
type ProjectsChallengeFilter,
useProjectsChallengeFilterState,
} from '~/components/projects/challenges/lists/ProjectsChallengeFilterContext';
import ProjectsSkillRoadmapSelectionDialog from '~/components/projects/skills/form/ProjectsSkillRoadmapSelectionDialog';
import Button from '~/components/ui/Button';
import CheckboxInput from '~/components/ui/CheckboxInput';
import Popover from '~/components/ui/Popover';
type Props = Readonly<{
filter: ProjectsChallengeFilter;
}>;
export default function ProjectsListFilterDropdown({ filter }: Props) {
const [selectedOptions, setSelectedOptions] = useProjectsChallengeFilterState(
filter.id,
);
const [showSkillsRoadmapDialog, setShowSkillsRoadmapDialog] = useState(false);
const onChange = (value: string) => {
const newFilters = new Set(selectedOptions);
if (newFilters.has(value)) {
newFilters.delete(value);
} else {
newFilters.add(value);
}
setSelectedOptions(Array.from(newFilters));
};
return (
<>
{filter.id === 'skills' ? (
<Button
icon={RiArrowDownSLine}
label={filter.label}
size="md"
variant="secondary"
onClick={() => setShowSkillsRoadmapDialog(true)}
/>
) : (
<Popover
trigger={
<Button
icon={RiArrowDownSLine}
label={filter.label}
size="md"
variant="secondary"
/>
}
width={filter.id === 'component-track' ? 'md' : 'sm'}>
<div className="flex flex-col gap-y-3">
{filter.options.map((option) => (
<div key={option.value} className="flex items-center">
<CheckboxInput
label={option.label}
size="sm"
value={selectedOptions.includes(option.value)}
onChange={() => onChange(option.value)}
/>
</div>
))}
</div>
</Popover>
)}
{showSkillsRoadmapDialog && (
<ProjectsSkillRoadmapSelectionDialog
defaultSkills={selectedOptions}
isShown={showSkillsRoadmapDialog}
onClose={() => setShowSkillsRoadmapDialog(false)}
onComplete={(newSkills) => {
setSelectedOptions(newSkills as Array<string>);
setShowSkillsRoadmapDialog(false);
}}
/>
)}
</>
);
}

View File

@ -0,0 +1,57 @@
import { RiArrowDownSLine } from 'react-icons/ri';
import Button from '~/components/ui/Button';
import CheckboxInput from '~/components/ui/CheckboxInput';
import Popover from '~/components/ui/Popover';
import {
type ProjectsChallengeFilter,
useProjectsChallengeFilterState,
} from '../ProjectsChallengeFilterContext';
type Props = Readonly<{
filter: ProjectsChallengeFilter;
}>;
export default function ProjectsChallengePopoverFilterInput({ filter }: Props) {
const [selectedOptions, setSelectedOptions] = useProjectsChallengeFilterState(
filter.id,
);
const onChange = (value: string) => {
const newFilters = new Set(selectedOptions);
if (newFilters.has(value)) {
newFilters.delete(value);
} else {
newFilters.add(value);
}
setSelectedOptions(Array.from(newFilters));
};
return (
<Popover
trigger={
<Button
icon={RiArrowDownSLine}
label={filter.label}
size="md"
variant="secondary"
/>
}
width={filter.width ? filter.width : 'sm'}>
<div className="flex flex-col gap-y-3">
{filter.options.map((option) => (
<div key={option.value} className="flex items-center">
<CheckboxInput
label={option.label}
size="sm"
value={selectedOptions.includes(option.value)}
onChange={() => onChange(option.value)}
/>
</div>
))}
</div>
</Popover>
);
}

View File

@ -0,0 +1,44 @@
import { useState } from 'react';
import { RiArrowDownSLine } from 'react-icons/ri';
import ProjectsSkillRoadmapSelectionDialog from '~/components/projects/skills/form/ProjectsSkillRoadmapSelectionDialog';
import Button from '~/components/ui/Button';
import {
type ProjectsChallengeFilter,
useProjectsChallengeFilterState,
} from '../ProjectsChallengeFilterContext';
type Props = Readonly<{
filter: ProjectsChallengeFilter;
}>;
export default function ProjectsChallengeSkillsFilterInput({ filter }: Props) {
const [selectedOptions, setSelectedOptions] = useProjectsChallengeFilterState(
filter.id,
);
const [showSkillsRoadmapDialog, setShowSkillsRoadmapDialog] = useState(false);
return (
<>
<Button
icon={RiArrowDownSLine}
label={filter.label}
size="md"
variant="secondary"
onClick={() => setShowSkillsRoadmapDialog(true)}
/>
{showSkillsRoadmapDialog && (
<ProjectsSkillRoadmapSelectionDialog
defaultSkills={selectedOptions}
isShown={showSkillsRoadmapDialog}
onClose={() => setShowSkillsRoadmapDialog(false)}
onComplete={(newSkills) => {
setSelectedOptions(newSkills as Array<string>);
setShowSkillsRoadmapDialog(false);
}}
/>
)}
</>
);
}

View File

@ -17,6 +17,7 @@
},
"devDependencies": {
"prettier": "latest",
"prettier-plugin-tailwindcss": "^0.5.9",
"turbo": "latest"
},
"engines": {

View File

@ -10,7 +10,10 @@ importers:
devDependencies:
prettier:
specifier: latest
version: 3.1.0
version: 3.2.5
prettier-plugin-tailwindcss:
specifier: ^0.5.9
version: 0.5.9(prettier@3.2.5)
turbo:
specifier: latest
version: 1.11.0
@ -12100,10 +12103,56 @@ packages:
prettier: 3.1.1
dev: true
/prettier@3.1.0:
resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
engines: {node: '>=14'}
hasBin: true
/prettier-plugin-tailwindcss@0.5.9(prettier@3.2.5):
resolution: {integrity: sha512-9x3t1s2Cjbut2QiP+O0mDqV3gLXTe2CgRlQDgucopVkUdw26sQi53p/q4qvGxMLBDfk/dcTV57Aa/zYwz9l8Ew==}
engines: {node: '>=14.21.3'}
peerDependencies:
'@ianvs/prettier-plugin-sort-imports': '*'
'@prettier/plugin-pug': '*'
'@shopify/prettier-plugin-liquid': '*'
'@trivago/prettier-plugin-sort-imports': '*'
prettier: ^3.0
prettier-plugin-astro: '*'
prettier-plugin-css-order: '*'
prettier-plugin-import-sort: '*'
prettier-plugin-jsdoc: '*'
prettier-plugin-marko: '*'
prettier-plugin-organize-attributes: '*'
prettier-plugin-organize-imports: '*'
prettier-plugin-style-order: '*'
prettier-plugin-svelte: '*'
prettier-plugin-twig-melody: '*'
peerDependenciesMeta:
'@ianvs/prettier-plugin-sort-imports':
optional: true
'@prettier/plugin-pug':
optional: true
'@shopify/prettier-plugin-liquid':
optional: true
'@trivago/prettier-plugin-sort-imports':
optional: true
prettier-plugin-astro:
optional: true
prettier-plugin-css-order:
optional: true
prettier-plugin-import-sort:
optional: true
prettier-plugin-jsdoc:
optional: true
prettier-plugin-marko:
optional: true
prettier-plugin-organize-attributes:
optional: true
prettier-plugin-organize-imports:
optional: true
prettier-plugin-style-order:
optional: true
prettier-plugin-svelte:
optional: true
prettier-plugin-twig-melody:
optional: true
dependencies:
prettier: 3.2.5
dev: true
/prettier@3.1.1:
@ -12112,6 +12161,12 @@ packages:
hasBin: true
dev: true
/prettier@3.2.5:
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
engines: {node: '>=14'}
hasBin: true
dev: true
/pretty-format@29.7.0:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}