Skip to content
Extraits de code Groupes Projets
Valider 258af877 rédigé par Mohamed Lemine BAILLAHI's avatar Mohamed Lemine BAILLAHI
Parcourir les fichiers

Merge branch 'feature/MYD-585' into 'develop'

MYD-585/Allow image field to be nullable in the user form

Closes MYD-585

See merge request !226
parents 8dddc25a 01d758b3
1 requête de fusion!226MYD-585/Allow image field to be nullable in the user form
Pipeline #5324 réussi avec les étapes
in 24 minutes et 21 secondes
......@@ -124,23 +124,24 @@ export const changeUserStatus= async (requestChangeUser : RequestChangeUserStatu
throw error;
}
};
export const saveUser = async (userdtoJson: string, imageFile: File) => {
export const saveUser = async (userdtoJson: string, imageFile?: File) => {
try {
const formData = new FormData();
formData.append("user", userdtoJson);
formData.append("imageFile", imageFile);
if (imageFile) {
formData.append("imageFile", imageFile);
}
const response = await axiosInstance.post(endpoints.user.add, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
return response.data;
} catch (error) {
throw error;
}
};
export const updateUser = async (userDtoJson: string, imageFile?: File) => {
try {
const formData = new FormData();
......
......@@ -8,7 +8,7 @@ import Container from '@mui/material/Container';
import { paths } from '@/routes/paths';
import {_userInvoices, _userAddressBook } from '../../../_mock';
import { _userInvoices, _userAddressBook } from '../../../_mock';
import Iconify from '@/components/iconify';
import { useSettingsContext } from '@/components/settings';
......@@ -24,7 +24,7 @@ import AccountChangePassword from '../account-change-password';
const TABS = [
{
value: 'general',
label: 'General',
label: 'Général',
icon: <Iconify icon="solar:user-id-bold" width={24} />,
},
// {
......@@ -35,7 +35,7 @@ const TABS = [
{
value: 'security',
label: 'Security',
label: 'Sécurité',
icon: <Iconify icon="ic:round-vpn-key" width={24} />,
},
];
......@@ -54,11 +54,11 @@ export default function AccountView() {
return (
<Container maxWidth={settings.themeStretch ? false : 'lg'}>
<CustomBreadcrumbs
heading="Account"
heading="Compte"
links={[
{ name: 'Dashboard', href: paths.dashboard.root },
{ name: 'User', href: paths.dashboard.admin.user.all_users },
{ name: 'Account' },
{ name: 'Tableau de bord', href: paths.dashboard.root },
{ name: 'Utilisateur', href: paths.dashboard.admin.user.all_users },
{ name: 'Compte' },
]}
sx={{
mb: { xs: 3, md: 5 },
......@@ -85,4 +85,3 @@ export default function AccountView() {
</Container>
);
}
......@@ -18,17 +18,17 @@ export default function UserCreateView() {
return (
<Container maxWidth={settings.themeStretch ? false : 'lg'}>
<CustomBreadcrumbs
heading="Create a new user"
heading="Créer un nouvel utilisateur"
links={[
{
name: 'Dashboard',
name: 'Tableau de bord',
href: paths.dashboard.root,
},
{
name: 'User',
name: 'Utilisateur',
href: paths.dashboard.admin.user.all_users,
},
{ name: 'New user' },
{ name: 'Nouvel utilisateur' },
]}
sx={{
mb: { xs: 3, md: 5 },
......
import React, { useCallback, useState } from "react";
import { useForm} from "react-hook-form";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import Stack from "@mui/material/Stack";
import Grid from "@mui/material/Grid";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import LoadingButton from "@mui/lab/LoadingButton";
import { useRouter } from "../../../../hooks/use-router";
......@@ -53,23 +52,30 @@ const NewUserSchema = Yup.object().shape({
lastName: Yup.string().required("Le nom est requis"),
phoneNumber: Yup.string()
.required("Le numéro de téléphone est requis")
.matches(/^[0-9]+$/, "Le numéro de téléphone doit contenir uniquement des chiffres")
.min(10, "Le numéro de téléphone doit comporter au moins 10 chiffres")
.max(15, "Le numéro de téléphone doit comporter au maximum 15 chiffres"),
imageUrl: Yup.string().nullable(),
password: Yup.string()
.required("Le mot de passe est requis")
.min(8, "Le mot de passe doit comporter au moins 8 caractères")
.matches(/[A-Z]/, "Le mot de passe doit comporter au moins une lettre majuscule")
.matches(/[a-z]/, "Le mot de passe doit comporter au moins une lettre minuscule")
.matches(
/[A-Z]/,
"Le mot de passe doit comporter au moins une lettre majuscule"
)
.matches(
/[a-z]/,
"Le mot de passe doit comporter au moins une lettre minuscule"
)
.matches(/[0-9]/, "Le mot de passe doit comporter au moins un chiffre")
.matches(/[!@#$%^&*(),.?":{}|<>]/, "Le mot de passe doit comporter au moins un caractère spécial"),
.matches(
/[!@#$%^&*(),.?":{}|<>]/,
"Le mot de passe doit comporter au moins un caractère spécial"
),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password")], "Les mots de passe doivent correspondre")
.required("La confirmation du mot de passe est requise"),
});
export type IRole = {
id: number | null;
name: string;
......@@ -95,13 +101,14 @@ export default function UserNewEditForm() {
const [imageFile, setImageFile] = useState<File | null>(null);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");
const [snackbarSeverity, setSnackbarSeverity] = useState<
"success" | "error"
>("success");
const [snackbarSeverity, setSnackbarSeverity] = useState<"success" | "error">(
"success"
);
const { roleData, roleError } = useGetRoles();
const handleTogglePasswordVisibility = () => setShowPassword((prev) => !prev);
const handleToggleConfirmPasswordVisibility = () => setShowConfirmPassword((prev) => !prev);
const handleToggleConfirmPasswordVisibility = () =>
setShowConfirmPassword((prev) => !prev);
const defaultValues: UserFormValues = {
pseudo: "",
......@@ -140,31 +147,27 @@ export default function UserNewEditForm() {
roles: { id: null, name: data.roles },
status: isVerified ? "APPROVED" : "PENDING",
};
const userdtoJson = JSON.stringify(userItem);
let renamedFile;
if (imageFile) {
const uniqueFileName = `${uuidv4()}-${imageFile.name}`;
const renamedFile = new File([imageFile], uniqueFileName, {
type: imageFile.type,
const uniqueFileName = `${uuidv4()}-${imageFile?.name}`;
renamedFile = new File([imageFile], uniqueFileName, {
type: imageFile?.type,
});
await saveUser(userdtoJson, renamedFile);
enqueueSnackbar("Create success!", { variant: "success" });
setSnackbarMessage("Create success!");
setSnackbarSeverity("success");
setSnackbarOpen(true);
reset();
} else {
enqueueSnackbar("Please upload an image", { variant: "warning" });
setSnackbarMessage("Please upload an image");
setSnackbarSeverity("error");
setSnackbarOpen(true);
}
else{
await saveUser(userdtoJson);
}
enqueueSnackbar("Création réussie!", { variant: "success" });
setSnackbarMessage("Création réussie!");
setSnackbarSeverity("success");
setSnackbarOpen(true);
reset();
} catch (error) {
console.error(error);
enqueueSnackbar("Create failed!", { variant: "error" });
setSnackbarMessage("Create failed!");
enqueueSnackbar("Échec de la création!", { variant: "error" });
setSnackbarMessage("Échec de la création!");
setSnackbarSeverity("error");
setSnackbarOpen(true);
}
......@@ -220,8 +223,8 @@ export default function UserNewEditForm() {
color: "text.disabled",
}}
>
Allowed *.jpeg, *.jpg, *.png
<br /> max size of {fData(3145728)}
Formats autorisés: *.jpeg, *.jpg, *.png
<br /> taille max {fData(3145728)}
</Typography>
}
/>
......@@ -234,11 +237,10 @@ export default function UserNewEditForm() {
label={
<>
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
Email Verified
Email rifié
</Typography>
<Typography variant="body2" sx={{ color: "text.secondary" }}>
Enabling this option will automatically send the
verification email.
Activer cette option enverra automatiquement l'email de vérification.
</Typography>
</>
}
......@@ -249,12 +251,12 @@ export default function UserNewEditForm() {
<Grid item xs={12} md={8}>
<Card>
<Stack spacing={3} sx={{ p: 3 }}>
<Typography variant="h6">User Information</Typography>
<Typography variant="h6">Informations utilisateur</Typography>
<RHFTextField name="firstName" label="Nom" />
<RHFTextField name="lastName" label="Prenom" />
<RHFTextField name="lastName" label="Prénom" />
<RHFTextField name="pseudo" label="Pseudo" />
<RHFTextField name="phoneNumber" label="Numero de telephone" />
<RHFTextField name="phoneNumber" label="Numéro de téléphone" />
<RHFTextField name="email" label="Email" />
<RHFTextField
name="password"
......@@ -272,13 +274,19 @@ export default function UserNewEditForm() {
/>
<RHFTextField
name="confirmPassword"
label="Confirmation de mot de passe"
label="Confirmation du mot de passe"
type={showConfirmPassword ? "text" : "password"}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleToggleConfirmPasswordVisibility}>
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
<IconButton
onClick={handleToggleConfirmPasswordVisibility}
>
{showConfirmPassword ? (
<VisibilityOff />
) : (
<Visibility />
)}
</IconButton>
</InputAdornment>
),
......@@ -286,19 +294,20 @@ export default function UserNewEditForm() {
/>
<RHFSelect
name="roles"
label="Roles"
placeholder="Select role"
label="Rôles"
placeholder="Sélectionnez un rôle"
onChange={(event) => {
setValue("roles", event.target.value, {
shouldValidate: true,
});
}}
>
{roleData && roleData.map((role) => (
<MenuItem key={role.id} value={role.name}>
{role.name}
</MenuItem>
))}
{roleData &&
roleData.map((role) => (
<MenuItem key={role.id} value={role.name}>
{role.name}
</MenuItem>
))}
</RHFSelect>
<Stack spacing={3} sx={{ mt: 5 }}>
......@@ -309,7 +318,7 @@ export default function UserNewEditForm() {
variant="contained"
loading={isSubmitting}
>
Save Changes
Enregistrer les modifications
</LoadingButton>
</Stack>
</Stack>
......@@ -331,4 +340,4 @@ export default function UserNewEditForm() {
</Snackbar>
</FormProvider>
);
}
\ No newline at end of file
}
......@@ -16,23 +16,23 @@ export default function UserRoleEditorView() {
return (
<Container maxWidth={settings.themeStretch ? false : "lg"}>
<CustomBreadcrumbs
heading="User Role Editor"
heading="Éditeur de Rôle Utilisateur"
links={[
{
name: "Dashboard",
name: "Tableau de bord",
href: paths.dashboard.root,
},
{
name: "User",
name: "Utilisateur",
href: paths.dashboard.admin.user.all_users,
},
{ name: "Role editor" },
{ name: "Éditeur de rôle" },
]}
sx={{
mb: { xs: 3, md: 5 },
}}
/>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="contained"
startIcon={<EditOutlinedIcon />}
......@@ -40,7 +40,7 @@ export default function UserRoleEditorView() {
mb: { xs: 3, md: 5 },
}}
>
Update Role
Mettre à jour le rôle
</Button>
</div>
......
......@@ -69,14 +69,14 @@ export default function UserRoleEditor() {
<Grid container spacing={2}>
<Grid item xs={12} sm={8}>
<Card>
<CardHeader title={`Select a role and change its permissions`} />
<CardHeader title={`Sélectionnez un rôle et modifiez ses permissions`} />
<CardContent>
<Grid container spacing={1}>
<Grid item xs={12} sm={6}>
<FormControl fullWidth>
<InputLabel id="select-label">Role</InputLabel>
<InputLabel id="select-label">Rôle</InputLabel>
<Select
label="Role"
label="Rôle"
labelId="select-label"
MenuProps={{
PaperProps: {
......@@ -120,7 +120,7 @@ export default function UserRoleEditor() {
startIcon={<Add />}
sx={{ width: "100%", justifyContent: "flex-start" }}
>
Add Role
Ajouter un rôle
</Button>
<AddRoleDialog
open={openAddRoleDialog}
......@@ -135,7 +135,7 @@ export default function UserRoleEditor() {
sx={{ width: "100%", justifyContent: "flex-start" }}
onClick={handleOpenAddPermissionDialog}
>
Add Permission
Ajouter une permission
</Button>
<AddPermissionDialog
open={openAddPermissionDialog}
......@@ -149,7 +149,7 @@ export default function UserRoleEditor() {
sx={{ width: "100%", justifyContent: "flex-start" }}
onClick={handleOpenDeletePermissionDialog}
>
Delete Permission
Supprimer une permission
</Button>
<DeletePermissionDialog
open={openDeletePermissionDialog}
......
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