[FEAT] live edit, remove is operational with confirm pop-in.
need to upgrade the code to be reusable
This commit is contained in:
parent
0687369164
commit
faaf3b280b
@ -1,18 +1,21 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactElement, ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { Button, Flex, IconButton, Text, Tooltip } from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FormControl,
|
MdErrorOutline,
|
||||||
FormControlProps,
|
MdHelpOutline,
|
||||||
FormErrorMessage,
|
MdLabelImportant,
|
||||||
FormHelperText,
|
MdRefresh,
|
||||||
FormLabel,
|
} from 'react-icons/md';
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { MdLabelImportant } from 'react-icons/md';
|
|
||||||
|
|
||||||
export type FormGroupProps = FormControlProps & {
|
export type FormGroupProps = {
|
||||||
error?: ReactNode;
|
error?: ReactNode;
|
||||||
help?: ReactNode;
|
help?: ReactNode;
|
||||||
label?: ReactNode;
|
label?: ReactNode;
|
||||||
|
isModify?: boolean;
|
||||||
|
onRestore?: () => void;
|
||||||
|
isRequired?: boolean;
|
||||||
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormGroup = ({
|
export const FormGroup = ({
|
||||||
@ -20,16 +23,45 @@ export const FormGroup = ({
|
|||||||
error,
|
error,
|
||||||
help,
|
help,
|
||||||
label,
|
label,
|
||||||
...props
|
isModify = false,
|
||||||
|
isRequired = false,
|
||||||
|
onRestore,
|
||||||
}: FormGroupProps) => (
|
}: FormGroupProps) => (
|
||||||
<FormControl marginTop="4px" {...props}>
|
<Flex
|
||||||
{!!label && <FormLabel>{label}</FormLabel>}
|
borderLeftWidth="3px"
|
||||||
|
borderLeftColor={error ? 'red' : isModify ? 'blue' : '#00000000'}
|
||||||
|
paddingLeft="7px"
|
||||||
|
paddingY="4px"
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
<Flex direction="row" width="full" gap="52px">
|
||||||
|
{!!label && (
|
||||||
|
<Text marginRight="auto" fontWeight="bold">
|
||||||
|
{label}{' '}
|
||||||
|
{isRequired && (
|
||||||
|
<Text as="span" color="red.600">
|
||||||
|
*
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{!!onRestore && isModify && (
|
||||||
|
<MdRefresh size="15px" onClick={onRestore} cursor="pointer" />
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
{children}
|
{children}
|
||||||
{!!help && <FormHelperText>{help}</FormHelperText>}
|
{!!help && (
|
||||||
|
<Flex direction="row">
|
||||||
|
<MdHelpOutline />
|
||||||
|
<Text>{help}</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
<FormErrorMessage>
|
{!!error && (
|
||||||
<MdLabelImportant />
|
<Flex direction="row">
|
||||||
{error}
|
<MdErrorOutline />
|
||||||
</FormErrorMessage>
|
<Text>{error}</Text>
|
||||||
</FormControl>
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
@ -45,7 +47,15 @@ export const FormSelectList = ({
|
|||||||
search,
|
search,
|
||||||
}: FormSelectListProps) => {
|
}: FormSelectListProps) => {
|
||||||
const displayedValue = optionToOptionDisplay(options, selected, search);
|
const displayedValue = optionToOptionDisplay(options, selected, search);
|
||||||
|
const scrollToRef = useRef<HTMLButtonElement | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollToRef?.current) {
|
||||||
|
scrollToRef?.current?.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'center',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<Flex
|
<Flex
|
||||||
@ -75,6 +85,7 @@ export const FormSelectList = ({
|
|||||||
backgroundColor={data.isSelected ? 'green.800' : '0x00000000'}
|
backgroundColor={data.isSelected ? 'green.800' : '0x00000000'}
|
||||||
_hover={{ backgroundColor: 'gray.400' }}
|
_hover={{ backgroundColor: 'gray.400' }}
|
||||||
onClick={() => onSelectValue(data)}
|
onClick={() => onSelectValue(data)}
|
||||||
|
ref={data.isSelected ? scrollToRef : undefined}
|
||||||
>
|
>
|
||||||
<Text marginRight="auto" autoFocus={false}>
|
<Text marginRight="auto" autoFocus={false}>
|
||||||
{data.name}
|
{data.name}
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
const getDifferences = (obj1: any, obj2: any): { [key: string]: boolean } => {
|
import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
|
||||||
const result: { [key: string]: boolean } = {};
|
|
||||||
for (const key in obj1) {
|
|
||||||
if (obj1.hasOwnProperty(key)) {
|
|
||||||
result[key] = obj1[key] !== obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
@ -19,55 +11,153 @@ const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// const onChangeValue = (key: string, data) => {
|
function getDifferences(
|
||||||
// console.log(`[${key}] data: ${data}`);
|
obj1: object,
|
||||||
// setNewData((previous) => {
|
obj2: object
|
||||||
// if (previous === undefined) {
|
): { [key: string]: boolean } {
|
||||||
// return previous;
|
// Create an empty object to store the differences
|
||||||
// }
|
const result: { [key: string]: boolean } = {};
|
||||||
// if (previous[key] === data) {
|
// Recursive function to compare values
|
||||||
// return previous;
|
function compareValues(value1: any, value2: any): boolean {
|
||||||
// }
|
// If both values are objects, compare their properties recursively
|
||||||
// const tmp = { ...previous };
|
if (isObject(value1) && isObject(value2)) {
|
||||||
// tmp[key] = data;
|
return hasAnyTrue(getDifferences(value1, value2));
|
||||||
// console.log(`data = ${JSON.stringify(tmp, null, 2)}`);
|
}
|
||||||
// return tmp;
|
// If both values are arrays, compare their elements
|
||||||
// });
|
if (isArray(value1) && isArray(value2)) {
|
||||||
// };
|
//console.log(`Check is array: ${JSON.stringify(value1)} =?= ${JSON.stringify(value2)}`);
|
||||||
|
if (value1.length !== value2.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < value1.length; i++) {
|
||||||
|
if (compareValues(value1[i], value2[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Otherwise, compare the values directly
|
||||||
|
//console.log(`compare : ${value1} =?= ${value2}`);
|
||||||
|
return value1 !== value2;
|
||||||
|
}
|
||||||
|
|
||||||
export const useFormidable = <TYPE = object,>({
|
// Get all keys from both objects
|
||||||
|
const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
|
||||||
|
|
||||||
|
// Iterate over all keys
|
||||||
|
for (const key of allKeys) {
|
||||||
|
if (compareValues(obj1[key], obj2[key])) {
|
||||||
|
result[key] = true;
|
||||||
|
} else {
|
||||||
|
result[key] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFormidable = <TYPE extends object = object>({
|
||||||
initialValues = {} as TYPE,
|
initialValues = {} as TYPE,
|
||||||
}: {
|
}: {
|
||||||
initialValues?: TYPE;
|
initialValues?: TYPE;
|
||||||
}) => {
|
}) => {
|
||||||
const [values, setValues] = useState<TYPE>({ ...initialValues } as TYPE);
|
const [values, setValues] = useState<TYPE>({ ...initialValues } as TYPE);
|
||||||
const initialData = useMemo<TYPE>(() => {
|
const [initialData, setInitialData] = useState<TYPE>(initialValues);
|
||||||
setValues({ ...initialValues } as TYPE);
|
|
||||||
return { ...initialValues } as TYPE;
|
|
||||||
}, [initialValues, setValues]);
|
|
||||||
const [valueChange, setValueChange] = useState<{ [key: string]: boolean }>(
|
const [valueChange, setValueChange] = useState<{ [key: string]: boolean }>(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const [isFormModified, setIsFormModified] = useState<boolean>(false);
|
const [isFormModified, setIsFormModified] = useState<boolean>(false);
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialData((previous) => {
|
||||||
|
//console.log(`FORMIDABLE: useMemo initial Values(${JSON.stringify(initialValues)})`);
|
||||||
|
const previousJson = JSON.stringify(previous);
|
||||||
|
const newJson = JSON.stringify(initialValues);
|
||||||
|
if (previousJson === newJson) {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
//console.log(`FORMIDABLE: ==> update new values`);
|
||||||
|
setValues({ ...initialValues });
|
||||||
|
const ret = getDifferences(initialValues, initialValues);
|
||||||
|
setValueChange(ret);
|
||||||
|
setIsFormModified(hasAnyTrue(ret));
|
||||||
|
return initialValues;
|
||||||
|
});
|
||||||
|
}, [
|
||||||
|
initialValues,
|
||||||
|
setInitialData,
|
||||||
|
setValues,
|
||||||
|
setValueChange,
|
||||||
|
setIsFormModified,
|
||||||
|
]);
|
||||||
const setValuesExternal = useCallback(
|
const setValuesExternal = useCallback(
|
||||||
(data: object) => {
|
(data: object) => {
|
||||||
|
//console.log(`FORMIDABLE: setValuesExternal(${JSON.stringify(data)}) ==> keys=${Object.keys(data)}`);
|
||||||
setValues((previous) => {
|
setValues((previous) => {
|
||||||
const newValues = { ...previous, ...data };
|
const newValues = { ...previous, ...data };
|
||||||
const ret = getDifferences(initialData, values);
|
const ret = getDifferences(initialData, newValues);
|
||||||
setValueChange(ret);
|
setValueChange(ret);
|
||||||
setIsFormModified(hasAnyTrue(ret));
|
setIsFormModified(hasAnyTrue(ret));
|
||||||
console.log(
|
|
||||||
` ppppppppppppppppppp ${JSON.stringify(ret, null, 2)} ==> ${hasAnyTrue(ret)}`
|
|
||||||
);
|
|
||||||
return newValues;
|
return newValues;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setValues, initialData]
|
[setValues, initialData]
|
||||||
);
|
);
|
||||||
|
const restoreValue = useCallback(
|
||||||
|
(data: object) => {
|
||||||
|
setValues((previous) => {
|
||||||
|
const keysInPrevious = Object.keys(previous);
|
||||||
|
const newValue = { ...previous };
|
||||||
|
let countModify = 0;
|
||||||
|
//console.log(`restore value ${JSON.stringify(data, null, 2)}`);
|
||||||
|
for (const key of Object.keys(data)) {
|
||||||
|
if (!keysInPrevious.includes(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (data[key] === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newValue[key] = initialValues[key];
|
||||||
|
countModify++;
|
||||||
|
}
|
||||||
|
if (countModify === 0) {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
//console.log(`initialData data ${JSON.stringify(initialData, null, 2)}`);
|
||||||
|
//console.log(`New data ${JSON.stringify(newValue, null, 2)}`);
|
||||||
|
const ret = getDifferences(initialData, newValue);
|
||||||
|
setValueChange(ret);
|
||||||
|
setIsFormModified(hasAnyTrue(ret));
|
||||||
|
return newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setValues, initialData, setIsFormModified, setValueChange]
|
||||||
|
);
|
||||||
|
const getDeltaData = useCallback(
|
||||||
|
({ omit = [], only }: { omit?: string[]; only?: string[] }) => {
|
||||||
|
const out = {};
|
||||||
|
Object.keys(valueChange).forEach((key) => {
|
||||||
|
if (omit.includes(key) || (only && !only.includes(key))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!valueChange[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tmpValue = values[key];
|
||||||
|
if (isNullOrUndefined(tmpValue)) {
|
||||||
|
out[key] = null;
|
||||||
|
} else {
|
||||||
|
out[key] = tmpValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
[valueChange, values]
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
values,
|
values,
|
||||||
valueChange,
|
valueChange,
|
||||||
isFormModified,
|
isFormModified,
|
||||||
setValues: setValuesExternal,
|
setValues: setValuesExternal,
|
||||||
|
getDeltaData,
|
||||||
|
restoreValue,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -60,7 +60,7 @@ export const SelectMultiple = ({
|
|||||||
|
|
||||||
const selectValue = (data: SelectMultipleValueDisplayType) => {
|
const selectValue = (data: SelectMultipleValueDisplayType) => {
|
||||||
const newValues = values?.includes(data.id)
|
const newValues = values?.includes(data.id)
|
||||||
? values.filter((elem) => data.id === elem)
|
? values.filter((elem) => data.id !== elem)
|
||||||
: [...(values ?? []), data.id];
|
: [...(values ?? []), data.id];
|
||||||
setShowList(false);
|
setShowList(false);
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
|
61
front2/src/components/popup/ConfirmPopUp.tsx
Normal file
61
front2/src/components/popup/ConfirmPopUp.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogBody,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
Button,
|
||||||
|
UseDisclosureReturn,
|
||||||
|
useDisclosure,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export type ConfirmPopUpProps = {
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
confirmTitle: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
disclosure: UseDisclosureReturn;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConfirmPopUp = ({
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
confirmTitle,
|
||||||
|
onConfirm,
|
||||||
|
disclosure,
|
||||||
|
}: ConfirmPopUpProps) => {
|
||||||
|
const onClickConfirm = () => {
|
||||||
|
onConfirm();
|
||||||
|
disclosure.onClose();
|
||||||
|
};
|
||||||
|
const cancelRef = useRef(null);
|
||||||
|
return (
|
||||||
|
<AlertDialog
|
||||||
|
isOpen={disclosure.isOpen}
|
||||||
|
leastDestructiveRef={cancelRef}
|
||||||
|
onClose={disclosure.onClose}
|
||||||
|
>
|
||||||
|
<AlertDialogOverlay>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||||
|
{title}
|
||||||
|
</AlertDialogHeader>
|
||||||
|
|
||||||
|
<AlertDialogBody>{body}</AlertDialogBody>
|
||||||
|
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<Button onClick={disclosure.onClose} ref={cancelRef}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button colorScheme="red" onClick={onClickConfirm} ml={3}>
|
||||||
|
{confirmTitle}
|
||||||
|
</Button>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialogOverlay>
|
||||||
|
</AlertDialog>
|
||||||
|
);
|
||||||
|
};
|
@ -2,8 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
IconButton,
|
||||||
FormLabel,
|
|
||||||
Input,
|
Input,
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
@ -12,205 +11,246 @@ import {
|
|||||||
ModalFooter,
|
ModalFooter,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
|
NumberDecrementStepper,
|
||||||
|
NumberIncrementStepper,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputStepper,
|
||||||
|
Text,
|
||||||
Textarea,
|
Textarea,
|
||||||
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { Formiz, useForm, useFormFields } from '@formiz/core';
|
import {
|
||||||
|
MdAdminPanelSettings,
|
||||||
|
MdDeleteForever,
|
||||||
|
MdEdit,
|
||||||
|
MdRemove,
|
||||||
|
} from 'react-icons/md';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Album, Track } from '@/back-api';
|
import { Track, TrackResource } from '@/back-api';
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
|
import { useFormidable } from '@/components/form/Formidable';
|
||||||
import { SelectMultiple } from '@/components/form/SelectMultiple';
|
import { SelectMultiple } from '@/components/form/SelectMultiple';
|
||||||
import { SelectSingle } from '@/components/form/SelectSingle';
|
import { SelectSingle } from '@/components/form/SelectSingle';
|
||||||
|
import { ConfirmPopUp } from '@/components/popup/ConfirmPopUp';
|
||||||
import { useOrderedAlbums } from '@/service/Album';
|
import { useOrderedAlbums } from '@/service/Album';
|
||||||
import { useOrderedArtists } from '@/service/Artist';
|
import { useOrderedArtists } from '@/service/Artist';
|
||||||
import { useOrderedGenders } from '@/service/Gender';
|
import { useOrderedGenders } from '@/service/Gender';
|
||||||
import { useSpecificTrack } from '@/service/Track';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
|
import { useSpecificTrack, useTrackService } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
|
|
||||||
export type TrackEditPopUpProps = {};
|
export type TrackEditPopUpProps = {};
|
||||||
|
|
||||||
const getDifferences = (obj1: any, obj2: any): { [key: string]: boolean } => {
|
|
||||||
const result: { [key: string]: boolean } = {};
|
|
||||||
for (const key in obj1) {
|
|
||||||
if (obj1.hasOwnProperty(key)) {
|
|
||||||
result[key] = obj1[key] !== obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key) && obj[key] === true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TrackEditPopUp = ({}: TrackEditPopUpProps) => {
|
export const TrackEditPopUp = ({}: TrackEditPopUpProps) => {
|
||||||
const { trackId } = useParams();
|
const { trackId } = useParams();
|
||||||
|
const trackIdInt = isNullOrUndefined(trackId)
|
||||||
|
? undefined
|
||||||
|
: parseInt(trackId, 10);
|
||||||
|
const { session } = useServiceContext();
|
||||||
const { dataGenders } = useOrderedGenders(undefined);
|
const { dataGenders } = useOrderedGenders(undefined);
|
||||||
const { dataArtist } = useOrderedArtists(undefined);
|
const { dataArtist } = useOrderedArtists(undefined);
|
||||||
const { dataAlbums } = useOrderedAlbums(undefined);
|
const { dataAlbums } = useOrderedAlbums(undefined);
|
||||||
const { dataTrack } = useSpecificTrack(
|
const { store } = useTrackService();
|
||||||
isNullOrUndefined(trackId) ? undefined : parseInt(trackId, 10)
|
const { dataTrack } = useSpecificTrack(trackIdInt);
|
||||||
);
|
const [admin, setAdmin] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const disclosure = useDisclosure();
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
navigate('../../', { relative: 'path' });
|
navigate('../../', { relative: 'path' });
|
||||||
};
|
};
|
||||||
const onSubmit = (values) => {
|
const onRemove = () => {
|
||||||
console.log(`onSubmit = ${values}`);
|
if (isNullOrUndefined(trackIdInt)) {
|
||||||
};
|
return;
|
||||||
const onValuesChange = (values, form) => {
|
}
|
||||||
console.log(`onValuesChange = ${values}`);
|
store.remove(
|
||||||
|
trackIdInt,
|
||||||
|
TrackResource.remove({
|
||||||
|
restConfig: session.getRestConfig(),
|
||||||
|
params: {
|
||||||
|
id: trackIdInt,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
onClose();
|
||||||
};
|
};
|
||||||
const initialRef = useRef(null);
|
const initialRef = useRef(null);
|
||||||
const finalRef = useRef(null);
|
const finalRef = useRef(null);
|
||||||
const form = useForm<Track>({
|
const form = useFormidable<Track>({
|
||||||
onSubmit,
|
//onSubmit,
|
||||||
onValuesChange,
|
//onValuesChange,
|
||||||
initialValues: { ...dataTrack },
|
initialValues: dataTrack,
|
||||||
onValid: () => console.log('onValid'),
|
//onValid: () => console.log('onValid'),
|
||||||
onInvalid: () => console.log('onInvalid'),
|
//onInvalid: () => console.log('onInvalid'),
|
||||||
});
|
});
|
||||||
const formValues = useFormFields({
|
const onSave = async () => {
|
||||||
connect: form,
|
if (isNullOrUndefined(trackIdInt)) {
|
||||||
selector: (field) => field.value,
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
const formValues = useFormFields({
|
|
||||||
connect: form,
|
|
||||||
fields: ['genderId'] as const,
|
|
||||||
selector: 'value',
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
console.log(`dataTrack : ${JSON.stringify(dataTrack, null, 2)}`);
|
|
||||||
console.log(`Redraw : ${JSON.stringify(formValues, null, 2)}`);
|
|
||||||
const [newData, setNewData] = useState<Track>();
|
|
||||||
const [changeData, setChangeData] = useState<{ [key: string]: boolean }>();
|
|
||||||
const [changeOneData, setChangeOneData] = useState<boolean>(true);
|
|
||||||
// initialize value when ready
|
|
||||||
useEffect(() => {
|
|
||||||
setNewData(dataTrack);
|
|
||||||
}, [dataTrack, setNewData, setChangeData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!newData) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ret = getDifferences(dataTrack, newData);
|
const dataThatNeedToBeUpdated = form.getDeltaData({ omit: ['covers'] });
|
||||||
setChangeData(ret);
|
console.log(`onSave = ${JSON.stringify(dataThatNeedToBeUpdated, null, 2)}`);
|
||||||
///////////////////////setChangeOneData(hasAnyTrue(ret));
|
store.update(
|
||||||
console.log(
|
TrackResource.patch({
|
||||||
`ChangeData=${JSON.stringify(ret, null, 2)} ChangeOneData=${hasAnyTrue(ret)}`
|
restConfig: session.getRestConfig(),
|
||||||
|
data: dataThatNeedToBeUpdated,
|
||||||
|
params: {
|
||||||
|
id: trackIdInt,
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}, [dataTrack, newData, setChangeData, setChangeOneData]);
|
};
|
||||||
|
|
||||||
// const onChangeValue = (key: string, data) => {
|
|
||||||
// console.log(`[${key}] data: ${data}`);
|
|
||||||
// setNewData((previous) => {
|
|
||||||
// if (previous === undefined) {
|
|
||||||
// return previous;
|
|
||||||
// }
|
|
||||||
// if (previous[key] === data) {
|
|
||||||
// return previous;
|
|
||||||
// }
|
|
||||||
// const tmp = { ...previous };
|
|
||||||
// tmp[key] = data;
|
|
||||||
// console.log(`data = ${JSON.stringify(tmp, null, 2)}`);
|
|
||||||
// return tmp;
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formiz connect={form} autoForm>
|
<Modal
|
||||||
<form noValidate onSubmit={form.submit}>
|
initialFocusRef={initialRef}
|
||||||
<Modal
|
finalFocusRef={finalRef}
|
||||||
initialFocusRef={initialRef}
|
closeOnOverlayClick={false}
|
||||||
finalFocusRef={finalRef}
|
onClose={onClose}
|
||||||
closeOnOverlayClick={false}
|
isOpen={true}
|
||||||
onClose={onClose}
|
>
|
||||||
isOpen={true}
|
<ModalOverlay />
|
||||||
>
|
<ModalContent>
|
||||||
<ModalOverlay />
|
<ModalHeader>Edit Track</ModalHeader>
|
||||||
<ModalContent>
|
<ModalCloseButton ref={finalRef} />
|
||||||
<ModalHeader>Edit Track</ModalHeader>
|
|
||||||
<ModalCloseButton ref={finalRef} />
|
|
||||||
|
|
||||||
<ModalBody pb={6}>
|
<ModalBody pb={6} gap="0px" paddingLeft="18px">
|
||||||
<FormGroup isRequired>
|
{admin && (
|
||||||
<FormLabel>Title</FormLabel>
|
<>
|
||||||
|
<FormGroup isRequired label="Id">
|
||||||
|
<Text>{dataTrack?.id}</Text>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup label="Data Id">
|
||||||
|
<Text>{dataTrack?.dataId}</Text>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup label="Action(s):">
|
||||||
|
<Button
|
||||||
|
onClick={disclosure.onOpen}
|
||||||
|
marginRight="auto"
|
||||||
|
variant="@danger"
|
||||||
|
>
|
||||||
|
<MdDeleteForever /> Remove Media
|
||||||
|
</Button>
|
||||||
|
</FormGroup>
|
||||||
|
<ConfirmPopUp
|
||||||
|
disclosure={disclosure}
|
||||||
|
title="Remove track"
|
||||||
|
body={`Remove Media [${dataTrack?.id}] ${dataTrack?.name}`}
|
||||||
|
confirmTitle="Remove"
|
||||||
|
onConfirm={onRemove}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!admin && (
|
||||||
|
<>
|
||||||
|
<FormGroup
|
||||||
|
isRequired
|
||||||
|
isModify={form.valueChange.name}
|
||||||
|
onRestore={() => form.restoreValue({ name: true })}
|
||||||
|
label="Title"
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
ref={initialRef}
|
ref={initialRef}
|
||||||
value={formValues.name}
|
value={form.values.name}
|
||||||
onChange={(e) => form.setValues({ name: e.target.value })}
|
onChange={(e) => form.setValues({ name: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup
|
||||||
<label>Description</label>
|
isModify={form.valueChange.description}
|
||||||
|
onRestore={() => form.restoreValue({ description: true })}
|
||||||
|
label="Description"
|
||||||
|
>
|
||||||
<Textarea
|
<Textarea
|
||||||
value={formValues.description}
|
value={form.values.description}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
form.setValues({ description: e.target.value })
|
form.setValues({ description: e.target.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup
|
||||||
<label>Gender</label>
|
isModify={form.valueChange.genderId}
|
||||||
|
onRestore={() => form.restoreValue({ genderId: true })}
|
||||||
|
label="Gender"
|
||||||
|
>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
value={formValues.genderId}
|
value={form.values.genderId}
|
||||||
options={dataGenders}
|
options={dataGenders}
|
||||||
onChange={(value) => {
|
onChange={(value) => form.setValues({ genderId: value })}
|
||||||
form.setValues({ genderId: value });
|
|
||||||
console.log(`event change value: ${value}`);
|
|
||||||
console.log(
|
|
||||||
`values: ${JSON.stringify(formValues, null, 2)}`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
keyValue="name"
|
keyValue="name"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup
|
||||||
<label>Artist(s)</label>
|
isModify={form.valueChange.artists}
|
||||||
|
onRestore={() => form.restoreValue({ artists: true })}
|
||||||
|
label="Artist(s)"
|
||||||
|
>
|
||||||
<SelectMultiple
|
<SelectMultiple
|
||||||
values={formValues.artists}
|
values={form.values.artists}
|
||||||
options={dataArtist}
|
options={dataArtist}
|
||||||
onChange={(value) => form.setValues({ artists: value })}
|
onChange={(value) => form.setValues({ artists: value })}
|
||||||
keyValue="name"
|
keyValue="name"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup
|
||||||
<label>Album</label>
|
isModify={form.valueChange.albumId}
|
||||||
|
onRestore={() => form.restoreValue({ albumId: true })}
|
||||||
|
label="Album"
|
||||||
|
>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
value={formValues.albumId}
|
value={form.values.albumId}
|
||||||
options={dataAlbums}
|
options={dataAlbums}
|
||||||
onChange={(value) => form.setValues({ albumId: value })}
|
onChange={(value) => form.setValues({ albumId: value })}
|
||||||
keyValue="name"
|
keyValue="name"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormControl>
|
<FormGroup
|
||||||
<FormLabel>track n°</FormLabel>
|
isModify={form.valueChange.track}
|
||||||
|
onRestore={() => form.restoreValue({ track: true })}
|
||||||
|
label="Track n°"
|
||||||
|
>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
value={formValues.track}
|
value={form.values.track}
|
||||||
onChange={(value) => form.setValues({ track: value })}
|
onChange={(_, value) => form.setValues({ track: value })}
|
||||||
/>
|
step={1}
|
||||||
</FormControl>
|
defaultValue={0}
|
||||||
</ModalBody>
|
min={0}
|
||||||
<ModalFooter>
|
max={1000}
|
||||||
{changeOneData && (
|
>
|
||||||
<Button colorScheme="blue" mr={3} type="submit">
|
<NumberInputField />
|
||||||
Save
|
<NumberInputStepper>
|
||||||
</Button>
|
<NumberIncrementStepper />
|
||||||
)}
|
<NumberDecrementStepper />
|
||||||
<Button onClick={onClose}>Cancel</Button>
|
</NumberInputStepper>
|
||||||
</ModalFooter>
|
</NumberInput>
|
||||||
</ModalContent>
|
</FormGroup>
|
||||||
</Modal>
|
</>
|
||||||
</form>
|
)}
|
||||||
</Formiz>
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
onClick={() => setAdmin((value) => !value)}
|
||||||
|
marginRight="auto"
|
||||||
|
>
|
||||||
|
{admin ? (
|
||||||
|
<>
|
||||||
|
<MdEdit />
|
||||||
|
Edit
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<MdAdminPanelSettings />
|
||||||
|
Admin
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
{!admin && form.isFormModified && (
|
||||||
|
<Button colorScheme="blue" mr={3} onClick={onSave}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -70,9 +70,9 @@ export default defineStyleConfig({
|
|||||||
'@danger': (props) =>
|
'@danger': (props) =>
|
||||||
customVariant({
|
customVariant({
|
||||||
theme: props.theme,
|
theme: props.theme,
|
||||||
bg: mode('error.600', 'error.300')(props),
|
bg: mode('error.600', 'error.600')(props),
|
||||||
bgHover: mode('error.700', 'error.400')(props),
|
bgHover: mode('error.700', 'error.500')(props),
|
||||||
bgActive: mode('error.600', 'error.300')(props),
|
bgActive: mode('error.600', 'error.500')(props),
|
||||||
color: mode('white', 'error.900')(props),
|
color: mode('white', 'error.900')(props),
|
||||||
boxColorFocus: mode('error.900', 'error.500')(props),
|
boxColorFocus: mode('error.900', 'error.500')(props),
|
||||||
}),
|
}),
|
||||||
|
@ -14,6 +14,8 @@ export type DataStoreType<TYPE> = {
|
|||||||
data: TYPE[];
|
data: TYPE[];
|
||||||
get: <MODEL>(value: MODEL, key?: string) => TYPE | undefined;
|
get: <MODEL>(value: MODEL, key?: string) => TYPE | undefined;
|
||||||
gets: <MODEL>(value: MODEL[] | undefined, key?: string) => TYPE[];
|
gets: <MODEL>(value: MODEL[] | undefined, key?: string) => TYPE[];
|
||||||
|
update: (request: Promise<TYPE>, key?: string) => void;
|
||||||
|
remove: (id: number | string, request: Promise<void>, key?: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDataStore = <TYPE>(
|
export const useDataStore = <TYPE>(
|
||||||
@ -85,16 +87,40 @@ export const useDataStore = <TYPE>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const update = useCallback(
|
const update = useCallback(
|
||||||
(value: TYPE, key?: string) => {
|
(request: Promise<TYPE>, key?: string) => {
|
||||||
const keyValue = key ?? primaryKey;
|
const keyValue = key ?? primaryKey;
|
||||||
const filterData = data.filter(
|
request
|
||||||
(localData: TYPE) => localData[keyValue] === value[keyValue]
|
.then((responseData: TYPE) => {
|
||||||
);
|
const filterData = data.filter(
|
||||||
filterData.push(value);
|
(localData: TYPE) => localData[keyValue] !== responseData[keyValue]
|
||||||
setData(filterData);
|
);
|
||||||
|
filterData.push(responseData);
|
||||||
|
setData(filterData);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(`catch an error: ... ${JSON.stringify(error, null, 2)}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[data, setData]
|
||||||
|
);
|
||||||
|
const remove = useCallback(
|
||||||
|
(id: number | string, request: Promise<void>, key?: string) => {
|
||||||
|
const keyValue = key ?? primaryKey;
|
||||||
|
request
|
||||||
|
.then(() => {
|
||||||
|
const filterData = data.filter(
|
||||||
|
(localData: TYPE) => localData[keyValue] !== id
|
||||||
|
);
|
||||||
|
setData(filterData);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(
|
||||||
|
`catch an error on delete: ... ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[data, setData]
|
[data, setData]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { isLoading, error, data, get, gets }; //, update};
|
return { isLoading, error, data, get, gets, update, remove };
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user