diff --git a/front2/src/components/form/FormGroup.tsx b/front2/src/components/form/FormGroup.tsx
index c4d631d..d0bdcb5 100644
--- a/front2/src/components/form/FormGroup.tsx
+++ b/front2/src/components/form/FormGroup.tsx
@@ -1,18 +1,21 @@
-import { ReactNode } from 'react';
+import { ReactElement, ReactNode } from 'react';
+import { Button, Flex, IconButton, Text, Tooltip } from '@chakra-ui/react';
import {
- FormControl,
- FormControlProps,
- FormErrorMessage,
- FormHelperText,
- FormLabel,
-} from '@chakra-ui/react';
-import { MdLabelImportant } from 'react-icons/md';
+ MdErrorOutline,
+ MdHelpOutline,
+ MdLabelImportant,
+ MdRefresh,
+} from 'react-icons/md';
-export type FormGroupProps = FormControlProps & {
+export type FormGroupProps = {
error?: ReactNode;
help?: ReactNode;
label?: ReactNode;
+ isModify?: boolean;
+ onRestore?: () => void;
+ isRequired?: boolean;
+ children: ReactNode;
};
export const FormGroup = ({
@@ -20,16 +23,45 @@ export const FormGroup = ({
error,
help,
label,
- ...props
+ isModify = false,
+ isRequired = false,
+ onRestore,
}: FormGroupProps) => (
-
- {!!label && {label}}
+
+
+ {!!label && (
+
+ {label}{' '}
+ {isRequired && (
+
+ *
+
+ )}
+
+ )}
+ {!!onRestore && isModify && (
+
+ )}
+
{children}
- {!!help && {help}}
+ {!!help && (
+
+
+ {help}
+
+ )}
-
-
- {error}
-
-
+ {!!error && (
+
+
+ {error}
+
+ )}
+
);
diff --git a/front2/src/components/form/FormSelectList.tsx b/front2/src/components/form/FormSelectList.tsx
index f890e1b..379c548 100644
--- a/front2/src/components/form/FormSelectList.tsx
+++ b/front2/src/components/form/FormSelectList.tsx
@@ -1,3 +1,5 @@
+import { useEffect, useRef } from 'react';
+
import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { isNullOrUndefined } from '@/utils/validator';
@@ -45,7 +47,15 @@ export const FormSelectList = ({
search,
}: FormSelectListProps) => {
const displayedValue = optionToOptionDisplay(options, selected, search);
-
+ const scrollToRef = useRef(null);
+ useEffect(() => {
+ if (scrollToRef?.current) {
+ scrollToRef?.current?.scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ });
+ }
+ }, []);
return (
onSelectValue(data)}
+ ref={data.isSelected ? scrollToRef : undefined}
>
{data.name}
diff --git a/front2/src/components/form/Formidable.tsx b/front2/src/components/form/Formidable.tsx
index 33cdc5f..965525a 100644
--- a/front2/src/components/form/Formidable.tsx
+++ b/front2/src/components/form/Formidable.tsx
@@ -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 } => {
- const result: { [key: string]: boolean } = {};
- for (const key in obj1) {
- if (obj1.hasOwnProperty(key)) {
- result[key] = obj1[key] !== obj2[key];
- }
- }
- return result;
-};
+import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
for (const key in obj) {
@@ -19,55 +11,153 @@ const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
return false;
};
-// 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;
-// });
-// };
+function getDifferences(
+ obj1: object,
+ obj2: object
+): { [key: string]: boolean } {
+ // Create an empty object to store the differences
+ const result: { [key: string]: boolean } = {};
+ // Recursive function to compare values
+ function compareValues(value1: any, value2: any): boolean {
+ // If both values are objects, compare their properties recursively
+ if (isObject(value1) && isObject(value2)) {
+ return hasAnyTrue(getDifferences(value1, value2));
+ }
+ // 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 = ({
+ // 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 = ({
initialValues = {} as TYPE,
}: {
initialValues?: TYPE;
}) => {
const [values, setValues] = useState({ ...initialValues } as TYPE);
- const initialData = useMemo(() => {
- setValues({ ...initialValues } as TYPE);
- return { ...initialValues } as TYPE;
- }, [initialValues, setValues]);
+ const [initialData, setInitialData] = useState(initialValues);
const [valueChange, setValueChange] = useState<{ [key: string]: boolean }>(
{}
);
const [isFormModified, setIsFormModified] = useState(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(
(data: object) => {
+ //console.log(`FORMIDABLE: setValuesExternal(${JSON.stringify(data)}) ==> keys=${Object.keys(data)}`);
setValues((previous) => {
const newValues = { ...previous, ...data };
- const ret = getDifferences(initialData, values);
+ const ret = getDifferences(initialData, newValues);
setValueChange(ret);
setIsFormModified(hasAnyTrue(ret));
- console.log(
- ` ppppppppppppppppppp ${JSON.stringify(ret, null, 2)} ==> ${hasAnyTrue(ret)}`
- );
return newValues;
});
},
[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 {
values,
valueChange,
isFormModified,
setValues: setValuesExternal,
+ getDeltaData,
+ restoreValue,
};
};
diff --git a/front2/src/components/form/SelectMultiple.tsx b/front2/src/components/form/SelectMultiple.tsx
index 9d028ff..b38897a 100644
--- a/front2/src/components/form/SelectMultiple.tsx
+++ b/front2/src/components/form/SelectMultiple.tsx
@@ -60,7 +60,7 @@ export const SelectMultiple = ({
const selectValue = (data: SelectMultipleValueDisplayType) => {
const newValues = values?.includes(data.id)
- ? values.filter((elem) => data.id === elem)
+ ? values.filter((elem) => data.id !== elem)
: [...(values ?? []), data.id];
setShowList(false);
if (onChange) {
diff --git a/front2/src/components/popup/ConfirmPopUp.tsx b/front2/src/components/popup/ConfirmPopUp.tsx
new file mode 100644
index 0000000..7277390
--- /dev/null
+++ b/front2/src/components/popup/ConfirmPopUp.tsx
@@ -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 (
+
+
+
+
+ {title}
+
+
+ {body}
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/front2/src/components/popup/TrackEditPopUp.tsx b/front2/src/components/popup/TrackEditPopUp.tsx
index 6be79e2..5e788cc 100644
--- a/front2/src/components/popup/TrackEditPopUp.tsx
+++ b/front2/src/components/popup/TrackEditPopUp.tsx
@@ -2,8 +2,7 @@ import { useEffect, useRef, useState } from 'react';
import {
Button,
- FormControl,
- FormLabel,
+ IconButton,
Input,
Modal,
ModalBody,
@@ -12,205 +11,246 @@ import {
ModalFooter,
ModalHeader,
ModalOverlay,
+ NumberDecrementStepper,
+ NumberIncrementStepper,
NumberInput,
+ NumberInputField,
+ NumberInputStepper,
+ Text,
Textarea,
+ useDisclosure,
} 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 { Album, Track } from '@/back-api';
+import { Track, TrackResource } from '@/back-api';
import { FormGroup } from '@/components/form/FormGroup';
+import { useFormidable } from '@/components/form/Formidable';
import { SelectMultiple } from '@/components/form/SelectMultiple';
import { SelectSingle } from '@/components/form/SelectSingle';
+import { ConfirmPopUp } from '@/components/popup/ConfirmPopUp';
import { useOrderedAlbums } from '@/service/Album';
import { useOrderedArtists } from '@/service/Artist';
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';
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) => {
const { trackId } = useParams();
+ const trackIdInt = isNullOrUndefined(trackId)
+ ? undefined
+ : parseInt(trackId, 10);
+ const { session } = useServiceContext();
const { dataGenders } = useOrderedGenders(undefined);
const { dataArtist } = useOrderedArtists(undefined);
const { dataAlbums } = useOrderedAlbums(undefined);
- const { dataTrack } = useSpecificTrack(
- isNullOrUndefined(trackId) ? undefined : parseInt(trackId, 10)
- );
+ const { store } = useTrackService();
+ const { dataTrack } = useSpecificTrack(trackIdInt);
+ const [admin, setAdmin] = useState(false);
const navigate = useNavigate();
+ const disclosure = useDisclosure();
const onClose = () => {
navigate('../../', { relative: 'path' });
};
- const onSubmit = (values) => {
- console.log(`onSubmit = ${values}`);
- };
- const onValuesChange = (values, form) => {
- console.log(`onValuesChange = ${values}`);
+ const onRemove = () => {
+ if (isNullOrUndefined(trackIdInt)) {
+ return;
+ }
+ store.remove(
+ trackIdInt,
+ TrackResource.remove({
+ restConfig: session.getRestConfig(),
+ params: {
+ id: trackIdInt,
+ },
+ })
+ );
+ onClose();
};
const initialRef = useRef(null);
const finalRef = useRef(null);
- const form = useForm