Simplify and better input models
This commit is contained in:
parent
63272dfb67
commit
ddf822e824
2836
front/pnpm-lock.yaml
generated
2836
front/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* Interface of the server (auto-generated code)
|
* Interface of the server (auto-generated code)
|
||||||
*/
|
*/
|
||||||
import { z as zod } from "zod";
|
import { z as zod } from 'zod';
|
||||||
|
|
||||||
|
|
||||||
export const ZodUserCreate = zod.object({
|
export const ZodUserCreate = zod.object({
|
||||||
login: zod.string().min(3).max(128),
|
login: zod.string().min(3).max(128),
|
||||||
email: zod.string().min(5).max(128),
|
email: zod.string().min(5).max(128),
|
||||||
password: zod.string().min(128).max(128),
|
password: zod.string().min(128).max(128),
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UserCreate = zod.infer<typeof ZodUserCreate>;
|
export type UserCreate = zod.infer<typeof ZodUserCreate>;
|
||||||
|
|
||||||
export function isUserCreate(data: any): data is UserCreate {
|
export function isUserCreate(data: any): data is UserCreate {
|
||||||
try {
|
try {
|
||||||
ZodUserCreate.parse(data);
|
ZodUserCreate.parse(data);
|
||||||
return true;
|
return true;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log(`Fail to parse data type='ZodUserCreate' error=${e}`);
|
console.log(`Fail to parse data type='ZodUserCreate' error=${e}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const ZodUserCreateWrite = zod.object({
|
export const ZodUserCreateWrite = zod.object({
|
||||||
login: zod.string().min(3).max(128).optional(),
|
login: zod.string().min(3).max(128).optional(),
|
||||||
email: zod.string().min(5).max(128).optional(),
|
email: zod.string().min(5).max(128).optional(),
|
||||||
password: zod.string().min(128).max(128).optional(),
|
password: zod.string().min(128).max(128).optional(),
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UserCreateWrite = zod.infer<typeof ZodUserCreateWrite>;
|
export type UserCreateWrite = zod.infer<typeof ZodUserCreateWrite>;
|
||||||
|
|
||||||
export function isUserCreateWrite(data: any): data is UserCreateWrite {
|
export function isUserCreateWrite(data: any): data is UserCreateWrite {
|
||||||
try {
|
try {
|
||||||
ZodUserCreateWrite.parse(data);
|
ZodUserCreateWrite.parse(data);
|
||||||
return true;
|
return true;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log(`Fail to parse data type='ZodUserCreateWrite' error=${e}`);
|
console.log(`Fail to parse data type='ZodUserCreateWrite' error=${e}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,11 @@
|
|||||||
import {
|
import { DragEventHandler, ReactNode, RefObject } from 'react';
|
||||||
DragEventHandler,
|
|
||||||
ReactNode,
|
|
||||||
RefObject,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import {
|
import { Box, BoxProps, Center, Flex, HStack, Image } from '@chakra-ui/react';
|
||||||
Box,
|
import { MdHighlightOff, MdUploadFile } from 'react-icons/md';
|
||||||
BoxProps,
|
|
||||||
Center,
|
|
||||||
Flex,
|
|
||||||
HStack,
|
|
||||||
Image,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
|
||||||
MdHighlightOff,
|
|
||||||
MdUploadFile,
|
|
||||||
} from 'react-icons/md';
|
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||||
|
|
||||||
import { useFormidableContextElement } from '../formidable';
|
import { useFormidableContextElement } from '../formidable';
|
||||||
|
|
||||||
export type DragNdropProps = {
|
export type DragNdropProps = {
|
||||||
@ -29,8 +16,8 @@ export type DragNdropProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DragNdrop = ({
|
export const DragNdrop = ({
|
||||||
onFilesSelected = () => { },
|
onFilesSelected = () => {},
|
||||||
onUriSelected = () => { },
|
onUriSelected = () => {},
|
||||||
width = '100px',
|
width = '100px',
|
||||||
height = '100px',
|
height = '100px',
|
||||||
}: DragNdropProps) => {
|
}: DragNdropProps) => {
|
||||||
@ -111,7 +98,9 @@ export const CenterIcon = ({
|
|||||||
top="50%"
|
top="50%"
|
||||||
left="50%"
|
left="50%"
|
||||||
transform="translate(-50%, -50%)"
|
transform="translate(-50%, -50%)"
|
||||||
>{children}</Box>
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -130,20 +119,15 @@ export type FormCoversProps = {
|
|||||||
export const FormCovers = ({
|
export const FormCovers = ({
|
||||||
name,
|
name,
|
||||||
ref,
|
ref,
|
||||||
onFilesSelected = () => { },
|
onFilesSelected = () => {},
|
||||||
onUriSelected = () => { },
|
onUriSelected = () => {},
|
||||||
onRemove = () => { },
|
onRemove = () => {},
|
||||||
...rest
|
...rest
|
||||||
}: FormCoversProps) => {
|
}: FormCoversProps) => {
|
||||||
const {value, isModify, onRestore} = useFormidableContextElement(name);
|
const { value } = useFormidableContextElement(name);
|
||||||
const urls =
|
const urls = DataUrlAccess.getListThumbnailUrl(value) ?? [];
|
||||||
DataUrlAccess.getListThumbnailUrl(value) ?? [];
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup name={name} {...rest}>
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<HStack wrap="wrap" width="full">
|
<HStack wrap="wrap" width="full">
|
||||||
{urls.map((data, index) => (
|
{urls.map((data, index) => (
|
||||||
<Flex align="flex-start" key={data}>
|
<Flex align="flex-start" key={data}>
|
||||||
|
@ -1,67 +1,128 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
import { Flex, Text } from '@chakra-ui/react';
|
import { Box, Flex, Text } from '@chakra-ui/react';
|
||||||
import { MdErrorOutline, MdHelpOutline, MdRefresh } from 'react-icons/md';
|
import { MdErrorOutline, MdHelpOutline, MdRefresh } from 'react-icons/md';
|
||||||
|
|
||||||
|
import { useFormidableContextElement } from '../formidable';
|
||||||
|
|
||||||
|
const DisplayLabel = ({
|
||||||
|
label,
|
||||||
|
isRequired,
|
||||||
|
}: {
|
||||||
|
label?: ReactNode;
|
||||||
|
isRequired: boolean;
|
||||||
|
}) => {
|
||||||
|
if (!label) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Text marginRight="auto" fontWeight="bold">
|
||||||
|
{label}{' '}
|
||||||
|
{isRequired && (
|
||||||
|
<Text as="span" color="red.600">
|
||||||
|
*
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DisplayHelp = ({ help }: { help?: ReactNode }) => {
|
||||||
|
if (!help) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Flex direction="row">
|
||||||
|
<MdHelpOutline />
|
||||||
|
<Text alignContent="center">{help}</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const DisplayError = ({ error }: { error?: ReactNode }) => {
|
||||||
|
if (!error) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Flex direction="row" color="red.600">
|
||||||
|
<MdErrorOutline />
|
||||||
|
<Text alignContent="center">{error}</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export type FormGroupProps = {
|
export type FormGroupProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
name: string;
|
||||||
error?: ReactNode;
|
error?: ReactNode;
|
||||||
help?: ReactNode;
|
help?: ReactNode;
|
||||||
label?: ReactNode;
|
label?: ReactNode;
|
||||||
isModify?: boolean;
|
|
||||||
onRestore?: () => void;
|
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
children: ReactNode;
|
disableSingleLine?: boolean;
|
||||||
enableModifyNotification?: boolean;
|
|
||||||
enableReset?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormGroup = ({
|
export const FormGroup = ({
|
||||||
children,
|
children,
|
||||||
error,
|
name,
|
||||||
help,
|
help,
|
||||||
label,
|
label,
|
||||||
isModify = false,
|
|
||||||
isRequired = false,
|
isRequired = false,
|
||||||
enableModifyNotification = false,
|
disableSingleLine,
|
||||||
enableReset = false,
|
}: FormGroupProps) => {
|
||||||
onRestore,
|
const { form, error, isModify, onRestore } =
|
||||||
}: FormGroupProps) => (
|
useFormidableContextElement(name);
|
||||||
<Flex
|
const enableModifyNotification =
|
||||||
borderLeftWidth="3px"
|
form.configuration.enableModifyNotification ?? false;
|
||||||
borderLeftColor={error ? 'red' : enableModifyNotification && isModify ? 'blue' : '#00000000'}
|
const enableReset = form.configuration.enableReset ?? false;
|
||||||
paddingLeft="7px"
|
const singleLine = disableSingleLine
|
||||||
paddingY="4px"
|
? false
|
||||||
width="full"
|
: form.configuration.singleLineForm;
|
||||||
direction="column"
|
return (
|
||||||
>
|
<Flex
|
||||||
<Flex direction="row" width="full" gap="52px">
|
borderLeftWidth="3px"
|
||||||
{!!label && (
|
borderLeftColor={
|
||||||
<Text marginRight="auto" fontWeight="bold">
|
error
|
||||||
{label}{' '}
|
? 'red.600'
|
||||||
{isRequired && (
|
: enableModifyNotification && isModify
|
||||||
<Text as="span" color="red.600">
|
? 'blue.600'
|
||||||
*
|
: '#00000000'
|
||||||
</Text>
|
}
|
||||||
)}
|
paddingLeft="7px"
|
||||||
</Text>
|
paddingY="4px"
|
||||||
|
width="full"
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
{singleLine && (
|
||||||
|
<>
|
||||||
|
<Flex direction="row" width="full" gap="52px">
|
||||||
|
<Box width="10%">
|
||||||
|
<DisplayLabel label={label} isRequired={isRequired} />
|
||||||
|
{!!onRestore && isModify && enableReset && (
|
||||||
|
<MdRefresh size="15px" onClick={onRestore} cursor="pointer" />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Flex direction="column" width={'90%'} gap="5px">
|
||||||
|
{children}
|
||||||
|
<DisplayHelp help={help} />
|
||||||
|
<DisplayError error={error} />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{!!onRestore && isModify && enableReset && (
|
{!singleLine && (
|
||||||
<MdRefresh size="15px" onClick={onRestore} cursor="pointer" />
|
<>
|
||||||
|
<Flex direction="row" width="full" gap="52px">
|
||||||
|
<Box width="full">
|
||||||
|
<DisplayLabel label={label} isRequired={isRequired} />
|
||||||
|
{!!onRestore && isModify && enableReset && (
|
||||||
|
<MdRefresh size="15px" onClick={onRestore} cursor="pointer" />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
{children}
|
||||||
|
<DisplayHelp help={help} />
|
||||||
|
<DisplayError error={error} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
{children}
|
);
|
||||||
{!!help && (
|
};
|
||||||
<Flex direction="row">
|
|
||||||
<MdHelpOutline />
|
|
||||||
<Text>{help}</Text>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!!error && (
|
|
||||||
<Flex direction="row">
|
|
||||||
<MdErrorOutline />
|
|
||||||
<Text>{error}</Text>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
|
@ -19,13 +19,10 @@ export const FormInput = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
...rest
|
...rest
|
||||||
}: FormInputProps) => {
|
}: FormInputProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const {value, onChange} = useFormidableContextElement(name);
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
name={name}
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { NumberInputField, NumberInputProps, NumberInputRoot } from '../ui/number-input';
|
|
||||||
import { useFormidableContextElement } from '../formidable';
|
import { useFormidableContextElement } from '../formidable';
|
||||||
|
import {
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputProps,
|
||||||
|
NumberInputRoot,
|
||||||
|
} from '../ui/number-input';
|
||||||
|
|
||||||
export type FormNumberProps = Pick<
|
export type FormNumberProps = Pick<
|
||||||
NumberInputProps,
|
NumberInputProps,
|
||||||
@ -25,15 +30,10 @@ export const FormNumber = ({
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
...rest
|
...rest
|
||||||
}: FormNumberProps) => {
|
}: FormNumberProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const { form, value, isModify, onChange, onRestore } =
|
||||||
|
useFormidableContextElement(name);
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup name={name} {...rest}>
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<NumberInputRoot
|
<NumberInputRoot
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -21,32 +21,29 @@ export const FormPassword = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
...rest
|
...rest
|
||||||
}: FormInputProps) => {
|
}: FormInputProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const {value, onChange} = useFormidableContextElement(name);
|
||||||
const [showPassword, setShowPassword] = useState<boolean>(false);
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||||
function toggleVisible(): void {
|
function toggleVisible(): void {
|
||||||
setShowPassword((value) => ! value)
|
setShowPassword((value) => ! value)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
name={name}
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<chakra.div position="relative">
|
<chakra.div position="relative" width="full">
|
||||||
<Input
|
<Input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
type={showPassword? "text" : "password"}
|
type={showPassword? "text" : "password"}
|
||||||
name={name}
|
name={name}
|
||||||
autoComplete={name}
|
autoComplete={name}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
paddingRight="47px"
|
paddingRight="47px"
|
||||||
/>
|
/>
|
||||||
<Button variant="ghost" onClick={toggleVisible} position="absolute" top="0" right="0" _hover={{bg:"#0000", shadow:"none", color:"black"}}>
|
<Button variant="ghost" onClick={toggleVisible} position="absolute" top="0" right="0" _hover={{bg:"#0000", shadow:"none", color:"black"}}>
|
||||||
{showPassword? <LuEye/> : <LuEyeOff/>}
|
{showPassword? <LuEye/> : <LuEyeOff/>}
|
||||||
</Button>
|
</Button>
|
||||||
</chakra.div>
|
</chakra.div>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { SelectSingle } from '@/components/select/SelectSingle';
|
import { SelectSingle } from '@/components/select/SelectSingle';
|
||||||
|
|
||||||
import { useFormidableContextElement } from '../formidable';
|
import { useFormidableContextElement } from '../formidable';
|
||||||
|
|
||||||
export type FormSelectProps = {
|
export type FormSelectProps = {
|
||||||
@ -37,21 +39,18 @@ export const FormSelect = ({
|
|||||||
addNewItem,
|
addNewItem,
|
||||||
...rest
|
...rest
|
||||||
}: FormSelectProps) => {
|
}: FormSelectProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const { form, value, isModify, onChange, onRestore } =
|
||||||
|
useFormidableContextElement(name);
|
||||||
// if set add capability to add the search item
|
// if set add capability to add the search item
|
||||||
const onCreate = !addNewItem
|
const onCreate = !addNewItem
|
||||||
? undefined
|
? undefined
|
||||||
: (data: string) => {
|
: (data: string) => {
|
||||||
addNewItem(data).then((data: object) => form.setValues({ [name]: data[keyInputKey] }));
|
addNewItem(data).then((data: object) =>
|
||||||
};
|
form.setValues({ [name]: data[keyInputKey] })
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup name={name} {...rest}>
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -2,7 +2,11 @@ import { RefObject } from 'react';
|
|||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { SelectMultiple } from '@/components/select/SelectMultiple';
|
import { SelectMultiple } from '@/components/select/SelectMultiple';
|
||||||
import { useFormidableContext, useFormidableContextElement } from '../formidable';
|
|
||||||
|
import {
|
||||||
|
useFormidableContext,
|
||||||
|
useFormidableContextElement,
|
||||||
|
} from '../formidable';
|
||||||
|
|
||||||
export type FormSelectMultipleProps = {
|
export type FormSelectMultipleProps = {
|
||||||
// Form: Name of the variable
|
// Form: Name of the variable
|
||||||
@ -35,21 +39,20 @@ export const FormSelectMultiple = ({
|
|||||||
addNewItem,
|
addNewItem,
|
||||||
...rest
|
...rest
|
||||||
}: FormSelectMultipleProps) => {
|
}: FormSelectMultipleProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const { form, value, isModify, onChange, onRestore } =
|
||||||
|
useFormidableContextElement(name);
|
||||||
// if set add capability to add the search item
|
// if set add capability to add the search item
|
||||||
const onCreate = !addNewItem
|
const onCreate = !addNewItem
|
||||||
? undefined
|
? undefined
|
||||||
: (data: string) => {
|
: (data: string) => {
|
||||||
addNewItem(data).then((data: object) => form.setValues({ [name]: [...(form.values[name] ?? []), data[keyInputKey]] }));
|
addNewItem(data).then((data: object) =>
|
||||||
};
|
form.setValues({
|
||||||
|
[name]: [...(form.values[name] ?? []), data[keyInputKey]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup name={name} {...rest}>
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<SelectMultiple
|
<SelectMultiple
|
||||||
//ref={ref}
|
//ref={ref}
|
||||||
values={value}
|
values={value}
|
||||||
|
@ -3,6 +3,7 @@ import { RefObject } from 'react';
|
|||||||
import { Textarea } from '@chakra-ui/react';
|
import { Textarea } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
|
|
||||||
import { useFormidableContextElement } from '../formidable';
|
import { useFormidableContextElement } from '../formidable';
|
||||||
|
|
||||||
export type FormTextareaProps = {
|
export type FormTextareaProps = {
|
||||||
@ -19,15 +20,10 @@ export const FormTextarea = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
...rest
|
...rest
|
||||||
}: FormTextareaProps) => {
|
}: FormTextareaProps) => {
|
||||||
const {form, value, isModify, onChange, onRestore} = useFormidableContextElement(name);
|
const { form, value, isModify, onChange, onRestore } =
|
||||||
|
useFormidableContextElement(name);
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup name={name} {...rest}>
|
||||||
enableModifyNotification={form.configuration.enableModifyNotification}
|
|
||||||
enableReset={form.configuration.enableReset}
|
|
||||||
isModify={isModify}
|
|
||||||
onRestore={onRestore}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<Textarea
|
<Textarea
|
||||||
name={name}
|
name={name}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
@ -1,26 +1,39 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { z as zod } from 'zod';
|
||||||
|
|
||||||
import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
|
import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
|
||||||
|
|
||||||
import { getDifferences, hasAnyTrue } from './utils';
|
import { getDifferences, hasAnyTrue } from './utils';
|
||||||
|
|
||||||
export type FormidableConfig = {
|
export type FormidableConfig = {
|
||||||
enableReset?: boolean;
|
enableReset?: boolean;
|
||||||
enableModifyNotification?: boolean;
|
enableModifyNotification?: boolean;
|
||||||
|
singleLineForm?: boolean;
|
||||||
};
|
};
|
||||||
const initialFormConfig: Required<FormidableConfig> = {
|
const initialFormConfig: Required<FormidableConfig> = {
|
||||||
enableReset: true,
|
enableReset: true,
|
||||||
enableModifyNotification: true,
|
enableModifyNotification: true,
|
||||||
|
singleLineForm: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFormidable = <TYPE extends object = object>({
|
export const useFormidable = <TYPE extends object = object>({
|
||||||
initialValues = {} as TYPE,
|
initialValues = {} as TYPE,
|
||||||
configuration: inputConfiguration = initialFormConfig,
|
configuration: inputConfiguration = initialFormConfig,
|
||||||
|
resolver = (_data: TYPE) => {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
}: {
|
}: {
|
||||||
initialValues?: TYPE;
|
initialValues?: TYPE;
|
||||||
configuration?: FormidableConfig;
|
configuration?: FormidableConfig;
|
||||||
|
resolver?: (data: any) => Record<string, string>;
|
||||||
}) => {
|
}) => {
|
||||||
const configuration: Required<FormidableConfig> = { ...initialFormConfig, ...inputConfiguration };
|
const configuration: Required<FormidableConfig> = {
|
||||||
|
...initialFormConfig,
|
||||||
|
...inputConfiguration,
|
||||||
|
};
|
||||||
const [values, setValues] = useState<TYPE>({ ...initialValues } as TYPE);
|
const [values, setValues] = useState<TYPE>({ ...initialValues } as TYPE);
|
||||||
|
const [errors, setErrors] = useState<object>({});
|
||||||
const [initialData, setInitialData] = useState<TYPE>(initialValues);
|
const [initialData, setInitialData] = useState<TYPE>(initialValues);
|
||||||
const [isModify, setIsModify] = useState<{ [key: string]: boolean }>({});
|
const [isModify, setIsModify] = useState<{ [key: string]: boolean }>({});
|
||||||
const [isFormModified, setIsFormModified] = useState<boolean>(false);
|
const [isFormModified, setIsFormModified] = useState<boolean>(false);
|
||||||
@ -57,10 +70,11 @@ export const useFormidable = <TYPE extends object = object>({
|
|||||||
const ret = getDifferences(initialData, newValues);
|
const ret = getDifferences(initialData, newValues);
|
||||||
setIsModify(ret);
|
setIsModify(ret);
|
||||||
setIsFormModified(hasAnyTrue(ret));
|
setIsFormModified(hasAnyTrue(ret));
|
||||||
|
setErrors(resolver(newValues));
|
||||||
return newValues;
|
return newValues;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setValues, initialData]
|
[setValues, initialData, setErrors, setIsFormModified, setIsModify]
|
||||||
);
|
);
|
||||||
const restoreValue = useCallback(
|
const restoreValue = useCallback(
|
||||||
(data: object) => {
|
(data: object) => {
|
||||||
@ -121,6 +135,7 @@ export const useFormidable = <TYPE extends object = object>({
|
|||||||
restoreValue,
|
restoreValue,
|
||||||
setValues: setValuesExternal,
|
setValues: setValuesExternal,
|
||||||
values,
|
values,
|
||||||
|
errors,
|
||||||
configuration,
|
configuration,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import {
|
||||||
|
ReactNode,
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
import { ReactNode, createContext, useCallback, useContext, useMemo } from 'react';
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { UseFormidableReturn } from './FormidableConfig';
|
import { UseFormidableReturn } from './FormidableConfig';
|
||||||
|
|
||||||
@ -12,47 +14,65 @@ export type FromContextProps = {
|
|||||||
|
|
||||||
export const formContext = createContext<FromContextProps>({
|
export const formContext = createContext<FromContextProps>({
|
||||||
form: {
|
form: {
|
||||||
getDeltaData: ({}:{omit?: string[], only?: string[]}) => {return {};},
|
getDeltaData: ({}: { omit?: string[]; only?: string[] }) => {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
isFormModified: false,
|
isFormModified: false,
|
||||||
isModify: { },
|
isModify: {},
|
||||||
restoreValues: () => {},
|
restoreValues: () => {},
|
||||||
restoreValue: (_data: object) => {},
|
restoreValue: (_data: object) => {},
|
||||||
setValues: (_data: object) => {},
|
setValues: (_data: object) => {},
|
||||||
values: {},
|
values: {},
|
||||||
configuration: {enableReset: false, enableModifyNotification: false}
|
errors: {},
|
||||||
}
|
configuration: {
|
||||||
|
enableReset: false,
|
||||||
|
enableModifyNotification: false,
|
||||||
|
singleLineForm: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export const useFormidableContext = () => {
|
export const useFormidableContext = () => {
|
||||||
const context = useContext(formContext);
|
const context = useContext(formContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error("useFormContext must be used within a FormProvider");
|
throw new Error('useFormContext must be used within a FormProvider');
|
||||||
}
|
}
|
||||||
if (!context.form) {
|
if (!context.form) {
|
||||||
throw new Error("useFormContext without defining a From");
|
throw new Error('useFormContext without defining a From');
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
export const useFormidableContextElement = (name: string) => {
|
export const useFormidableContextElement = (name: string) => {
|
||||||
const {form} = useFormidableContext();
|
const { form } = useFormidableContext();
|
||||||
if (name === undefined) {
|
if (name === undefined) {
|
||||||
console.error("Can not request useFormidableContextElement with empty 'name'");
|
console.error(
|
||||||
|
"Can not request useFormidableContextElement with empty 'name'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const onChange = useCallback((value) => {
|
const onChange = useCallback(
|
||||||
console.log(`new values: ${name}=>${value}`);
|
(value) => {
|
||||||
form.setValues({ [name]: value });
|
console.log(`new values: ${name}=>${value}`);
|
||||||
}, [name, form, form.setValues]);
|
form.setValues({ [name]: value });
|
||||||
|
},
|
||||||
|
[name, form, form.setValues]
|
||||||
|
);
|
||||||
const onRestore = useCallback(() => {
|
const onRestore = useCallback(() => {
|
||||||
form.restoreValue({ [name]: true });
|
form.restoreValue({ [name]: true });
|
||||||
}, [name, form, form.restoreValue]);
|
}, [name, form, form.restoreValue]);
|
||||||
return {form, value: form.values[name], isModify: form.isModify[name], onChange, onRestore};
|
return {
|
||||||
|
form,
|
||||||
|
value: form.values[name],
|
||||||
|
error: form.errors[name],
|
||||||
|
isModify: form.isModify[name],
|
||||||
|
onChange,
|
||||||
|
onRestore,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormidableContextProps= {
|
export type FormidableContextProps = {
|
||||||
form: UseFormidableReturn;
|
form: UseFormidableReturn;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const FormidableContext = ({
|
export const FormidableContext = ({
|
||||||
form,
|
form,
|
||||||
@ -65,9 +85,6 @@ export const FormidableContext = ({
|
|||||||
[form]
|
[form]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<formContext.Provider value={memoContext}>
|
<formContext.Provider value={memoContext}>{children}</formContext.Provider>
|
||||||
{children}
|
|
||||||
</formContext.Provider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { useFormidable } from './FormidableConfig';
|
import { useFormidable } from './FormidableConfig';
|
||||||
import { FormidableContext } from './FormidableContext';
|
import { FormidableContext } from './FormidableContext';
|
||||||
import { Box } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
export interface FormidableFormProps<TYPE extends object = object>{
|
export interface FormidableFormProps<TYPE extends object = object> {
|
||||||
form: ReturnType<typeof useFormidable<TYPE>>;
|
form: ReturnType<typeof useFormidable<TYPE>>;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
onSubmit?:( data: TYPE) => void;
|
onSubmit?: (data: TYPE) => void;
|
||||||
onSubmitDelta?:( data: Partial<TYPE>) => void;
|
onSubmitDelta?: (data: Partial<TYPE>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FormidableForm = <TYPE extends object = object>({
|
export const FormidableForm = <TYPE extends object = object>({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onSubmitDelta,
|
onSubmitDelta,
|
||||||
form,
|
form,
|
||||||
children,
|
children,
|
||||||
}: FormidableFormProps<TYPE>) => {
|
}: FormidableFormProps<TYPE>) => {
|
||||||
const handleSubmit = (event: React.FormEvent) => {
|
const handleSubmit = (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const hasErrors = false;//Object.values(errors).some((err) => err);
|
const hasErrors = false; //Object.values(errors).some((err) => err);
|
||||||
if (!hasErrors) {
|
if (!hasErrors) {
|
||||||
console.log(`request From submit !!! ${JSON.stringify(form.values, null, 2)}`);
|
console.log(
|
||||||
|
`request From submit !!! ${JSON.stringify(form.values, null, 2)}`
|
||||||
|
);
|
||||||
if (onSubmit) {
|
if (onSubmit) {
|
||||||
onSubmit(form.values);
|
onSubmit(form.values);
|
||||||
}
|
}
|
||||||
@ -37,9 +36,8 @@ onSubmitDelta,
|
|||||||
return (
|
return (
|
||||||
<FormidableContext form={form}>
|
<FormidableContext form={form}>
|
||||||
<Box as="form" onSubmit={handleSubmit}>
|
<Box as="form" onSubmit={handleSubmit}>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</FormidableContext>
|
</FormidableContext>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,4 +5,7 @@ export {
|
|||||||
} from './FormidableContext'
|
} from './FormidableContext'
|
||||||
export {
|
export {
|
||||||
useFormidable
|
useFormidable
|
||||||
} from './FormidableConfig';
|
} from './FormidableConfig';
|
||||||
|
export {
|
||||||
|
zodResolver
|
||||||
|
} from './utils';
|
@ -1,56 +1,87 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { ZodError } from 'zod';
|
||||||
|
|
||||||
import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
|
import { isArray, isNullOrUndefined, isObject } from '@/utils/validator';
|
||||||
|
|
||||||
export const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
export const hasAnyTrue = (obj: { [key: string]: boolean }): boolean => {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (obj.hasOwnProperty(key) && obj[key] === true) {
|
if (obj.hasOwnProperty(key) && obj[key] === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getDifferences(
|
export function getDifferences(
|
||||||
obj1: object,
|
obj1: object,
|
||||||
obj2: object
|
obj2: object
|
||||||
): { [key: string]: boolean } {
|
): { [key: string]: boolean } {
|
||||||
// Create an empty object to store the differences
|
// Create an empty object to store the differences
|
||||||
const result: { [key: string]: boolean } = {};
|
const result: { [key: string]: boolean } = {};
|
||||||
// Recursive function to compare values
|
// Recursive function to compare values
|
||||||
function compareValues(value1: any, value2: any): boolean {
|
function compareValues(value1: any, value2: any): boolean {
|
||||||
// If both values are objects, compare their properties recursively
|
// If both values are objects, compare their properties recursively
|
||||||
if (isObject(value1) && isObject(value2)) {
|
if (isObject(value1) && isObject(value2)) {
|
||||||
return hasAnyTrue(getDifferences(value1, 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;
|
|
||||||
}
|
}
|
||||||
|
// If both values are arrays, compare their elements
|
||||||
// Get all keys from both objects
|
if (isArray(value1) && isArray(value2)) {
|
||||||
const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
|
//console.log(`Check is array: ${JSON.stringify(value1)} =?= ${JSON.stringify(value2)}`);
|
||||||
|
if (value1.length !== value2.length) {
|
||||||
// Iterate over all keys
|
return true;
|
||||||
for (const key of allKeys) {
|
}
|
||||||
if (compareValues(obj1[key], obj2[key])) {
|
for (let i = 0; i < value1.length; i++) {
|
||||||
result[key] = true;
|
if (compareValues(value1[i], value2[i])) {
|
||||||
} else {
|
return true;
|
||||||
result[key] = false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return result;
|
// Otherwise, compare the values directly
|
||||||
|
//console.log(`compare : ${value1} =?= ${value2}`);
|
||||||
|
return value1 !== value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 zodResolver = (zodModel) => {
|
||||||
|
return (data: any) => {
|
||||||
|
try {
|
||||||
|
console.log(`check resolver of: ${JSON.stringify(data, null, 2)}`);
|
||||||
|
zodModel.parse(data);
|
||||||
|
return {};
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ZodError) {
|
||||||
|
console.log(
|
||||||
|
`catch error with resolver: ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
const formattedErrors = error.issues.reduce(
|
||||||
|
(acc, issue) => {
|
||||||
|
if (issue.path.length > 0) {
|
||||||
|
acc[issue.path[0]] = issue.message;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
console.log(`get errors: ${JSON.stringify(formattedErrors, null, 2)}`);
|
||||||
|
return formattedErrors;
|
||||||
|
}
|
||||||
|
// prevent zod error
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { Button, Table, Text } from '@chakra-ui/react';
|
import { Button, Table, Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { UserCreateWrite, ZodUserCreateWrite } from '@/back-api';
|
||||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||||
|
import { ParameterLayout } from '@/components/ParameterLayout';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
|
|
||||||
import { UserCreateWrite } from '@/back-api';
|
|
||||||
import { useFormidable } from '@/components/formidable/FormidableConfig';
|
|
||||||
import { FormInput } from '@/components/form/FormInput';
|
import { FormInput } from '@/components/form/FormInput';
|
||||||
import { FormPassword } from '@/components/form/FormPassword';
|
import { FormPassword } from '@/components/form/FormPassword';
|
||||||
import { ParameterLayout } from '@/components/ParameterLayout';
|
import { Formidable, zodResolver } from '@/components/formidable';
|
||||||
|
import { useFormidable } from '@/components/formidable/FormidableConfig';
|
||||||
import { UserService } from '@/service/user.service';
|
import { UserService } from '@/service/user.service';
|
||||||
|
|
||||||
const options: Intl.DateTimeFormatOptions = {
|
const options: Intl.DateTimeFormatOptions = {
|
||||||
year: "numeric",
|
year: 'numeric',
|
||||||
month: "2-digit",
|
month: '2-digit',
|
||||||
day: "2-digit",
|
day: '2-digit',
|
||||||
hour: "2-digit",
|
hour: '2-digit',
|
||||||
minute: "2-digit",
|
minute: '2-digit',
|
||||||
second: "2-digit",
|
second: '2-digit',
|
||||||
//timeZoneName: "short",
|
//timeZoneName: "short",
|
||||||
};
|
};
|
||||||
export const ManageAccountPage = () => {
|
export const ManageAccountPage = () => {
|
||||||
@ -26,55 +26,66 @@ export const ManageAccountPage = () => {
|
|||||||
<TopBar title="Manage accounts" />
|
<TopBar title="Manage accounts" />
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<ParameterLayout.Root>
|
<ParameterLayout.Root>
|
||||||
<ParameterLayout.HeaderBase title="Users" description="List of all users"/>
|
<ParameterLayout.HeaderBase
|
||||||
<ParameterLayout.Content>
|
title="Users"
|
||||||
<Table.Root size="sm" variant="outline" interactive>
|
description="List of all users"
|
||||||
<Table.Header>
|
/>
|
||||||
<Table.Row>
|
<ParameterLayout.Content>
|
||||||
<Table.ColumnHeader>id</Table.ColumnHeader>
|
<Table.Root size="sm" variant="outline" interactive>
|
||||||
<Table.ColumnHeader>Login</Table.ColumnHeader>
|
<Table.Header>
|
||||||
<Table.ColumnHeader>e-mail</Table.ColumnHeader>
|
<Table.Row>
|
||||||
<Table.ColumnHeader>blocked</Table.ColumnHeader>
|
<Table.ColumnHeader>id</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>avatar</Table.ColumnHeader>
|
<Table.ColumnHeader>Login</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader textAlign="end">Last connection</Table.ColumnHeader>
|
<Table.ColumnHeader>e-mail</Table.ColumnHeader>
|
||||||
</Table.Row>
|
<Table.ColumnHeader>blocked</Table.ColumnHeader>
|
||||||
</Table.Header>
|
<Table.ColumnHeader>avatar</Table.ColumnHeader>
|
||||||
<Table.Body>
|
<Table.ColumnHeader textAlign="end">
|
||||||
{users?.map((data) => (
|
Last connection
|
||||||
<Table.Row key={data.id}>
|
</Table.ColumnHeader>
|
||||||
<Table.Cell>{data.id}</Table.Cell>
|
</Table.Row>
|
||||||
<Table.Cell>{data.login}</Table.Cell>
|
</Table.Header>
|
||||||
<Table.Cell>{data.email}</Table.Cell>
|
<Table.Body>
|
||||||
<Table.Cell>{data.blocked? "true" : "false"}</Table.Cell>
|
{users?.map((data) => (
|
||||||
<Table.Cell>{data.avatar? "yes" : "no"}</Table.Cell>
|
<Table.Row key={data.id}>
|
||||||
<Table.Cell textAlign="end">
|
<Table.Cell>{data.id}</Table.Cell>
|
||||||
{data.lastConnection ? (new Date(data.lastConnection)).toLocaleDateString("fr-FR", options): ""}
|
<Table.Cell>{data.login}</Table.Cell>
|
||||||
</Table.Cell>
|
<Table.Cell>{data.email}</Table.Cell>
|
||||||
</Table.Row>
|
<Table.Cell>{data.blocked ? 'true' : 'false'}</Table.Cell>
|
||||||
))}
|
<Table.Cell>{data.avatar ? 'yes' : 'no'}</Table.Cell>
|
||||||
</Table.Body>
|
<Table.Cell textAlign="end">
|
||||||
</Table.Root>
|
{data.lastConnection
|
||||||
</ParameterLayout.Content>
|
? new Date(data.lastConnection).toLocaleDateString(
|
||||||
<ParameterLayout.Footer/>
|
'fr-FR',
|
||||||
|
options
|
||||||
|
)
|
||||||
|
: ''}
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
))}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
</ParameterLayout.Content>
|
||||||
|
<ParameterLayout.Footer />
|
||||||
</ParameterLayout.Root>
|
</ParameterLayout.Root>
|
||||||
<CreateUserComponent/>
|
<CreateUserComponent />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateUserComponent = () => {
|
export const CreateUserComponent = () => {
|
||||||
|
|
||||||
const form = useFormidable<UserCreateWrite>({
|
const form = useFormidable<UserCreateWrite>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
login: "",
|
login: '',
|
||||||
email: "",
|
email: '',
|
||||||
password: "",
|
password: '',
|
||||||
},
|
},
|
||||||
configuration: {
|
configuration: {
|
||||||
enableModifyNotification: false,
|
enableModifyNotification: false,
|
||||||
enableReset: false,
|
enableReset: false,
|
||||||
}
|
singleLineForm: true,
|
||||||
|
},
|
||||||
|
resolver: zodResolver(ZodUserCreateWrite),
|
||||||
});
|
});
|
||||||
|
|
||||||
//const { connect, lastError, isConnectionLoading } = useLogin();
|
//const { connect, lastError, isConnectionLoading } = useLogin();
|
||||||
@ -84,36 +95,35 @@ export const CreateUserComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ParameterLayout.Root>
|
<Formidable.From form={form}>
|
||||||
<ParameterLayout.HeaderBase title="Create users" />
|
<ParameterLayout.Root>
|
||||||
<ParameterLayout.Content>
|
<ParameterLayout.HeaderBase
|
||||||
{userCreate.error && <Text colorPalette="@danger">{userCreate.error.message}</Text>}
|
title="Create a users"
|
||||||
<FormInput
|
description="Add a new user on the server"
|
||||||
form={form}
|
|
||||||
name="login"
|
|
||||||
isRequired
|
|
||||||
label="Username"
|
|
||||||
/>
|
/>
|
||||||
<FormPassword
|
<ParameterLayout.Content>
|
||||||
form={form}
|
{userCreate.error && (
|
||||||
name="password"
|
<Text colorPalette="@danger">{userCreate.error.message}</Text>
|
||||||
isRequired
|
)}
|
||||||
label="Password"
|
<FormInput name="login" isRequired label="Username" />
|
||||||
/>
|
<FormInput name="email" isRequired label="E-mail" />
|
||||||
</ParameterLayout.Content>
|
<FormPassword name="password" isRequired label="Password" />
|
||||||
<ParameterLayout.Footer>
|
</ParameterLayout.Content>
|
||||||
<Button
|
<ParameterLayout.Footer>
|
||||||
marginLeft="55%"
|
<Button
|
||||||
marginTop="10px"
|
marginLeft="auto"
|
||||||
variant="solid"
|
marginTop="10px"
|
||||||
background="green"
|
variant="solid"
|
||||||
width="45%"
|
background="green"
|
||||||
disabled={userCreate.isCalling}
|
width="250px"
|
||||||
onClick={onSubmit}
|
maxWidth="45%"
|
||||||
>
|
disabled={userCreate.isCalling}
|
||||||
Login
|
onClick={onSubmit}
|
||||||
</Button>
|
>
|
||||||
</ParameterLayout.Footer>
|
Login
|
||||||
</ParameterLayout.Root>);
|
</Button>
|
||||||
|
</ParameterLayout.Footer>
|
||||||
}
|
</ParameterLayout.Root>
|
||||||
|
</Formidable.From>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -7,7 +7,6 @@ import { FormInput } from '@/components/form/FormInput';
|
|||||||
import { useLogin } from './useLogin';
|
import { useLogin } from './useLogin';
|
||||||
import { FormPassword } from '@/components/form/FormPassword';
|
import { FormPassword } from '@/components/form/FormPassword';
|
||||||
import { FormidableForm } from '@/components/formidable/FormidableForm';
|
import { FormidableForm } from '@/components/formidable/FormidableForm';
|
||||||
import { on } from 'events';
|
|
||||||
|
|
||||||
type LoginFormInputs = {
|
type LoginFormInputs = {
|
||||||
login: string;
|
login: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user