Calculation: add InsuranceTable
This commit is contained in:
parent
e36241af56
commit
c6f73b6b8f
@ -0,0 +1,22 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import type { ComponentType } from 'react';
|
||||
import { useRowOptions } from 'stores/tables/insurance/hooks';
|
||||
import { useInsuranceValue } from './hooks';
|
||||
import type { Keys } from './types';
|
||||
|
||||
export function buildOptionComponent<T>(key: string, Component: ComponentType<T>, valueName: Keys) {
|
||||
return observer((props: T) => {
|
||||
const [value, setValue] = useInsuranceValue(key, valueName);
|
||||
const options = useRowOptions(valueName);
|
||||
|
||||
return <Component value={value} options={options} setValue={setValue} {...props} />;
|
||||
});
|
||||
}
|
||||
|
||||
export function buildValueComponent<T>(key: string, Component: ComponentType<T>, valueName: Keys) {
|
||||
return observer((props: T) => {
|
||||
const [value, setValue] = useInsuranceValue(key, valueName);
|
||||
|
||||
return <Component value={value} setValue={setValue} {...props} />;
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import type { ColumnsType } from 'antd/lib/table';
|
||||
import { MAX_INSURANCE } from 'constants/values';
|
||||
import InputNumber from 'Elements/InputNumber';
|
||||
import Select from 'Elements/Select';
|
||||
import { buildOptionComponent, buildValueComponent } from './builders';
|
||||
import type * as Insurance from './types';
|
||||
|
||||
export const columns: ColumnsType<Insurance.Row> = [
|
||||
{
|
||||
key: 'policyType',
|
||||
dataIndex: 'policyType',
|
||||
title: 'Тип полиса',
|
||||
},
|
||||
{
|
||||
key: 'insuranceCompany',
|
||||
dataIndex: 'insuranceCompany',
|
||||
title: 'Страховая компания',
|
||||
render: (_, record) => {
|
||||
const Component = buildOptionComponent(record.key, Select, 'insuranceCompany');
|
||||
|
||||
return <Component />;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'insured',
|
||||
dataIndex: 'insured',
|
||||
title: 'Плательщик',
|
||||
render: (_, record) => {
|
||||
const Component = buildOptionComponent(record.key, Select, 'insured');
|
||||
|
||||
return <Component />;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'insCost',
|
||||
dataIndex: 'insCost',
|
||||
title: 'Стоимость за первый период',
|
||||
render: (_, record) => {
|
||||
const Component = buildValueComponent(record.key, InputNumber, 'insCost');
|
||||
|
||||
return <Component min={0} max={MAX_INSURANCE} step={1000} precision={2} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'insTerm',
|
||||
dataIndex: 'insTerm',
|
||||
title: 'Срок страхования',
|
||||
render: (_, record) => {
|
||||
const Component = buildOptionComponent(record.key, Select, 'insTerm');
|
||||
|
||||
return <Component />;
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,28 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRowValue } from 'stores/tables/insurance/hooks';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
export function useInsuranceValue(key, valueName) {
|
||||
const [storeValue, setStoreValue] = useRowValue(key, valueName);
|
||||
const [value, setValue] = useState(storeValue);
|
||||
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const debouncedSetStoreValue = useDebouncedCallback(setStoreValue, 350, { maxWait: 1000 });
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (storeValue !== value) {
|
||||
debouncedSetStoreValue(value);
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[value]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(storeValue);
|
||||
}, [storeValue]);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
import Table from 'Elements/Table';
|
||||
import { useStore } from 'stores/hooks';
|
||||
import styled from 'styled-components';
|
||||
import { columns } from './config';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
td > * {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
function InsuranceTable() {
|
||||
const store = useStore();
|
||||
|
||||
const { values } = store.$tables.insurance;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Table
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
dataSource={values}
|
||||
scroll={{
|
||||
x: true,
|
||||
}}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default InsuranceTable;
|
||||
@ -0,0 +1,16 @@
|
||||
import type { BaseOption } from 'Elements/types';
|
||||
|
||||
export type Row = {
|
||||
key: string;
|
||||
policyType: string;
|
||||
insuranceCompany: string | null;
|
||||
insured: 100_000_000 | 100_000_001 | null;
|
||||
insCost: number;
|
||||
insTerm: 100_000_000 | 100_000_001 | null;
|
||||
};
|
||||
|
||||
export type Keys = keyof Row;
|
||||
|
||||
export type Options = {
|
||||
[Key in keyof Row]?: BaseOption<Row[Key]>[];
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import renderFormRows from 'Components/Calculation/lib/render-rows';
|
||||
import { Flex } from 'UIKit/grid';
|
||||
import { id, rows, title } from './config';
|
||||
import InsuranceTable from './InsuranceTable';
|
||||
|
||||
function Insurance() {
|
||||
const renderedRows = renderFormRows(rows);
|
||||
@ -8,7 +9,7 @@ function Insurance() {
|
||||
return (
|
||||
<Flex flexDirection="column">
|
||||
{renderedRows}
|
||||
{/* TODO: add Insurance Table */}
|
||||
<InsuranceTable />
|
||||
{/* TODO: add FinGAP Table */}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import type { InputNumberProps as AntInputNumberProps } from 'antd';
|
||||
import { Form, InputNumber as AntInputNumber } from 'antd';
|
||||
import type { FC } from 'react';
|
||||
import type { BaseElementProps } from './types';
|
||||
|
||||
const { Item: FormItem } = Form;
|
||||
|
||||
export default function InputNumber({
|
||||
export default (function InputNumber({
|
||||
setValue,
|
||||
status,
|
||||
isValid,
|
||||
@ -23,7 +24,7 @@ export default function InputNumber({
|
||||
/>
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
} as FC<InputNumberProps>);
|
||||
|
||||
type InputNumberProps = AntInputNumberProps<number>;
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import type { SelectProps } from 'antd';
|
||||
import { Form, Select as AntSelect } from 'antd';
|
||||
import type { FC } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import type { BaseElementProps, BaseOption } from './types';
|
||||
|
||||
@ -8,7 +10,7 @@ type ElementProps = {
|
||||
options: BaseOption[];
|
||||
};
|
||||
|
||||
export default function Select({
|
||||
export default (function Select({
|
||||
value = null,
|
||||
setValue,
|
||||
options = [],
|
||||
@ -41,6 +43,6 @@ export default function Select({
|
||||
/>
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
} as FC<SelectProps>);
|
||||
|
||||
export { type SelectProps } from 'antd';
|
||||
|
||||
4
Elements/Table.js
Normal file
4
Elements/Table.js
Normal file
@ -0,0 +1,4 @@
|
||||
/* eslint-disable unicorn/filename-case */
|
||||
/* eslint-disable no-restricted-exports */
|
||||
|
||||
export { Table as default } from 'antd';
|
||||
@ -3,12 +3,12 @@ export type Status = 'Default' | 'Disabled' | 'Loading' | 'Hidden';
|
||||
export type BaseElementProps<ValueType> = {
|
||||
value: ValueType;
|
||||
setValue: (value: ValueType) => void;
|
||||
status: Status;
|
||||
isValid: boolean;
|
||||
help: string;
|
||||
status?: Status;
|
||||
isValid?: boolean;
|
||||
help?: string;
|
||||
};
|
||||
|
||||
export type BaseOption = {
|
||||
export type BaseOption<T = any> = {
|
||||
label: string;
|
||||
value: any;
|
||||
value: T;
|
||||
};
|
||||
|
||||
68
config/tables/insurance-table.ts
Normal file
68
config/tables/insurance-table.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/* eslint-disable object-curly-newline */
|
||||
import type * as Insurance from 'Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
|
||||
export const defaultOptions: Insurance.Options = {
|
||||
insured: [
|
||||
{
|
||||
label: 'ЛП',
|
||||
value: 100_000_000,
|
||||
},
|
||||
{
|
||||
label: 'ЛД',
|
||||
value: 100_000_001,
|
||||
},
|
||||
],
|
||||
insTerm: [
|
||||
{
|
||||
label: '12 месяцев',
|
||||
value: 100_000_000,
|
||||
},
|
||||
{
|
||||
label: 'Срок ДЛ',
|
||||
value: 100_000_001,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const defaultRows: Insurance.Row[] = [
|
||||
{
|
||||
key: 'osago',
|
||||
policyType: 'ОСАГО',
|
||||
insuranceCompany: null,
|
||||
insured: 100_000_000,
|
||||
insCost: 0,
|
||||
insTerm: 100_000_000,
|
||||
},
|
||||
{
|
||||
key: 'kasko',
|
||||
policyType: 'КАСКО',
|
||||
insuranceCompany: null,
|
||||
insured: 100_000_000,
|
||||
insCost: 0,
|
||||
insTerm: null,
|
||||
},
|
||||
{
|
||||
key: 'dgo',
|
||||
policyType: 'ДГО',
|
||||
insuranceCompany: null,
|
||||
insured: null,
|
||||
insCost: 0,
|
||||
insTerm: null,
|
||||
},
|
||||
{
|
||||
key: 'ns',
|
||||
policyType: 'НС',
|
||||
insuranceCompany: null,
|
||||
insured: null,
|
||||
insCost: 0,
|
||||
insTerm: null,
|
||||
},
|
||||
{
|
||||
key: 'finGAP',
|
||||
policyType: 'Safe Finance',
|
||||
insuranceCompany: null,
|
||||
insured: 100_000_000,
|
||||
insCost: 0,
|
||||
insTerm: null,
|
||||
},
|
||||
];
|
||||
@ -1,5 +1,6 @@
|
||||
import initializeApollo from 'apollo/client';
|
||||
import Calculation from 'Components/Calculation';
|
||||
import * as insuranceTableConfig from 'config/tables/insurance-table';
|
||||
import type { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { fetchUser } from 'services/user';
|
||||
@ -33,6 +34,12 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async (ctx) =>
|
||||
props: {
|
||||
user,
|
||||
initialApolloState: apolloClient.cache.extract(),
|
||||
tables: {
|
||||
insurance: {
|
||||
values: insuranceTableConfig.defaultRows,
|
||||
options: insuranceTableConfig.defaultOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -12,12 +12,19 @@ export function initializeStore(initialData) {
|
||||
const _store = store ?? new RootStore();
|
||||
|
||||
if (initialData) {
|
||||
const { user = null, calculation } = initialData;
|
||||
const { user = null, calculation, tables } = initialData;
|
||||
|
||||
_store.$user.hydrate(user);
|
||||
if (calculation?.values) _store.$calculation.$values.hydrate(calculation.values);
|
||||
if (calculation?.statuses) _store.$calculation.$status.hydrate(calculation.statuses);
|
||||
if (calculation?.options) _store.$calculation.$options.hydrate(calculation.options);
|
||||
|
||||
if (tables.insurance) {
|
||||
_store.$tables.insurance.hydrate({
|
||||
values: tables.insurance.values,
|
||||
options: tables.insurance.options,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window === 'undefined') return _store;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
/* eslint-disable import/no-cycle */
|
||||
import { enableStaticRendering } from 'mobx-react-lite';
|
||||
import CalculationStore from './calculation';
|
||||
import TablesStore from './tables';
|
||||
import UserStore from './user';
|
||||
|
||||
enableStaticRendering(typeof window === 'undefined');
|
||||
@ -8,9 +9,11 @@ enableStaticRendering(typeof window === 'undefined');
|
||||
export default class RootStore {
|
||||
$user: UserStore;
|
||||
$calculation: CalculationStore;
|
||||
$tables: TablesStore;
|
||||
|
||||
constructor() {
|
||||
this.$user = new UserStore(this);
|
||||
this.$calculation = new CalculationStore(this);
|
||||
this.$tables = new TablesStore(this);
|
||||
}
|
||||
}
|
||||
|
||||
10
stores/tables/index.ts
Normal file
10
stores/tables/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type RootStore from 'stores/root';
|
||||
import InsuranceTable from './insurance';
|
||||
|
||||
export default class TablesStore {
|
||||
insurance: InsuranceTable;
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
this.insurance = new InsuranceTable(rootStore);
|
||||
}
|
||||
}
|
||||
23
stores/tables/insurance/hooks.js
Normal file
23
stores/tables/insurance/hooks.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { useStore } from 'stores/hooks';
|
||||
|
||||
export function useRowValue(key, valueName) {
|
||||
const { $tables } = useStore();
|
||||
|
||||
const storeValue = $tables.insurance.getRowValue(key, valueName);
|
||||
|
||||
function setStoreValue(value) {
|
||||
$tables.insurance.setRowValues(key, {
|
||||
[valueName]: value,
|
||||
});
|
||||
}
|
||||
|
||||
return [storeValue, setStoreValue];
|
||||
}
|
||||
|
||||
export function useRowOptions(valueName) {
|
||||
const { $tables } = useStore();
|
||||
|
||||
const options = $tables.insurance.getRowOptions(valueName);
|
||||
|
||||
return options;
|
||||
}
|
||||
44
stores/tables/insurance/index.ts
Normal file
44
stores/tables/insurance/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/* eslint-disable object-curly-newline */
|
||||
import type * as Insurance from 'Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
import { mergeWith } from 'lodash';
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
import type RootStore from 'stores/root';
|
||||
|
||||
export interface InsuranceTableData {
|
||||
values: Insurance.Row[];
|
||||
options: Insurance.Options;
|
||||
}
|
||||
|
||||
export default class InsuranceTable {
|
||||
root: RootStore;
|
||||
values: Insurance.Row[] = [];
|
||||
options: Insurance.Options = {};
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
this.root = rootStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
hydrate = ({ values: initialValues, options: initialOptions }: InsuranceTableData) => {
|
||||
this.values = initialValues;
|
||||
this.options = initialOptions;
|
||||
};
|
||||
|
||||
getRowValue(key: string, valueName: Insurance.Keys) {
|
||||
const rowIndex = this.values.findIndex((x) => x.key === key);
|
||||
|
||||
return this.values[rowIndex][valueName];
|
||||
}
|
||||
|
||||
setRowValues = (key: string, row: Partial<Insurance.Row>) => {
|
||||
const rowIndex = this.values.findIndex((x) => x.key === key);
|
||||
|
||||
if (rowIndex >= 0) {
|
||||
mergeWith(this.values[rowIndex], row);
|
||||
}
|
||||
};
|
||||
|
||||
getRowOptions(valueName: Insurance.Keys) {
|
||||
return this.options[valueName];
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import type { User } from 'services/user/types';
|
||||
import type { CalculationOptions } from 'stores/calculation/options/types';
|
||||
import type { CalculationStatuses } from 'stores/calculation/statuses/types';
|
||||
import type { CalculationValues } from 'stores/calculation/values/types';
|
||||
import type { InsuranceTableData } from 'stores/tables/insurance';
|
||||
|
||||
export interface BasePageProps {
|
||||
user: User;
|
||||
@ -12,4 +13,7 @@ export interface BasePageProps {
|
||||
statuses?: CalculationStatuses;
|
||||
options?: CalculationOptions;
|
||||
};
|
||||
tables: {
|
||||
insurance: InsuranceTableData;
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user