mirror of
https://github.com/rjNemo/melon_frontend
synced 2026-06-12 05:16:46 +00:00
refactor: extract billfrom component
This commit is contained in:
parent
9b418efb77
commit
2b948f8271
4 changed files with 196 additions and 154 deletions
99
src/components/billForm.tsx
Normal file
99
src/components/billForm.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { Button, Form, Input, Switch } from 'antd';
|
||||||
|
import { BaseSyntheticEvent } from 'react';
|
||||||
|
import { Control, Controller, UseFormRegister } from 'react-hook-form';
|
||||||
|
import { BillFormType } from '../types/bill';
|
||||||
|
import { InputSelect } from './inputSelect';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onFinish: (e?: BaseSyntheticEvent) => Promise<void>;
|
||||||
|
control: Control<BillFormType>;
|
||||||
|
register: UseFormRegister<BillFormType>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function BillForm({ onFinish, control, register }: Props) {
|
||||||
|
return (
|
||||||
|
<Form layout="vertical" onFinish={onFinish}>
|
||||||
|
<Form.Item label="Customer name">
|
||||||
|
<Input placeholder="Cameron Doe" {...register('name')} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Customer phone number">
|
||||||
|
<Input placeholder="06 12 34 56 78" {...register('phoneNumber')} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Price">
|
||||||
|
<Input placeholder="75.00" addonAfter="€" {...register('price')} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="Start">
|
||||||
|
<Input type="date" {...register('start')} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="End">
|
||||||
|
<Input type="date" {...register('end')} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<InputSelect
|
||||||
|
control={control}
|
||||||
|
name="room"
|
||||||
|
label="Room"
|
||||||
|
placeholder="Choose a room to stay"
|
||||||
|
options={['T2 Corail', 'T3 Azur']}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form.Item label="Customers number">
|
||||||
|
<Input type="number" {...register('customers')} placeholder="1" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<InputSelect
|
||||||
|
control={control}
|
||||||
|
name="platform"
|
||||||
|
label="Booking Platform"
|
||||||
|
placeholder="Select a booking platform"
|
||||||
|
options={['Site', 'Booking.com', 'AirBnB', 'TripAdvisor', 'Perso']}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="taxes"
|
||||||
|
defaultValue={true}
|
||||||
|
render={({ field }) => {
|
||||||
|
const { value, ...props } = field;
|
||||||
|
return (
|
||||||
|
<Form.Item label="Include taxes">
|
||||||
|
<Switch
|
||||||
|
checkedChildren="yes"
|
||||||
|
unCheckedChildren="no"
|
||||||
|
defaultChecked
|
||||||
|
checked={value}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputSelect
|
||||||
|
control={control}
|
||||||
|
name="paymentMethod"
|
||||||
|
label="Payment method"
|
||||||
|
placeholder="How do you want to pay?"
|
||||||
|
options={['Card', 'Cash', 'Cheque', 'Transfer']}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputSelect
|
||||||
|
control={control}
|
||||||
|
name="paymentStatus"
|
||||||
|
label="Payment status"
|
||||||
|
placeholder="What is the current payment status?"
|
||||||
|
options={['Pending', 'Canceled', 'Completed', 'Refunded']}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" htmlType="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { Button, message, Space, Typography } from 'antd';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { fetchOneBill, sendBillAsPDF } from '../api';
|
|
||||||
import { withLayout } from '../layouts/main';
|
|
||||||
import { Bill } from '../types/bill';
|
|
||||||
import NotFoundPage from './notFound';
|
|
||||||
|
|
||||||
export type QueryParams = { id: string };
|
|
||||||
|
|
||||||
const BillPage = () => {
|
|
||||||
// Hooks
|
|
||||||
const { id } = useParams<QueryParams>();
|
|
||||||
const [sent, setSent] = useState(false);
|
|
||||||
|
|
||||||
// Local State
|
|
||||||
const [bill, setBill] = useState<Bill>({} as Bill);
|
|
||||||
|
|
||||||
// Effects
|
|
||||||
useEffect(() => {
|
|
||||||
const loadBIllById = async (id: string) => {
|
|
||||||
const billID = parseInt(id);
|
|
||||||
const { data: loadedBill, error } = await fetchOneBill(billID);
|
|
||||||
if (!loadedBill || error) {
|
|
||||||
return <NotFoundPage />;
|
|
||||||
}
|
|
||||||
setBill(() => loadedBill);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
loadBIllById(id);
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
// Logic
|
|
||||||
const handleSendPDF = (id: number) => {
|
|
||||||
sendBillAsPDF(id);
|
|
||||||
message.success('The bill will be sent to the customer');
|
|
||||||
setSent(() => true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<Typography.Title>Facture #VFNI{`${bill.id}`.padStart(4, '0')}</Typography.Title>
|
|
||||||
<Space>
|
|
||||||
<Button type="primary" onClick={() => handleSendPDF(bill.id)} disabled={sent}>
|
|
||||||
{sent ? 'The bill is on its way to your customer' : 'Send Bill'}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Space>
|
|
||||||
<Typography.Text>{bill?.name}</Typography.Text>
|
|
||||||
<Typography.Text>{bill?.price} €</Typography.Text>
|
|
||||||
<Typography.Text>
|
|
||||||
from {bill?.start} to {bill?.end}
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withLayout(BillPage);
|
|
||||||
89
src/pages/bill/index.tsx
Normal file
89
src/pages/bill/index.tsx
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { Button, Col, Divider, message, PageHeader, Space, Typography } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
|
import { fetchOneBill, sendBillAsPDF } from '../../api';
|
||||||
|
import { withLayout } from '../../layouts/main';
|
||||||
|
import { Bill } from '../../types/bill';
|
||||||
|
import NotFoundPage from '../notFound';
|
||||||
|
import { BillSent } from './billSent';
|
||||||
|
import { EditBillForm } from './editBillForm';
|
||||||
|
|
||||||
|
export type QueryParams = { id: string };
|
||||||
|
|
||||||
|
const BillPage = () => {
|
||||||
|
// Hooks
|
||||||
|
const { id } = useParams<QueryParams>();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
// Local State
|
||||||
|
const [sent, setSent] = useState(false);
|
||||||
|
const [edit, setEdit] = useState(false);
|
||||||
|
const [bill, setBill] = useState<Bill>({} as Bill);
|
||||||
|
|
||||||
|
// Effects
|
||||||
|
useEffect(() => {
|
||||||
|
const loadBIllById = async (id: string) => {
|
||||||
|
const billID = parseInt(id);
|
||||||
|
const { data: loadedBill, error } = await fetchOneBill(billID);
|
||||||
|
if (!loadedBill || error) {
|
||||||
|
return <NotFoundPage />;
|
||||||
|
}
|
||||||
|
setBill(() => loadedBill);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
loadBIllById(id);
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
// Logic
|
||||||
|
const handleSendPDF = (id: number) => {
|
||||||
|
sendBillAsPDF(id);
|
||||||
|
message.success('The bill will be sent to the customer');
|
||||||
|
setSent(() => true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = edit ? (
|
||||||
|
<EditBillForm />
|
||||||
|
) : sent ? (
|
||||||
|
<BillSent />
|
||||||
|
) : (
|
||||||
|
<Col>
|
||||||
|
<Typography.Text>{bill?.name}</Typography.Text>
|
||||||
|
<Typography.Text>{bill?.price} €</Typography.Text>
|
||||||
|
<Typography.Text>
|
||||||
|
from {bill?.start} to {bill?.end}
|
||||||
|
</Typography.Text>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHeader
|
||||||
|
onBack={() => history.goBack()}
|
||||||
|
title={`Facture #VFNI${bill.id?.toString().padStart(4, '0')}`}
|
||||||
|
subTitle={bill.name}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{content}
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
<Button type="primary" onClick={() => handleSendPDF(bill.id)} disabled={sent || edit}>
|
||||||
|
Send Bill
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
setSent(() => false);
|
||||||
|
setEdit(() => true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withLayout(BillPage);
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { Button, Form, Input, Switch } from 'antd';
|
import { useForm } from 'react-hook-form';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { createBill } from '../api';
|
import { createBill } from '../api';
|
||||||
import { InputSelect } from '../components/inputSelect';
|
import { BillForm } from '../components/billForm';
|
||||||
import { withLayout } from '../layouts/main';
|
import { withLayout } from '../layouts/main';
|
||||||
import { BillForm } from '../types/bill';
|
import { BillFormType } from '../types/bill';
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
// Hooks
|
// Hooks
|
||||||
const { register, handleSubmit, control } = useForm<BillForm>();
|
const { register, handleSubmit, control } = useForm<BillFormType>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
// Logic
|
// Logic
|
||||||
|
|
@ -18,95 +17,11 @@ const HomePage = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<>
|
||||||
<section>
|
<h1>Create a new bill</h1>
|
||||||
<h1>Create a new bill</h1>
|
|
||||||
|
|
||||||
<Form layout="vertical" onFinish={onSubmit}>
|
<BillForm onFinish={onSubmit} control={control} register={register} />
|
||||||
<Form.Item label="Customer name">
|
</>
|
||||||
<Input placeholder="Cameron Doe" {...register('name')} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="Customer phone number">
|
|
||||||
<Input placeholder="06 12 34 56 78" {...register('phoneNumber')} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="Price">
|
|
||||||
<Input placeholder="75.00" addonAfter="€" {...register('price')} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="Start">
|
|
||||||
<Input type="date" {...register('start')} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label="End">
|
|
||||||
<Input type="date" {...register('end')} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<InputSelect
|
|
||||||
control={control}
|
|
||||||
name="room"
|
|
||||||
label="Room"
|
|
||||||
placeholder="Choose a room to stay"
|
|
||||||
options={['T2 Corail', 'T3 Azur']}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Form.Item label="Customers number">
|
|
||||||
<Input type="number" {...register('customers')} placeholder="1" />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<InputSelect
|
|
||||||
control={control}
|
|
||||||
name="platform"
|
|
||||||
label="Booking Platform"
|
|
||||||
placeholder="Select a booking platform"
|
|
||||||
options={['Site', 'Booking.com', 'AirBnB', 'TripAdvisor', 'Perso']}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="taxes"
|
|
||||||
defaultValue={true}
|
|
||||||
render={({ field }) => {
|
|
||||||
const { value, ...props } = field;
|
|
||||||
return (
|
|
||||||
<Form.Item label="Include taxes">
|
|
||||||
<Switch
|
|
||||||
checkedChildren="yes"
|
|
||||||
unCheckedChildren="no"
|
|
||||||
defaultChecked
|
|
||||||
checked={value}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<InputSelect
|
|
||||||
control={control}
|
|
||||||
name="paymentMethod"
|
|
||||||
label="Payment method"
|
|
||||||
placeholder="How do you want to pay?"
|
|
||||||
options={['Card', 'Cash', 'Cheque', 'Transfer']}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<InputSelect
|
|
||||||
control={control}
|
|
||||||
name="paymentStatus"
|
|
||||||
label="Payment status"
|
|
||||||
placeholder="What is the current payment status?"
|
|
||||||
options={['Pending', 'Canceled', 'Completed', 'Refunded']}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Form.Item>
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue