summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/containers/ContractorForm.tsx51
-rw-r--r--src/containers/Contractors.tsx33
-rw-r--r--src/containers/ProductForm.tsx48
-rw-r--r--src/containers/Products.tsx32
-rw-r--r--src/containers/Service/Service.tsx19
-rw-r--r--src/containers/Service/ServiceContext.tsx20
-rw-r--r--src/containers/Service/ServiceForm.tsx45
-rw-r--r--src/containers/Service/ServiceList.tsx28
-rw-r--r--src/hooks/useAPIClient.ts57
-rw-r--r--src/index.tsx24
-rw-r--r--src/services.js29
11 files changed, 193 insertions, 193 deletions
diff --git a/src/containers/ContractorForm.tsx b/src/containers/ContractorForm.tsx
index 7f0d660..a67eabe 100644
--- a/src/containers/ContractorForm.tsx
+++ b/src/containers/ContractorForm.tsx
@@ -1,51 +1,16 @@
import React from 'react';
-import { useParams, useHistory } from 'react-router-dom';
-import { Formik, Form, Field } from 'formik';
-import Page, { Action } from './Page';
+import { Form, Field } from 'formik';
import Input from '../components/Input';
-import { useContractor } from '../hooks/useAPIClient';
-import { post, patch } from '../requests';
-
-interface Params {
- id: string;
-}
-
-const actions: Action[] = [
- { name: 'Назад', variant: 'outlined', route: '..' },
- { name: 'Сохранить', type: 'submit', form: 'contractorForm' },
-];
const ContractorForm: React.FC = () => {
- const history = useHistory();
- const { id } = useParams<Params>();
- const { data: contractor } = useContractor(id);
-
- const onSubmit = (values: any) => {
- const promise = id
- ? patch(`/contractors/${id}`, values)
- : post('/contractors', values);
- return promise.then(() => history.push('/contractors'));
- };
-
return (
- <Page title={id ? contractor?.name : 'Новый контрагент'} actions={actions}>
- {(!id || contractor) && (
- <Formik
- initialValues={contractor || { name: '', debt: '', vatId: '' }}
- onSubmit={onSubmit}
- >
- {() => (
- <Form id="contractorForm">
- <div className="max-w-lg">
- <Field name="name" label="Название" as={Input} />
- <Field name="vatId" label="УНП" as={Input} />
- <Field name="debt" type="number" label="Долг ($)" as={Input} />
- </div>
- </Form>
- )}
- </Formik>
- )}
- </Page>
+ <Form id="form">
+ <div className="max-w-lg">
+ <Field name="name" label="Название" as={Input} />
+ <Field name="vatId" label="УНП" as={Input} />
+ <Field name="debt" type="number" label="Долг ($)" as={Input} />
+ </div>
+ </Form>
);
};
diff --git a/src/containers/Contractors.tsx b/src/containers/Contractors.tsx
deleted file mode 100644
index 5d589ea..0000000
--- a/src/containers/Contractors.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import { useHistory } from 'react-router-dom';
-import Page from './Page';
-import ListTable from '../components/ListTable';
-import { useContractors } from '../hooks/useAPIClient';
-
-const fields = [
- { key: 'vatId', label: 'УНП' },
- { key: 'name', label: 'Название' },
- { key: 'debt', label: 'Долг' },
-];
-
-const actions = [
- { name: 'Добавить', route: 'contractors/add' },
-];
-
-const Contractors: React.FC = () => {
- const history = useHistory();
- const { data: contractors } = useContractors();
-
- const handleRowClick = (index: number) => {
- const contractor = contractors && contractors[index];
- history.push(`/contractors/edit/${contractor?._id}`);
- };
-
- return (
- <Page title="Контрагенты" actions={actions}>
- <ListTable items={contractors} fields={fields} handleRowClick={handleRowClick} />
- </Page>
- );
-};
-
-export default Contractors;
diff --git a/src/containers/ProductForm.tsx b/src/containers/ProductForm.tsx
index 0d21df9..4d01881 100644
--- a/src/containers/ProductForm.tsx
+++ b/src/containers/ProductForm.tsx
@@ -1,50 +1,16 @@
import React from 'react';
-import { useParams, useHistory } from 'react-router-dom';
-import { Formik, Form, Field } from 'formik';
-import Page, { Action } from './Page';
+import { Form, Field } from 'formik';
import Input from '../components/Input';
-import { useProduct } from '../hooks/useAPIClient';
-import { post, patch } from '../requests';
-interface Params {
- id: string;
-}
-
-const actions: Action[] = [
- { name: 'Назад', variant: 'outlined', route: '..' },
- { name: 'Сохранить', type: 'submit', form: 'productForm' },
-];
const ProductForm: React.FC = () => {
- const history = useHistory();
- const { id } = useParams<Params>();
- const { data: product } = useProduct(id);
-
- const onSubmit = (values: any) => {
- const promise = id
- ? patch(`/products/${id}`, values)
- : post('/products', values);
- return promise.then(() => history.push('/products'));
- };
-
return (
- <Page title={id ? product?.name : 'Новый товар'} actions={actions}>
- {(!id || product) && (
- <Formik
- initialValues={product || { name: '', price: '' }}
- onSubmit={onSubmit}
- >
- {() => (
- <Form id="productForm">
- <div className="max-w-lg">
- <Field name="name" label="Название" as={Input} />
- <Field name="price" type="number" label="Цена ($)" as={Input} />
- </div>
- </Form>
- )}
- </Formik>
- )}
- </Page>
+ <Form id="form">
+ <div className="max-w-lg">
+ <Field name="name" label="Название" as={Input} />
+ <Field name="price" type="number" label="Цена ($)" as={Input} />
+ </div>
+ </Form>
);
};
diff --git a/src/containers/Products.tsx b/src/containers/Products.tsx
deleted file mode 100644
index 0b6ea70..0000000
--- a/src/containers/Products.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from 'react';
-import { useHistory } from 'react-router-dom';
-import Page from './Page';
-import ListTable from '../components/ListTable';
-import { useProducts } from '../hooks/useAPIClient';
-
-const fields = [
- { key: 'name', label: 'Название' },
- { key: 'price', label: 'Цена' },
-];
-
-const actions = [
- { name: 'Добавить', route: 'products/add' },
-];
-
-const Products: React.FC = () => {
- const history = useHistory();
- const { data: products } = useProducts();
-
- const handleRowClick = (index: number) => {
- const product = products && products[index];
- history.push(`/products/edit/${product?._id}`);
- };
-
- return (
- <Page title="Товары" actions={actions}>
- <ListTable items={products} fields={fields} handleRowClick={handleRowClick} />
- </Page>
- );
-};
-
-export default Products;
diff --git a/src/containers/Service/Service.tsx b/src/containers/Service/Service.tsx
new file mode 100644
index 0000000..3a7cbee
--- /dev/null
+++ b/src/containers/Service/Service.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Route, Switch, useRouteMatch } from 'react-router-dom';
+import ServiceList from './ServiceList';
+import ServiceForm from './ServiceForm';
+
+
+const Service: React.FC = () => {
+ const { path } = useRouteMatch();
+
+ return (
+ <Switch>
+ <Route exact path={path} component={ServiceList} />
+ <Route path={`${path}/add`} component={ServiceForm} />
+ <Route path={`${path}/edit/:id`} component={ServiceForm} />
+ </Switch>
+ );
+};
+
+export default Service;
diff --git a/src/containers/Service/ServiceContext.tsx b/src/containers/Service/ServiceContext.tsx
new file mode 100644
index 0000000..2602936
--- /dev/null
+++ b/src/containers/Service/ServiceContext.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+
+export interface ServiceParams {
+ route: string;
+ name: string;
+ nameSingular: string;
+ tableFields: any[];
+ Form: React.FC<any>;
+}
+
+const ServiceContext = React.createContext<ServiceParams>({
+ route: '',
+ name: '',
+ nameSingular: '',
+ tableFields: [],
+ Form: () => null,
+});
+
+export const ServiceProvider = ServiceContext.Provider;
+export default ServiceContext;
diff --git a/src/containers/Service/ServiceForm.tsx b/src/containers/Service/ServiceForm.tsx
new file mode 100644
index 0000000..273f5bd
--- /dev/null
+++ b/src/containers/Service/ServiceForm.tsx
@@ -0,0 +1,45 @@
+import React, { useContext } from 'react';
+import { useParams, useHistory } from 'react-router-dom';
+import { Formik } from 'formik';
+import Page, { Action } from '../Page';
+import hooks from '../../hooks/useAPIClient';
+import { post, patch } from '../../requests';
+import ServiceContext from './ServiceContext';
+
+interface Params {
+ id: string;
+}
+
+const actions: Action[] = [
+ { name: 'Назад', variant: 'outlined', route: '..' },
+ { name: 'Сохранить', type: 'submit', form: 'form' },
+];
+
+const ServiceForm: React.FC = () => {
+ const service = useContext(ServiceContext);
+ const history = useHistory();
+ const { id } = useParams<Params>();
+ const { data: item } = hooks[service.route].useItem(id);
+
+ const onSubmit = (values: any) => {
+ const promise = id
+ ? patch(`/${service.route}/${id}`, values)
+ : post(`/${service.route}`, values);
+ return promise.then(() => history.push(`/${service.route}`));
+ };
+
+ return (
+ <Page title={id ? item?.name : `Новый ${service.nameSingular}`} actions={actions}>
+ {(!id || item) && (
+ <Formik
+ initialValues={item || { name: '', debt: '', vatId: '' }}
+ onSubmit={onSubmit}
+ >
+ <service.Form />
+ </Formik>
+ )}
+ </Page>
+ );
+};
+
+export default ServiceForm;
diff --git a/src/containers/Service/ServiceList.tsx b/src/containers/Service/ServiceList.tsx
new file mode 100644
index 0000000..6af2d1b
--- /dev/null
+++ b/src/containers/Service/ServiceList.tsx
@@ -0,0 +1,28 @@
+import React, { useContext } from 'react';
+import { useHistory } from 'react-router-dom';
+import Page from '../Page';
+import ListTable from '../../components/ListTable';
+import hooks from '../../hooks/useAPIClient';
+import ServiceContext from './ServiceContext';
+
+
+const ServiceList: React.FC = () => {
+ const service = useContext(ServiceContext);
+ const history = useHistory();
+ const { data } = hooks[service.route].useList();
+
+ const actions = [{ name: 'Добавить', route: `/${service.route}/add` }];
+
+ const handleRowClick = (index: number) => {
+ const item = data && data[index];
+ history.push(`/${service.route}/edit/${item?._id}`);
+ };
+
+ return (
+ <Page title={service.name} actions={actions}>
+ <ListTable items={data} fields={service.tableFields} handleRowClick={handleRowClick} />
+ </Page>
+ );
+};
+
+export default ServiceList;
diff --git a/src/hooks/useAPIClient.ts b/src/hooks/useAPIClient.ts
index eb427f1..8f3a077 100644
--- a/src/hooks/useAPIClient.ts
+++ b/src/hooks/useAPIClient.ts
@@ -1,11 +1,32 @@
import useSWR, { responseInterface } from 'swr';
import _ from 'lodash';
import { get } from '../requests';
+import services from '../services';
type Response<T> = responseInterface<T, Error>;
const fetcher = (endpoint: string) => get(endpoint).then(response => response.data);
+const createServiceHooks = <Item = any>(service: string) => {
+ const useList = (options = {}): Response<Item[]> => {
+ return useSWR(`/${service}`, fetcher, options);
+ };
+
+ const useItem = (_id: string): Response<Item> => {
+ const { data: preloadedItems } = useList({ revalidateOnMount: false });
+ const result = useSWR(_id && `/${service}/${_id}`, fetcher);
+ if (!result.data && result.isValidating) {
+ // If we are waiting for the first result, check if we can maybe
+ // get the data from already cached list for the time-being
+ const item = _.find(preloadedItems, { _id });
+ return { ...result, data: item } as Response<Item>;
+ }
+ return result;
+ };
+
+ return { useItem, useList };
+};
+
// Products
export interface Product {
_id: string;
@@ -18,23 +39,6 @@ export interface Product {
updatedAt: string;
}
-export const useProducts = (options = {}): Response<Product[]> => {
- return useSWR('/products', fetcher, options);
-};
-
-export const useProduct = (_id: string): Response<Product> => {
- const { data: preloadedProducts } = useProducts({ revalidateOnMount: false });
- const result = useSWR(_id && `/products/${_id}`, fetcher);
- if (!result.data && result.isValidating) {
- // If we are waiting for the first result, check if we can maybe
- // get the data from already cached list for the time-being
- const product = _.find(preloadedProducts, { _id });
- return { ...result, data: product };
- }
- return result;
-};
-
-
// Contractors
export interface Contractor {
_id: string;
@@ -45,18 +49,9 @@ export interface Contractor {
debt: number;
}
-export const useContractors = (options = {}): Response<Product[]> => {
- return useSWR('/contractors', fetcher, options);
-};
+const hooks = services.reduce((acc, { route }) => {
+ return _.set(acc, route, createServiceHooks(route));
+}, {});
-export const useContractor = (_id: string): Response<Product> => {
- const { data: preloadedContractors } = useContractors({ revalidateOnMount: false });
- const result = useSWR(_id && `/contractors/${_id}`, fetcher);
- if (!result.data && result.isValidating) {
- // If we are waiting for the first result, check if we can maybe
- // get the data from already cached list for the time-being
- const contractor = _.find(preloadedContractors, { _id });
- return { ...result, data: contractor };
- }
- return result;
-};
+
+export default hooks as any;
diff --git a/src/index.tsx b/src/index.tsx
index 4ec1db0..932fbf3 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -8,16 +8,13 @@ import {
} from 'react-router-dom';
import Header from './components/Header';
import Home from './containers/Home';
-import Products from './containers/Products';
-import ProductForm from './containers/ProductForm';
-import Contractors from './containers/Contractors';
-import ContractorForm from './containers/ContractorForm';
+import Service from './containers/Service/Service';
+import { ServiceProvider } from './containers/Service/ServiceContext';
+import services from './services';
const navigation = [
{ name: 'Главная', route: '/' },
- { name: 'Товары', route: '/products' },
- { name: 'Контрагенты', route: '/contractors' },
- { name: 'Накладные', route: '/waybills' },
+ ...services.map(({ name, route }) => ({ name, route: `/${route}` })),
];
const App: React.FC = () => (
@@ -25,12 +22,13 @@ const App: React.FC = () => (
<Header navigation={navigation} />
<Switch>
<Route exact path="/" component={Home} />
- <Route exact path="/products" component={Products} />
- <Route exact path="/products/add" component={ProductForm} />
- <Route exact path="/products/edit/:id" component={ProductForm} />
- <Route exact path="/contractors" component={Contractors} />
- <Route exact path="/contractors/add" component={ContractorForm} />
- <Route exact path="/contractors/edit/:id" component={ContractorForm} />
+ {services.map(service => (
+ <Route path={`/${service.route}`}>
+ <ServiceProvider value={service}>
+ <Service />
+ </ServiceProvider>
+ </Route>
+ ))}
</Switch>
</Router>
);
diff --git a/src/services.js b/src/services.js
new file mode 100644
index 0000000..83fc425
--- /dev/null
+++ b/src/services.js
@@ -0,0 +1,29 @@
+import ContractorForm from './containers/ContractorForm';
+import ProductForm from './containers/ProductForm';
+import { ServiceParams } from './containers/Service/ServiceContext';
+
+const services: ServiceParams[] = [
+ {
+ route: 'products',
+ name: 'Товары',
+ nameSingular: 'Товар',
+ tableFields: [
+ { key: 'name', label: 'Название' },
+ { key: 'price', label: 'Цена' },
+ ],
+ Form: ProductForm,
+ },
+ {
+ route: 'contractors',
+ name: 'Контрагенты',
+ nameSingular: 'Контрагент',
+ tableFields: [
+ { key: 'vatId', label: 'УНП' },
+ { key: 'name', label: 'Название' },
+ { key: 'debt', label: 'Долг' },
+ ],
+ Form: ContractorForm,
+ },
+];
+
+export default services;