[FEAT] upgrade upload of data...

This commit is contained in:
Edouard DUPIN 2025-02-26 15:00:14 +01:00
parent 6df71e3341
commit 88f65f0806
13 changed files with 553 additions and 309 deletions

View File

@ -1,6 +1,6 @@
import { useRef, useState } from 'react';
import { useRef } from 'react';
import { Button, Text, useDisclosure } from '@chakra-ui/react';
import { Button, Tabs, Text, useDisclosure } from '@chakra-ui/react';
import { MdAdminPanelSettings, MdDeleteForever, MdEdit } from 'react-icons/md';
import { useNavigate, useParams } from 'react-router-dom';
@ -9,15 +9,14 @@ import { FormGroupShow } from '@/components/form/FormGroup';
import { FormInput } from '@/components/form/FormInput';
import { FormNumber } from '@/components/form/FormNumber';
import { FormSelect } from '@/components/form/FormSelect';
import { FormSelectMultiple } from '@/components/form/FormSelectMultiple';
import { FormTextarea } from '@/components/form/FormTextarea';
import { ConfirmPopUp } from '@/components/popup/ConfirmPopUp';
import {
DialogBody,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
DialogBody,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
} from '@/components/ui/dialog';
import { useMediaService, useSpecificMedia } from '@/service/Media';
import { useOrderedSeasons } from '@/service/Season';
@ -31,32 +30,31 @@ import { Formidable, useFormidable } from '../formidable';
export type MediaEditPopUpProps = {};
export const MediaEditPopUp = ({}: MediaEditPopUpProps) => {
const { MediaId } = useParams();
const MediaIdInt = isNullOrUndefined(MediaId)
const { mediaId } = useParams();
const mediaIdInt = isNullOrUndefined(mediaId)
? undefined
: parseInt(MediaId, 10);
: parseInt(mediaId, 10);
const { session } = useServiceContext();
const { dataTypes } = useOrderedTypes(undefined);
const { dataSeries } = useOrderedSeries(undefined);
const { dataSeasons } = useOrderedSeasons(undefined);
const { dataTypes } = useOrderedTypes();
const { dataSeries } = useOrderedSeries();
const { dataSeasons } = useOrderedSeasons();
const { store } = useMediaService();
const { dataMedia } = useSpecificMedia(MediaIdInt);
const [admin, setAdmin] = useState(false);
const { dataMedia } = useSpecificMedia(mediaIdInt);
const navigate = useNavigate();
const disclosure = useDisclosure();
const onClose = () => {
navigate('../../', { relative: 'path' });
navigate('../../..', { relative: 'path' });
};
const onRemove = () => {
if (isNullOrUndefined(MediaIdInt)) {
if (isNullOrUndefined(mediaIdInt)) {
return;
}
store.remove(
MediaIdInt,
mediaIdInt,
MediaResource.remove({
restConfig: session.getRestConfig(),
params: {
id: MediaIdInt,
id: mediaIdInt,
},
})
);
@ -69,7 +67,7 @@ export const MediaEditPopUp = ({}: MediaEditPopUpProps) => {
deltaConfig: { omit: ['covers'] },
});
const onSave = async (dataDelta: MediaWrite) => {
if (isNullOrUndefined(MediaIdInt)) {
if (isNullOrUndefined(mediaIdInt)) {
return;
}
console.log(`onSave = ${JSON.stringify(dataDelta, null, 2)}`);
@ -78,7 +76,7 @@ export const MediaEditPopUp = ({}: MediaEditPopUpProps) => {
restConfig: session.getRestConfig(),
data: dataDelta,
params: {
id: MediaIdInt,
id: mediaIdInt,
},
})
);
@ -99,8 +97,48 @@ export const MediaEditPopUp = ({}: MediaEditPopUpProps) => {
{/* <DialogCloseButton ref={finalRef} /> */}
<DialogBody pb={6} gap="0px" paddingLeft="18px">
{admin && (
<>
<Tabs.Root defaultValue="edit" variant="outline">
<Tabs.List>
<Tabs.Trigger value="edit">
<MdEdit />
Edit
</Tabs.Trigger>
<Tabs.Trigger value="admin">
<MdAdminPanelSettings />
Admin
</Tabs.Trigger>
</Tabs.List>
{/* ---------------------- Other Tabs --------------------------- */}
<Tabs.Content value="edit">
<FormInput
name="name"
isRequired
label="Title"
ref={initialRef}
/>
<FormTextarea name="description" label="Description" />
<FormSelect name="typeId" options={dataTypes} label="Type" />
<FormSelect
name="seriesId"
options={dataSeries}
label="Series(s)"
/>
<FormSelect
name="seasonId"
options={dataSeasons}
label="Season"
/>
<FormNumber
name="episode"
label="Episode n°"
step={1}
//defaultValue={0}
min={0}
max={1000}
/>
</Tabs.Content>
{/* ---------------------- Other Tabs --------------------------- */}
<Tabs.Content value="admin">
<FormGroupShow isRequired label="Id">
<Text>{dataMedia?.id}</Text>
</FormGroupShow>
@ -123,62 +161,18 @@ export const MediaEditPopUp = ({}: MediaEditPopUpProps) => {
confirmTitle="Remove"
onConfirm={onRemove}
/>
</>
)}
{!admin && (
<>
<FormInput
name="name"
isRequired
label="Title"
ref={initialRef}
/>
<FormTextarea name="description" label="Description" />
<FormSelect
name="TypeId"
options={dataTypes}
label="Type"
/>
<FormSelectMultiple
name="Series"
options={dataSeries}
label="Series(s)"
/>
<FormSelect name="SeasonId" options={dataSeasons} label="Season" />
<FormNumber
name="Media"
label="Media n°"
step={1}
//defaultValue={0}
min={0}
max={1000}
/>
</>
)}
</Tabs.Content>
</Tabs.Root>
</DialogBody>
<DialogFooter>
<Button
onClick={() => setAdmin((value) => !value)}
marginRight="auto"
>
{admin ? (
<>
<MdEdit />
Edit
</>
) : (
<>
<MdAdminPanelSettings />
Admin
</>
)}
<Button onClick={onClose} marginRight="auto">
Cancel
</Button>
{!admin && form.isFormModified && (
<Button colorScheme="blue" mr={3} type="submit">
{form.isFormModified && (
<Button colorPalette="blue" type="submit">
Save
</Button>
)}
<Button onClick={onClose}>Cancel</Button>
</DialogFooter>
</Formidable.From>
</DialogContent>

View File

@ -57,7 +57,8 @@ export const SelectMultiple = ({
return [];
}
return transformedOption.filter((element) => {
return values.includes(element[keyKey]);
console.log(`plop ${JSON.stringify(values, null, 2)}`);
return values?.includes(element[keyKey]);
});
}, [values, transformedOption]);

View File

@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { Box, Button, Flex, Input, Table, Text } from '@chakra-ui/react';
import { LuTrash } from 'react-icons/lu';
@ -12,9 +12,10 @@ import {
Series,
SeriesResource,
Type,
TypeResource
TypeResource,
} from '@/back-api';
import { PageLayout } from '@/components/Layout/PageLayout';
import { ParameterLayout } from '@/components/ParameterLayout';
import { TopBar } from '@/components/TopBar/TopBar';
import { FormSelect } from '@/components/form/FormSelect';
import { Formidable, useFormidable } from '@/components/formidable';
@ -23,10 +24,20 @@ import {
NumberInputField,
NumberInputRoot,
} from '@/components/ui/number-input';
import { useMediaService } from '@/service/Media';
import { useOrderedSeries, useOrderedSeriesWithType, useSeriesService } from '@/service/Series';
import {
MediaExpandSeason,
useFilteredMedia,
useMediaExpandSeason,
useMediaService,
} from '@/service/Media';
import {
useOrderedSeries,
useOrderedSeriesWithType,
useSeriesService,
} from '@/service/Series';
import { useServiceContext } from '@/service/ServiceContext';
import { useOrderedTypes, useTypeService } from '@/service/Type';
import { useDebounce, useDebouncedCallback } from '@/utils/debouncedCallback';
import { isNullOrUndefined } from '@/utils/validator';
export class ElementList {
@ -40,7 +51,7 @@ export class ElementList {
export class FileParsedElement {
public isSended: boolean = false;
public nameDetected: boolean = false;
public mediaIdDetected: boolean = false;
public episodeDetected: boolean = false;
public seasonId?: Season['id'] = undefined;
public seriesId?: Series['id'] = undefined;
constructor(
@ -50,7 +61,7 @@ export class FileParsedElement {
public universe?: string,
public series?: string,
public season?: number,
public mediaId?: number
public episode?: number
) {
console.log(`Unique element: ${uniqueId}`);
// nothing to do.
@ -66,7 +77,11 @@ export class FileFailParsedElement {
// nothing to do.
}
}
type InspectionType = {
episodeDetected: boolean;
nameDetected: boolean;
media: MediaExpandSeason;
};
type FormInsertData = {
typeId?: number;
seriesId?: number;
@ -81,26 +96,95 @@ export const AddPage = () => {
const [needSend, setNeedSend] = useState<boolean>(false);
// list of all files already registered in the bdd to compare with the current list of files.
const [listFileInBdd, setListFileInBdd] = useState<any[] | undefined>(
undefined
);
const { dataTypes } = useOrderedTypes();
const { dataSeries: dataSeriesFull } = useOrderedSeries();
const { store: storeType } = useTypeService();
const { store: storeSeries } = useSeriesService();
const { store: storeMedia } = useMediaService();
const { session } = useServiceContext();
const form = useFormidable<FormInsertData>({
configuration: {
enableModifyNotification: false,
enableReset: false,
},
});
// list of all files already registered in the bdd to compare with the current list of files.
const [listFileInBdd, setListFileInBdd] = useState<
InspectionType[] | undefined
>(undefined);
const formValuesSelectForCheck = useDebounce(form.values, 700);
const { medias } = useFilteredMedia(
formValuesSelectForCheck.typeId,
formValuesSelectForCheck.seriesId
);
const { mediasExpand } = useMediaExpandSeason(medias);
useEffect(() => {
handleCheck('');
}, [
formValuesSelectForCheck,
formValuesSelectForCheck.typeId,
mediasExpand,
parsedElement,
]);
const handleCheck = useDebouncedCallback((_newValue) => {
console.log(`values has changes ... ${mediasExpand.length} medias`);
// find the good case: (ignore the universe that have no sense...)
if (formValuesSelectForCheck.typeId === undefined) {
setListFileInBdd(undefined);
return;
}
const out: InspectionType[] = [];
mediasExpand.forEach((media) => {
out.push({
episodeDetected: false,
nameDetected: false,
media,
});
});
// clear previous data:
out.forEach((elem) => {
elem.episodeDetected = false;
elem.nameDetected = false;
});
parsedElement.forEach((elem) => {
elem.episodeDetected = false;
elem.nameDetected = false;
});
// Detect the identical season + identical ID:
console.log(`check:'${JSON.stringify(parsedElement, null, 2)}'`);
out.forEach((checkElem) => {
parsedElement.forEach((parsedElem) => {
if (
`${parsedElem.season}` === `${checkElem.media?.season?.name}` &&
`${parsedElem.episode}` === `${checkElem.media?.episode}`
) {
checkElem.episodeDetected = true;
parsedElem.episodeDetected = true;
}
if (
parsedElem.title?.length !== undefined &&
parsedElem.title?.length !== 0 &&
checkElem.media?.name?.length !== 0 &&
checkElem.media?.name?.length !== 0 &&
(parsedElem.title.startsWith(checkElem.media?.name) ||
checkElem.media?.name.startsWith(parsedElem.title))
) {
checkElem.nameDetected = true;
parsedElem.nameDetected = true;
}
});
});
setListFileInBdd(
out.sort((a, b) => {
const aPriority = a.episodeDetected || a.nameDetected ? 1 : 0;
const bPriority = b.episodeDetected || b.nameDetected ? 1 : 0;
return bPriority - aPriority;
})
);
}, 1000);
// I think this does not work ...
const { dataSeries } = useOrderedSeriesWithType(form.values["typeId"]);
const { dataSeries } = useOrderedSeriesWithType(form.values.typeId);
const updateNeedSend = () => {
if (parsedElement.length === 0) {
@ -145,8 +229,8 @@ export const AddPage = () => {
updateNeedSend();
};
const onMediaId = (data: FileParsedElement, value: any): void => {
data.mediaId = value;
const onEpisode = (data: FileParsedElement, value: any): void => {
data.episode = value;
setParsedElement([...parsedElement]);
updateNeedSend();
};
@ -163,15 +247,16 @@ export const AddPage = () => {
setSuggestedSeries(undefined);
//setSuggestedSeason(undefined);
};
const regex = /^(?:(?<universe>[\w. -]+):)?((?<series>[\w. -]+?)((-s| S)(?<season>\d{1,5}))?(?:(-e|E)(?<episode>\d{1,5}))[- ])?\s*(?<title>.+?)\.(webm|WEBM|Webm|mkv|MKV|Mkv)$/;
const regex =
/^(?:(?<universe>[\w. -]+):)?((?<series>[\w. -]+?)((-s| S)(?<season>\d{1,5}))?(?:(-e|E)(?<episode>\d{1,5}))[- ])?\s*(?<title>.+?)\.(webm|WEBM|Webm|mkv|MKV|Mkv)$/;
const addFileWithMetaData = (file: File, id: number) => {
// parsedElement: FileParsedElement[] = [];
let universe: string | undefined = undefined;
let series: string | undefined = undefined;
let season: number | undefined = undefined;
let mediaIdNumber: number | undefined = undefined;
let episodeNumber: number | undefined = undefined;
let title: string = '';
form.restoreValues();
@ -180,21 +265,25 @@ export const AddPage = () => {
const match = file.name.match(regex);
if (match?.groups) {
universe = match.groups.universe || undefined;
series = match.groups.series ? match.groups.series.trim() : undefined;
season = match.groups.season? parseInt(match.groups.season, 10) : undefined;
mediaIdNumber = match.groups.episode ? parseInt(match.groups.episode, 10) : undefined;
title = match.groups.title.trim();
universe = match.groups.universe || undefined;
series = match.groups.series ? match.groups.series.trim() : undefined;
season = match.groups.season
? parseInt(match.groups.season, 10)
: undefined;
episodeNumber = match.groups.episode
? parseInt(match.groups.episode, 10)
: undefined;
title = match.groups.title.trim();
} else {
console.log("❌ not match :", file.name);
console.log('❌ not match :', file.name);
title = file.name.trim();
}
if (season && isNaN(season)) {
season = undefined;
}
if (mediaIdNumber && isNaN(mediaIdNumber)) {
mediaIdNumber = undefined;
if (episodeNumber && isNaN(episodeNumber)) {
episodeNumber = undefined;
}
// remove extension
title = title.replace(new RegExp('\\.(webm|WEBM|Webm|mkv|MKV|Mkv)'), '');
@ -205,7 +294,7 @@ export const AddPage = () => {
universe,
series,
season,
mediaIdNumber
episodeNumber
);
console.log(`==>${JSON.stringify(tmp, null, 2)}`);
@ -316,10 +405,10 @@ export const AddPage = () => {
typeId: `${form.values['typeId']}`,
seriesId: `${form.values['seriesId']}`,
season: `${parsedElement[index].season}`,
episode: `${parsedElement[index].mediaId}`,
episode: `${parsedElement[index].episode}`,
};
console.log(`data= ${JSON.stringify(data, null, 2)}`);
console.error("Not_ implemented");
console.error('Not_ implemented');
storeMedia
.update(
MediaResource.uploadMedia({
@ -384,7 +473,7 @@ export const AddPage = () => {
restConfig: session.getRestConfig(),
data: {
name: data,
parentId: form.values["typeId"]
parentId: form.values['typeId'],
},
})
);
@ -394,140 +483,160 @@ export const AddPage = () => {
<>
<TopBar title="Add new media" />
<PageLayout>
<Flex
direction="column"
width="80%"
marginX="auto"
padding="10px"
gap="10px"
>
<Flex direction="column" width="full">
<Flex>
<Text flex={1}>format:</Text>
<Text flex={4}>
The format of the media permit to automatic find meta-data:<br />
<li>Universe:Series name-s05-e22-Title.webm/mkv</li>
<li>Universe:Series name S05E22 Title.webm/mkv</li>
<b>example:</b> Stargate:SG1-s05-e22-Tolans.webm
</Text>
<ParameterLayout.Root>
<ParameterLayout.HeaderBase
title="New Media"
description="Add a new media in the library."
/>
<ParameterLayout.Content>
<Flex direction="column" width="full">
<Flex>
<Text flex={1}>format:</Text>
<Text flex={4}>
The format of the media permit to automatic find meta-data:
<br />
<li>Universe:Series name-s05-e22-Title.webm/mkv</li>
<li>Universe:Series name S05E22 Title.webm/mkv</li>
<b>example:</b> Stargate:SG1-s05-e22-Tolans.webm
</Text>
</Flex>
<Flex>
<Text flex={1}>Media:</Text>
<Input
flex={4}
type="file"
placeholder="Select a media file"
accept=".webm,.mkv"
multiple
onChange={onChangeFile}
/>
</Flex>
</Flex>
<Flex>
<Text flex={1}>Media:</Text>
<Input
flex={4}
type="file"
placeholder="Select a media file"
accept=".webm,.mkv"
multiple
onChange={onChangeFile}
/>
</Flex>
</Flex>
{parsedElement && parsedElement.length !== 0 && (
<Formidable.From form={form} onSubmit={sendFile}>
<Text fontSize="30px">Meta-data:</Text>
<FormSelect
label="Type"
name="typeId"
options={dataTypes}
addNewItem={addNewType}
isRequired
/>
<FormSelect
label="Series"
name="seriesId"
options={dataSeries}
addNewItem={addNewSeries}
suggestion={suggestedSeries}
disabled={form.values["typeId"] === undefined}
/>
</ParameterLayout.Content>
<ParameterLayout.Footer />
</ParameterLayout.Root>
<Table.Root
colorPalette="striped"
colorScheme="teal"
background="gray.700"
>
<Table.Header>
<Table.Row>
<Table.ColumnHeader width="10%">
Season ID
</Table.ColumnHeader>
<Table.ColumnHeader width="10%">
Media ID
</Table.ColumnHeader>
<Table.ColumnHeader width="full">Title</Table.ColumnHeader>
<Table.ColumnHeader>actions</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{parsedElement.map((data) => (
<Table.Row key={data.uniqueId}>
<Table.Cell>
{form.values["seriesId"] &&
<NumberInputRoot
value={data.season ? `${data.season}` : undefined}
onValueChange={(e) => onSeasonId(data, e.value)}
min={0}
max={5000}
backgroundColor={
data.mediaIdDetected === true
? 'darkred'
: undefined
}
>
<NumberInputField />
</NumberInputRoot>
}
</Table.Cell>
<Table.Cell>
{form.values["seriesId"] &&
<NumberInputRoot
value={data.mediaId ? `${data.mediaId}` : undefined}
onValueChange={(e) => onMediaId(data, e.value)}
min={0}
max={5000}
backgroundColor={
data.mediaIdDetected === true
? 'darkred'
: undefined
}
>
<NumberInputField />
</NumberInputRoot>
}
</Table.Cell>
<Table.Cell>
<Input
type="text"
placeholder="Name of the Media"
value={data.title}
onChange={(e) => onTitle(data, e.target.value)}
backgroundColor={
data.title === '' ? 'darkred' : undefined
}
/>
{data.nameDetected === true && (
<>
<br />
<Text as="span" color="@danger">
^^^This title already exist !!!
</Text>
</>
)}
</Table.Cell>
<Table.Cell>
<Button
colorPalette="@danger"
onClick={(e) => removeElementFromList(data, e.target)}
>
<LuTrash /> Remove
</Button>
</Table.Cell>
{parsedElement && parsedElement.length !== 0 && (
<Formidable.From form={form} onSubmit={sendFile}>
<ParameterLayout.Root>
<ParameterLayout.HeaderBase
title="Meta-data:"
description="Edit the data that might be upload."
/>
<ParameterLayout.Content>
<FormSelect
label="Type"
name="typeId"
options={dataTypes}
addNewItem={addNewType}
isRequired
/>
<FormSelect
label="Series"
name="seriesId"
options={dataSeries}
addNewItem={addNewSeries}
suggestion={suggestedSeries}
disabled={form.values['typeId'] === undefined}
/>
<Table.Root
colorPalette="striped"
colorScheme="teal"
background="gray.700"
>
<Table.Header>
<Table.Row>
<Table.ColumnHeader width="10%">
Season ID
</Table.ColumnHeader>
<Table.ColumnHeader width="10%">
Media ID
</Table.ColumnHeader>
<Table.ColumnHeader width="full">
Title
</Table.ColumnHeader>
<Table.ColumnHeader>actions</Table.ColumnHeader>
</Table.Row>
))}
</Table.Body>
</Table.Root>
<Flex marginY="15px">
</Table.Header>
<Table.Body>
{parsedElement.map((data) => (
<Table.Row key={data.uniqueId}>
<Table.Cell>
{form.values['seriesId'] && (
<NumberInputRoot
value={data.season ? `${data.season}` : undefined}
onValueChange={(e) => onSeasonId(data, e.value)}
min={0}
max={5000}
backgroundColor={
data.episodeDetected === true
? 'darkred'
: undefined
}
>
<NumberInputField />
</NumberInputRoot>
)}
</Table.Cell>
<Table.Cell>
{form.values['seriesId'] && (
<NumberInputRoot
value={
data.episode ? `${data.episode}` : undefined
}
onValueChange={(e) => onEpisode(data, e.value)}
min={0}
max={5000}
backgroundColor={
data.episodeDetected === true
? 'darkred'
: undefined
}
>
<NumberInputField />
</NumberInputRoot>
)}
</Table.Cell>
<Table.Cell>
<Input
type="text"
placeholder="Name of the Media"
value={data.title}
onChange={(e) => onTitle(data, e.target.value)}
backgroundColor={
data.title === ''
? 'purple'
: data.nameDetected === true
? 'darkred'
: undefined
}
/>
{data.nameDetected === true && (
<>
<br />
<Text as="span" color="red">
^^^This title already exist !!!
</Text>
</>
)}
</Table.Cell>
<Table.Cell>
<Button
colorPalette="@danger"
onClick={(e) =>
removeElementFromList(data, e.target)
}
>
<LuTrash /> Remove
</Button>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</ParameterLayout.Content>
<ParameterLayout.Footer>
<Button
colorPalette="brand"
type="submit"
@ -537,51 +646,51 @@ export const AddPage = () => {
>
<MdCloudUpload /> Upload
</Button>
</Flex>
</Formidable.From>
)}
</ParameterLayout.Footer>
</ParameterLayout.Root>
</Formidable.From>
)}
{listFileInBdd && (
<Table.Root
fontPalette="striped"
colorScheme="teal"
background="gray.700"
>
<Table.Header>
<Table.Row>
<Table.ColumnHeader>Media ID</Table.ColumnHeader>
<Table.ColumnHeader width="full">Title</Table.ColumnHeader>
<Table.ColumnHeader>actions</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{listFileInBdd.map((data) => (
{listFileInBdd && (
<ParameterLayout.Root>
<ParameterLayout.HeaderBase
title="In base"
description="List of file in the base for the Type/series ... (2 seconds before update)"
/>
<ParameterLayout.Content>
<Table.Root
fontPalette="striped"
colorScheme="teal"
background="gray.700"
>
<Table.Header>
<Table.Row>
<Table.Cell>
<Text
color={
data.episodeDetected === true ? 'red' : undefined
}
>
{data.MediaId}
</Text>
</Table.Cell>
<Table.Cell>
<Text
color={data.nameDetected === true ? 'red' : undefined}
>
{data.title}
</Text>
</Table.Cell>
<Table.Cell></Table.Cell>
<Table.ColumnHeader>Media ID</Table.ColumnHeader>
<Table.ColumnHeader>Data ID</Table.ColumnHeader>
<Table.ColumnHeader>Season</Table.ColumnHeader>
<Table.ColumnHeader>Episode</Table.ColumnHeader>
<Table.ColumnHeader width="full">Title</Table.ColumnHeader>
<Table.ColumnHeader>Date</Table.ColumnHeader>
<Table.ColumnHeader>Actions</Table.ColumnHeader>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)}
{parsedFailedElement && (
<>
</Table.Header>
<Table.Body>
{listFileInBdd.map((data) => (
<MediaDetectionDetail data={data} />
))}
</Table.Body>
</Table.Root>
</ParameterLayout.Content>
<ParameterLayout.Footer />
</ParameterLayout.Root>
)}
{parsedFailedElement && (
<ParameterLayout.Root>
<ParameterLayout.HeaderBase
title="Rejected:"
description="List of files that has been removed due to parsing error"
/>
<ParameterLayout.Content>
<Text fontSize="30px">Rejected:</Text>
<Table.Root
colorPalette="striped"
@ -603,10 +712,12 @@ export const AddPage = () => {
))}
</Table.Body>
</Table.Root>
</>
)}
<Box height="250px" width="full" />
</Flex>
</ParameterLayout.Content>
<ParameterLayout.Footer />
</ParameterLayout.Root>
)}
<Box height="50%" minHeight="50%" width="99%" />
{/* upload pop-in */}
{indexUpload !== undefined && (
<PopUpUploadProgress
title="Upload File(s)"
@ -624,3 +735,37 @@ export const AddPage = () => {
</>
);
};
export const MediaDetectionDetail = ({ data }: { data: InspectionType }) => {
return (
<Table.Row>
<Table.Cell>
<Text>{data.media.id}</Text>
</Table.Cell>
<Table.Cell>
<Text>{data.media.dataId.toUpperCase()}</Text>
</Table.Cell>
<Table.Cell>
<Text color={data.episodeDetected === true ? 'red' : undefined}>
{data.media.season?.name}
</Text>
</Table.Cell>
<Table.Cell>
<Text color={data.episodeDetected === true ? 'red' : undefined}>
{data.media.episode}
</Text>
</Table.Cell>
<Table.Cell>
<Text
userSelect="text"
color={data.nameDetected === true ? 'red' : undefined}
>
{data.media.name}
</Text>
</Table.Cell>
<Table.Cell>
<Text>{data.media.date}</Text>
</Table.Cell>
<Table.Cell></Table.Cell>
</Table.Row>
);
};

View File

@ -11,10 +11,10 @@ import { Route, Routes, useNavigate } from 'react-router-dom';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { DisplayMediaFullId } from '@/components/media/DisplayMediaFullId';
import { SeasonEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { Button } from '@/components/ui/button';
import { useColorModeValue } from '@/components/ui/color-mode';
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';

View File

@ -7,12 +7,16 @@ import { Covers } from '@/components/Cover';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { SeasonEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { SeasonEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { useColorModeValue } from '@/components/ui/color-mode';
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
import { useActivePlaylistService, useSeasonVideo, useSpecificSeason } from '@/service';
import {
useActivePlaylistService,
useSeasonVideo,
useSpecificSeason,
} from '@/service';
export const SeasonDetailPage = () => {
const { SeasonId } = useParams();

View File

@ -7,9 +7,9 @@ import { Covers } from '@/components/Cover';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { SeasonEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { SeasonEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { useActivePlaylistService } from '@/service/ActivePlaylist';
import { useSeasonVideo, useSpecificSeason } from '@/service/Season';
import { useSpecificSeries } from '@/service/Series';

View File

@ -11,7 +11,7 @@ import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
//import { useMediasOfAType } from '@/service/Media';
import { DisplayMediaFull } from '@/components/media/DisplayMediaFull';
import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { DisplaySeries } from '@/components/series/DisplaySeries';
import { useColorModeValue } from '@/components/ui/color-mode';
import {

View File

@ -11,7 +11,7 @@ import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
//import { useMediasOfAType } from '@/service/Media';
import { DisplayMediaFull } from '@/components/media/DisplayMediaFull';
import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { DisplaySeason } from '@/components/season/DisplaySeason';
import { useColorModeValue } from '@/components/ui/color-mode';
import {
@ -34,11 +34,11 @@ export const TypesSeriesDetailPage = () => {
const navigate = useNavigate();
const onSelectSeasonItem = (mediaId: number) => {
navigate(`/type/${typeId}/series/${seriesId}/season/${mediaId}`);
}
};
const onSelectItem = (mediaId: number) => {
let currentPlay = 0;
const listMediaId: number[] = [];
console.log(`select item:${mediaId}`);
console.log(`select item:${mediaId}`);
if (!videoWithType) {
console.log('Fail to get Type...');
return;
@ -108,8 +108,8 @@ export const TypesSeriesDetailPage = () => {
>
{seasonOfSeriesId?.map((data) => (
<Box
key={data.id}
width="full"
key={data.id}
width="full"
maxWidth="calc(min(450px,80%))"
height="150px"
border="1px"
@ -137,8 +137,8 @@ export const TypesSeriesDetailPage = () => {
/>
</Box>
))}
</HStack>
<Box width="full" height="10px" background="red"/>
</HStack>
<Box width="full" height="10px" background="red" />
<HStack
wrap="wrap"
gap="20px"
@ -148,7 +148,7 @@ export const TypesSeriesDetailPage = () => {
>
{videoWithType?.map((data) => (
<Box
key={data.id}
key={data.id}
width="200px"
height="300px"
border="1px"

View File

@ -10,7 +10,7 @@ import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { DisplayMediaListFull } from '@/components/media/DisplayMediaListFull';
import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
import { MediaEditPopUp } from '@/components/popup/MediaEditPopUp';
import { useColorModeValue } from '@/components/ui/color-mode';
import {
useActivePlaylistService,
@ -29,7 +29,11 @@ export const TypesSeriesSeasonDetailPage = () => {
const { dataType } = useSpecificType(typeIdInt);
const { dataSeries } = useSpecificSeries(seriesIdInt);
const { dataSeason } = useSpecificSeason(seasonIdInt);
const { videoWithType: videos } = useTypeSeriesSeasonGetVideo(typeIdInt, seriesIdInt, seasonIdInt);
const { videoWithType: videos } = useTypeSeriesSeasonGetVideo(
typeIdInt,
seriesIdInt,
seasonIdInt
);
const navigate = useNavigate();
const onSelectItem = (mediaId: number) => {
let currentPlay = 0;
@ -107,7 +111,7 @@ export const TypesSeriesSeasonDetailPage = () => {
{videos?.map((data) => (
<Box
key={data.id}
width={{sm:"95%", base:"calc(max(300px,50%))"}}
width={{ sm: '95%', base: 'calc(max(300px,50%))' }}
height="60px"
border="1px"
borderColor="brand.900"

View File

@ -1,9 +1,12 @@
import { useMemo } from 'react';
import { Media, MediaResource } from '@/back-api';
import { Media, MediaResource, Season } from '@/back-api';
import { useServiceContext } from '@/service/ServiceContext';
import { SessionServiceProps } from '@/service/session';
import { DataStoreType, useDataStore } from '@/utils/data-store';
import { DataTools, TypeCheck } from '@/utils/data-tools';
import { useSeasonService } from './Season';
export type MediaServiceProps = {
store: DataStoreType<Media>;
@ -42,3 +45,46 @@ export const useSpecificMedia = (id: number | undefined) => {
return { isLoading: store.isLoading, dataMedia };
};
export const useFilteredMedia = (typeId?: number, seriesId?: number) => {
const { store } = useMediaService();
const medias = useMemo(() => {
if (typeId === undefined) {
return [];
}
return DataTools.getsWhere(
store.data,
[
{
check: TypeCheck.EQUAL,
key: 'typeId',
value: typeId,
},
{
check: TypeCheck.EQUAL,
key: 'seriesId',
value: seriesId,
},
],
['name']
);
}, [store.data, typeId, seriesId]);
return { isLoading: store.isLoading, medias };
};
export type MediaExpandSeason = Media & {
season?: Season;
};
export const useMediaExpandSeason = (medias: Media[]) => {
const { store } = useSeasonService();
const mediasExpand = useMemo(() => {
const out: MediaExpandSeason[] = [];
medias.forEach((media) => {
if (media.seasonId === undefined) {
out.push(media);
}
const tmp = DataTools.get(store.data, media.seasonId, 'id');
out.push({ ...media, season: tmp });
});
return out;
}, [store.data, medias]);
return { isLoading: store.isLoading, mediasExpand };
};

View File

@ -43,6 +43,9 @@ export const useOrderedSeries = (nameFilter?: string) => {
const { store } = useSeriesService();
const dataSeries = useMemo(() => {
let tmpData = store.data;
if (tmpData == undefined) {
return [];
}
if (!isNullOrUndefined(nameFilter)) {
tmpData = DataTools.getNameLike(tmpData, nameFilter);
}
@ -60,7 +63,10 @@ export const useOrderedSeries = (nameFilter?: string) => {
}, [store.data, nameFilter]);
return { isLoading: store.isLoading, dataSeries };
};
export const useOrderedSeriesWithType = (typeId?: Type["id"], nameFilter?: string) => {
export const useOrderedSeriesWithType = (
typeId?: Type['id'],
nameFilter?: string
) => {
const { store } = useSeriesService();
const dataSeries = useMemo(() => {
let tmpData = store.data;
@ -70,6 +76,7 @@ export const useOrderedSeriesWithType = (typeId?: Type["id"], nameFilter?: strin
if (typeId === undefined) {
return [];
}
console.log('RRRRRRRRRRRRRRRRRRRRRRRegenarate ');
return DataTools.getsWhere(
tmpData,
[

View File

@ -84,10 +84,10 @@ export const useTypeCountVideo = (id?: number) => {
return { isLoading: store.isLoading, countVideoWithType };
};
export const useTypeGetVideo = (id?: number) => {
export const useTypeGetVideo = (typeId?: number) => {
const { store } = useMediaService();
const videoWithType = useMemo(() => {
if (id === undefined) {
if (typeId === undefined) {
return [];
}
return DataTools.getsWhere(
@ -96,7 +96,7 @@ export const useTypeGetVideo = (id?: number) => {
{
check: TypeCheck.EQUAL,
key: 'typeId',
value: id,
value: typeId,
},
{
check: TypeCheck.EQUAL,
@ -111,7 +111,7 @@ export const useTypeGetVideo = (id?: number) => {
],
['name']
);
}, [store.data, id]);
}, [store.data, typeId]);
return { isLoading: store.isLoading, videoWithType };
};
@ -154,7 +154,11 @@ export const useTypeSeriesGetVideo = (idType?: number, idSeries?: number) => {
return { isLoading: store.isLoading, videoWithType };
};
export const useTypeSeriesSeasonGetVideo = (idType?: number, idSeries?: number, idSeason?: number) => {
export const useTypeSeriesSeasonGetVideo = (
idType?: number,
idSeries?: number,
idSeason?: number
) => {
const { store } = useMediaService();
const videoWithType = useMemo(() => {
if (idType === undefined) {

View File

@ -0,0 +1,39 @@
import { useCallback, useEffect, useRef, useState } from 'react';
export const useDebouncedCallback = (
callback: (...args: any[]) => void,
delay: number = 2000
) => {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const debouncedFunction = useCallback(
(...args: any[]) => {
// Cancel timeout if it previously exist
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// start a new timer
timeoutRef.current = setTimeout(() => {
callback(...args);
}, delay);
},
[callback, delay]
);
return debouncedFunction;
};
export const useDebounce = <T>(value: T, delay: number = 2000): T => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
};