diff --git a/src/shared/_mock/_job.ts b/src/shared/_mock/_job.ts index b39a55f484942445b1466f890c24d9fa681e875c..a8fae4c3b30b0ef6638d343ee2b456eca3b70cf2 100644 --- a/src/shared/_mock/_job.ts +++ b/src/shared/_mock/_job.ts @@ -27,7 +27,7 @@ export const JOB_SKILL_OPTIONS = [ 'Problem Diagnosis', ]; -export const JOB_BADGE_OPTIONS = ['Badge A', 'Badge B', 'Badge C']; +export const JOB_BADGE_OPTIONS = ['certification A', 'certification B', 'certification C']; export const JOB_EXPERIENCE_OPTIONS = [ { label: 'Aucune expérience', value: 'Aucune expérience' }, @@ -42,9 +42,9 @@ export const JOB_PUBLISH_OPTIONS = [ ]; export const JOB_SORT_OPTIONS = [ - { label: 'Plus récents', value: 'latest' }, - { label: 'Populaires', value: 'popular' }, - { label: 'Plus anciens', value: 'oldest' }, + { label: 'Plus récents', value: 'Plus récents' }, + { label: 'Populaires', value: 'Populaires' }, + { label: 'Plus anciens', value: 'Plus anciens' }, ]; const CANDIDATES = [...Array(12)].map((_, index) => ({ @@ -115,13 +115,14 @@ export const _jobs = [...Array(12)].map((_, index) => { salary, publish, company, + skills: ['JavaScript', 'Spring boot', 'Angular'], content: CONTENT, title: _mock.jobTitle(index), totalViews: _mock.number.nativeL(index), prestations: PRESTATIONS, // Add a sample value exigences: EXIGENCES, // Add a sample value - certifs: ['certification A, Certification B'], // Add a sample array with one certification - createdAt: _mock.time(index), + certifs: ['certification A', 'certification B'], // Add a sample array with one certification + createdAt: new Date(_mock.time(index)), candidates: CANDIDATES, }; }); diff --git a/src/shared/_mock/assets.ts b/src/shared/_mock/assets.ts index 9aa15b3456157b99284c2c80fcea516364f12fd9..baf2a6f59c1777ce375c7850540f1258b38955f2 100644 --- a/src/shared/_mock/assets.ts +++ b/src/shared/_mock/assets.ts @@ -36,8 +36,8 @@ export const _booleans = [ // ---------------------------------------------------------------------- export const _prices = [ - 83.74, 97.14, 68.71, 85.21, 52.17, 25.18, 43.84, 60.98, 98.42, 53.37, 72.75, 56.61, 64.55, 77.32, - 60.62, 79.81, 93.68, 47.44, 76.24, 92.87, 72.91, 20.54, 94.25, 37.51, + 781.74, 97.14, 68.71, 85.21, 52.17, 25.18, 43.84, 60.98, 1981.42, 533.37, 349.75, 1235.61, 64.55, + 77.32, 60.62, 79.81, 1289.68, 47.44, 1892.24, 92.87, 72.91, 20.54, 94.25, 372.51, ]; export const _ratings = [ diff --git a/src/shared/sections/job/job-details-content.tsx b/src/shared/sections/job/job-details-content.tsx index 3f5cb09807bbb750413e78552c16bad1dff0e707..e71c3dd0780e0cf4ed9ee82742fa56e385a54d71 100644 --- a/src/shared/sections/job/job-details-content.tsx +++ b/src/shared/sections/job/job-details-content.tsx @@ -34,18 +34,6 @@ export function JobDetailsContent({ job }: Props) { </Stack> </Stack> <Markdown children={job?.exigences} /> - {/* <Stack spacing={2}> - <Typography variant="h6">Skills</Typography> - <Stack direction="row" alignItems="center" spacing={1}> - {job?.skills.map((skill) => <Chip key={skill} label={skill} variant="soft" />)} - </Stack> - </Stack> */} - {/* <Stack spacing={2}> - <Typography variant="h6">Benefits</Typography> - <Stack direction="row" alignItems="center" spacing={1}> - {job?.benefits.map((benefit) => <Chip key={benefit} label={benefit} variant="soft" />)} - </Stack> - </Stack> */} </Card> ); @@ -53,30 +41,16 @@ export function JobDetailsContent({ job }: Props) { <Card sx={{ p: 3, gap: 2, display: 'flex', flexDirection: 'column' }}> {[ { - label: 'Date posted', + label: 'Date de publication', value: fDate(job?.createdAt), icon: <Iconify icon="solar:calendar-date-bold" />, }, - // { - // label: 'Expiration date', - // value: fDate(job?.expiredDate), - // icon: <Iconify icon="solar:calendar-date-bold" />, - // }, - // { - // label: 'Employment type', - // value: job?.employmentTypes, - // icon: <Iconify icon="solar:clock-circle-bold" />, - // }, + { - label: 'Offered salary', + label: 'Salaire proposé', value: job?.salary.negotiable ? 'Negotiable' : fCurrency(job?.salary.price), icon: <Iconify icon="solar:wad-of-money-bold" />, }, - // { - // label: 'Experience', - // value: job?.experience, - // icon: <Iconify icon="carbon:skill-level-basic" />, - // }, ].map((item) => ( <Stack key={item.label} spacing={1.5} direction="row"> {item.icon} diff --git a/src/shared/sections/job/job-filters-result.tsx b/src/shared/sections/job/job-filters-result.tsx index 3a6577b08b9dbd2df14338d4ce1c64ac4bd326a2..b2b5d231eea2b584511fe521ac3e5a5c2d38c1bc 100644 --- a/src/shared/sections/job/job-filters-result.tsx +++ b/src/shared/sections/job/job-filters-result.tsx @@ -15,74 +15,33 @@ type Props = { }; export function JobFiltersResult({ filters, totalResults, sx }: Props) { - const handleRemoveEmploymentTypes = (inputValue: string) => { - const newValue = filters.state.employmentTypes.filter((item) => item !== inputValue); - filters.setState({ employmentTypes: newValue }); - }; - - const handleRemoveExperience = () => { - filters.setState({ experience: 'all' }); - }; - - const handleRemoveRoles = (inputValue: string) => { - const newValue = filters.state.roles.filter((item) => item !== inputValue); - filters.setState({ roles: newValue }); - }; - - const handleRemoveLocations = (inputValue: string) => { - const newValue = filters.state.locations.filter((item) => item !== inputValue); - filters.setState({ locations: newValue }); - }; - - const handleRemoveBenefits = (inputValue: string) => { - const newValue = filters.state.benefits.filter((item) => item !== inputValue); - filters.setState({ benefits: newValue }); - }; + // const handleRemoveEmploymentTypes = (inputValue: string) => { + // const newValue = filters.state.employmentTypes.filter((item) => item !== inputValue); + // filters.setState({ employmentTypes: newValue }); + // }; + + // const handleRemoveExperience = () => { + // filters.setState({ experience: 'all' }); + // }; + + // const handleRemoveRoles = (inputValue: string) => { + // const newValue = filters.state.roles.filter((item) => item !== inputValue); + // filters.setState({ roles: newValue }); + // }; + + // const handleRemoveLocations = (inputValue: string) => { + // const newValue = filters.state.locations.filter((item) => item !== inputValue); + // filters.setState({ locations: newValue }); + // }; + + // const handleRemoveBenefits = (inputValue: string) => { + // const newValue = filters.state.benefits.filter((item) => item !== inputValue); + // filters.setState({ benefits: newValue }); + // }; return ( - <FiltersResult totalResults={totalResults} onReset={filters.onResetState} sx={sx}> - <FiltersBlock label="Employment types:" isShow={!!filters.state.employmentTypes.length}> - {filters.state.employmentTypes.map((item) => ( - <Chip - {...chipProps} - key={item} - label={item} - onDelete={() => handleRemoveEmploymentTypes(item)} - /> - ))} - </FiltersBlock> - - <FiltersBlock label="Experience:" isShow={filters.state.experience !== 'all'}> - <Chip {...chipProps} label={filters.state.experience} onDelete={handleRemoveExperience} /> - </FiltersBlock> - - <FiltersBlock label="Roles:" isShow={!!filters.state.roles.length}> - {filters.state.roles.map((item) => ( - <Chip {...chipProps} key={item} label={item} onDelete={() => handleRemoveRoles(item)} /> - ))} - </FiltersBlock> - - <FiltersBlock label="Locations:" isShow={!!filters.state.locations.length}> - {filters.state.locations.map((item) => ( - <Chip - {...chipProps} - key={item} - label={item} - onDelete={() => handleRemoveLocations(item)} - /> - ))} - </FiltersBlock> - - <FiltersBlock label="Benefits:" isShow={!!filters.state.benefits.length}> - {filters.state.benefits.map((item) => ( - <Chip - {...chipProps} - key={item} - label={item} - onDelete={() => handleRemoveBenefits(item)} - /> - ))} - </FiltersBlock> - </FiltersResult> + <div> + <h1>test</h1> + </div> ); } diff --git a/src/shared/sections/job/job-filters.tsx b/src/shared/sections/job/job-filters.tsx index bb940aa27550cdad6a5f854291ba9e7f98cb39cd..9ed3f74b99c8156c781ecbd99051f0e8968016e7 100644 --- a/src/shared/sections/job/job-filters.tsx +++ b/src/shared/sections/job/job-filters.tsx @@ -10,6 +10,11 @@ import Button from '@mui/material/Button'; import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; +import Slider from '@mui/material/Slider'; +import Chip from '@mui/material/Chip'; +import Autocomplete from '@mui/material/Autocomplete'; +import TextField from '@mui/material/TextField'; +import Stack from '@mui/material/Stack'; import { Iconify } from 'src/shared/components/iconify'; import { Scrollbar } from 'src/shared/components/scrollbar'; @@ -22,9 +27,45 @@ type Props = { onOpen: () => void; onClose: () => void; filters: UseSetStateReturn<IJobFilters>; + options: { + salary: [number, number]; + totalViews: number; + certifs: string[]; + skills: string[]; + }; }; -export function JobFilters({ open, canReset, onOpen, onClose, filters }: Props) { +export function JobFilters({ open, canReset, onOpen, onClose, filters, options }: Props) { + const handleSalaryChange = useCallback( + (event: Event, newValue: number | number[]) => { + if (Array.isArray(newValue) && newValue.length === 2) { + filters.setState({ salary: newValue as [number, number] }); + } + }, + [filters] + ); + + const handleTotalViewsChange = useCallback( + (event: Event, newValue: number | number[]) => { + filters.setState({ totalViews: newValue as number }); + }, + [filters] + ); + + const handleCertifsChange = useCallback( + (event: React.SyntheticEvent, newValue: string[]) => { + filters.setState({ certifs: newValue }); + }, + [filters] + ); + + const handleSkillsChange = useCallback( + (event: React.SyntheticEvent, newValue: string[]) => { + filters.setState({ skills: newValue }); + }, + [filters] + ); + const renderHead = ( <> <Box display="flex" alignItems="center" sx={{ py: 2, pr: 1, pl: 2.5 }}> @@ -47,6 +88,88 @@ export function JobFilters({ open, canReset, onOpen, onClose, filters }: Props) </> ); + const renderSalaryFilter = ( + <Box> + <Typography variant="subtitle2" gutterBottom> + Salary Range + </Typography> + <Slider + value={filters.state.salary} + onChange={handleSalaryChange} + valueLabelDisplay="auto" + min={0} + max={2000} // Adjust this max value as needed + step={500} + marks={[ + { value: 0, label: '$0' }, + { value: 500, label: '$500' }, + { value: 1000, label: '$100' }, + { value: 1500, label: '$150' }, + { value: 2000, label: '$200' }, + ]} + /> + <Box display="flex" justifyContent="space-between"> + <Typography variant="caption">{filters.state.salary[0]}</Typography> + <Typography variant="caption">{filters.state.salary[1]}</Typography> + </Box> + </Box> + ); + + const renderTotalViewsFilter = ( + <Box> + <Typography variant="subtitle2" gutterBottom> + Minimum Total Views + </Typography> + <Slider + value={filters.state.totalViews} + onChange={handleTotalViewsChange} + valueLabelDisplay="auto" + min={0} + max={options.totalViews} + /> + </Box> + ); + + const renderCertifsFilter = ( + <Box> + <Typography variant="subtitle2" gutterBottom> + Certifications + </Typography> + <Autocomplete + multiple + options={options.certifs} + value={filters.state.certifs} + onChange={handleCertifsChange} + renderInput={(params) => <TextField {...params} variant="outlined" />} + renderTags={(value, getTagProps) => + value.map((option, index) => ( + <Chip variant="outlined" label={option} {...getTagProps({ index })} /> + )) + } + /> + </Box> + ); + + const renderSkillsFilter = ( + <Box> + <Typography variant="subtitle2" gutterBottom> + Skills + </Typography> + <Autocomplete + multiple + options={options.skills} + value={filters.state.skills} + onChange={handleSkillsChange} + renderInput={(params) => <TextField {...params} variant="outlined" />} + renderTags={(value, getTagProps) => + value.map((option, index) => ( + <Chip variant="outlined" label={option} {...getTagProps({ index })} /> + )) + } + /> + </Box> + ); + return ( <> <Button @@ -72,9 +195,12 @@ export function JobFilters({ open, canReset, onOpen, onClose, filters }: Props) {renderHead} <Scrollbar sx={{ px: 2.5, py: 3 }}> - <Typography> - Filter options would go here, but the current IJobFilters interface is empty. - </Typography> + <Stack spacing={3}> + {renderSalaryFilter} + {renderTotalViewsFilter} + {renderCertifsFilter} + {renderSkillsFilter} + </Stack> </Scrollbar> </Drawer> </> diff --git a/src/shared/sections/job/job-new-edit-form.tsx b/src/shared/sections/job/job-new-edit-form.tsx index 138f3fee29afe40969d6387bc7ba1e145a7f7000..04d0a2ec2743cf46caec1303045e6bc9169d49b1 100644 --- a/src/shared/sections/job/job-new-edit-form.tsx +++ b/src/shared/sections/job/job-new-edit-form.tsx @@ -26,6 +26,7 @@ import { _roles, // JOB_SKILL_OPTIONS, JOB_BADGE_OPTIONS, + JOB_SKILL_OPTIONS, // JOB_BENEFIT_OPTIONS, // JOB_EXPERIENCE_OPTIONS, // JOB_EMPLOYMENT_TYPE_OPTIONS, @@ -141,7 +142,11 @@ export function JobNewEditForm({ currentJob }: Props) { <Stack spacing={1.5}> <Typography variant="subtitle2">Contenu</Typography> - <Field.Editor name="content" sx={{ maxHeight: 480 }} /> + <Field.Editor + name="content" + placeholder="Création d'un site e-commerce responsive. Intégration catalogue produits, système de paiement sécurisé, gestion des stocks. Optimisation SEO." + sx={{ maxHeight: 480 }} + /> </Stack> </Stack> </Card> @@ -158,52 +163,25 @@ export function JobNewEditForm({ currentJob }: Props) { <Divider /> <Stack spacing={3} sx={{ p: 3 }}> - {/* <Stack spacing={1}> - <Typography variant="subtitle2">Type demploi</Typography> - <Field.MultiCheckbox - row - name="employmentTypes" - options={JOB_EMPLOYMENT_TYPE_OPTIONS} - sx={{ gap: 4 }} - /> - </Stack> */} - - {/* <Stack spacing={1}> - <Typography variant="subtitle2">Experience</Typography> - <Field.RadioGroup - row - name="experience" - options={JOB_EXPERIENCE_OPTIONS} - sx={{ gap: 4 }} - /> - </Stack> - - <Stack spacing={1.5}> - <Typography variant="subtitle2">Role</Typography> - <Field.Autocomplete - name="role" - autoHighlight - options={_roles.map((option) => option)} - getOptionLabel={(option) => option} - renderOption={(props, option) => ( - <li {...props} key={option}> - {option} - </li> - )} - /> - </Stack> */} - <Stack spacing={1.5}> <Typography variant="subtitle2">Quelles sont les prestations attendues ?</Typography> - <Field.Editor name="prestations" sx={{ maxHeight: 480 }} /> + <Field.Editor + name="prestations" + placeholder="Développement front-end et back-end, design UX/UI, mise en place CMS, formation client." + sx={{ maxHeight: 480 }} + /> </Stack> <Stack spacing={1.5}> <Typography variant="subtitle2">Quelles sont vos exigences ?</Typography> - <Field.Editor name="exigences" sx={{ maxHeight: 480 }} /> + <Field.Editor + name="exigences" + placeholder="Expérience en e-commerce, maîtrise HTML/CSS/JS/PHP, connaissance WooCommerce/Shopify." + sx={{ maxHeight: 480 }} + /> </Stack> - {/* <Stack spacing={1.5}> + <Stack spacing={1.5}> <Typography variant="subtitle2">Compétences</Typography> <Field.Autocomplete name="skills" @@ -230,7 +208,7 @@ export function JobNewEditForm({ currentJob }: Props) { )) } /> - </Stack> */} + </Stack> <Stack spacing={1.5}> <Typography variant="subtitle2">Certifs</Typography> <Field.Autocomplete diff --git a/src/shared/sections/job/job-search.tsx b/src/shared/sections/job/job-search.tsx index a6f1e23be8564acca3a49c238e8654b348d4d2e0..7dddd7a547f5880e6cf9651c86e9edbd98c58d91 100644 --- a/src/shared/sections/job/job-search.tsx +++ b/src/shared/sections/job/job-search.tsx @@ -58,7 +58,7 @@ export function JobSearch({ search, onSearch }: Props) { renderInput={(params) => ( <TextField {...params} - placeholder="Search..." + placeholder="Recherche..." onKeyUp={handleKeyUp} InputProps={{ ...params.InputProps, diff --git a/src/shared/sections/job/job-sort.tsx b/src/shared/sections/job/job-sort.tsx index ad0f2f184390b1ada23ab3765eabc35e643ed62c..cafdf6b12c51a5ce9d4d9519ac7dab196a471179 100644 --- a/src/shared/sections/job/job-sort.tsx +++ b/src/shared/sections/job/job-sort.tsx @@ -33,7 +33,7 @@ export function JobSort({ sort, onSort, sortOptions }: Props) { } sx={{ fontWeight: 'fontWeightSemiBold' }} > - Sort by: + Trier par : <Box component="span" sx={{ ml: 0.5, fontWeight: 'fontWeightBold', textTransform: 'capitalize' }} diff --git a/src/shared/sections/job/view/job-details-view.tsx b/src/shared/sections/job/view/job-details-view.tsx index a14cf69d52bad5b693a058739f1b5c62dd7f38b2..662de39e754bd0f134ff40622b33833c40c013c7 100644 --- a/src/shared/sections/job/view/job-details-view.tsx +++ b/src/shared/sections/job/view/job-details-view.tsx @@ -27,7 +27,7 @@ type Props = { }; export function JobDetailsView({ job }: Props) { - const tabs = useTabs('content'); + // const tabs = useTabs('content'); // const [publish, setPublish] = useState(job?.publish); @@ -35,25 +35,25 @@ export function JobDetailsView({ job }: Props) { // setPublish(newValue); // }, []); - const renderTabs = ( - <Tabs value={tabs.value} onChange={tabs.onChange} sx={{ mb: { xs: 3, md: 5 } }}> - {JOB_DETAILS_TABS.map((tab) => ( - <Tab - key={tab.value} - iconPosition="end" - value={tab.value} - label={tab.label} - icon={ - tab.value === 'candidates' ? ( - <Label variant="filled">{job?.candidates.length}</Label> - ) : ( - '' - ) - } - /> - ))} - </Tabs> - ); + // const renderTabs = ( + // <Tabs value={tabs.value} onChange={tabs.onChange} sx={{ mb: { xs: 3, md: 5 } }}> + // {JOB_DETAILS_TABS.map((tab) => ( + // <Tab + // key={tab.value} + // iconPosition="end" + // value={tab.value} + // label={tab.label} + // icon={ + // tab.value === 'candidates' ? ( + // <Label variant="filled">{job?.candidates.length}</Label> + // ) : ( + // '' + // ) + // } + // /> + // ))} + // </Tabs> + // ); return ( <DashboardContent> diff --git a/src/shared/sections/job/view/job-list-view.tsx b/src/shared/sections/job/view/job-list-view.tsx index 17ba88368530b070e51ddae3f3f69298cf828341..758584bceae4e841fa9cb785e7898f5bede9f660 100644 --- a/src/shared/sections/job/view/job-list-view.tsx +++ b/src/shared/sections/job/view/job-list-view.tsx @@ -2,7 +2,7 @@ import type { IJobItem, IJobFilters } from 'src/shared/types/job'; -import { useState, useCallback } from 'react'; +import { useState, useCallback, useEffect } from 'react'; import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; @@ -19,6 +19,8 @@ import { DashboardContent } from 'src/shared/layouts/dashboard'; import { _jobs, _roles, + JOB_BADGE_OPTIONS, + JOB_SKILL_OPTIONS, JOB_SORT_OPTIONS, // JOB_BENEFIT_OPTIONS, // JOB_EXPERIENCE_OPTIONS, @@ -40,27 +42,33 @@ import { JobFiltersResult } from '../job-filters-result'; export function JobListView() { const openFilters = useBoolean(); - const [sortBy, setSortBy] = useState('latest'); + const [sortBy, setSortBy] = useState('plus_recents'); + const [filteredJobs, setFilteredJobs] = useState<IJobItem[]>([]); + + const filters = useSetState<IJobFilters>({ + salary: [0, 200000], + skills: [], + totalViews: 0, + certifs: [], + createdAt: [null, null], + }); + + useEffect(() => { + const sortedAndFilteredData = applyFilter({ inputData: _jobs, filters: filters.state, sortBy }); + setFilteredJobs(sortedAndFilteredData); + console.log('use effect trigger'); + }, [sortBy, filters.state]); const search = useSetState<{ query: string; results: IJobItem[]; }>({ query: '', results: [] }); - const filters = useSetState<IJobFilters>({ - salary: 0, - // salary: [] - // roles: [], - // locations: [], - // benefits: [], - // experience: 'all', - // employmentTypes: [], - }); - const dataFiltered = applyFilter({ inputData: _jobs, filters: filters.state, sortBy }); - const canReset = 'all'; - // filters.state.roles.length > 0 || + // const canReset = 'all'; + const canReset = false; + // filters.state.roles.length > 0 || // // filters.state.locations.length > 0 || // filters.state.benefits.length > 0 || // filters.state.employmentTypes.length > 0 || @@ -69,23 +77,25 @@ export function JobListView() { // const notFound = !dataFiltered.length && canReset; const handleSortBy = useCallback((newValue: string) => { + console.log('Sorting changed to:', newValue); + setSortBy(newValue); }, []); - // const handleSearch = useCallback( - // (inputValue: string) => { - // search.setState({ query: inputValue }); + const handleSearch = useCallback( + (inputValue: string) => { + search.setState({ query: inputValue }); - // if (inputValue) { - // const results = _jobs.filter( - // (job) => job.title.toLowerCase().indexOf(search.state.query.toLowerCase()) !== -1 - // ); + if (inputValue) { + const results = _jobs.filter( + (job) => job.title.toLowerCase().indexOf(search.state.query.toLowerCase()) !== -1 + ); - // search.setState({ results }); - // } - // }, - // [search] - // ); + search.setState({ results }); + } + }, + [search] + ); const renderFilters = ( <Stack @@ -94,22 +104,22 @@ export function JobListView() { alignItems={{ xs: 'flex-end', sm: 'center' }} direction={{ xs: 'column', sm: 'row' }} > - {/* <JobSearch search={search} onSearch={handleSearch} /> */} + <JobSearch search={search} onSearch={handleSearch} /> <Stack direction="row" spacing={1} flexShrink={0}> - {/* <JobFilters + <JobFilters filters={filters} - // canReset={canReset} + canReset={canReset} open={openFilters.value} onOpen={openFilters.onTrue} onClose={openFilters.onFalse} options={{ - roles: _roles, - benefits: JOB_BENEFIT_OPTIONS.map((option) => option.label), - employmentTypes: JOB_EMPLOYMENT_TYPE_OPTIONS.map((option) => option.label), - experiences: ['all', ...JOB_EXPERIENCE_OPTIONS.map((option) => option.label)], + salary: [0, 100000], + totalViews: 10000, + certifs: ['all', ...JOB_BADGE_OPTIONS], + skills: ['all', ...JOB_SKILL_OPTIONS], }} - /> */} + /> <JobSort sort={sortBy} onSort={handleSortBy} sortOptions={JOB_SORT_OPTIONS} /> </Stack> @@ -121,7 +131,7 @@ export function JobListView() { return ( <DashboardContent> <CustomBreadcrumbs - heading="List" + // heading="Free" links={[ { name: 'Freelance', href: paths.freelancers.root }, { name: 'Job', href: paths.freelancers.jobs }, @@ -134,7 +144,7 @@ export function JobListView() { variant="contained" startIcon={<Iconify icon="mingcute:add-line" />} > - New job + Publier un projet </Button> } sx={{ mb: { xs: 3, md: 5 } }} @@ -162,44 +172,56 @@ type ApplyFilterProps = { }; const applyFilter = ({ inputData, filters, sortBy }: ApplyFilterProps) => { - // const { employmentTypes, experience, roles, locations, benefits } = filters; - // const {salary} = filters; + const { salary, skills, totalViews, certifs, createdAt } = filters; + let sortedData = [...inputData]; + console.log('Filters:', filters); // Sort by - if (sortBy === 'latest') { - inputData = orderBy(inputData, ['createdAt'], ['desc']); + if (sortBy === 'Plus récents') { + sortedData.sort( + (a, b) => new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime() + ); + console.log('Sorting by most recent'); + } else if (sortBy === 'Plus anciens') { + console.log('trigger plus anciens'); + sortedData.sort( + (a, b) => new Date(a.createdAt || 0).getTime() - new Date(b.createdAt || 0).getTime() + ); + console.log('Sorting by oldest'); + } else if (sortBy === 'Populaires') { + sortedData = orderBy(sortedData, ['totalViews'], ['desc']); + console.log('Sorting by popularity'); } - if (sortBy === 'oldest') { - inputData = orderBy(inputData, ['createdAt'], ['asc']); - } - - if (sortBy === 'popular') { - inputData = orderBy(inputData, ['totalViews'], ['desc']); - } - - // Filters - // if (employmentTypes.length) { - // inputData = inputData.filter((job) => - // job.employmentTypes.some((item) => employmentTypes.includes(item)) - // ); - // } - - // if (experience !== 'all') { - // inputData = inputData.filter((job) => job.experience === experience); - // } - - // if (roles.length) { - // inputData = inputData.filter((job) => roles.includes(job.role)); - // } - - // if (locations.length) { - // inputData = inputData.filter((job) => job.locations.some((item) => locations.includes(item))); - // } - - // if (benefits.length) { - // inputData = inputData.filter((job) => job.benefits.some((item) => benefits.includes(item))); - // } - - return inputData; + return sortedData.filter((job) => { + // Salary Range Filter + if (salary[0] > job.salary.price || salary[1] < job.salary.price) { + return false; + } + + // Total Views Filter + if (job.totalViews < totalViews) { + return false; + } + + // Skills Filter + if (skills.length > 0 && !skills.every((skill) => job.skills.includes(skill))) { + return false; + } + + // Certifications (Badges) Filter + if (certifs.length > 0 && !certifs.every((cert) => job.certifs.includes(cert))) { + return false; + } + + // Date Range Filter + if (createdAt[0] && createdAt[1]) { + const jobDate = new Date(job.createdAt || 0); + if (jobDate < createdAt[0] || jobDate > createdAt[1]) { + return false; + } + } + + return true; + }); }; diff --git a/src/shared/types/job.ts b/src/shared/types/job.ts index 6dab6abcbd293694ad6c086b4db9ba5273fd771d..efbbf84075bd8ac588be8943be7d61eda33acb9e 100644 --- a/src/shared/types/job.ts +++ b/src/shared/types/job.ts @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------- export type IJobFilters = { - // salary: number; - // roles: string[]; - // experience: string; - // locations: string[]; - // benefits: string[]; - // employmentTypes: string[]; + salary: [number, number]; + skills: string[]; + totalViews: number; + certifs: string[]; + createdAt: [Date | null, Date | null]; }; export type IJobCandidate = { @@ -31,23 +30,16 @@ export type IJobSalary = { export type IJobItem = { id: string; - // role: string; title: string; content: string; publish: string; - // skills: string[]; + skills: string[]; totalViews: number; - // experience: string; salary: IJobSalary; prestations: string; exigences: string; certifs: string[]; - // benefits: string[]; - // locations: string[]; company: IJobCompany; - createdAt: string | null; - // employmentTypes: string[]; - // workingSchedule: string[]; - // expiredDate: string | null; + createdAt: Date | null; candidates: IJobCandidate[]; };