From ef742ea9b2f246f74eae74169675a331679ad41c Mon Sep 17 00:00:00 2001 From: eug-vs Date: Fri, 26 Mar 2021 01:56:04 +0300 Subject: feat: add strong typing where possible --- src/components/Input.tsx | 4 ++- src/components/ListTable.tsx | 4 +-- src/hooks/useAPIClient.ts | 22 --------------- src/hooks/useQuery.ts | 2 +- src/lib/ServiceContext.tsx | 16 +++++------ src/services/contractors/ContractorPanel.tsx | 3 ++- src/services/contractors/index.ts | 3 ++- src/services/products/index.ts | 5 ++-- src/services/transfers/TransferForm.tsx | 5 ++-- src/services/transfers/TransfersUpload.tsx | 12 ++++----- src/services/transfers/index.ts | 4 +-- src/services/types.ts | 40 ++++++++++++++++++++++++++++ src/services/waybills/WaybillForm.tsx | 7 ++--- src/services/waybills/WaybillPanel.tsx | 5 ++-- src/services/waybills/index.ts | 3 ++- 15 files changed, 81 insertions(+), 54 deletions(-) create mode 100644 src/services/types.ts diff --git a/src/components/Input.tsx b/src/components/Input.tsx index a8a6f31..298d14c 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -3,17 +3,19 @@ import { Field } from 'formik'; export interface Props extends React.InputHTMLAttributes { label?: string; + ref?: React.Ref } const focusStyles = 'focus:outline-none focus:shadow focus:border-gray-400'; const baseStyles = 'p-2 border bg-white border-gray-300 rounded-sm'; -const InputBase: React.FC = ({ label, ...props }) => { +const InputBase: React.FC = ({ label, ref, ...props }) => { return (
JSX.Element | string; } -interface Props { - items?: any[]; +interface Props { + items?: T[]; fields: Field[]; handleRowClick?: (index: number) => void; } diff --git a/src/hooks/useAPIClient.ts b/src/hooks/useAPIClient.ts index b523d1f..e992b42 100644 --- a/src/hooks/useAPIClient.ts +++ b/src/hooks/useAPIClient.ts @@ -28,28 +28,6 @@ const registerServiceHooks = (service: string): void => { hooks[service] = { useItem, useList }; }; -// Products -export interface Product { - _id: string; - name: string; - description: string; - price: number; - quantity: number; - specs: any; - createdAt: string; - updatedAt: string; -} - -// Contractors -export interface Contractor { - _id: string; - name: string; - fullName: string; - vatId: string; - type: string; - debt: number; -} - hooks.account = { useList: () => { const { data: transfers } = useSWR('/transfers', fetcher); diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts index b2cb628..f025a46 100644 --- a/src/hooks/useQuery.ts +++ b/src/hooks/useQuery.ts @@ -1,6 +1,6 @@ import { useLocation } from 'react-router-dom'; -const useQuery = () => { +const useQuery = (): Record => { const location = useLocation(); const searchParams = new URLSearchParams(location.search); return Object.fromEntries(searchParams); diff --git a/src/lib/ServiceContext.tsx b/src/lib/ServiceContext.tsx index 93cac4e..f7170d1 100644 --- a/src/lib/ServiceContext.tsx +++ b/src/lib/ServiceContext.tsx @@ -7,22 +7,22 @@ export interface Action extends ButtonProps { name: string; } -export interface PanelProps { - item: any; - mutate: (item: any) => void; +export interface PanelProps { + item: T; + mutate: (item: T) => void; } -export interface ServiceParams { +export interface ServiceParams { route: string; name: string; tableFields: Field[]; nameSingular?: string; - default?: Record; + default?: Partial; routes?: Record; actions?: Action[]; - rowLink?: (item: any) => string; - Form?: React.FC>; - Panel?: React.FC; + rowLink?: (item: T) => string; + Form?: React.FC>; + Panel?: React.FC>; } const ServiceContext = React.createContext({ diff --git a/src/services/contractors/ContractorPanel.tsx b/src/services/contractors/ContractorPanel.tsx index f7ed8ad..6e703b5 100644 --- a/src/services/contractors/ContractorPanel.tsx +++ b/src/services/contractors/ContractorPanel.tsx @@ -1,9 +1,10 @@ import React from 'react'; import Button from '../../components/Button'; import { PanelProps } from '../../lib/ServiceContext'; +import { Contractor } from '../types'; -const ContractorPanel: React.FC = ({ item }) => { +const ContractorPanel: React.FC> = ({ item }) => { return (
diff --git a/src/services/contractors/index.ts b/src/services/contractors/index.ts index 2341f3a..9730eff 100644 --- a/src/services/contractors/index.ts +++ b/src/services/contractors/index.ts @@ -1,8 +1,9 @@ import Form from './ContractorForm'; import Panel from './ContractorPanel'; import { ServiceParams } from '../../lib/ServiceContext'; +import { Contractor } from '../types'; -const service: ServiceParams = { +const service: ServiceParams = { route: 'contractors', name: 'Контрагенты', nameSingular: 'Контрагент', diff --git a/src/services/products/index.ts b/src/services/products/index.ts index c869541..8e09eb2 100644 --- a/src/services/products/index.ts +++ b/src/services/products/index.ts @@ -1,7 +1,8 @@ import Form from './ProductForm'; import { ServiceParams } from '../../lib/ServiceContext'; +import { Product } from '../types'; -const service: ServiceParams = { +const service: ServiceParams = { route: 'products', name: 'Товары', nameSingular: 'Товар', @@ -12,7 +13,7 @@ const service: ServiceParams = { ], default: { name: '', - price: '', + price: 0, quantity: 0, }, Form, diff --git a/src/services/transfers/TransferForm.tsx b/src/services/transfers/TransferForm.tsx index 3502609..92846a3 100644 --- a/src/services/transfers/TransferForm.tsx +++ b/src/services/transfers/TransferForm.tsx @@ -4,12 +4,13 @@ import moment from 'moment'; import Input from '../../components/Input'; import Select from '../../components/Select'; import hooks from '../../hooks/useAPIClient'; +import { Contractor, Transfer } from '../types'; -const mapper = (item: any) => ({ key: item._id, label: item.name }); +const mapper = (item: Contractor) => ({ key: item._id, label: item.name }); -const TransferForm: React.FC> = ({ setFieldValue, values }) => { +const TransferForm: React.FC> = ({ setFieldValue, values }) => { const { data: contractors } = hooks.contractors.useList(); if (!values.date) setFieldValue('date', moment().format('YYYY-MM-DD')); diff --git a/src/services/transfers/TransfersUpload.tsx b/src/services/transfers/TransfersUpload.tsx index c14a5c1..56bcd7e 100644 --- a/src/services/transfers/TransfersUpload.tsx +++ b/src/services/transfers/TransfersUpload.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { useHistory } from 'react-router-dom'; import { Form, Formik } from 'formik'; import Input from '../../components/Input'; @@ -8,15 +8,15 @@ import { post } from '../../requests'; const TransfersUpload: React.FC = () => { const history = useHistory(); + const inputRef = useRef(null); const handleSubmitFile = () => { const reader = new FileReader(); - const element = document.getElementById('file') as HTMLInputElement; - const file = element?.files?.[0]; + const file = inputRef?.current?.files?.[0]; if (file) { reader.readAsDataURL(file); - reader.onload = (e: any) => { - const uri = e.target.result; + reader.onload = (event: ProgressEvent) => { + const uri = event.target?.result; post('/uploads', { uri }).then(history.goBack); }; } @@ -34,7 +34,7 @@ const TransfersUpload: React.FC = () => { >
- +
diff --git a/src/services/transfers/index.ts b/src/services/transfers/index.ts index 37f7b11..1782103 100644 --- a/src/services/transfers/index.ts +++ b/src/services/transfers/index.ts @@ -2,9 +2,10 @@ import Form from './TransferForm'; import UploadPage from './TransfersUpload'; import { transformOperation } from '../transforms'; import { ServiceParams } from '../../lib/ServiceContext'; +import { Transfer } from '../types'; -const service: ServiceParams = { +const service: ServiceParams = { route: 'transfers', name: 'Переводы', nameSingular: 'Перевод', @@ -27,7 +28,6 @@ const service: ServiceParams = { ], default: { operation: 'in', - records: [], }, routes: { upload: UploadPage }, Form, diff --git a/src/services/types.ts b/src/services/types.ts new file mode 100644 index 0000000..e6e01db --- /dev/null +++ b/src/services/types.ts @@ -0,0 +1,40 @@ +interface BaseModel { + _id: string; + createdAt: string; + updatedAt: string; +} + +export interface Contractor extends BaseModel { + name: string; + vatId: string; + debt: number; +} + +export interface Product extends BaseModel { + name: string; + description: string; + price: number; + quantity: number; +} + +export interface Transfer extends BaseModel { + date: string; + operation: 'in' | 'out'; + contractorId: string; + amount: number; + contractor?: Contractor; +} + +export interface Waybill extends BaseModel { + date: string; + operation: 'in' | 'out'; + status: 'waiting' | 'executed' | 'cancelled'; + contractorId: string; + contractor?: Contractor; + records: { + productId: string; + price: number; + quantity: number; + }[]; + total: number; +} diff --git a/src/services/waybills/WaybillForm.tsx b/src/services/waybills/WaybillForm.tsx index c11ab9e..613989f 100644 --- a/src/services/waybills/WaybillForm.tsx +++ b/src/services/waybills/WaybillForm.tsx @@ -7,12 +7,13 @@ import Button from '../../components/Button'; import Select from '../../components/Select'; import Paper from '../../components/Paper'; import hooks from '../../hooks/useAPIClient'; +import { Product, Contractor, Waybill } from '../types'; -const mapper = (item: any) => ({ key: item._id, label: item.name }); +const mapper = (item: Product | Contractor) => ({ key: item._id, label: item.name }); -const WaybillForm: React.FC> = ({ setFieldValue, values }) => { +const WaybillForm: React.FC> = ({ setFieldValue, values }) => { const { data: products } = hooks.products.useList(); const { data: contractors } = hooks.contractors.useList(); @@ -51,7 +52,7 @@ const WaybillForm: React.FC> = ({ setFieldValue, values }) => { />
- {values.records.map((record: any, index: number) => ( + {values.records.map((record, index) => (