Skip to content
Extraits de code Groupes Projets
Valider 7cbd8b89 rédigé par elmehdi.amratiriffi's avatar elmehdi.amratiriffi
Parcourir les fichiers

feat:last commit for tests interfaces

parent 4299ca77
Branches
1 requête de fusion!30feat:last commit for tests interfaces
Pipeline #4326 réussi avec l'étape
in 4 minutes et 49 secondes
Affichage de
avec 711 ajouts et 26 suppressions
import {Main} from 'src/shared/sections/Condidature/List/index';
export default function Page(){
return <Main/>
}
import {Main} from 'src/shared/sections/Condidature/edit_email/index';
export default function Page(){
return <Main/>
}
import {Main} from 'src/shared/sections/Condidature/email/index';
export default function Page(){
return <Main/>
}
import {Main} from 'src/shared/sections/Condidature/invite_condidat/index';
export default function Page(){
return <Main/>
}
import {Main} from 'src/shared/sections/Tests/coding_excercice';
export default function Page(){
return <Main/>
}
import {Main} from 'src/shared/sections/Tests/project/index';
export default function Page(){
return <Main/>
}
......@@ -93,6 +93,7 @@ export const paths = {
details: (id: string) => `${ROOTS.DASHBOARD}/${ROOTS.FREELANCERS}/job/${id}`,
},
tests: {
root: `${ROOTS.DASHBOARD}/Tests`,
fileManager: `${ROOTS.DASHBOARD}/Tests/new-test`,
technologies_choice: `${ROOTS.DASHBOARD}/Tests/technologies-choice`,
......@@ -100,6 +101,14 @@ export const paths = {
choix_type_question: `${ROOTS.DASHBOARD}/Tests/question_type`,
QCM: `${ROOTS.DASHBOARD}/Tests/QCM`,
Free_text: `${ROOTS.DASHBOARD}/Tests/free_text`,
Project :`${ROOTS.DASHBOARD}/Tests/project`,
coding_excercice : `${ROOTS.DASHBOARD}/Tests/coding_excercice`,
},
Condidature : {
root :`${ROOTS.DASHBOARD}/Condidature/invite_condidat`,
email : `${ROOTS.DASHBOARD}/Condidature/email`,
edit_email : `${ROOTS.DASHBOARD}/Condidature/edit_email`,
Liste_condidats:`${ROOTS.DASHBOARD}/Condidature/List`,
},
stats: {
root: `${ROOTS.STATS}`,
......
......@@ -83,8 +83,21 @@ export const navData = [
{ title: 'Choix type de question', path: paths.dashboard.tests.choix_type_question },
{ title: 'QCM', path: paths.dashboard.tests.QCM },
{ title: 'Free text', path: paths.dashboard.tests.Free_text },
{title: 'Project',path : paths.dashboard.tests.Project},
{title : 'Coding excercice',path:paths.dashboard.tests.coding_excercice},
],
},
{
title: 'Gestion de Condidature',
path: paths.dashboard.Condidature.root,
icon : ICONS.lock,
children :[
{title : 'Invite_condidat',path:paths.dashboard.Condidature.root},
{title :'Invitation par email ',path: paths.dashboard.Condidature.email},
{title : 'Modifier email ' , path: paths.dashboard.Condidature.edit_email},
{title : 'Liste condidature ' , path: paths.dashboard.Condidature.Liste_condidats},
]
},
{
title: 'Recreteur',
path: paths.dashboard.recruiter.root,
......
// Import the DataGridCustom component
import { DataGridCustom } from './view';
// Example data to pass to the DataGridCustom component
const exampleData = [
{
id: '1',
testName: 'Mobile - Android, CSS, Flutter, HTML, JavaScript',
recruteur: 'John Doe',
condidat: 'mehdiamarti33@gmail.co',
invitationDate: 'Aug 21, 2024 7:30 PM',
score: 4.5,
},
{
id: '2',
testName: 'Test 2',
recruteur: 'Alice Johnson',
condidat: 'mehdiamarti33@gmail.com',
invitationDate: 'Aug 21, 2024 7:30 PM',
score: 3.8,
},
];
export function Main() {
return (
<DataGridCustom data={exampleData} />
);
}
\ No newline at end of file
"use client";
import type { IDateValue } from 'src/types/common';
import type { RatingProps } from '@mui/material/Rating';
import type {
GridSlots,
GridColDef,
GridFilterItem,
GridFilterOperator,
GridRowSelectionModel,
GridColumnVisibilityModel,
GridFilterInputValueProps,
} from '@mui/x-data-grid';
import { useRef, useMemo, useState, useImperativeHandle } from 'react';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import Rating from '@mui/material/Rating';
import {
DataGrid,
gridClasses,
GridToolbarExport,
GridToolbarContainer,
GridToolbarQuickFilter,
GridToolbarFilterButton,
GridToolbarColumnsButton,
GridToolbarDensitySelector,
} from '@mui/x-data-grid';
import { fDate, fTime } from 'src/utils/format-time';
import { EmptyContent } from 'src/shared/components/empty-content';
// ----------------------------------------------------------------------
const baseColumns: GridColDef[] = [
{ field: 'testName', headerName: 'TestName', flex: 1, minWidth: 160 },
{ field: 'recruteur', headerName: 'Recruteur', flex: 1, minWidth: 160 },
{ field: 'emai',headerName: 'Condidat',
flex: 1,
minWidth: 160,
editable: true,
renderCell: (params) => (
<Link color="inherit" noWrap>
{params.row.condidat}
</Link>
),
},
{
field: 'invitationDate',
headerName: 'Invitation Date',
flex: 1,
minWidth: 160,
renderCell: (params) => (
<Stack
spacing={0.5}
sx={{
height: 1,
lineHeight: 1,
textAlign: 'right',
justifyContent: 'center',
}}
>
<Box component="span">{fDate(params.row.invitationDate)}</Box>
<Box component="span" sx={{ color: 'text.secondary', typography: 'caption' }}>
{fTime(params.row.invitationDate)}
</Box>
</Stack>
),
},
{
type: 'number',
field: 'score',
headerName: 'Score',
width: 140,
renderCell: (params) => (
<Rating size="small" value={params.row.score} precision={0.5} readOnly />
),
},
];
// ----------------------------------------------------------------------
type Props = {
data: {
id: string;
testName: string;
recruteur: string;
condidat: string;
invitationDate: IDateValue;
score: number;
}[];
};
const HIDE_COLUMNS = { id: false };
const HIDE_COLUMNS_TOGGLABLE = ['id', 'actions'];
export function DataGridCustom({ data: rows }: Props) {
const [filterButtonEl, setFilterButtonEl] = useState<HTMLButtonElement | null>(null);
const [selectedRows, setSelectedRows] = useState<GridRowSelectionModel>([]);
const [columnVisibilityModel, setColumnVisibilityModel] =
useState<GridColumnVisibilityModel>(HIDE_COLUMNS);
const columns = useMemo(
() =>
baseColumns.map((col) =>
col.field === 'score' ? { ...col, filterOperators: ratingOnlyOperators } : col
),
[]
);
const getTogglableColumns = () =>
columns
.filter((column) => !HIDE_COLUMNS_TOGGLABLE.includes(column.field))
.map((column) => column.field);
const selected = rows.filter((row) => selectedRows.includes(row.id)).map((_row) => _row.id);
console.info('SELECTED ROWS', selected);
return (
<DataGrid
checkboxSelection
disableRowSelectionOnClick
rows={rows}
columns={columns}
onRowSelectionModelChange={(newSelectionModel) => {
setSelectedRows(newSelectionModel);
}}
columnVisibilityModel={columnVisibilityModel}
onColumnVisibilityModelChange={(newModel) => setColumnVisibilityModel(newModel)}
slots={{
toolbar: CustomToolbar as GridSlots['toolbar'],
noRowsOverlay: () => <EmptyContent />,
noResultsOverlay: () => <EmptyContent title="No results found" />,
}}
slotProps={{
panel: { anchorEl: filterButtonEl },
toolbar: { setFilterButtonEl, showQuickFilter: true },
columnsManagement: { getTogglableColumns },
}}
sx={{ [`& .${gridClasses.cell}`]: { alignItems: 'center', display: 'inline-flex' } }}
/>
);
}
// ----------------------------------------------------------------------
interface CustomToolbarProps {
setFilterButtonEl: React.Dispatch<React.SetStateAction<HTMLButtonElement | null>>;
}
function CustomToolbar({ setFilterButtonEl }: CustomToolbarProps) {
return (
<GridToolbarContainer>
<GridToolbarQuickFilter />
<Box sx={{ flexGrow: 1 }} />
<GridToolbarColumnsButton />
<GridToolbarFilterButton ref={setFilterButtonEl} />
<GridToolbarDensitySelector />
<GridToolbarExport />
</GridToolbarContainer>
);
}
// ----------------------------------------------------------------------
function RatingInputValue({ item, applyValue, focusElementRef }: GridFilterInputValueProps) {
const ratingRef: React.Ref<any> = useRef(null);
useImperativeHandle(focusElementRef, () => ({
focus: () => {
ratingRef.current.querySelector(`input[value="${Number(item.value) || ''}"]`).focus();
},
}));
const handleFilter: RatingProps['onChange'] = (event, newValue) => {
applyValue({ ...item, value: newValue });
};
return (
<Rating
ref={ratingRef}
precision={0.5}
value={Number(item.value)}
onChange={handleFilter}
name="custom-rating-filter-operator"
sx={{ ml: 2 }}
/>
);
}
const ratingOnlyOperators: GridFilterOperator[] = [
{
label: 'Above',
value: 'above',
getApplyFilterFn: (filterItem: GridFilterItem) => {
if (!filterItem.field || !filterItem.value || !filterItem.operator) {
return null;
}
return (params): boolean => Number(params.value) >= Number(filterItem.value);
},
InputComponent: RatingInputValue,
InputComponentProps: { type: 'number' },
getValueAsString: (value: number) => `${value} Stars`,
},
];
\ No newline at end of file
import {EditEmail } from './view';
import { ProductEditView} from './product-edit-view'
export function Main(){
return <>
<EditEmail/>
< ProductEditView/>
</>
}
\ No newline at end of file
'use client';
import type { IProductItem } from 'src/types/product';
import Box from '@mui/material/Box';
import Switch from '@mui/material/Switch';
import { ProductNewEditForm } from './product-new-edit-form';
// ----------------------------------------------------------------------
type Props = {
product?: IProductItem;
};
export function ProductEditView({ product }: Props) {
return <>
<ProductNewEditForm currentProduct={product} />
<Box >
<Switch inputProps={{ id: 'publish-switch' }} />
Enregistrez cet e-mail d invitation pour ce test
</Box>
</>
}
import type { IProductItem } from 'src/types/product';
import { z as zod } from 'zod';
import { useForm } from 'react-hook-form';
import { useMemo, useState, useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import {
PRODUCT_CATEGORY_GROUP_OPTIONS,
} from 'src/shared/_mock';
import { toast } from 'src/shared/components/snackbar';
import { Form, Field, schemaHelper } from 'src/shared/components/hook-form';
// ----------------------------------------------------------------------
export type NewProductSchemaType = zod.infer<typeof NewProductSchema>;
export const NewProductSchema = zod.object({
name: zod.string().min(1, { message: 'Name is required!' }),
description: schemaHelper.editor({ message: { required_error: 'Description is required!' } }),
images: schemaHelper.files({ message: { required_error: 'Images is required!' } }),
code: zod.string().min(1, { message: 'Product code is required!' }),
sku: zod.string().min(1, { message: 'Product sku is required!' }),
quantity: zod.number().min(1, { message: 'Quantity is required!' }),
colors: zod.string().array().nonempty({ message: 'Choose at least one option!' }),
sizes: zod.string().array().nonempty({ message: 'Choose at least one option!' }),
tags: zod.string().array().min(2, { message: 'Must have at least 2 items!' }),
gender: zod.string().array().nonempty({ message: 'Choose at least one option!' }),
price: zod.number().min(1, { message: 'Price should not be $0.00' }),
// Not required
category: zod.string(),
priceSale: zod.number(),
subDescription: zod.string(),
taxes: zod.number(),
saleLabel: zod.object({ enabled: zod.boolean(), content: zod.string() }),
newLabel: zod.object({ enabled: zod.boolean(), content: zod.string() }),
});
// ----------------------------------------------------------------------
type Props = {
currentProduct?: IProductItem;
};
export function ProductNewEditForm({ currentProduct }: Props) {
const [includeTaxes] = useState(false);
const defaultValues = useMemo(
() => ({
name: currentProduct?.name || '',
description: currentProduct?.description || '',
subDescription: currentProduct?.subDescription || '',
images: currentProduct?.images || [],
//
code: currentProduct?.code || '',
sku: currentProduct?.sku || '',
price: currentProduct?.price || 0,
quantity: currentProduct?.quantity || 0,
priceSale: currentProduct?.priceSale || 0,
tags: currentProduct?.tags || [],
taxes: currentProduct?.taxes || 0,
gender: currentProduct?.gender || [],
category: currentProduct?.category || PRODUCT_CATEGORY_GROUP_OPTIONS[0].classify[1],
colors: currentProduct?.colors || [],
sizes: currentProduct?.sizes || [],
newLabel: currentProduct?.newLabel || { enabled: false, content: '' },
saleLabel: currentProduct?.saleLabel || { enabled: false, content: '' },
}),
[currentProduct]
);
const methods = useForm<NewProductSchemaType>({
resolver: zodResolver(NewProductSchema),
defaultValues,
});
const {
reset,
setValue,
handleSubmit,
} = methods;
useEffect(() => {
if (currentProduct) {
reset(defaultValues);
}
}, [currentProduct, defaultValues, reset]);
useEffect(() => {
if (includeTaxes) {
setValue('taxes', 0);
} else {
setValue('taxes', currentProduct?.taxes || 0);
}
}, [currentProduct?.taxes, includeTaxes, setValue]);
const onSubmit = handleSubmit(async (data) => {
try {
await new Promise((resolve) => setTimeout(resolve, 500));
reset();
toast.success(currentProduct ? 'Update success!' : 'Create success!');
console.info('DATA', data);
} catch (error) {
console.error(error);
}
});
const renderDetails = (
<Stack spacing={3} sx={{ p: 3 }}>
<Stack spacing={1.5}>
<Stack direction="row" alignItems="center" spacing={1}>
<Typography variant="subtitle2">Corps de l email</Typography>
</Stack>
<Field.Editor name="description" sx={{ maxHeight: 480 }} />
</Stack>
</Stack>
);
return (
<Form methods={methods} onSubmit={onSubmit}>
<Stack spacing={{ xs: 3, md: 5 }} sx={{ mr: 'auto', maxWidth: { xs: 720, xl: 880 } }}>
{renderDetails}
</Stack>
</Form>
);
}
'use client';
import React from 'react';
import { Icon } from '@iconify/react';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Autocomplete from '@mui/material/Autocomplete';
export function EditEmail(){
return (
<>
<Box display="flex" alignItems="center" >
<Icon icon="ic:outline-email" style={{ fontSize: '60px' }} />
<Typography variant="h3"> Modifier Invitation par email</Typography>
</Box>
<Typography paragraph sx ={{mt : 3,mb : 3}} >
Test name: Mobile - Android, CSS, Flutter, HTML, JavaScript - Senior
</Typography>
<Autocomplete
freeSolo
options={[]}
getOptionLabel={(option) => option}
renderInput={(params) => <TextField {...params} label="Objet" style={{ width: '800px' }} />}
/>
</>
);
}
\ No newline at end of file
import {EmailInvitation } from './view';
export function Main(){
return <EmailInvitation/>
}
\ No newline at end of file
'use client';
import React from 'react';
import { Icon } from '@iconify/react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { ComponentContainer } from 'src/components/component-block';
export function EmailInvitation(){
return (
<ComponentContainer>
<Box display="flex" alignItems="center" >
<Icon icon="ic:outline-email" style={{ fontSize: '60px' }} />
<Typography variant="h3">Invitation par email</Typography>
</Box>
<Typography paragraph>
Test name: Mobile - Android, CSS, Flutter, HTML, JavaScript - Senior
</Typography>
<Box width = "80%"sx ={{border: '1px Dashed black' ,padding: '16px'}} >
<Typography width = "60%" >
Objet : Évaluation technique
</Typography>
<a href="" style={{ marginLeft: 650 }}>Modifier l email</a>
<Typography>
Bonjour mehdi,
</Typography>
<Typography paragraph sx = {{mt :3}}>
Votre candidature a retenu notre attention et nous vous invitons à passer une évaluation technique dans le cadre de notre processus de recrutement. </Typography>
<Typography paragraph sx = {{mt :3}}>
Vous êtes libre de choisir le moment qui vous convient le mieux pour commencer votre évaluation. Lorsque vous êtes prêt, veuillez cliquer sur ce lien ci-dessous : </Typography>
<a href=""> Ouvrir le test</a>
<Typography paragraph sx = {{mt :3}}>
Bonne chance!
</Typography>
</Box>
</ComponentContainer>
);
}
\ No newline at end of file
import {InviteCondidate} from './invite_condidate';
export function Main (){
return( <InviteCondidate/>
);
}
\ No newline at end of file
'use client';
import { Icon } from '@iconify/react';
import React, { useState } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Autocomplete from '@mui/material/Autocomplete';
import { ComponentContainer } from 'src/components/component-block';
export function InviteCondidate() {
const [showAdditionalComponent, setShowAdditionalComponent] = useState(false);
const handleNomClick = () => {
setShowAdditionalComponent(true);
};
return (
<ComponentContainer>
<Box display="flex" alignItems="center">
<Icon icon="solar:zip-file-bold" style={{ fontSize: '60px' }} />
<Typography variant="h3">Invite candidates</Typography>
</Box>
<Typography paragraph>
Test name: Mobile - Android, CSS, Flutter, HTML, JavaScript - Senior
</Typography>
<Box display="flex" flexDirection="row" gap={4}>
<Autocomplete
freeSolo
options={[]}
getOptionLabel={(option) => option}
renderInput={(params) => (
<TextField
{...params}
label="Nom"
style={{ width: '250px' }}
onClick={handleNomClick}
/>
)}
/>
<Autocomplete
freeSolo
options={[]}
getOptionLabel={(option) => option}
renderInput={(params) => <TextField {...params} label="Email" style={{ width: '250px' }} />}
/>
</Box>
{showAdditionalComponent && (
<Box display="flex" flexDirection="row" gap={4} sx={{ mt: 3 }}>
<Autocomplete
freeSolo
options={[]}
getOptionLabel={(option) => option}
renderInput={(params) => <TextField {...params} onClick={handleNomClick}
label="Nom" style={{ width: '250px' }} />}
/>
<Autocomplete
freeSolo
options={[]}
getOptionLabel={(option) => option}
renderInput={(params) => <TextField {...params} label="Email" onClick={handleNomClick} style={{ width: '250px' }} /> }
/>
</Box>
)}
<Button variant="contained" style={{ width: '300px' }} sx={{ ml: 12 }}>
L’étape prochaine
</Button>
</ComponentContainer>
);
}
\ No newline at end of file
import type { IProductItem } from 'src/types/product';
import { z as zod } from 'zod';
import { Icon } from '@iconify/react';
import { Icon } from '@iconify/react';
import { useForm } from 'react-hook-form';
import { useMemo, useState, useEffect} from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useMemo, useState, useEffect, useCallback } from 'react';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { useRouter } from 'src/routes/hooks';
import {
PRODUCT_CATEGORY_GROUP_OPTIONS,
} from 'src/shared/_mock';
......@@ -50,9 +50,9 @@ type Props = {
};
export function ProductNewEditForm({ currentProduct }: Props) {
const router = useRouter();
const [includeTaxes, setIncludeTaxes] = useState(false);
const [includeTaxes] = useState(false);
const defaultValues = useMemo(
() => ({
......@@ -85,13 +85,10 @@ export function ProductNewEditForm({ currentProduct }: Props) {
const {
reset,
watch,
setValue,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
if (currentProduct) {
......@@ -119,21 +116,9 @@ export function ProductNewEditForm({ currentProduct }: Props) {
}
});
const handleRemoveFile = useCallback(
(inputFile: File | string) => {
const filtered = values.images && values.images?.filter((file) => file !== inputFile);
setValue('images', filtered);
},
[setValue, values.images]
);
const handleRemoveAllFiles = useCallback(() => {
setValue('images', [], { shouldValidate: true });
}, [setValue]);
const handleChangeIncludeTaxes = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setIncludeTaxes(event.target.checked);
}, []);
const renderDetails = (
......
'use client';
import { Icon } from '@iconify/react';
import { Icon } from '@iconify/react';
import Box from '@mui/material/Box';
import Switch from '@mui/material/Switch';
......@@ -12,7 +11,8 @@ import Autocomplete from '@mui/material/Autocomplete';
export function Reponse() {
return (
<Box display="flex" flexDirection="row" gap={1} alignItems="center">
<Box display="flex" flexDirection="row" gap={1} alignItems="center">
<Icon icon="gg:add" style={{ fontSize: '32px' }}/>
<Autocomplete
freeSolo
......@@ -31,5 +31,6 @@ export function Reponse() {
</Box>
</Box>
);
}
\ No newline at end of file
0% ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter