mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Add user form and fix bug
This commit is contained in:
@@ -33,7 +33,6 @@ const useForm = (
|
|||||||
|
|
||||||
// Get a local copy of stateSchema
|
// Get a local copy of stateSchema
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDisable(true); // Disable button in initial render.
|
|
||||||
setInitialErrorState();
|
setInitialErrorState();
|
||||||
}, []); // eslint-disable-line
|
}, []); // eslint-disable-line
|
||||||
|
|
||||||
@@ -116,8 +115,10 @@ const useForm = (
|
|||||||
|
|
||||||
// Making sure that there's no error in the state
|
// Making sure that there's no error in the state
|
||||||
// before calling the submit callback function
|
// before calling the submit callback function
|
||||||
|
// and disabled button
|
||||||
if (!validateErrorState()) {
|
if (!validateErrorState()) {
|
||||||
submitFormCallback(values);
|
submitFormCallback(values);
|
||||||
|
setDisable(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[validateErrorState, submitFormCallback, values],
|
[validateErrorState, submitFormCallback, values],
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { forwardRef, useEffect } from 'react';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Dialog from '../../components/Dialog';
|
||||||
|
import UserForm from './Form';
|
||||||
|
|
||||||
|
// Interfaces
|
||||||
|
import { IDialogProps } from '../../globals/interfaces';
|
||||||
|
|
||||||
|
const UserDialog = forwardRef((props, ref) => {
|
||||||
|
const dialogRef = ref as React.MutableRefObject<
|
||||||
|
HTMLDialogElement | undefined
|
||||||
|
>;
|
||||||
|
const { onClose } = props;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dialogRef.current) {
|
||||||
|
dialogRef.current.addEventListener('click', (e: MouseEvent) => {
|
||||||
|
const dialogDimensions = dialogRef.current!.getBoundingClientRect();
|
||||||
|
if (
|
||||||
|
e.clientX < dialogDimensions.left ||
|
||||||
|
e.clientX > dialogDimensions.right ||
|
||||||
|
e.clientY < dialogDimensions.top ||
|
||||||
|
e.clientY > dialogDimensions.bottom
|
||||||
|
) {
|
||||||
|
dialogRef.current!.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog title={'Add user'} onClose={onClose} ref={dialogRef}>
|
||||||
|
<UserForm onClose={onClose!} />
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}) as React.FC<IDialogProps>;
|
||||||
|
|
||||||
|
export default UserDialog;
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
// Styled
|
||||||
|
import Input from '../../commons/styles/Input';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import TextArea from '../../commons/styles/TextArea';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Form from '../../components/Form';
|
||||||
|
import FormRow from '../../components/FormRow';
|
||||||
|
import Button from '../../commons/styles/Button.ts';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { TKeyValue, TStateSchema } from '../../globals/types';
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
import useForm from '../../hooks/useForm';
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {
|
||||||
|
isValidAddress,
|
||||||
|
isValidNumber,
|
||||||
|
isValidPhoneNumber,
|
||||||
|
isValidString,
|
||||||
|
} from '../../helpers/validators';
|
||||||
|
|
||||||
|
const FormBtn = styled(Button)`
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&[disabled] {
|
||||||
|
background-color: var(--disabled-btn-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface IUserFormProp {
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserForm = ({ onClose }: IUserFormProp) => {
|
||||||
|
const [reset, setReset] = useState(true);
|
||||||
|
|
||||||
|
// Define your state schema
|
||||||
|
const stateSchema: TStateSchema = {
|
||||||
|
fullName: { value: '', error: '' },
|
||||||
|
identifiedCode: { value: '', error: '' },
|
||||||
|
phone: { value: '', error: '' },
|
||||||
|
room: { value: '', error: '' },
|
||||||
|
address: { value: '', error: '' },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Submit form
|
||||||
|
const onSubmitForm = (state: TKeyValue) => {
|
||||||
|
alert(JSON.stringify(state, null, 2));
|
||||||
|
onClose();
|
||||||
|
onResetForm();
|
||||||
|
};
|
||||||
|
|
||||||
|
const { values, errors, dirty, handleOnChange, handleOnSubmit, disable } =
|
||||||
|
useForm(
|
||||||
|
stateSchema,
|
||||||
|
{
|
||||||
|
fullName: {
|
||||||
|
required: true,
|
||||||
|
validator: {
|
||||||
|
func: (input) => isValidString(input),
|
||||||
|
error: 'Invalid full name format.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
identifiedCode: {
|
||||||
|
required: true,
|
||||||
|
validator: {
|
||||||
|
func: (input) => isValidNumber(input),
|
||||||
|
error: 'Invalid identified code format.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
required: true,
|
||||||
|
validator: {
|
||||||
|
func: (input) => isValidPhoneNumber(input),
|
||||||
|
error: 'Invalid phone format.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
room: {
|
||||||
|
required: true,
|
||||||
|
validator: {
|
||||||
|
func: (input) => isValidNumber(input),
|
||||||
|
error: 'Invalid room number format.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
required: true,
|
||||||
|
validator: {
|
||||||
|
func: (input) => isValidAddress(input),
|
||||||
|
error: 'Invalid address format.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSubmitForm,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { fullName, identifiedCode, phone, room, address } = values;
|
||||||
|
|
||||||
|
// Reset form
|
||||||
|
const onResetForm = () => {
|
||||||
|
setReset(() => !reset);
|
||||||
|
Object.keys(values).forEach((key) => (values[key] = ''));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form onSubmit={handleOnSubmit}>
|
||||||
|
<Input type="hidden" id="id" />
|
||||||
|
<FormRow
|
||||||
|
label="Full Name"
|
||||||
|
error={
|
||||||
|
errors.fullName && dirty.fullName ? (errors.fullName as string) : ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="fullName"
|
||||||
|
value={fullName as string}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
|
||||||
|
<FormRow
|
||||||
|
label="Identified Code"
|
||||||
|
error={
|
||||||
|
errors.identifiedCode && dirty.identifiedCode
|
||||||
|
? (errors.identifiedCode as string)
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="identifiedCode"
|
||||||
|
value={identifiedCode as string}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
|
||||||
|
<FormRow
|
||||||
|
label="Phone"
|
||||||
|
error={errors.phone && dirty.phone ? (errors.phone as string) : ''}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="phone"
|
||||||
|
value={phone as string}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
|
||||||
|
<FormRow
|
||||||
|
label="Room"
|
||||||
|
error={errors.room && dirty.room ? (errors.room as string) : ''}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
name="room"
|
||||||
|
value={room as string}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
|
||||||
|
<FormRow
|
||||||
|
label="Address"
|
||||||
|
error={
|
||||||
|
errors.address && dirty.address ? (errors.address as string) : ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
name="address"
|
||||||
|
rows={3}
|
||||||
|
value={address as string}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
|
||||||
|
<Form.Action>
|
||||||
|
<FormBtn type="submit" name="submit" disabled={disable}>
|
||||||
|
Add
|
||||||
|
</FormBtn>
|
||||||
|
<FormBtn type="button" styled="secondary" onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</FormBtn>
|
||||||
|
</Form.Action>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserForm;
|
||||||
@@ -1,21 +1,38 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Direction from '../../commons/styles/Direction';
|
import Direction from '../../commons/styles/Direction';
|
||||||
import Button from '../../commons/styles/Button';
|
import Button from '../../commons/styles/Button.ts';
|
||||||
import UserTable from './UserTable';
|
import UserTable from './Table';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledUser, Title } from './styled';
|
import { StyledUser, Title } from './styled';
|
||||||
|
import UserDialog from './Dialog';
|
||||||
|
|
||||||
const User = () => {
|
const User = () => {
|
||||||
|
const dialogRef = useRef<HTMLDialogElement>();
|
||||||
|
|
||||||
|
const openDialog = () => {
|
||||||
|
dialogRef.current?.showModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialogRef.current?.close();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<StyledUser>
|
<StyledUser>
|
||||||
<Direction type="horizontal">
|
<Direction type="horizontal">
|
||||||
<Title>List User</Title>
|
<Title>List User</Title>
|
||||||
<Button>Add user</Button>
|
<Button onClick={openDialog}>Add user</Button>
|
||||||
</Direction>
|
</Direction>
|
||||||
|
|
||||||
<UserTable />
|
<UserTable />
|
||||||
</StyledUser>
|
</StyledUser>
|
||||||
|
|
||||||
|
<UserDialog onClose={closeDialog} ref={dialogRef} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user