[FEAT] ready gor update and suggestion

This commit is contained in:
Edouard DUPIN 2024-09-01 23:10:40 +02:00
parent 619e9aa4b8
commit 08eb0f878a
8 changed files with 152 additions and 103 deletions

View File

@ -71,17 +71,19 @@ export const AddableItem = () => {
form={form}
variableName={'data'}
addNewItem={(data: string) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
return new Promise((resolve, _rejects) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
});
upperId++;
return [...previous, { id: upperId, name: data }];
});
upperId++;
return [...previous, { id: upperId, name: data }];
resolve({ id: upperId, name: data });
});
return upperId;
}}
options={data}
/>

View File

@ -26,7 +26,9 @@ export type FormSelectProps = {
// in the option specify the value field
keyInputValue?: string;
// Add capability to add an item (no key but only value)
addNewItem?: (data: string) => number | string;
addNewItem?: (data: string) => Promise<any>;
// if a suggestion exist at the auto compleat
suggestion?: string;
};
export const FormSelect = ({
@ -35,8 +37,9 @@ export const FormSelect = ({
ref,
placeholder,
options,
keyInputKey,
keyInputKey = 'id',
keyInputValue = 'name',
suggestion,
addNewItem,
...rest
}: FormSelectProps) => {
@ -44,9 +47,8 @@ export const FormSelect = ({
const onCreate = !addNewItem
? undefined
: (data: string) => {
const ret = addNewItem(data);
form.setValues({ [variableName]: ret });
};
addNewItem(data).then((data: object) => form.setValues({ [variableName]: data[keyInputKey] }));
};
return (
<FormGroup
isModify={form.isModify[variableName]}
@ -61,6 +63,7 @@ export const FormSelect = ({
keyKey={keyInputKey}
keyValue={keyInputValue}
onCreate={onCreate}
suggestion={suggestion}
/>
</FormGroup>
);

View File

@ -71,17 +71,19 @@ export const AddableItem = () => {
form={form}
variableName={'data'}
addNewItem={(data: string) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
return new Promise((resolve, _rejects) => {
let upperId = 0;
setData((previous) => {
previous.forEach((element) => {
if (element['id'] > upperId) {
upperId = element['id'];
}
});
upperId++;
return [...previous, { id: upperId, name: data }];
});
upperId++;
return [...previous, { id: upperId, name: data }];
resolve({ id: upperId, name: data });
});
return upperId;
}}
options={data}
/>

View File

@ -24,7 +24,7 @@ export type FormSelectMultipleProps = {
// in the option specify the value field
keyInputValue?: string;
// Add capability to add an item (no key but only value)
addNewItem?: (data: string) => number | string;
addNewItem?: (data: string) => Promise<any>;
};
export const FormSelectMultiple = ({
@ -33,7 +33,7 @@ export const FormSelectMultiple = ({
ref,
placeholder,
options,
keyInputKey,
keyInputKey = 'id',
keyInputValue = 'name',
addNewItem,
...rest
@ -42,11 +42,8 @@ export const FormSelectMultiple = ({
const onCreate = !addNewItem
? undefined
: (data: string) => {
const ret = addNewItem(data);
form.setValues({
[variableName]: [...(form.values[variableName] ?? []), ret],
});
};
addNewItem(data).then((data: object) => form.setValues({ [variableName]: [...(form.values[variableName] ?? []), data[keyInputKey]] }));
};
return (
<FormGroup
isModify={form.isModify[variableName]}

View File

@ -1,4 +1,4 @@
import { RefObject, useMemo, useRef, useState } from 'react';
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
import {
Button,
@ -11,7 +11,7 @@ import {
Wrap,
WrapItem,
} from '@chakra-ui/react';
import { MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
import { MdEdit, MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
import { SelectList, SelectListModel } from '@/components/select/SelectList';
import { isNullOrUndefined } from '@/utils/validator';
@ -25,6 +25,8 @@ export type SelectMultipleProps = {
ref?: RefObject<any>;
// if set add capability to add the search item
onCreate?: (data: string) => void;
// if a suggestion exist at the auto compleat
suggestion?: string;
};
export const SelectMultiple = ({
@ -34,6 +36,7 @@ export const SelectMultiple = ({
ref,
keyKey = 'id',
keyValue = keyKey,
suggestion,
onCreate,
}: SelectMultipleProps) => {
const [showList, setShowList] = useState(false);
@ -45,9 +48,18 @@ export const SelectMultiple = ({
} as SelectListModel;
});
}, [options, keyKey, keyValue]);
const [currentSearch, setCurrentSearch] = useState<string | undefined>(
undefined
const [hasSuggestion, setHasSuggestion] = useState<boolean>(
onCreate && suggestion ? true : false
);
const [currentSearch, setCurrentSearch] = useState<string | undefined>(
onCreate ? suggestion : undefined
);
useEffect(() => {
if (suggestion) {
setCurrentSearch(suggestion);
setHasSuggestion(true);
}
}, [suggestion]);
const refFocus = ref ?? useRef<HTMLInputElement | null>(null);
const selectedOptions = useMemo(() => {
@ -76,6 +88,7 @@ export const SelectMultiple = ({
return <Spinner />;
}
const onChangeInput = (value: string): void => {
setHasSuggestion(false);
if (value === '') {
setCurrentSearch(undefined);
} else {
@ -91,9 +104,9 @@ export const SelectMultiple = ({
const createNewItem = !onCreate
? undefined
: (data: string) => {
onCreate(data);
setCurrentSearch(undefined);
};
onCreate(data);
setCurrentSearch(undefined);
};
return (
<Flex direction="column" width="full" gap="0px">
@ -124,7 +137,7 @@ export const SelectMultiple = ({
//onSubmit={onSubmit}
onFocus={() => setShowList(true)}
onBlur={() => setTimeout(() => setShowList(false), 200)}
value={showList ? (currentSearch ?? '') : ''}
value={showList ? (currentSearch ?? '') : hasSuggestion ? `suggest: ${currentSearch}` : ''}
borderRadius="5px 0 0 5px"
/>
<Button
@ -135,6 +148,8 @@ export const SelectMultiple = ({
>
{showList ? (
<MdKeyboardArrowUp color="gray.300" />
) : hasSuggestion ? (
<MdEdit color="gray.300" />
) : (
<MdKeyboardArrowDown color="gray.300" />
)}

View File

@ -1,8 +1,9 @@
import { RefObject, useMemo, useRef, useState } from 'react';
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Flex, Input, Spinner } from '@chakra-ui/react';
import {
MdClose,
MdEdit,
MdKeyboardArrowDown,
MdKeyboardArrowUp,
} from 'react-icons/md';
@ -19,6 +20,8 @@ export type SelectSingleProps = {
ref?: RefObject<any>;
// if set add capability to add the search item
onCreate?: (data: string) => void;
// if a suggestion exist at the auto compleat
suggestion?: string;
};
export const SelectSingle = ({
@ -28,6 +31,7 @@ export const SelectSingle = ({
ref,
keyKey = 'id',
keyValue = keyKey,
suggestion,
onCreate,
}: SelectSingleProps) => {
const [showList, setShowList] = useState(false);
@ -39,9 +43,18 @@ export const SelectSingle = ({
} as SelectListModel;
});
}, [options, keyKey, keyValue]);
const [currentSearch, setCurrentSearch] = useState<string | undefined>(
undefined
const [hasSuggestion, setHasSuggestion] = useState<boolean>(
onCreate && suggestion ? true : false
);
const [currentSearch, setCurrentSearch] = useState<string | undefined>(
onCreate ? suggestion : undefined
);
useEffect(() => {
if (suggestion) {
setCurrentSearch(suggestion);
setHasSuggestion(true);
}
}, [suggestion]);
const refFocus = ref ?? useRef<HTMLInputElement | null>(null);
const selectedOptions = useMemo(() => {
if (isNullOrUndefined(value)) {
@ -61,6 +74,7 @@ export const SelectSingle = ({
return <Spinner />;
}
function onChangeInput(value: string): void {
setHasSuggestion(false);
if (value === '') {
setCurrentSearch(undefined);
} else {
@ -68,6 +82,7 @@ export const SelectSingle = ({
}
}
const onRemoveItem = () => {
setHasSuggestion(false);
if (selectedOptions) {
if (onChange) {
onChange(undefined);
@ -81,9 +96,10 @@ export const SelectSingle = ({
const createNewItem = !onCreate
? undefined
: (data: string) => {
onCreate(data);
setCurrentSearch(undefined);
};
onCreate(data);
setCurrentSearch(undefined);
setHasSuggestion(false);
};
return (
<Flex direction="column" width="full" gap="0px">
@ -95,7 +111,7 @@ export const SelectSingle = ({
onFocus={() => setShowList(true)}
onBlur={() => setTimeout(() => setShowList(false), 200)}
value={
showList ? (currentSearch ?? '') : (selectedOptions?.name ?? '')
showList ? (currentSearch ?? '') : (selectedOptions?.name ?? hasSuggestion ? `suggest: ${currentSearch}` : '')
}
backgroundColor={
showList || !selectedOptions ? undefined : 'green.500'
@ -112,6 +128,8 @@ export const SelectSingle = ({
<MdClose color="gray.300" />
) : showList ? (
<MdKeyboardArrowUp color="gray.300" />
) : hasSuggestion ? (
<MdEdit color="gray.300" />
) : (
<MdKeyboardArrowDown color="gray.300" />
)}

View File

@ -1,11 +1,13 @@
import { Album, AlbumResource, Artist, ArtistResource, Gender, GenderResource } from '@/back-api';
import { useFormidable } from '@/components/form/Formidable';
import { FormInput } from '@/components/form/FormInput';
import { FormSelect } from '@/components/form/FormSelect';
import { PageLayout } from '@/components/Layout/PageLayout';
import { TopBar } from '@/components/TopBar/TopBar';
import { useOrderedAlbums } from '@/service/Album';
import { useOrderedArtists } from '@/service/Artist';
import { useAlbumService, useOrderedAlbums } from '@/service/Album';
import { useArtistService, useOrderedArtists } from '@/service/Artist';
import { useGenderService, useOrderedGenders } from '@/service/Gender';
import { useServiceContext } from '@/service/ServiceContext';
import { isNullOrUndefined } from '@/utils/validator';
import { Flex, Text, Input, Table, TableCaption, TableContainer, Tbody, Td, Tfoot, Th, Thead, Tr, Button } from '@chakra-ui/react';
import { useState } from 'react';
@ -72,6 +74,10 @@ export const AddPage = () => {
const { dataGenders } = useOrderedGenders();
const { dataArtist } = useOrderedArtists();
const { dataAlbums } = useOrderedAlbums();
const { store: storeGender } = useGenderService();
const { store: storeArtist } = useArtistService();
const { store: storeAlbum } = useAlbumService();
const { session } = useServiceContext();
const updateNeedSend = () => {
if (parsedElement.length === 0) {
@ -270,7 +276,8 @@ export const AddPage = () => {
// add it in the list.
return tmp;
}
const [suggestedArtist, setSuggestedArtist] = useState<string | undefined>(undefined);
const [suggestedAlbum, setSuggestedAlbum] = useState<string | undefined>(undefined);
const onChangeFile = (value: any): void => {
clearData();
@ -286,8 +293,6 @@ export const AddPage = () => {
updateNeedSend();
return;
}
// we verify with the first value to remove all unknown ...
// clean different gender:
// clean different artist:
for (let iii = 1; iii < parsedElementTmp.length; iii++) {
@ -312,24 +317,36 @@ export const AddPage = () => {
setParsedElement(parsedElementTmp);
setParsedFailedElement(parsedFailedElementTmp);
console.log(`check : ${JSON.stringify(parsedElementTmp[0])}`)
setGlobalArtist(parsedElementTmp[0].artist);
// find artistId:
console.log(`try find artist : ${parsedElementTmp[0].artist}`);
let artistFound = false;
dataArtist.forEach((data) => {
if (data.name?.toLowerCase() === parsedElementTmp[0].artist?.toLowerCase()) {
console.log(` find artist : ${data.id}`);
form.setValues({ artistId: data.id });
artistFound = true;
}
})
if (!artistFound) {
setSuggestedArtist(parsedElementTmp[0].artist);
}
setGlobalAlbum(parsedElementTmp[0].album);
// try to find album
console.log(`try find album : ${parsedElementTmp[0].album}`);
let albumFound = false;
dataAlbums.forEach((data) => {
if (data.name?.toLowerCase() === parsedElementTmp[0].album?.toLowerCase()) {
console.log(` find album : ${data.id}`);
form.setValues({ albumId: data.id });
albumFound = true;
}
})
if (!albumFound) {
setSuggestedAlbum(parsedElementTmp[0].album);
}
updateNeedSend();
setArtistId(undefined);
setAlbumId(undefined);
@ -521,41 +538,33 @@ export const AddPage = () => {
// });
}
const eventPopUpAlbum = (event: string): void => {
console.log(`GET event: ${event}`);
// this.popInService.close('popin-new-album');
}
const eventPopUpArtist = (event: string): void => {
console.log(`GET event: ${event}`);
//this.popInService.close('popin-new-artist');
}
const eventPopUpType = (event: string): void => {
console.log(`GET event: ${event}`);
// this.popInService.close('popin-new-type');
}
const eventPopUpPlaylist = (event: string): void => {
console.log(`GET event: ${event}`);
// this.popInService.close('popin-new-playlist');
}
const newAlbum = (): void => {
console.log('Request new Album...');
// this.popInService.open('popin-new-album');
}
const newArtist = (): void => {
console.log('Request new Artist...');
// this.popInService.open('popin-new-artist');
}
const newType = (): void => {
console.log('Request new Type...');
// this.popInService.open('popin-create-type');
}
const newPlaylist = () => {
console.log('Request new Playlist...');
// this.popInService.open('popin-new-playlist');
}
const form = useFormidable<FormInsertData>({});
const addNewGender = (data: string): Promise<Gender> => {
return storeGender.update(GenderResource.post({
restConfig: session.getRestConfig(),
data: {
name: data,
},
}));
}
const addNewArtist = (data: string): Promise<Artist> => {
return storeArtist.update(ArtistResource.post({
restConfig: session.getRestConfig(),
data: {
name: data,
},
}));
}
const addNewAlbum = (data: string): Promise<Album> => {
return storeAlbum.update(AlbumResource.post({
restConfig: session.getRestConfig(),
data: {
name: data,
},
}));
}
return (
<>
<TopBar title="Add new media" />
@ -584,10 +593,9 @@ export const AddPage = () => {
</Flex>
{parsedElement && parsedElement.length !== 0 && (<>
<Text fontSize="30px">Meta-data:</Text>
<FormSelect label="Gender" form={form} variableName='genderId' options={dataGenders} keyInputValue='name' />
<FormSelect label="Artist" form={form} variableName='artistId' options={dataArtist} keyInputValue='name' />
<FormInput label="Album Title (TODO: fusion with next)" isRequired form={form} variableName='title' />
<FormSelect label="Album" form={form} variableName='albumId' options={dataAlbums} keyInputValue='name' />
<FormSelect label="Gender" form={form} variableName='genderId' options={dataGenders} keyInputValue='name' addNewItem={addNewGender} />
<FormSelect label="Artist" form={form} variableName='artistId' options={dataArtist} keyInputValue='name' addNewItem={addNewArtist} suggestion={suggestedArtist} />
<FormSelect label="Album" form={form} variableName='albumId' options={dataAlbums} keyInputValue='name' addNewItem={addNewAlbum} suggestion={suggestedAlbum} />
<TableContainer>
<Table variant='striped' colorScheme='teal' background="gray.700">
<Thead>
@ -605,13 +613,13 @@ export const AddPage = () => {
placeholder="e?"
value={data.trackId}
onChange={e => onTrackId(data, e.target.value)}
//[class.error]="data.trackIdDetected === true"
backgroundColor={data.trackIdDetected === true ? "darkred" : undefined}
/></Td>
<Td><Input type="text"
placeholder="Name of the Media"
value={data.title}
onChange={e => onTitle(data, e.target.value)}
//[class.error]="data.title === ''"
backgroundColor={data.title === '' ? "darkred" : undefined}
/>
{data.nameDetected === true &&
<><br />

View File

@ -15,7 +15,7 @@ export type DataStoreType<TYPE> = {
data: TYPE[];
get: <MODEL>(value: MODEL, key?: string) => TYPE | undefined;
gets: <MODEL>(value: MODEL[] | undefined, key?: string) => TYPE[];
update: (request: Promise<TYPE>, key?: string) => void;
update: (request: Promise<TYPE>, key?: string) => Promise<TYPE>;
remove: (id: number | string, request: Promise<void>, key?: string) => void;
};
@ -96,19 +96,23 @@ export const useDataStore = <TYPE>(
);
const update = useCallback(
(request: Promise<TYPE>, key?: string) => {
(request: Promise<TYPE>, key?: string): Promise<TYPE> => {
const keyValue = key ?? primaryKey;
request
.then((responseData: TYPE) => {
const filterData = data.filter(
(localData: TYPE) => localData[keyValue] !== responseData[keyValue]
);
filterData.push(responseData);
setData(filterData);
})
.catch((error: RestErrorResponse) => {
toastAPIError(error);
});
return new Promise((resolve, rejects) => {
request
.then((responseData: TYPE) => {
const filterData = data.filter(
(localData: TYPE) => localData[keyValue] !== responseData[keyValue]
);
filterData.push(responseData);
setData(filterData);
resolve(responseData);
})
.catch((error: RestErrorResponse) => {
toastAPIError(error);
rejects(error);
});
});
},
[data, setData]
);