diff --git a/server/controllers/mediatheque.ts b/server/controllers/mediatheque.ts index ad24e2a73..7b34aa252 100644 --- a/server/controllers/mediatheque.ts +++ b/server/controllers/mediatheque.ts @@ -3,7 +3,6 @@ import { Brackets } from 'typeorm'; import type { Filter } from '../../types/mediatheque.type'; import { Activity } from '../entities/activity'; -import { User } from '../entities/user'; import { AppDataSource } from '../utils/data-source'; import { Controller } from './controller'; @@ -40,7 +39,8 @@ mediathequeController.post({ path: '' }, async (req: Request, res: Response) => }), ); }); - const activities = await subQueryBuilder.andWhere('activity.status = :status', { status: 0 }).getMany(); + const activitiesToFilter = await subQueryBuilder.getMany(); + const activities = activitiesToFilter.filter((status) => status.status === 0); res.send(activities); } catch (error) { console.error('Error fetching media data:', error); diff --git a/src/components/admin/mediatheque/CheckboxAdmin.tsx b/src/components/admin/mediatheque/CheckboxAdmin.tsx index 7a49f50b2..6b48290b8 100644 --- a/src/components/admin/mediatheque/CheckboxAdmin.tsx +++ b/src/components/admin/mediatheque/CheckboxAdmin.tsx @@ -5,12 +5,17 @@ import Checkbox from '@mui/material/Checkbox'; import MediathequeContext from 'src/contexts/mediathequeContext'; import PelicoNeutre from 'src/svg/pelico/pelico_neutre.svg'; -const CheckboxAdmin = ({ isChecked, onCheckboxChange }) => { +interface CheckboxAdminProps { + isChecked: boolean; + onCheckboxChange: (checked: boolean) => void; +} + +const CheckboxAdmin = ({ isChecked, onCheckboxChange }: CheckboxAdminProps) => { const label = { inputProps: { 'aria-label': 'Pelico' } }; const { setFilters, setUseAdminData } = useContext(MediathequeContext); const handleCheckboxChange = (event: { target: { checked: unknown } }) => { - onCheckboxChange(event.target.checked); + onCheckboxChange(event.target.checked as boolean); if (!event.target.checked) { setFilters([[]]); setUseAdminData(false); diff --git a/src/components/admin/mediatheque/DownloadButton.tsx b/src/components/admin/mediatheque/DownloadButton.tsx index ad3276f04..5cbf93be3 100644 --- a/src/components/admin/mediatheque/DownloadButton.tsx +++ b/src/components/admin/mediatheque/DownloadButton.tsx @@ -17,8 +17,9 @@ import type { Activity } from 'types/activity.type'; export default function DownloadButton() { const { enqueueSnackbar } = useSnackbar(); const [loading, setLoading] = useState(false); + const { allFiltered } = useContext(MediathequeContext); - const onDownload = async (videoUrl: string) => { + const onDownloadVideo = async (videoUrl: string) => { try { const response = await axiosRequest({ method: 'GET', @@ -44,7 +45,24 @@ export default function DownloadButton() { } }; - const { allFiltered } = useContext(MediathequeContext); + const onDownloadSound = async (soundUrl: string) => { + try { + const adjustedUrl = soundUrl.startsWith('/api') ? soundUrl.replace(/^\/api/, '') : soundUrl; + + const response = await axiosRequest({ + method: 'GET', + url: adjustedUrl, + responseType: 'blob', + }); + return response.data; + } catch (error) { + console.error(error); + enqueueSnackbar("Une erreur est survenue lors du téléchargement de l'audio...", { + variant: 'error', + }); + throw error; + } + }; const getActivityLabel = (type: number) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -70,6 +88,7 @@ export default function DownloadButton() { const zip = new JSZip(); const imagePromises: Promise[] = []; const videoPromises: Promise[] = []; + const soundPromises: Promise[] = []; data.forEach((item: Activity) => { const activityLabel = getActivityLabel(item.type); @@ -100,11 +119,24 @@ export default function DownloadButton() { }); imagePromises.push(imagePromise); + } else if (contentItem.type === 'sound') { + const soundUrl = contentItem.value; + const soundFileName = `${activityLabel} ${subThemeLabel} son activité id n°${item.id} ${contentIndex + 1}.mp3`; + + const soundPromise = onDownloadSound(soundUrl) + .then((blob) => { + zip.file(soundFileName, blob); + }) + .catch((err) => { + console.error(`Failed to fetch sound from ${soundUrl}:`, err); + }); + + soundPromises.push(soundPromise); } else if (contentItem.type === 'video') { const videoUrl = contentItem.value; const videoFileName = `${activityLabel} ${subThemeLabel} video activité id n°${item.id} ${contentIndex + 1}.mp4`; - const videoPromise = onDownload(videoUrl) + const videoPromise = onDownloadVideo(videoUrl) .then((downloadLink) => fetch(downloadLink)) .then((response) => response.blob()) .then((blob) => { @@ -119,7 +151,7 @@ export default function DownloadButton() { }); }); - await Promise.all([...imagePromises, ...videoPromises]); + await Promise.all([...imagePromises, ...videoPromises, ...soundPromises]); const content = await zip.generateAsync({ type: 'blob' }); diff --git a/src/components/admin/mediatheque/ModalFilter.tsx b/src/components/admin/mediatheque/ModalFilter.tsx index 3ed20ca8c..721b6a95b 100644 --- a/src/components/admin/mediatheque/ModalFilter.tsx +++ b/src/components/admin/mediatheque/ModalFilter.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; import RefreshIcon from '@mui/icons-material/Refresh'; import Box from '@mui/material/Box'; @@ -29,12 +29,16 @@ const ModalFilter = () => { const [open, setOpen] = React.useState(false); const handleOpen = () => setOpen(true); const handleClose = () => setOpen(false); + const [updateFiltersKey, setUpdateFiltersKey] = useState(0); + const [isChecked, setIsChecked] = useState(false); - const { setFilters, setOffset } = useContext(MediathequeContext); + const { setFilters, setUseAdminData } = useContext(MediathequeContext); const handleResetFilters = () => { + setUseAdminData(false); setFilters([[]]); - setOffset(0); + setUpdateFiltersKey((prevKey) => prevKey + 1); + setIsChecked(false); }; return ( @@ -61,12 +65,12 @@ const ModalFilter = () => { > X - +
- + diff --git a/src/contexts/mediathequeContext.tsx b/src/contexts/mediathequeContext.tsx index 731224a36..a70c72fef 100644 --- a/src/contexts/mediathequeContext.tsx +++ b/src/contexts/mediathequeContext.tsx @@ -10,11 +10,34 @@ type MediathequeProviderProps = { type MediathequeContextType = { filters: Array; setFilters: React.Dispatch>>; - allFiltered: any[]; + allFiltered: []; useAdminData: boolean; setUseAdminData: React.Dispatch>; }; +interface UserData { + id: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + content: any; + subType: number; + type: number; + villageId: number; + userId: number; + user: { type: number; school: string }; + village: { name: string }; + data: { + mascotteImage?: string; + verseFinalMixUrl?: string; + verseMixUrl?: string; + verseMixWithIntroUrl?: string; + verseMixWithVocalsUrl?: string; + odd?: { imageStory?: string; imageUrl?: string }; + object?: { imageStory?: string; imageUrl?: string }; + place?: { imageStory?: string; imageUrl?: string }; + tale?: { imageStory?: string; imageUrl?: string }; + }; +} + const MediathequeContext = createContext({ filters: [], setFilters: () => {}, @@ -28,80 +51,68 @@ export const MediathequeProvider: React.FC = ({ childr const [useAdminData, setUseAdminData] = useState(false); const { data: usersData } = useGetMediatheque(filters); - const [dataToUse, setDataToUse] = useState([]); + const [dataToUse, setDataToUse] = useState<[]>([]); useEffect(() => { const activitiesMediaFinder = usersData ?.filter(({ type }: { type: number }) => ![3, 5, 11].includes(type)) - .map( - ({ - id, - content, - subType, - type, - villageId, - userId, - user, - village, - data, - }: { + .map(({ id, content, subType, type, villageId, userId, user, village, data }: UserData) => { + const result: { id: number; - content: object; subType: number; type: number; villageId: number; userId: number; - user: object; - village: object; - data: object; - }) => { - const result = { id, subType, type, villageId, userId, content: [], user, village }; - if (type === 8 || type === 12 || type === 13 || type === 14) { - if (type === 8) { - result.content.push({ type: 'image', value: data.mascotteImage }); - } - if (type === 12) { - result.content.push({ type: 'sound', value: data.verseFinalMixUrl }); - result.content.push({ type: 'sound', value: data.verseMixUrl }); - result.content.push({ type: 'sound', value: data.verseMixWithIntroUrl }); - result.content.push({ type: 'sound', value: data.verseMixWithVocalsUrl }); - } - if (type === 13 || type === 14) { - const properties = ['odd', 'object', 'place', 'tale']; + content: Array<{ type: string; value: string | undefined }>; + user: { type: number; school: string }; + village: { name: string }; + } = { id, subType, type, villageId, userId, content: [], user, village }; + if (type === 8 || type === 12 || type === 13 || type === 14) { + if (type === 8) { + result.content.push({ type: 'image', value: data.mascotteImage }); + } + if (type === 12) { + result.content.push({ type: 'sound', value: data.verseFinalMixUrl }); + // result.content.push({ type: 'sound', value: data.verseMixUrl }); + // result.content.push({ type: 'sound', value: data.verseMixWithIntroUrl }); + // result.content.push({ type: 'sound', value: data.verseMixWithVocalsUrl }); + } + if (type === 13 || type === 14) { + const properties = ['odd', 'object', 'place', 'tale']; - properties.forEach((prop) => { + properties.forEach((prop: string) => { + const propData = data[prop as keyof typeof data]; + if (typeof propData === 'object' && propData !== null) { if (prop === 'tale') { - result.content.push({ type: 'image', value: data[prop].imageStory }); + result.content.push({ type: 'image', value: propData.imageStory }); } else { - result.content.push({ type: 'image', value: data[prop].imageUrl }); - } - }); - } - } - if (content.game) { - content.game.map(({ inputs }) => - inputs.map((input: { type: number; selectedValue: string }) => { - if (input.type === 3 || input.type === 4) { - result.content.push({ type: input.type === 3 ? 'image' : 'video', value: input.selectedValue }); + result.content.push({ type: 'image', value: propData.imageUrl }); } - }), - ); - } else { - content.map(({ type, value }: { type: string; value: string }) => { - const wantedTypes = ['image', 'video', 'sound']; - if (wantedTypes.includes(type)) { - result.content.push({ type, value }); } }); } - return result; - }, - ); - - console.log('activitiesMediaFinder', activitiesMediaFinder); + } + if (content.game) { + content.game.map(({ inputs }: { inputs: Array<{ type: number; selectedValue: string }> }) => + inputs.map((input) => { + if (input.type === 3 || input.type === 4) { + result.content.push({ type: input.type === 3 ? 'image' : 'video', value: input.selectedValue }); + } + }), + ); + } else { + content.map(({ type, value }: { type: string; value: string }) => { + const wantedTypes = ['image', 'video', 'sound']; + if (wantedTypes.includes(type)) { + result.content.push({ type, value }); + } + }); + } + return result; + }); - const activitiesWithMediaOnly = activitiesMediaFinder?.filter((a) => a.content.length > 0); - const activitiesFromPelico = activitiesWithMediaOnly?.filter((a) => [0, 1, 2].includes(a.user.type)); + const activitiesWithMediaOnly = activitiesMediaFinder?.filter((a: UserData) => a.content.length > 0); + const activitiesFromPelico = activitiesWithMediaOnly?.filter((a: UserData) => [0, 1, 2].includes(a.user.type)); if (useAdminData) { setDataToUse(activitiesFromPelico || []); @@ -121,8 +132,6 @@ export const MediathequeProvider: React.FC = ({ childr [filters, dataToUse, useAdminData], ); - console.log('data to use = ', dataToUse); - return {children}; }; diff --git a/src/pages/admin/newportal/medialibrary/index.tsx b/src/pages/admin/newportal/medialibrary/index.tsx index 76aa23b1f..edade3b78 100644 --- a/src/pages/admin/newportal/medialibrary/index.tsx +++ b/src/pages/admin/newportal/medialibrary/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useRef, useState } from 'react'; +import React, { useContext, useState } from 'react'; // Tout doit être responsive