[front]: error handling

This commit is contained in:
it-is-not-alright 2024-10-22 22:42:58 +04:00
parent ef20e8e4e3
commit 93f98c25eb
53 changed files with 82 additions and 23 deletions

View File

@ -1,4 +1,4 @@
import { Heading } from '@components/ui';
import { Heading, Paragraph } from '@components/ui';
import { WindmillForm } from '@components/ux';
import { WindmillFormResponse } from '@components/ux/windmill-form';
import React, { useState } from 'react';
@ -6,26 +6,35 @@ import React, { useState } from 'react';
import styles from './styles.module.scss';
export function HomePage() {
const [formResponse, setFormResponse] = useState<WindmillFormResponse>({
power: [],
image: '',
});
const [result, setResult] = useState<WindmillFormResponse | null>(null);
const [error, setError] = useState<string | null>(null);
const handleFormResponse = (response: WindmillFormResponse) => {
setFormResponse(response);
const handleFormSuccess = (response: WindmillFormResponse) => {
setResult(response);
setError(null);
};
const handleFormFail = (message: string) => {
setError(message);
setResult(null);
};
return (
<div className={styles.page}>
<div className={styles.wrapperForm}>
<WindmillForm onResponse={handleFormResponse} />
<WindmillForm onSuccess={handleFormSuccess} onFail={handleFormFail} />
</div>
<div className={styles.result}>
<Heading tag="h3">Result</Heading>
<div className={styles.power}>{formResponse.power.join(' ')}</div>
<div className={styles.image}>
{formResponse.image && <img src={formResponse.image} alt="Image" />}
</div>
{result && (
<>
<div className={styles.power}>{result.power.join(' ')}</div>
<div className={styles.image}>
{result.image && <img src={result.image} alt="Image" />}
</div>
</>
)}
{error && <Paragraph>{error}</Paragraph>}
</div>
</div>
);

View File

@ -12,14 +12,14 @@
}
.result {
display: grid;
display: flex;
flex-direction: column;
padding: 20px;
border-radius: 10px;
background-color: var(--clr-layer-200);
box-shadow: 0px 1px 2px var(--clr-shadow-100);
gap: 20px;
grid-area: result;
grid-template-columns: 1fr;
}
.image {

View File

@ -1,7 +1,8 @@
import { Button, DateInput, Heading } from '@components/ui';
import { dateToInputString } from '@utils/date';
import { Controller, useForm } from '@utils/form';
import clsx from 'clsx';
import React from 'react';
import React, { useState } from 'react';
import { downloadImage, getWindmillData } from 'src/api';
import { WindmillTable } from '../windmill-table';
@ -10,20 +11,53 @@ import styles from './styles.module.scss';
import { WindmillFormProps, WindmillFormStore } from './types';
export function WindmillForm({
onResponse,
onSuccess,
onFail,
className,
...props
}: WindmillFormProps) {
const [pending, setPending] = useState<boolean>(false);
const { control, reset, getValues } = useForm<WindmillFormStore>({
initialValues,
});
const classNames = clsx(className, styles.form);
const date = dateToInputString(new Date());
const validate = (values: Partial<WindmillFormStore>) => {
if (!values.dateFrom) {
onFail('Field "from" is required');
return false;
}
if (!values.dateTo) {
onFail('Field "to" is required');
return false;
}
if (
!values.windmills?.length ||
values.windmills.some((r) => !r.x || !r.y)
) {
onFail('The table is filled incorrectly');
return false;
}
return true;
};
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
const res = await getWindmillData(getValues());
const image = await downloadImage(res.file_name);
onResponse({ power: res.data, image: URL.createObjectURL(image) });
const values = getValues();
if (!validate(values)) {
return;
}
setPending(true);
try {
const res = await getWindmillData(values);
const image = await downloadImage(res.file_name);
onSuccess({ power: res.data, image: URL.createObjectURL(image) });
} catch {
onFail('Fetch error');
} finally {
setPending(false);
}
};
const handleResetButtonClick = () => {
@ -36,11 +70,15 @@ export function WindmillForm({
<div className={styles.dateRangeBox}>
<Controller
{...control('dateFrom')}
render={(params) => <DateInput placeholder="from" {...params} />}
render={(params) => (
<DateInput placeholder="from" max={date} {...params} />
)}
/>
<Controller
{...control('dateTo')}
render={(params) => <DateInput placeholder="to" {...params} />}
render={(params) => (
<DateInput placeholder="to" max={date} {...params} />
)}
/>
</div>
<Controller
@ -48,7 +86,9 @@ export function WindmillForm({
render={(params) => <WindmillTable {...params} />}
/>
<div className={styles.buttonBox}>
<Button type="submit">Submit</Button>
<Button type="submit" pending={pending}>
Submit
</Button>
<Button variant="secondary" onClick={handleResetButtonClick}>
Reset
</Button>

View File

@ -16,5 +16,6 @@ export type WindmillFormResponse = {
};
export type WindmillFormProps = {
onResponse: (response: WindmillFormResponse) => void;
onSuccess: (response: WindmillFormResponse) => void;
onFail: (message: string) => void;
} & React.ComponentProps<'form'>;

View File

@ -0,0 +1 @@
export * from './input';

View File

@ -0,0 +1,8 @@
export const dateToInputString = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.