refactoring (#18)

* chore: update deps to fix CVEs

* cleanup

* use variable for app routes

* refactor

* ⬆️ react router

* ⬆️ react

* delete pem files
This commit is contained in:
Ruidy 2022-09-10 14:33:51 +02:00 committed by GitHub
parent ed4ca135e7
commit a9bf6936b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 730 additions and 658 deletions

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ yarn-error.log*
.vscode/ .vscode/
.idea/ .idea/
*.pem

View file

@ -1,25 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIENjCCAp6gAwIBAgIQDi8FrIt59I2QjzJ7mfvXlDANBgkqhkiG9w0BAQsFADBr
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExIDAeBgNVBAsMF3J1aWR5
QG1vb25zdG9uZSAoUnVpZHkpMScwJQYDVQQDDB5ta2NlcnQgcnVpZHlAbW9vbnN0
b25lIChSdWlkeSkwHhcNMjEwNzEzMTI0NzE1WhcNMjMxMDEzMTI0NzE1WjBLMScw
JQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxIDAeBgNVBAsM
F3J1aWR5QG1vb25zdG9uZSAoUnVpZHkpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAv6wTfi7TqOn54LB/pwF0T7CdRwdoo1Lokyee/olOBRsLAYpmeRSN
BPmrtPm8UB2z/xgpgo7UfZ2c9wjDP5rkOk8XA/LJtdpPTsLlex+kZPsD4TNwqO1J
JYjoOFI/25eCGu3SQ8L4J/JXxrpvgiNw/s/CR75sB8pk8mL9CD7WJtU07NM48A+1
ODTINoO73ZOqO4XTftHImyb3PxoyTos0LS2igfPysXRw/bmUMzkpaZyqX29ZfS+E
vK/vNaR01IMZ/aqGuwHm339E2EZHkJfM0ttiu4jOsS41CsjqxyLSWC3V8NW688T+
QUchi1XUR6eROKjmmEVT53B69S5rWhAPWQIDAQABo3YwdDAOBgNVHQ8BAf8EBAMC
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUz7wYgdxcbzkHFwRB
Nx6SdbPNcpYwLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAAAAAA
AAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBgQBTuPUOJMPqaTplBB7q+CmMjSL1ZJjz
hz2BUZZOjmEBqedZYKxhhtAt4XJGAlcU5Koyf12j+stGRAEk/FSanE0z0xDbeZut
xDwptcVXMLOpBmokvC5f0jKfNwtB89uoDkTdIxAjo3ftLOORqyeJGmiwLjXnYSxg
VS5bET/91Nnkj1hLdiA8QzHYoC7hr25lipUwAL67gElrSzxdNduHPGjv5DrZFrbX
sklxgtGmLh0yDk42zinL6bJAVuuXIF+tiQ9fXxKhdnR90tPyQk4PM7Mvzvr00udZ
wuoKZ9L1Zd2G4lKu2Kg9UCCIP3RFi4Al6/5oCW1LS+ZvNLnbY/Rf11dUtHvl8ao4
yoLJ+viNALDbMk/9YPt6xEOq+gYgqiDXzJycdX8yw/J7K5iG99ov1dC7Q/F14yxd
3h3jwQ7uAoxPy1IADvnvSfmxKS8n7S+9wTKA37vPG1E/4bF0vAx+bNiJM7AF5Jy2
cR3PXtrjoU/ymIdky2nNkAOhdBxH4I0goU4=
-----END CERTIFICATE-----

28
key.pem
View file

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/rBN+LtOo6fng
sH+nAXRPsJ1HB2ijUuiTJ57+iU4FGwsBimZ5FI0E+au0+bxQHbP/GCmCjtR9nZz3
CMM/muQ6TxcD8sm12k9OwuV7H6Rk+wPhM3Co7UkliOg4Uj/bl4Ia7dJDwvgn8lfG
um+CI3D+z8JHvmwHymTyYv0IPtYm1TTs0zjwD7U4NMg2g7vdk6o7hdN+0cibJvc/
GjJOizQtLaKB8/KxdHD9uZQzOSlpnKpfb1l9L4S8r+81pHTUgxn9qoa7Aebff0TY
RkeQl8zS22K7iM6xLjUKyOrHItJYLdXw1brzxP5BRyGLVdRHp5E4qOaYRVPncHr1
LmtaEA9ZAgMBAAECggEAKuwJ4XPzChx7TIm5zFhTB5VxYvqRwCFM28c/n5h31ETe
DhZEJFEp0zdl8D0MopREPpfWk9oQU6D8CYX400GIYlZbvRKdgQ/GOskCQrQvSD29
/KxcSjf47WLfPd2NV0tyYz1mnK4IjEmExofxeJ8fKrCzvQUzg/sa5/yq1THdGwy3
7Sf+i1xugni0dHPzEFdBPv27GqVtIulKvzYZEP4hEH9r+J7kqfcWdWuR8X3dOMoJ
Ow3Dd9geZV8px3HvCtk7uYYeR0YtqJjlyns/xcfqCg84P91BNtdZmvzd0gYPcedw
gq1PfnTlfwA9vcvQ9xZyPaolNsKWY4dYtbFa2+FVQQKBgQDHRMney+YhFPRUGzSI
gj0B2PWgfzajnQPfxKJo+UrbBPmPl6vFKChOFDmE7OaiFAZxC6Wtf7l9amWryGfy
mHWOK5YvzqJktsl8M8bDfYMc5sT3CjJ8LmmRZBYmVilzTIs+KFQSm7CLQimK7st6
9jZ5Jr0TSJPgIF0oX/kD532rNQKBgQD2PaIFueoxAh94IiPhKIC0Izn99rxKdJIS
p7yCP9hHmRbCvo24TSggLpxUQy+YLF0LQfF3+wazSa5Dlyzn4Z8MpQsQJf+Nn49d
1GogNdVnz7zKdiSOBrNT2+SAgEGzeOuam2aeBYfbGc8ClfRbXFY1O8dacoOFZLvk
UngHRxl0FQKBgEXcF+nEEioG7qGN6Bzzi+/Z4EMkVkOGCJWqmkWzIEsos1VkFxRH
v9mfvYqcgz7PRnLbYnoxDRaq9noDCG5EhPIS1dRs3QHyEzE39bcTiaPYQ7VYHdrr
apdOP3WxP1bsvuRLvy3G513rFCVhEnVJXE+dBSx9hVpN/lrpSqPTxHsBAoGBAKWE
ACiBM14F40lJCX0LUJfFJfIDq8UQXi+T+C5E5AgcayQ0GebMVjBrpEEveAp9p30t
sjXH0lUpLa3ZLCIhkltZhPox0HlNy3xyj3NfPDcvd+NPTnr5Hgk8QXfuvjr5CMU/
t9Z4OT2ZN4CxczMF+hyGNhYh6y367CEpHe0maYGRAoGBAIJgOPxtPhk/uNd1wQ4U
UkBzRR+tGpTqeA24j9t1KITJBD+AMIfhR7/3Py8X+vQQq0z7TkfzHSnQdF9Zn9m9
DzihbA39nM+MVMRJijgYrl8U+b98G2QIE8V6Rd2NIPsMeNLxDX3sM8kJ45dev62Q
1S3db2Lkv0wtq1bGbMgRw+LI
-----END PRIVATE KEY-----

View file

@ -9,26 +9,26 @@
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"dependencies": { "dependencies": {
"antd": "^4.16.6", "antd": "^4.23.1",
"axios": "^0.21.2", "axios": "^0.27.2",
"react": "^17.0.2", "react": "^18.2.0",
"react-dom": "^17.0.2", "react-dom": "^18.2.0",
"react-hook-form": "^7.8.0", "react-hook-form": "^7.35.0",
"react-router-dom": "^5.2.0", "react-router-dom": "^6.3.0",
"react-scripts": "^5.0.0" "react-scripts": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^11.1.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^14.4.3",
"@types/jest": "^26.0.15", "@types/jest": "^29.0.0",
"@types/node": "^12.0.0", "@types/node": "^18.7.16",
"@types/react": "^17.0.11", "@types/react": "^18.0.19",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.1.8", "@types/react-router-dom": "^5.3.3",
"prettier": "^2.2.1", "prettier": "^2.7.1",
"typescript": "^4.3.4", "typescript": "^4.8.3",
"web-vitals": "^1.0.1" "web-vitals": "^3.0.1"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [

View file

@ -5,10 +5,7 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico"/> <link rel="icon" href="%PUBLIC_URL%/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000"/> <meta name="theme-color" content="#000000"/>
<meta <meta name="description" content="Web site created using create-react-app"/>
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/> <link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
<title>Melon | Property Management</title> <title>Melon | Property Management</title>

View file

@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react"; import { render, screen } from '@testing-library/react';
import App from "./App"; import App from './App';
test("renders learn react link", () => { test('renders learn react link', () => {
render(<App />); render(<App />);
const linkElement = screen.getByText(/learn react/i); const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument(); expect(linkElement).toBeInTheDocument();

View file

@ -1,4 +1,4 @@
import Router from "./Router"; import Router from './Router';
export default function App() { export default function App() {
return <Router />; return <Router />;

View file

@ -1,4 +1,4 @@
import { Route, Switch } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import BillPage from './pages/bill'; import BillPage from './pages/bill';
import BillsPage from './pages/bills'; import BillsPage from './pages/bills';
import HomePage from './pages/home'; import HomePage from './pages/home';
@ -6,55 +6,57 @@ import NewBillPage from './pages/newBill';
import NotFoundPage from './pages/notFound'; import NotFoundPage from './pages/notFound';
import ReportPage from './pages/report'; import ReportPage from './pages/report';
type RouteConfig = { type Params = {
path: string; path: string;
component: ({ ...props }: any) => JSX.Element; component: ({ ...props }: any) => JSX.Element;
exact?: boolean; };
export const AppRoutes = {
home: '/',
bills: '/bills',
reports: '/reports',
catchAll: '*'
}; };
export default function Router() { export default function Router() {
const routes: RouteConfig[] = [ const routes: Params[] = [
// Home // Home
{ {
path: '/', path: AppRoutes.home,
component: HomePage, component: HomePage
exact: true
}, },
// Bills // Bills
{ {
path: '/bills/new', path: `${AppRoutes.bills}/new`,
component: NewBillPage, component: NewBillPage
exact: true
}, },
{ {
path: '/bills', path: AppRoutes.bills,
component: BillsPage, component: BillsPage
exact: true
}, },
{ {
path: '/bills/:id', path: `${AppRoutes.bills}/:id`,
component: BillPage component: BillPage
}, },
// Reports // Reports
{ {
path: '/reports', path: AppRoutes.reports,
component: ReportPage, component: ReportPage
exact: true
}, },
// … rest // … rest
{ {
path: '*', path: AppRoutes.catchAll,
component: NotFoundPage component: NotFoundPage
} }
]; ];
return ( return (
<Switch> <>
{routes.map(({ exact, path, component: Component }) => ( <Routes>
<Route key={path} exact={!!exact} path={path}> {routes.map(({ path, component: Component }) => (
<Component /> <Route key={path} path={path} element={<Component />} />
</Route>
))} ))}
</Switch> </Routes>
</>
); );
} }

View file

@ -1,9 +1,11 @@
import { client, Response } from '.'; import { client, Response } from '.';
import { Bill, BillFormType, billFrom } from '../types/bill'; import { Bill, BillFormType, billFrom } from '../types/bill';
export const billsURL = '/bills';
export const createBill = async (data: BillFormType) => { export const createBill = async (data: BillFormType) => {
try { try {
const { data: response } = await client.post<number>('/bills', data); const { data: response } = await client.post<number>(billsURL, data);
return response; return response;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -13,7 +15,7 @@ export const createBill = async (data: BillFormType) => {
export const updateBill = async (id: number, data: BillFormType): Promise<Response<void>> => { export const updateBill = async (id: number, data: BillFormType): Promise<Response<void>> => {
try { try {
const { data: response } = await client.put<void>(`/bills/${id}`, data); const { data: response } = await client.put<void>(`${billsURL}/${id}`, data);
return { data: response }; return { data: response };
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -23,7 +25,7 @@ export const updateBill = async (id: number, data: BillFormType): Promise<Respon
export const fetchOneBill = async (id: number): Promise<Response<Bill>> => { export const fetchOneBill = async (id: number): Promise<Response<Bill>> => {
try { try {
const { data } = await client.get<Bill>(`/bills/${id}`); const { data } = await client.get<Bill>(`${billsURL}/${id}`);
return { data: billFrom(data) }; return { data: billFrom(data) };
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -33,7 +35,7 @@ export const fetchOneBill = async (id: number): Promise<Response<Bill>> => {
export const fetchAllBills = async () => { export const fetchAllBills = async () => {
try { try {
const { data } = await client.get<Bill[]>('/bills'); const { data } = await client.get<Bill[]>(billsURL);
return data; return data;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -43,7 +45,7 @@ export const fetchAllBills = async () => {
export const sendBillAsPDF = async (id: number) => { export const sendBillAsPDF = async (id: number) => {
try { try {
const { data } = await client.post<boolean>(`/bills/${id}/send`); const { data } = await client.post<boolean>(`${billsURL}/${id}/send`);
return data; return data;
} catch (error) { } catch (error) {
console.error(error); console.error(error);

View file

@ -1,8 +1,8 @@
import axios from 'axios'; import axios from 'axios';
const BASE_URL = process.env.REACT_APP_API_URL; const baseURL = process.env.REACT_APP_API_URL;
export const client = axios.create({ baseURL: BASE_URL }); export const client = axios.create({ baseURL });
export type Response<T> = { export type Response<T> = {
data?: T; data?: T;

View file

@ -1,19 +1,27 @@
import { client, Response } from '.'; import { client, Response } from '.';
import { Report, ReportFormType, ReportType } from '../types/report'; import { Report, ReportFormType, ReportType } from '../types/report';
export const reportsURL = '/reports';
export const fetchReport = async ({ export const fetchReport = async ({
type, type,
year, year,
month month
}: ReportFormType): Promise<Response<Report>> => { }: ReportFormType): Promise<Response<Report>> => {
const baseQueryURL = `/reports/?report_type=${type}&year=${year}`; const params =
const queryURL =
type === ReportType.monthly && month type === ReportType.monthly && month
? baseQueryURL.concat(`&month=${month + 1}`) ? {
: baseQueryURL; report_type: type,
month: month + 1,
year
}
: {
report_type: type,
year
};
try { try {
const { data } = await client.get<Report>(queryURL); const { data } = await client.get<Report>(reportsURL, { params });
return { return {
data: { data: {
// @ts-ignore // @ts-ignore

View file

@ -1,5 +1,5 @@
import { Form, Select } from "antd"; import { Form, Select } from 'antd';
import { Controller } from "react-hook-form"; import { Controller } from 'react-hook-form';
type InputSelectProps = { type InputSelectProps = {
control: any; control: any;
@ -20,11 +20,11 @@ export const InputSelect = ({ control, name, label, placeholder, options }: Inpu
placeholder={placeholder} placeholder={placeholder}
optionFilterProp="children" optionFilterProp="children"
filterOption={(input, option) => filterOption={(input, option) =>
option?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 option?.label!.toLowerCase().indexOf(input.toLowerCase())! >= 0
} }
options={options.map((label, value) => ({ options={options.map((label, value) => ({
value, value,
label, label
}))} }))}
{...field} {...field}
/> />

View file

@ -1 +1 @@
@import "antd/dist/antd.css"; @import 'antd/dist/antd.css';

View file

@ -1,18 +1,16 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import App from './App'; import App from './App';
import './index.css'; import './index.css';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
ReactDOM.render( ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
<BrowserRouter> <BrowserRouter>
<App /> <App />
</BrowserRouter> </BrowserRouter>
</React.StrictMode>, </React.StrictMode>
document.getElementById('root')
); );
reportWebVitals(); reportWebVitals();
export { enumToList } from './lib/enums';

View file

@ -1,15 +1,16 @@
import { Layout, Menu, Row } from 'antd'; import { Layout, Menu, Row } from 'antd';
import { Link, useRouteMatch } from 'react-router-dom'; import { Link, useMatch } from 'react-router-dom';
import { AppRoutes } from '../Router';
const { Header, Content, Footer } = Layout; const { Header, Content, Footer } = Layout;
export const withLayout = export const withLayout =
(Component: (arg: any) => JSX.Element) => (Component: (arg: any) => JSX.Element) =>
({ ...props }: any) => { ({ ...props }: any) => {
const { url } = useRouteMatch();
const menuItems = [ const menuItems = [
{ label: 'Home', path: '/' }, { label: 'Home', path: AppRoutes.home },
{ label: 'Bills', path: '/bills' } { label: 'Bills', path: AppRoutes.bills },
{ label: 'Reports', path: AppRoutes.reports }
]; ];
return ( return (
@ -22,7 +23,7 @@ export const withLayout =
theme="dark" theme="dark"
mode="horizontal" mode="horizontal"
selectedKeys={[ selectedKeys={[
(menuItems.findIndex((item) => url.includes(item.path)) + 1).toString() (menuItems.findIndex((item) => useMatch(item.path)) + 1).toString()
]} ]}
> >
{menuItems.map(({ label, path }, index) => ( {menuItems.map(({ label, path }, index) => (

View file

@ -1,15 +1,15 @@
import { Button, Result } from 'antd'; import { Button, Result } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
export function BillSent() { export function BillSent() {
const history = useHistory(); const navigate = useNavigate();
return ( return (
<Result <Result
status="success" status="success"
title="Bill sent" title="Bill sent"
subTitle="The bill is on its way to your customer" subTitle="The bill is on its way to your customer"
extra={[ extra={[
<Button key="back" type="primary" onClick={() => history.push('/')}> <Button key="back" type="primary" onClick={() => navigate('/')}>
Go Back Home Go Back Home
</Button> </Button>
]} ]}

View file

@ -1,7 +1,7 @@
import { Button, Col, Divider, message, PageHeader, Space, Typography } from 'antd'; import { Button, Col, Divider, message, PageHeader, Space, Typography } from 'antd';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { fetchOneBill, sendBillAsPDF, updateBill } from '../../api/bills'; import { fetchOneBill, sendBillAsPDF, updateBill } from '../../api/bills';
import { BillForm } from '../../components/billForm'; import { BillForm } from '../../components/billForm';
import { withLayout } from '../../layouts/main'; import { withLayout } from '../../layouts/main';
@ -13,7 +13,7 @@ export type QueryParams = { id: string };
const BillPage = () => { const BillPage = () => {
// Hooks // Hooks
let { id } = useParams<QueryParams>(); let { id } = useParams<QueryParams>();
const history = useHistory(); const navigate = useNavigate();
const { handleSubmit, control, reset } = useForm<BillFormType>(); const { handleSubmit, control, reset } = useForm<BillFormType>();
// Local State // Local State
@ -36,7 +36,7 @@ const BillPage = () => {
); );
useEffect(() => { useEffect(() => {
loadBill(id); loadBill(id!);
}, [id, loadBill, reset]); }, [id, loadBill, reset]);
// Logic // Logic
@ -47,12 +47,12 @@ const BillPage = () => {
}; };
const onSubmit = handleSubmit(async (data) => { const onSubmit = handleSubmit(async (data) => {
const { error } = await updateBill(parseInt(id), data); const { error } = await updateBill(parseInt(id!), data);
if (error) { if (error) {
message.error(`Update failed because of ${error}`); message.error(`Update failed because of ${error}`);
} }
setEdit(() => false); setEdit(() => false);
loadBill(id); loadBill(id!);
}); });
const content = edit ? ( const content = edit ? (
@ -64,7 +64,9 @@ const BillPage = () => {
<Typography.Text>{bill.name}</Typography.Text> <Typography.Text>{bill.name}</Typography.Text>
<Typography.Text>{bill.price} </Typography.Text> <Typography.Text>{bill.price} </Typography.Text>
<Typography.Text> <Typography.Text>
<>
from {bill.start} to {bill.end} from {bill.start} to {bill.end}
</>
</Typography.Text> </Typography.Text>
</Col> </Col>
); );
@ -72,7 +74,7 @@ const BillPage = () => {
return ( return (
<> <>
<PageHeader <PageHeader
onBack={() => history.goBack()} onBack={() => navigate(-1)}
title={`Facture #VFNI${bill.id?.toString().padStart(4, '0')}`} title={`Facture #VFNI${bill.id?.toString().padStart(4, '0')}`}
subTitle={bill.name} subTitle={bill.name}
/> />

View file

@ -1,19 +1,19 @@
import { Button, Row } from 'antd'; import { Button, Row } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { withLayout } from '../layouts/main'; import { withLayout } from '../layouts/main';
function HomePage() { function HomePage() {
// Hooks // Hooks
const history = useHistory(); const navigate = useNavigate();
return ( return (
<> <>
<h1>Rent it Like a Pro</h1> <h1>Rent it Like a Pro</h1>
<Row> <Row>
<Button type="primary" size="large" onClick={() => history.push('/bills/new')}> <Button type="primary" size="large" onClick={() => navigate('/bills/new')}>
Add Bill Add Bill
</Button> </Button>
<Button type="default" size="large" onClick={() => history.push('/reports')}> <Button type="default" size="large" onClick={() => navigate('/reports')}>
Edit Report Edit Report
</Button> </Button>
</Row> </Row>

View file

@ -1,5 +1,5 @@
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { createBill } from '../../api/bills'; import { createBill } from '../../api/bills';
import { BillForm } from '../../components/billForm'; import { BillForm } from '../../components/billForm';
import { withLayout } from '../../layouts/main'; import { withLayout } from '../../layouts/main';
@ -8,12 +8,12 @@ import { BillFormType } from '../../types/bill';
const NewBillPage = () => { const NewBillPage = () => {
// Hooks // Hooks
const { handleSubmit, control } = useForm<BillFormType>(); const { handleSubmit, control } = useForm<BillFormType>();
const history = useHistory(); const navigate = useNavigate();
// Logic // Logic
const onSubmit = handleSubmit(async (data) => { const onSubmit = handleSubmit(async (data) => {
const newId = await createBill(data); const newId = await createBill(data);
history.push(`/bills/${newId}`); navigate(`/bills/${newId}`);
}); });
return ( return (

View file

@ -1,14 +1,14 @@
import { Button, Image, Row } from 'antd'; import { Button, Image, Row } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import NotFoundImage from '../assets/notFound/404.png'; import NotFoundImage from '../assets/notFound/404.png';
import { withLayout } from '../layouts/main'; import { withLayout } from '../layouts/main';
function NotFoundPage() { function NotFoundPage() {
const history = useHistory(); const navigate = useNavigate();
return ( return (
<> <>
<Row justify="center"> <Row justify="center">
<Button size="large" type="link" onClick={() => history.push('/')}> <Button size="large" type="link" onClick={() => navigate('/')}>
Go Back to Safety Go Back to Safety
</Button> </Button>
</Row> </Row>

View file

@ -1,8 +1,8 @@
import { ReportHandler } from "web-vitals"; import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => { const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry) { if (onPerfEntry) {
import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry); getCLS(onPerfEntry);
getFID(onPerfEntry); getFID(onPerfEntry);
getFCP(onPerfEntry); getFCP(onPerfEntry);

View file

@ -1,5 +1 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';

View file

@ -1,11 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@ -23,10 +19,6 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": [ "include": ["src"],
"src" "exclude": ["node_modules"]
],
"exclude": [
"node_modules"
]
} }

1080
yarn.lock

File diff suppressed because it is too large Load diff