Skip to content
Extraits de code Groupes Projets
Valider a39c622e rédigé par salaheddine zidani's avatar salaheddine zidani
Parcourir les fichiers

integrate media management frontend with backend

parent 1fa8a02f
Branches
1 requête de fusion!122MYD-304/ integrate media management frontend with backend
Pipeline #2284 en échec avec l'étape
in 3 minutes et 45 secondes
Affichage de
avec 350 ajouts et 266 suppressions
import { useMemo } from "react";
import useSWR from "swr";
import { _orders } from "../_mock";
import { endpoints } from './server';
import { mutate, revalidateEvents } from "swr/_internal";
import { success } from "@/theme/palette";
import { IMedia } from "../types/media";
import { title } from "process";
import { Description } from "@mui/icons-material";
import { AxiosResponse } from "axios";
import axios, { AxiosRequestConfig } from 'axios';
import { comment } from 'postcss';
import { shippingClassesList } from '../_mock/_shipping';
export const HOST_API = 'http://localhost:8082';
export const axiosInstance = axios.create({ baseURL: HOST_API });
axiosInstance.interceptors.response.use(
(res) => res,
(error) => Promise.reject((error.response && error.response.data) || 'Something went wrong')
);
export default axiosInstance;
export const fetcher = async (args: string | [string, AxiosRequestConfig]) => {
const [url, config] = Array.isArray(args) ? args : [args];
const res = await axiosInstance.get(url, { ...config });
return res.data;
};
const options = {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
}
///////////////////-----------Media Management----------///////////////////////
//////////////////////////////////////////////////////////////////////////////
export function useGetMedias() {
const URL = [endpoints.media.getAll];
const { data, error } = useSWR<IMedia[]>(URL, fetcher, options);
const memoizedValue = useMemo(
() => ({
mediasData: (data as IMedia[]) || [],
mediasError: error,
}),
[data, error]
);
return memoizedValue;
}
export function useGetMedia(mediaId: string) {
const { data, error } = useSWR<IMedia>(
mediaId ? endpoints.media.getById(mediaId) : null,
fetcher
);
const memoizedValue = useMemo(
() => ({
mediaData: data,
mediaError: error,
}),
[data, error]
);
return memoizedValue;
}
export const useUploadMedias = async (formData: FormData): Promise<void> => {
try {
let url = endpoints.media.add;
const userId = "159";
formData.append('userId', userId);
const response: AxiosResponse = await axiosInstance.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
mutate(endpoints.media.getAll);
} catch (error) {
throw error;
}
};
export async function useUpdateMedia(id: string, updatedMedia: any) {
const formData = {
id: updatedMedia.id,
title: updatedMedia.title,
description: updatedMedia.description,
userId: updatedMedia.userId,
}
const response = await axiosInstance.put(endpoints.media.edit(id), formData);
const updatedData: IMedia = response.data;
mutate([endpoints.media.getAll], (medias: IMedia[] = []) =>
medias.map((media) =>
media.id === id ? updatedData : media
),
true
);
return response.data as IMedia;
}
export async function useDeleteMedia(mediaIDs: string[]) {
const response = await axiosInstance.delete(endpoints.media.delete(mediaIDs));
mutate(endpoints.media.getAll);
return response.status;
}
......@@ -3,7 +3,7 @@ import { comment } from 'postcss';
import { shippingClassesList } from '../_mock/_shipping';
export const HOST_API = 'http://localhost:8080';
export const HOST_API = 'http://localhost:8081';
export const axiosInstance = axios.create({ baseURL: HOST_API });
......@@ -93,5 +93,13 @@ export const endpoints = {
edit:(shippingAreaId:string)=> `/shipping/shipping-area/${shippingAreaId}`,
delete:(shippingAreaId:string)=> `/shipping/shipping-area/${shippingAreaId}`,
},
}
},
media:{
getAll: `/api/medias/all-medias`,
getById:(mediaId:string)=> `/api/medias/${mediaId}`,
add: "/api/medias/upload-medias",
edit:(mediaId:string)=> `/api/medias/update-media/${mediaId}`,
delete:(mediaIDs:string[])=> `/api/medias/delete/${mediaIDs}`,
download:(mediaId:string)=> `/api/medias/download`
},
};
......@@ -2,9 +2,33 @@ import { Colissimo, FlatRate, FreeShipping, IShippingAreaItem, MondialRelay, Poi
import { useMemo } from "react";
import useSWR from "swr";
import { _orders } from "../_mock";
import axiosInstance, { fetcher, endpoints } from './server';
import { endpoints } from './server';
import { mutate, revalidateEvents } from "swr/_internal";
import { success } from "@/theme/palette";
import axios, { AxiosRequestConfig } from 'axios';
import { comment } from 'postcss';
import { shippingClassesList } from '../_mock/_shipping';
export const HOST_API = 'http://localhost:8080';
export const axiosInstance = axios.create({ baseURL: HOST_API });
axiosInstance.interceptors.response.use(
(res) => res,
(error) => Promise.reject((error.response && error.response.data) || 'Something went wrong')
);
export default axiosInstance;
export const fetcher = async (args: string | [string, AxiosRequestConfig]) => {
const [url, config] = Array.isArray(args) ? args : [args];
const res = await axiosInstance.get(url, { ...config });
return res.data;
};
const options = {
......
......@@ -36,7 +36,7 @@ export default function MediaManagerFileDetails({
...other
}: Props) {
const { title, name, size, url, type, modifiedAt, createdAt, author, description } = item;
const { title, name, size, url, fileType, modifiedAt, createdAt, userId, description } = item;
const properties = useBoolean(true);
......@@ -55,7 +55,7 @@ export default function MediaManagerFileDetails({
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Téléversé par :
</Box>
{author}
michel
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
......@@ -75,12 +75,6 @@ export default function MediaManagerFileDetails({
</Box>
{fData(size)}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Type :
</Box>
{fileFormat(type)}
</Stack>
</>
)}
</Stack>
......@@ -113,12 +107,6 @@ export default function MediaManagerFileDetails({
bgcolor: 'background.neutral',
}}
>
<FileThumbnail
imageView
file={type === 'folder' ? type : url}
sx={{ width: 64, height: 64 }}
imgSx={{ borderRadius: 1 }}
/>
<Typography variant="subtitle2" sx={{ wordBreak: 'break-all' }}>
{title}
......
......@@ -39,15 +39,6 @@ export default function MediaManagerFiltersResult({
onFilters('name', '');
}, [onFilters]);
const handleRemoveTypes = useCallback(
(inputValue: string) => {
const newValue = filters.type.filter((item) => item !== inputValue);
onFilters('type', newValue);
},
[filters.type, onFilters]
);
const handleRemoveDate = useCallback(() => {
onFilters('startDate', null);
onFilters('endDate', null);
......@@ -63,14 +54,7 @@ export default function MediaManagerFiltersResult({
</Box>
<Stack flexGrow={1} spacing={1} direction="row" flexWrap="wrap" alignItems="center">
{!!filters.type.length && (
<Block label="Types:">
{filters.type.map((item) => (
<Chip key={item} label={item} size="small" onDelete={() => handleRemoveTypes(item)} />
))}
</Block>
)}
{filters.startDate && filters.endDate && (
<Block label="Date:">
<Chip size="small" label={shortLabel} onDelete={handleRemoveDate} />
......
......@@ -44,7 +44,6 @@ export default function MediaManagerFilters({
}: Props) {
const popover = usePopover();
const renderLabel = filters.type.length ? filters.type.slice(0, 2).join(',') : 'All type';
const handleFilterName = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
......@@ -67,21 +66,6 @@ export default function MediaManagerFilters({
[onFilters]
);
const handleFilterType = useCallback(
(newValue: string) => {
const checked = filters.type.includes(newValue)
? filters.type.filter((value) => value !== newValue)
: [...filters.type, newValue];
onFilters('type', checked);
},
[filters.type, onFilters]
);
const handleResetType = useCallback(() => {
popover.onClose();
onFilters('type', []);
}, [onFilters, popover]);
const renderFilterName = (
<TextField
value={filters.name}
......@@ -100,76 +84,6 @@ export default function MediaManagerFilters({
/>
);
const renderFilterType = (
<>
<Button
color="inherit"
onClick={popover.onOpen}
endIcon={
<Iconify
icon={popover.open ? 'eva:arrow-ios-upward-fill' : 'eva:arrow-ios-downward-fill'}
sx={{ ml: -0.5 }}
/>
}
>
{renderLabel}
{filters.type.length > 2 && (
<Label color="info" sx={{ ml: 1 }}>
+{filters.type.length - 2}
</Label>
)}
</Button>
<CustomPopover open={popover.open} onClose={popover.onClose} sx={{ p: 2.5 }}>
<Stack spacing={2.5}>
<Box
gap={1}
display="grid"
gridTemplateColumns={{
xs: 'repeat(2, 1fr)',
sm: 'repeat(4, 1fr)',
}}
>
{typeOptions.map((type) => {
const selected = filters.type.includes(type);
return (
<CardActionArea
key={type}
onClick={() => handleFilterType(type)}
sx={{
p: 1,
borderRadius: 1,
cursor: 'pointer',
border: (theme) => `solid 1px ${alpha(theme.palette.grey[500], 0.08)}`,
...(selected && {
bgcolor: 'action.selected',
}),
}}
>
<Stack spacing={1} direction="row" alignItems="center">
<FileThumbnail file={type} />
<Typography variant={selected ? 'subtitle2' : 'body2'}>{type}</Typography>
</Stack>
</CardActionArea>
);
})}
</Box>
<Stack spacing={1.5} direction="row" alignItems="center" justifyContent="flex-end">
<Button variant="outlined" color="inherit" onClick={handleResetType}>
Vider
</Button>
<Button variant="contained" onClick={popover.onClose}>
Valider
</Button>
</Stack>
</Stack>
</CustomPopover>
</>
);
const renderFilterDate = (
<>
<Button
......@@ -212,8 +126,6 @@ export default function MediaManagerFilters({
<Stack spacing={1} direction="row" alignItems="center" justifyContent="flex-end" flexGrow={1}>
{renderFilterDate}
{renderFilterType}
</Stack>
</Stack>
);
......
......@@ -65,12 +65,21 @@ export default function MediaManagerGridView({
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = dataFiltered.slice(indexOfFirstItem, indexOfLastItem);
const [imageUrl, setImageUrl] = useState('');
const extractUrl = (url: string) => {
const match = url.match(/Optional\[(.*)\]/);
return match ? match[1] : url;
setImageUrl(extractUrl(url));
};
return (
<>
<Box ref={containerRef}>
<MediaManagerPanel
title="Images"
subTitle={`${dataFiltered.filter((item) => item.type !== 'folder').length} files`}
subTitle={`images`}
onOpen={upload.onTrue}
/>
......@@ -88,11 +97,12 @@ export default function MediaManagerGridView({
<>
<Tooltip title={media.name}>
<Box>
<Card sx={{ width: "180", height: "180", maxHeight: "180", boxShadow: selected.includes(media.id) ? '0px 0px 10px 5px #228B22' : 'none' }} onClick={() => handleMediaClick(media)}>
<Card sx={{ height:200, boxShadow: selected.includes(media.id) ? '0px 0px 10px 5px #228B22' : '2px solid #ddd'}} onClick={() => handleMediaClick(media)}>
<CardActionArea>
<CardMedia
component="img"
image={media.url}
image={extractUrl(media.url)}
sx={{ height: '100%', objectFit: 'cover' }}
/>
<Checkbox
size="medium"
......
import { useState, useCallback } from 'react';
import { useState, useCallback, useEffect } from 'react';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
......@@ -48,7 +48,7 @@ type Props = {
export default function MediaManagerTableRow({ row, selected, onSelectRow, onEditRow, onDeleteRow }: Props) {
const theme = useTheme();
const { title, description, name, url, author, createdAt } = row;
const { title, description, name, userId, createdAt, modifiedAt, url } = row;
const { enqueueSnackbar } = useSnackbar();
......@@ -60,6 +60,17 @@ export default function MediaManagerTableRow({ row, selected, onSelectRow, onEdi
const popover = usePopover();
const [imageUrl, setImageUrl] = useState('');
useEffect(() => {
const extractUrl = (url:string) => {
const match = url.match(/Optional\[(.*)\]/);
return match ? match[1] : url;
};
setImageUrl(extractUrl(url));
}, [row])
const handleClick = useDoubleClick({
click: () => {
details.onTrue();
......@@ -118,9 +129,9 @@ export default function MediaManagerTableRow({ row, selected, onSelectRow, onEdi
</TableCell>
<TableCell onClick={handleClick} sx={{ whiteSpace: 'nowrap' }}>
<Grid container alignItems="center" spacing={1} mt={1} direction={"row"}>
<Grid container alignItems="center" spacing={1} direction={"row"}>
<Grid item md={2}>
<Box component="img" src={url} height={80}
<Box component="img" src={imageUrl}
sx={{
objectFit: 'cover',
objectPosition: 'center',
......@@ -140,13 +151,13 @@ export default function MediaManagerTableRow({ row, selected, onSelectRow, onEdi
</TableCell>
<TableCell onClick={handleClick} sx={{ whiteSpace: 'nowrap' }}>
{author}
Michel
</TableCell>
<TableCell onClick={handleClick} sx={{ whiteSpace: 'nowrap' }}>
<ListItemText
primary={fDate(createdAt)}
secondary={fTime(createdAt)}
primary={fDate(modifiedAt)}
secondary={fTime(modifiedAt)}
primaryTypographyProps={{ typography: 'body2' }}
secondaryTypographyProps={{
mt: 0.5,
......
......@@ -27,7 +27,7 @@ const TABLE_HEAD = [
{ id: 'url', label: '' },
{ id: 'name', label: 'Nom', width: 180 },
{ id: 'author', label: 'Auteur / Autrice', width: 180 },
{ id: 'createdAt', label: 'Téléverser le', width: 120 },
{ id: 'modifiedAt', label: 'Modifier le', width: 120 },
{ id: '', width: 88 },
];
......
......@@ -34,7 +34,7 @@ export default function MultiMediaConfirmDialog({
...other
}: Props) {
const { title, name, size, url, type, modifiedAt, createdAt, author, description } = item;
const { title, name, size, url, modifiedAt, createdAt, userId, description } = item;
const properties = useBoolean(true);
......@@ -76,7 +76,7 @@ export default function MultiMediaConfirmDialog({
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Téléversé par :
</Box>
{author}
Michel
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
......@@ -106,12 +106,6 @@ export default function MultiMediaConfirmDialog({
</Box>
{fData(size)}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Type :
</Box>
{fileFormat(type)}
</Stack>
</>
)}
</Stack>
......@@ -139,13 +133,6 @@ export default function MultiMediaConfirmDialog({
}}
>
<Stack direction={'row'}>
<Box height={'250px'} width={'250px'}>
<FileThumbnail
imageView
file={type === 'folder' ? type : url}
imgSx={{ borderRadius: 1 }}
/>
</Box>
<Box width={'50%'} sx={{ marginLeft: "20px" }}>
{renderPropertiesTop}
</Box>
......
......@@ -46,7 +46,7 @@ export default function MultiMediaGridView({
<Stack direction="row" alignItems="center" spacing={1} flexGrow={1}>
<Typography variant="h6"> Images </Typography>
</Stack>
<Box sx={{ typography: 'body2', color: 'text.disabled', mt: 0.5 }}>{`${dataFiltered.filter((item) => item.type !== 'folder').length} images`}</Box>
<Box sx={{ typography: 'body2', color: 'text.disabled', mt: 0.5 }}>{`images`}</Box>
</Stack>
{!!selected?.length && (
<>
......
......@@ -17,13 +17,13 @@ import { IMedia, IMediaFilters, IMediaFilterValue } from '@/shared/types/media';
import FileManagerFilters from '../../media-manager-filters';
import FileManagerFiltersResult from '../../media-manager-filters-result';
import MultiMediaGridView from './Multi-media-grid-view';
import { useGetMedias } from '@/shared/api/media';
// ----------------------------------------------------------------------
const defaultFilters: IMediaFilters = {
name: '',
type: [],
startDate: null,
endDate: null,
};
......@@ -43,7 +43,9 @@ export default function SelectMultiMediaView() {
const confirm = useBoolean();
const [tableData, setTableData] = useState<IMedia[]>(_allMedias);
const {mediasData, mediasError} = useGetMedias();
const [tableData, setTableData] = useState<IMedia[]>(mediasData);
const [filters, setFilters] = useState(defaultFilters);
......@@ -57,7 +59,7 @@ export default function SelectMultiMediaView() {
});
const canReset =
!!filters.name || !!filters.type.length || (!!filters.startDate && !!filters.endDate);
!!filters.name || (!!filters.startDate && !!filters.endDate);
const notFound = (!dataFiltered.length && canReset) || !dataFiltered.length;
......@@ -164,7 +166,7 @@ function applyFilter({
filters: IMediaFilters;
dateError: boolean;
}) {
const { name, type, startDate, endDate } = filters;
const { name, startDate, endDate } = filters;
const stabilizedThis = inputData.map((el, index) => [el, index] as const);
......@@ -182,10 +184,6 @@ function applyFilter({
);
}
if (type.length) {
inputData = inputData.filter((file) => type.includes(fileFormat(file.type)));
}
if (!dateError) {
if (startDate && endDate) {
inputData = inputData.filter((file) => isBetween(file.createdAt, startDate, endDate));
......
......@@ -33,7 +33,7 @@ export default function SingleMediaConfirmDialog({
...other
}: Props) {
const { title, name, size, url, type, modifiedAt, createdAt, author, description } = item;
const { title, name, size, url, modifiedAt, createdAt, userId, description } = item;
const properties = useBoolean(true);
......@@ -69,7 +69,7 @@ export default function SingleMediaConfirmDialog({
<Box component="span" sx={{ width: 82, color: 'text.secondary', mr: 2 }}>
Téléversé par :
</Box>
{author}
Michel
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 82, color: 'text.secondary', mr: 2 }}>
......@@ -99,12 +99,6 @@ export default function SingleMediaConfirmDialog({
</Box>
{fData(size)}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 82, color: 'text.secondary', mr: 2 }}>
Type :
</Box>
{fileFormat(type)}
</Stack>
</>
)}
</Stack>
......@@ -133,13 +127,6 @@ export default function SingleMediaConfirmDialog({
}}
>
<Stack direction={'row'}>
<Box height={'250px'} width={'250px'}>
<FileThumbnail
imageView
file={type === 'folder' ? type : url}
imgSx={{ borderRadius: 1 }}
/>
</Box>
<Box width={'50%'} sx={{ marginLeft: "20px" }}>
{renderPropertiesTop}
</Box>
......
......@@ -17,13 +17,13 @@ import { IMedia, IMediaFilters, IMediaFilterValue } from '@/shared/types/media';
import FileManagerFilters from '../../media-manager-filters';
import FileManagerFiltersResult from '../../media-manager-filters-result';
import SingleMediaGrid from './Single-media-grid';
import { useGetMedias } from '@/shared/api/media';
// ----------------------------------------------------------------------
const defaultFilters: IMediaFilters = {
name: '',
type: [],
startDate: null,
endDate: null,
};
......@@ -43,7 +43,9 @@ export default function SelectSingleMediaView() {
const confirm = useBoolean();
const [tableData, setTableData] = useState<IMedia[]>(_allMedias);
const {mediasData, mediasError} = useGetMedias();
const [tableData, setTableData] = useState<IMedia[]>(mediasData);
const [filters, setFilters] = useState(defaultFilters);
......@@ -57,7 +59,7 @@ export default function SelectSingleMediaView() {
});
const canReset =
!!filters.name || !!filters.type.length || (!!filters.startDate && !!filters.endDate);
!!filters.name || (!!filters.startDate && !!filters.endDate);
const notFound = (!dataFiltered.length && canReset) || !dataFiltered.length;
......@@ -166,7 +168,7 @@ function applyFilter({
filters: IMediaFilters;
dateError: boolean;
}) {
const { name, type, startDate, endDate } = filters;
const { name, startDate, endDate } = filters;
const stabilizedThis = inputData.map((el, index) => [el, index] as const);
......@@ -183,11 +185,7 @@ function applyFilter({
(file) => file.name.toLowerCase().indexOf(name.toLowerCase()) !== -1
);
}
if (type.length) {
inputData = inputData.filter((file) => type.includes(fileFormat(file.type)));
}
if (!dateError) {
if (startDate && endDate) {
inputData = inputData.filter((file) => isBetween(file.createdAt, startDate, endDate));
......
......@@ -38,7 +38,7 @@ export default function SingleMediaGrid({
<Box ref={containerRef}>
<MediaManagerPanel
title="Images"
subTitle={`${dataFiltered.filter((item) => item.type !== 'folder').length} images`}
subTitle={`images`}
/>
<Box
......
......@@ -36,6 +36,18 @@ const EditImageModal: React.FC<ImageEditorProps> = ({ open, onClose, image }) =>
}
}, [image]);
const [imageUrl, setImageUrl] = useState('');
useEffect(() => {
const extractUrl = (url:string) => {
const match = url.match(/Optional\[(.*)\]/);
return match ? match[1] : url;
};
if(image?.url)
setImageUrl(extractUrl(image?.url));
}, [image])
const handleScaleChange = (event: Event, newValue: number | number[]) => {
setScale(newValue as number);
};
......@@ -107,7 +119,7 @@ const EditImageModal: React.FC<ImageEditorProps> = ({ open, onClose, image }) =>
<div style={{ position: 'relative', width: width, height: height }}>
<AvatarEditor
ref={editorRef}
image={image.url}
image={imageUrl}
width={width}
height={height}
scale={scale}
......
......@@ -28,6 +28,7 @@ import { _allMedias } from '@/shared/_mock';
import CustomBreadcrumbs from "@/shared/components/custom-breadcrumbs";
import { FormLabel } from '@mui/material';
import EditImageModal from './edit-image/EditImageModal';
import { useGetMedia, useUpdateMedia } from '@/shared/api/media';
// ----------------------------------------------------------------------
......@@ -36,7 +37,7 @@ type Props = {
};
export default function EditMediaView({ id }: Props) {
const mediaFinded = _allMedias.find(media => media.id === id)
const { mediaData, mediaError } = useGetMedia(id);
const router = useRouter();
......@@ -55,16 +56,16 @@ export default function EditMediaView({ id }: Props) {
});
const defaultValues = useMemo(() => ({
title: mediaFinded?.title || '',
name: mediaFinded?.name || '',
size: mediaFinded?.size || '',
url: mediaFinded?.url || '',
type: mediaFinded?.type || '',
modifiedAt: mediaFinded?.modifiedAt || '',
createdAt: mediaFinded?.createdAt || '',
author: mediaFinded?.author || '',
description: mediaFinded?.description || '',
}), [mediaFinded]);
title: mediaData?.title || '',
name: mediaData?.name || '',
size: mediaData?.size || '',
url: mediaData?.url || '',
type: mediaData?.fileType || '',
modifiedAt: mediaData?.modifiedAt || '',
createdAt: mediaData?.createdAt || '',
author: mediaData?.userId || '',
description: mediaData?.description || '',
}), [mediaData]);
const methods = useForm({
resolver: yupResolver(MediaSchema),
......@@ -81,17 +82,26 @@ export default function EditMediaView({ id }: Props) {
const values = watch();
const [imageUrl, setImageUrl] = useState('');
useEffect(() => {
const extractUrl = (url: string) => {
const match = url.match(/Optional\[(.*)\]/);
return match ? match[1] : url;
};
if (mediaData)
setImageUrl(extractUrl(mediaData?.url));
}, [mediaData])
useEffect(() => {
if (mediaFinded) {
if (mediaData) {
reset(defaultValues);
}
console.log(defaultValues)
}, [mediaFinded, defaultValues, reset])
}, [mediaData, defaultValues, reset])
const onSubmit = handleSubmit(async (data) => {
try {
await new Promise((resolve) => setTimeout(resolve, 500));
reset();
useUpdateMedia(id, data);
enqueueSnackbar('Edit success!', { variant: 'success' });
router.push(paths.dashboard.media.root);
console.info('Form Data:', data);
......@@ -132,14 +142,9 @@ export default function EditMediaView({ id }: Props) {
/>
<Stack alignItems="center" justifyContent="space-between" sx={{ p: 2.5 }}>
<RHFTextField name="title" label="Titre du média" />
<FormLabel sx={{ mt: 2, mr: '86%' }}>Description</FormLabel>
<RHFEditor simple name="description"
onChange={(value) => {
console.log(value);
}}
/>
<Button variant="outlined" onClick={handleOpen} sx={{ width: '100%', mt:2 }}>Modifier image</Button>
<EditImageModal open={open} onClose={handleClose} image={mediaFinded} />
<RHFTextField name="description" label="Description du média" sx={{mt: 2}} multiline rows={4}/>
<Button variant="outlined" onClick={handleOpen} sx={{ width: '100%', mt: 2 }}>Modifier image</Button>
<EditImageModal open={open} onClose={handleClose} image={mediaData} />
</Stack>
<Grid sx={{ display: 'flex', justifyContent: "left" }}>
......@@ -161,12 +166,15 @@ export default function EditMediaView({ id }: Props) {
bgcolor: 'background.neutral',
}}
>
<FileThumbnail
imageView
file={defaultValues.type === 'folder' ? defaultValues.type : defaultValues.url}
sx={{ width: 20, height: 20 }}
imgSx={{ borderRadius: 1 }}
/>
<Grid item md={2}>
<Box component="img" src={imageUrl}
sx={{
objectFit: 'cover',
objectPosition: 'center',
borderRadius: '8px',
}}
/>
</Grid>
<Typography variant="subtitle1" sx={{ wordBreak: 'break-all' }}>
{defaultValues.name}
......@@ -174,18 +182,6 @@ export default function EditMediaView({ id }: Props) {
<Divider sx={{ borderStyle: 'dashed' }} />
<Stack spacing={1.5}>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Titre du média :
</Box>
{defaultValues.title}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Description du média :
</Box>
{defaultValues.description}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Modifié le :
......@@ -212,9 +208,15 @@ export default function EditMediaView({ id }: Props) {
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Type :
Titre du média :
</Box>
{fileFormat(defaultValues.type)}
{defaultValues.title}
</Stack>
<Stack direction="row" sx={{ typography: 'caption', textTransform: 'capitalize' }}>
<Box component="span" sx={{ width: 90, color: 'text.secondary', mr: 2 }}>
Description du média :
</Box>
{defaultValues.description}
</Stack>
</Stack>
</Stack>
......
'use client';
import { useState, useCallback } from 'react';
import { useState, useCallback, useEffect } from 'react';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import Container from '@mui/material/Container';
......@@ -9,11 +9,10 @@ import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import { useBoolean } from '@/hooks/use-boolean';
import { isAfter, isBetween } from '@/utils/format-time';
import { _allMedias, MEDIA_TYPE_OPTIONS } from '@/shared/_mock';
import { MEDIA_TYPE_OPTIONS } from '@/shared/_mock';
import Iconify from '@/components/iconify';
import { useSnackbar } from '@/shared/components/snackbar';
import EmptyContent from '@/shared/components/empty-content';
import { fileFormat } from '@/shared/components/file-thumbnail';
import { ConfirmDialog } from '@/shared/components/custom-dialog';
import { useSettingsContext } from '@/shared/components/settings';
import { useTable, getComparator } from '@/shared/components/table';
......@@ -25,14 +24,12 @@ import FileManagerFilters from '../media-manager-filters';
import FileManagerGridView from '../media-manager-grid-view';
import FileManagerFiltersResult from '../media-manager-filters-result';
import NewMediaDialog from './media-new-dialog';
import AddMediaButton from './Add-media-button';
import { useGetMedias, useDeleteMedia } from '@/shared/api/media'; // Adjust the import based on your file structure
// ----------------------------------------------------------------------
const defaultFilters: IMediaFilters = {
name: '',
type: [],
startDate: null,
endDate: null,
};
......@@ -56,19 +53,24 @@ export default function MediaManagerView() {
const [view, setView] = useState('list');
const [tableData, setTableData] = useState<IMedia[]>(_allMedias);
const { mediasData } = useGetMedias();
const [tableData, setTableData] = useState<IMedia[]>(mediasData);
const [filters, setFilters] = useState(defaultFilters);
const dateError = isAfter(filters.startDate, filters.endDate);
useEffect(() => {
setTableData(mediasData);
}, [mediasData]);
const dataFiltered = applyFilter({
inputData: tableData,
comparator: getComparator(table.order, table.orderBy),
filters,
dateError,
});
console.log(tableData)
const dataInPage = dataFiltered.slice(
table.page * table.rowsPerPage,
......@@ -76,7 +78,7 @@ export default function MediaManagerView() {
);
const canReset =
!!filters.name || !!filters.type.length || (!!filters.startDate && !!filters.endDate);
!!filters.name || (!!filters.startDate && !!filters.endDate);
const notFound = (!dataFiltered.length && canReset) || !dataFiltered.length;
......@@ -111,30 +113,58 @@ export default function MediaManagerView() {
[router]
);
const handleDeleteItem = useCallback(
(id: string) => {
const deleteRow = tableData.filter((row) => row.id !== id);
enqueueSnackbar('Delete success!');
setTableData(deleteRow);
table.onUpdatePageDeleteRow(dataInPage.length);
const handleDeleteItem = useCallback(async (path: string) => {
try {
const status = await useDeleteMedia([path]);
if (status === 200) {
const deleteRow = tableData.filter((row) => row.path !== path);
enqueueSnackbar('Delete success!');
setTableData(deleteRow);
table.onUpdatePageDeleteRow(dataInPage.length);
} else {
enqueueSnackbar('Failed to delete!', { variant: 'error' });
}
} catch (error) {
enqueueSnackbar('Failed to delete!', { variant: 'error' });
console.error('Error deleting media:', error);
}
},
[dataInPage.length, enqueueSnackbar, table, tableData]
);
const handleDeleteItems = useCallback(() => {
const deleteRows = tableData.filter((row) => !table.selected.includes(row.id));
const handleDeleteItems = useCallback(async () => {
try {
const mediasToDelete: string[] = table.selected.map((id) => {
const media = tableData.find((row) => row.id === id);
return media ? media.id : null;
}).filter((id): id is string => id !== null);
enqueueSnackbar('Delete success!');
if (mediasToDelete.length === 0) {
enqueueSnackbar('Failed to delete: No valid id found!', { variant: 'error' });
return;
}
const status = await useDeleteMedia(mediasToDelete);
if (status === 200) {
const deleteRows = tableData.filter((row) => !table.selected.includes(row.id));
enqueueSnackbar('Delete success!');
setTableData(deleteRows);
setTableData(deleteRows);
table.onUpdatePageDeleteRows({
totalRowsInPage: dataInPage.length,
totalRowsFiltered: dataFiltered.length,
});
table.onUpdatePageDeleteRows({
totalRowsInPage: dataInPage.length,
totalRowsFiltered: dataFiltered.length,
});
} else {
enqueueSnackbar('Failed to delete!', { variant: 'error' });
}
} catch (error) {
enqueueSnackbar('Failed to delete!', { variant: 'error' });
console.error('Error deleting media:', error);
}
}, [dataFiltered.length, dataInPage.length, enqueueSnackbar, table, tableData]);
const renderFilters = (
......@@ -278,7 +308,7 @@ function applyFilter({
filters: IMediaFilters;
dateError: boolean;
}) {
const { name, type, startDate, endDate } = filters;
const { name, startDate, endDate } = filters;
const stabilizedThis = inputData.map((el, index) => [el, index] as const);
......@@ -296,10 +326,6 @@ function applyFilter({
);
}
if (type.length) {
inputData = inputData.filter((file) => type.includes(fileFormat(file.type)));
}
if (!dateError) {
if (startDate && endDate) {
inputData = inputData.filter((file) => isBetween(file.createdAt, startDate, endDate));
......
......@@ -10,6 +10,8 @@ import Dialog, { DialogProps } from '@mui/material/Dialog';
import Iconify from '@/components/iconify';
import { Upload } from '@/shared/components/upload';
import { enqueueSnackbar } from 'notistack';
import { useUploadMedias } from '@/shared/api/media';
// ----------------------------------------------------------------------
......@@ -47,21 +49,33 @@ export default function NewMediaDialog({
}, [open]);
const handleDrop = useCallback(
(acceptedMedias: File[]) => {
const newMedias = acceptedMedias.map((media) =>
Object.assign(media, {
preview: URL.createObjectURL(media),
(acceptedFiles: File[]) => {
const newFiles = acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
);
setMedias([...medias, ...newMedias]);
setMedias([...medias, ...newFiles]);
},
[medias]
);
const handleUpload = () => {
const handleUpload = async () => {
onClose();
console.info('ON UPLOAD');
const formData = new FormData();
medias.forEach(file => formData.append('medias', file));
try {
await useUploadMedias(formData);
if (onCreate) {
onCreate();
}
console.info('Upload réussi');
} catch (error) {
console.info('Une erreur s\'est produite lors de l\'upload');
console.info('Upload error:', error);
}
};
const handleRemoveMedia = (inputMedia: File | string) => {
......
......@@ -185,7 +185,7 @@ export default function ShippingClassTableView() {
},
[tableData, table, enqueueSnackbar]
);
return (
<>
......
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