diff --git a/.eslintrc.json b/.eslintrc.json index b14406f..6e2922a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -72,6 +72,7 @@ "case": "kebabCase", "ignore": ["^.*.(jsx|tsx)$"] } - ] + ], + "import/no-unresolved": "warn" } } diff --git a/Components/Calculation/Form/Leasing/config.ts b/Components/Calculation/Form/Leasing/config.ts new file mode 100644 index 0000000..0c70117 --- /dev/null +++ b/Components/Calculation/Form/Leasing/config.ts @@ -0,0 +1,14 @@ +/* eslint-disable object-curly-newline */ +import type { SectionsConfig } from 'Components/Calculation/types/sections'; + +const config: SectionsConfig = [ + [['selectProduct'], { gridTemplateColumns: '1fr' }], + [['tbxLeaseObjectPrice', 'tbxVATInLeaseObjectPrice', 'tbxLeaseObjectPriceWthtVAT']], + [['selectSupplierCurrency', 'tbxSupplierDiscountRub', 'tbxSupplierDiscountPerc']], + [['tbxFirstPaymentPerc', 'tbxFirstPaymentRub']], + [['tbxLeasingPeriod', 'tbxSaleBonus', 'tbxRedemptionPaymentSum']], + [['selectSubsidy', 'labelSubsidySum']], + [['tbxLastPaymentPerc', 'tbxLastPaymentRub', 'radioLastPaymentRule']], +]; + +export default config; diff --git a/Components/Calculation/Form/Leasing/index.jsx b/Components/Calculation/Form/Leasing/index.jsx new file mode 100644 index 0000000..6295346 --- /dev/null +++ b/Components/Calculation/Form/Leasing/index.jsx @@ -0,0 +1,8 @@ +import renderSections from 'Components/Calculation/layout/renderSections'; +import config from './config'; + +function Leasing() { + return renderSections(config); +} + +export default Leasing; diff --git a/Components/Calculation/builders/build-action.tsx b/Components/Calculation/builders/build-action.tsx index 08b1012..2c66572 100644 --- a/Components/Calculation/builders/build-action.tsx +++ b/Components/Calculation/builders/build-action.tsx @@ -2,15 +2,18 @@ import { observer } from 'mobx-react-lite'; import type { FC } from 'react'; import { useStatus } from 'stores/calculation/statuses/hooks'; -import type { Elements } from '../config/map-actions'; +import type { Elements } from '../config/map/actions'; import type { ElementsProps } from '../types/elements-props'; type BuilderProps = { elementName: Elements; - actionName: string; + valueName: string; }; -export default function buildAction(Component: FC, { elementName, actionName }: BuilderProps) { +export default function buildAction( + Component: FC, + { elementName, valueName: actionName }: BuilderProps +) { return observer((props) => { const status = useStatus(elementName); diff --git a/Components/Calculation/builders/build-computed.tsx b/Components/Calculation/builders/build-computed.tsx index 4c3c915..9a76cea 100644 --- a/Components/Calculation/builders/build-computed.tsx +++ b/Components/Calculation/builders/build-computed.tsx @@ -3,17 +3,17 @@ import type { FC } from 'react'; import { useStatus } from 'stores/calculation/statuses/hooks'; import type { ComputedValues } from 'stores/calculation/values/computed'; import { useComputedValue } from 'stores/calculation/values/hooks'; -import type { Elements } from '../config/map-computed'; +import type { Elements } from '../config/map/computed'; import type { ElementsProps } from '../types/elements-props'; type BuilderProps = { elementName: Elements; - computedValueName: ComputedValues; + valueName: ComputedValues; }; export default function buildComputedValue( Component: FC, - { elementName, computedValueName }: BuilderProps + { elementName, valueName: computedValueName }: BuilderProps ) { return observer((props) => { const computedValue = useComputedValue(computedValueName); diff --git a/Components/Calculation/builders/build-options.tsx b/Components/Calculation/builders/build-options.tsx index d205162..ae89432 100644 --- a/Components/Calculation/builders/build-options.tsx +++ b/Components/Calculation/builders/build-options.tsx @@ -5,7 +5,7 @@ import { useStatus } from 'stores/calculation/statuses/hooks'; import { useValidation } from 'stores/calculation/validation/hooks'; import { useSetValue, useValue } from 'stores/calculation/values/hooks'; import type { Values } from 'stores/calculation/values/types'; -import type { Elements } from '../config/map-values'; +import type { Elements } from '../config/map/values'; import type { ElementsProps } from '../types/elements-props'; type BuilderProps = { diff --git a/Components/Calculation/builders/build-value.tsx b/Components/Calculation/builders/build-value.tsx index 06677d0..fb2f0bc 100644 --- a/Components/Calculation/builders/build-value.tsx +++ b/Components/Calculation/builders/build-value.tsx @@ -4,7 +4,7 @@ import { useStatus } from 'stores/calculation/statuses/hooks'; import { useValidation } from 'stores/calculation/validation/hooks'; import { useSetValue, useValue } from 'stores/calculation/values/hooks'; import type { Values } from 'stores/calculation/values/types'; -import type { Elements } from '../config/map-values'; +import type { Elements } from '../config/map/values'; import type { ElementsProps } from '../types/elements-props'; type BuilderProps = { diff --git a/Components/Calculation/config/elements-components.ts b/Components/Calculation/config/elements-components.ts index 77fdffa..49c6c6a 100644 --- a/Components/Calculation/config/elements-components.ts +++ b/Components/Calculation/config/elements-components.ts @@ -7,12 +7,12 @@ import Radio from 'Elements/Radio'; import Select from 'Elements/Select'; import Switch from 'Elements/Switch'; import Text from 'Elements/Text'; -import type { Elements as ActionElements } from './map-actions'; -import type { Elements as ComputedElements } from './map-computed'; -import type { Elements } from './map-values'; +import type { Elements as ActionElements } from './map/actions'; +import type { Elements as ComputedElements } from './map/computed'; +import type { Elements as ValuesElements } from './map/values'; const components: Record< - Elements | ComputedElements | ActionElements, + ValuesElements | ComputedElements | ActionElements, (props: any) => JSX.Element > = { selectProduct: Select, diff --git a/Components/Calculation/config/elements-render.tsx b/Components/Calculation/config/elements-render.tsx index 049ab92..313d920 100644 --- a/Components/Calculation/config/elements-render.tsx +++ b/Components/Calculation/config/elements-render.tsx @@ -1,103 +1,204 @@ +/* eslint-disable object-curly-newline */ import Link from 'Elements/Link'; import Tooltip from 'Elements/Tooltip'; -import type { FC, ReactNode } from 'react'; +import type { ReactNode } from 'react'; +import styled from 'styled-components'; import { Flex } from 'UIKit/grid'; +import { min } from 'UIKit/mq'; import buildReadonly from '../builders/build-readonly'; -import type { Elements } from './map-values'; +import builders from './elements-builders'; +import components from './elements-components'; +import elementsProps from './elements-props'; +import titles from './elements-titles'; +import map from './map'; -function Head({ children }: { children: ReactNode }) { +const ElementTitle = styled.h5` + color: rgba(0, 0, 0, 0.75); + font-weight: 600; + font-size: 13px; + line-height: 1.5; + margin: 0 8px 3px 0; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + + ${min('laptop')} { + font-size: 14px; + } +`; + +function Head({ title, addon }: { title: string; addon?: ReactNode }) { return ( - {children} + {title} + {addon} ); } -type RenderProps = { render: (Title: FC, Element: FC) => JSX.Element }; +function Container({ children }: { children: ReactNode }) { + return {children}; +} -const render: Partial> = { +type RenderProps = { render: () => JSX.Element }; + +const renderElements = (Object.keys(map) as (keyof typeof map)[]).reduce((acc, elementName) => { + const title = titles[elementName]; + const valueName = map[elementName]; + const Component = components[elementName]; + const props = elementsProps[elementName]; + const builder = builders[elementName]; + + const Element = builder(Component, { + elementName, + valueName, + }); + + acc[elementName] = { + render: () => ( + + + + + ), + }; + + return acc; +}, {} as Record); + +const overrideRenderElements: Partial> = { selectLead: { - render: (Title, Element) => { + render: () => { + const title = titles.selectLead; + const valueName = map.selectLead; + const Component = components.selectLead; + const props = elementsProps.selectLead; + const builder = builders.selectLead; + + const Element = builder(Component, { + elementName: 'selectLead', + valueName, + }); + const LinkComponent = buildReadonly(Link, { elementName: 'linkLeadUrl', valueName: 'leadUrl', }); return ( - - - - <LinkComponent text="Открыть в CRM" /> - </Head> - <Element /> - </Flex> + <Container> + <Head title={title} addon={<LinkComponent text="Открыть в CRM" />} /> + <Element {...props} style={{ width: '100%' }} /> + </Container> ); }, }, selectOpportunity: { - render: (Title, Element) => { + render: () => { + const title = titles.selectOpportunity; + const valueName = map.selectOpportunity; + const Component = components.selectOpportunity; + const props = elementsProps.selectOpportunity; + const builder = builders.selectOpportunity; + + const Element = builder(Component, { + elementName: 'selectOpportunity', + valueName, + }); + const LinkComponent = buildReadonly(Link, { elementName: 'linkOpportunityUrl', valueName: 'opportunityUrl', }); return ( - <Flex flexDirection="column"> - <Head> - <Title /> - <LinkComponent text="Открыть в CRM" /> - </Head> - <Element /> - </Flex> + <Container> + <Head title={title} addon={<LinkComponent text="Открыть в CRM" />} /> + <Element {...props} style={{ width: '100%' }} /> + </Container> ); }, }, selectQuote: { - render: (Title, Element) => { + render: () => { + const title = titles.selectQuote; + const valueName = map.selectQuote; + const Component = components.selectQuote; + const props = elementsProps.selectQuote; + const builder = builders.selectQuote; + + const Element = builder(Component, { + elementName: 'selectQuote', + valueName, + }); + const LinkComponent = buildReadonly(Link, { elementName: 'linkQuoteUrl', valueName: 'quoteUrl', }); return ( - <Flex flexDirection="column"> - <Head> - <Title /> - <LinkComponent text="Открыть в CRM" /> - </Head> - <Element /> - </Flex> + <Container> + <Head title={title} addon={<LinkComponent text="Открыть в CRM" />} /> + <Element {...props} style={{ width: '100%' }} /> + </Container> ); }, }, tbxVehicleTaxInYear: { - render: (Title, Component) => ( - <Tooltip title="Без учета налога на роскошь" placement="topLeft"> - <Flex flexDirection="column"> - <Title /> - <Component /> - </Flex> - </Tooltip> - ), + render: () => { + const title = titles.tbxVehicleTaxInYear; + const valueName = map.tbxVehicleTaxInYear; + const Component = components.tbxVehicleTaxInYear; + const props = elementsProps.tbxVehicleTaxInYear; + const builder = builders.tbxVehicleTaxInYear; + + const Element = builder(Component, { + elementName: 'tbxVehicleTaxInYear', + valueName, + }); + + return ( + <Tooltip title="Без учета налога на роскошь" placement="topLeft"> + <Container> + <Head title={title} /> + <Element {...props} style={{ width: '100%' }} /> + </Container> + </Tooltip> + ); + }, }, selectHighSeasonStart: { - render: (Title, Component) => ( - <Tooltip title="С какого платежа начинается полный высокий сезон" placement="topLeft"> - <Flex flexDirection="column"> - <Title /> - <Component /> - </Flex> - </Tooltip> - ), + render: () => { + const title = titles.selectHighSeasonStart; + const valueName = map.selectHighSeasonStart; + const Component = components.selectHighSeasonStart; + const props = elementsProps.selectHighSeasonStart; + const builder = builders.selectHighSeasonStart; + + const Element = builder(Component, { + elementName: 'selectHighSeasonStart', + valueName, + }); + + return ( + <Tooltip title="С какого платежа начинается полный высокий сезон" placement="topLeft"> + <Container> + <Head title={title} /> + <Element {...props} style={{ width: '100%' }} /> + </Container> + </Tooltip> + ); + }, }, }; -export default render; +export default Object.assign(renderElements, overrideRenderElements); diff --git a/Components/Calculation/config/elements-titles.ts b/Components/Calculation/config/elements-titles.ts index 86f6d8b..f203578 100644 --- a/Components/Calculation/config/elements-titles.ts +++ b/Components/Calculation/config/elements-titles.ts @@ -1,7 +1,8 @@ -import type { Elements as ComputedElements } from './map-computed'; -import type { Elements } from './map-values'; +import type { Elements as ActionElements } from './map/actions'; +import type { Elements as ComputedElements } from './map/computed'; +import type { Elements as ValuesElements } from './map/values'; -const titles: Record<Elements | ComputedElements, string> = { +const titles: Record<ValuesElements | ComputedElements | ActionElements, string> = { selectLead: 'Интерес', selectOpportunity: 'Лизинговая сделка', selectQuote: 'Предложение', @@ -151,6 +152,10 @@ const titles: Record<Elements | ComputedElements, string> = { labelLeaseObjectRisk: 'Риск ПЛ', labelRegistrationDescription: 'Описание регистрации', tbxInsKaskoPriceLeasePeriod: 'Стоимость страховки КАСКО на весь срок', + + /** Action Elements */ + btnCalculate: '', + btnCreateKP: '', }; export default titles; diff --git a/Components/Calculation/config/map-actions.ts b/Components/Calculation/config/map/actions.ts similarity index 65% rename from Components/Calculation/config/map-actions.ts rename to Components/Calculation/config/map/actions.ts index 777dbf1..24b9adf 100644 --- a/Components/Calculation/config/map-actions.ts +++ b/Components/Calculation/config/map/actions.ts @@ -7,10 +7,6 @@ const elementsToActions = wrapElementsMap({ btnCreateKP: 'create-kp', }); +export default elementsToActions; + export type Elements = keyof typeof elementsToActions; - -export function getAction(elementName: Elements) { - const actionName = elementsToActions[elementName]; - - return actionName; -} diff --git a/Components/Calculation/config/map-computed.ts b/Components/Calculation/config/map/computed.ts similarity index 81% rename from Components/Calculation/config/map-computed.ts rename to Components/Calculation/config/map/computed.ts index 6b51730..604b87f 100644 --- a/Components/Calculation/config/map-computed.ts +++ b/Components/Calculation/config/map/computed.ts @@ -11,8 +11,6 @@ const elementsToComputed = wrapElementsMap({ labelRegistrationDescription: 'registrationDescription', }); -export type Elements = keyof typeof elementsToComputed; +export default elementsToComputed; -export function getComputedValueName(elementName: Elements) { - return elementsToComputed[elementName]; -} +export type Elements = keyof typeof elementsToComputed; diff --git a/Components/Calculation/config/map/index.js b/Components/Calculation/config/map/index.js new file mode 100644 index 0000000..0bd9939 --- /dev/null +++ b/Components/Calculation/config/map/index.js @@ -0,0 +1,5 @@ +import actions from './actions'; +import computed from './computed'; +import values from './values'; + +export default Object.assign(values, computed, actions); diff --git a/Components/Calculation/config/map-values.ts b/Components/Calculation/config/map/values.ts similarity index 99% rename from Components/Calculation/config/map-values.ts rename to Components/Calculation/config/map/values.ts index 0f68fb8..f158493 100644 --- a/Components/Calculation/config/map-values.ts +++ b/Components/Calculation/config/map/values.ts @@ -150,6 +150,8 @@ const elementsToValues = wrapElementsMap({ linkDownloadKp: 'kpUrl', }); +export default elementsToValues; + type ElementsValues = typeof elementsToValues; export type ElementsTypes = { diff --git a/Components/Calculation/layout/renderSections.tsx b/Components/Calculation/layout/renderSections.tsx new file mode 100644 index 0000000..790ad85 --- /dev/null +++ b/Components/Calculation/layout/renderSections.tsx @@ -0,0 +1,39 @@ +import { Box } from 'UIKit/grid'; +import elementsRender from '../config/elements-render'; +import type { SectionsConfig } from '../types/sections'; + +function renderSections(config: SectionsConfig) { + const rows = config.map(([elements, style]) => { + const renderedElements = elements.map((elementName) => { + const render = elementsRender[elementName]?.render; + + return render(); + }); + + return ( + <Box + sx={{ + display: 'grid', + gridTemplateColumns: ['1fr', '1fr', 'repeat(3, 1fr)'], + gap: '10px', + ...style, + }} + > + {renderedElements} + </Box> + ); + }); + + return ( + <Box + sx={{ + display: 'grid', + gridAutoRows: 'auto', + }} + > + {rows} + </Box> + ); +} + +export default renderSections; diff --git a/Components/Calculation/types/sections.ts b/Components/Calculation/types/sections.ts new file mode 100644 index 0000000..07d8dee --- /dev/null +++ b/Components/Calculation/types/sections.ts @@ -0,0 +1,8 @@ +import type { BoxProps } from 'rebass/styled-components'; +import type { Elements as ActionElements } from '../config/map/actions'; +import type { Elements as ComputedElements } from '../config/map/computed'; +import type { Elements as ValuesElements } from '../config/map/values'; + +export type SectionsConfig = Array< + [elements: (ValuesElements | ComputedElements | ActionElements)[], style?: BoxProps['style']] +>; diff --git a/Elements/Select.tsx b/Elements/Select.tsx index 51dba15..5756db1 100644 --- a/Elements/Select.tsx +++ b/Elements/Select.tsx @@ -11,7 +11,7 @@ type ElementProps = { export default function Select({ value = null, setValue, - options, + options = [], status, isValid, help, diff --git a/stores/calculation/options/index.ts b/stores/calculation/options/index.ts index d3ba2cc..9e8eea1 100644 --- a/stores/calculation/options/index.ts +++ b/stores/calculation/options/index.ts @@ -4,7 +4,7 @@ /* eslint-disable no-confusing-arrow */ /* eslint-disable object-curly-newline */ /* eslint-disable import/no-cycle */ -import type { Elements } from 'Components/Calculation/config/map-values'; +import type { Elements } from 'Components/Calculation/config/map/values'; import defaultOptions from 'config/default-options'; import type { BaseOption } from 'Elements/types'; import { makeAutoObservable } from 'mobx'; diff --git a/stores/calculation/values/index.ts b/stores/calculation/values/index.ts index 99c9ab8..ad465a5 100644 --- a/stores/calculation/values/index.ts +++ b/stores/calculation/values/index.ts @@ -1,8 +1,8 @@ /* eslint-disable implicit-arrow-linebreak */ /* eslint-disable object-curly-newline */ /* eslint-disable import/no-cycle */ -import type { Elements, ElementsTypes } from 'Components/Calculation/config/map-values'; -import { getValueName } from 'Components/Calculation/config/map-values'; +import type { Elements, ElementsTypes } from 'Components/Calculation/config/map/values'; +import { getValueName } from 'Components/Calculation/config/map/values'; import defaultValues from 'config/default-values'; import { makeAutoObservable } from 'mobx'; import type RootStore from '../../root';