Compare commits

...

260 Commits

Author SHA1 Message Date
vchikalkin
3c25ba30d0 merge branch 'release/dyn-4885' 2024-12-10 10:11:43 +03:00
vchikalkin
ba36eb502b merge branch 'release/dyn-4872' 2024-12-03 12:09:07 +03:00
vchikalkin
2c9a2bd30c fix selectDealerPerson validation 2024-10-18 16:29:07 +03:00
vchikalkin
f35acee64b fix tbxVATInLeaseObjectPrice block/unblock by cbxPartialVAT 2024-10-18 14:56:50 +03:00
vchikalkin
a4c02a747f merge branch 'release/dyn-4790' 2024-10-18 13:39:03 +03:00
vchikalkin
66b1e2cdcb merge branch 'fix/irr-bonus-reaction' 2024-09-23 09:59:51 +03:00
vchikalkin
81eca1c6e0 merge branch 'release/dyn-4762' 2024-09-19 10:12:18 +03:00
vchikalkin
89f2b3b68c merge branch 'release/dyn-4760' 2024-09-19 10:11:30 +03:00
vchikalkin
549bd901da partial fix release/4710 2024-09-12 16:50:38 +03:00
vchikalkin
6aad2d3aa2 apps/web: countSeats default value = 4 2024-09-12 16:25:04 +03:00
vchikalkin
09959e3ac3 apps/web: graphql: GetInsuranceCompanies -> sort совком first 2024-09-09 16:00:20 +03:00
vchikalkin
7014462d42 merge branch 'release/dyn-4744' 2024-09-09 15:39:50 +03:00
vchikalkin
62417b88ba merge branch release/dyn-4710 2024-09-05 17:36:38 +03:00
vchikalkin
bf0b5c9907 apps/web: sort evo_planpayments by evo_plandate 2024-08-20 17:42:24 +03:00
vchikalkin
5347c3ef6c apps/web: add check min bonus 2024-08-20 15:52:21 +03:00
vchikalkin
194292c083 apps/web: fix elt kasko list filter 2024-08-19 10:23:14 +03:00
vchikalkin
7d023dba1e apps/web: change elt/kasko error text 2024-08-19 10:03:13 +03:00
vchikalkin
9c63cadf29 merge branch release/dyn-4497 2024-08-15 11:54:28 +03:00
vchikalkin
88b7501b4d apps/web: pass device info to create-kp 2024-08-01 13:06:33 +03:00
vchikalkin
8f53187a3c apps/web: elt kasko fix outsideRoads 2024-07-23 11:09:05 +03:00
vchikalkin
ef8059b3b1 Reapply "web: process/add-product: disable selectInsNSIB reaction"
This reverts commit 0eccd799c79840fe8aa93461880b6ae61a217857.
2024-07-22 16:38:22 +03:00
vchikalkin
8627233a17 favicon: change icons for android & iphone 2024-07-19 13:48:51 +03:00
vchikalkin
2ece25dcb6 apps/web: changes site.webmanifest & add favicon.ico to <head> 2024-07-18 23:08:29 +03:00
vchikalkin
0eccd799c7 Revert "web: process/add-product: disable selectInsNSIB reaction"
This reverts commit 671b25a087ac9dc32bd25c209a7d5797d58d8bcd.
2024-07-18 11:37:29 +03:00
vchikalkin
2e8c333374 web\process\configurator\lib\helper: fix getRates 2024-07-18 11:33:32 +03:00
vchikalkin
671b25a087 web: process/add-product: disable selectInsNSIB reaction 2024-07-16 10:13:15 +03:00
vchikalkin
4a314326fb packages: upgrade next@14.2.5 2024-07-15 17:44:44 +03:00
vchikalkin
d0270a3c9e web: add service worker for pwa install 2024-07-15 15:08:11 +03:00
vchikalkin
71fc44e7a6 Dockerfile: specify pnpm version 2024-07-15 11:07:39 +03:00
vchikalkin
d6f5c73b9d merge branch dyn-4250_migrate-to-new-graphql 2024-07-15 11:04:47 +03:00
vchikalkin
d7908bb5da merge branch release/dyn-4515 2024-07-09 11:16:30 +03:00
vchikalkin
ed5391076c merge branch release/dyn-4514 2024-07-05 11:08:17 +03:00
vchikalkin
4a190e962a fix build: specify turbo version 2024-06-21 16:10:28 +03:00
vchikalkin
4644dbb404 merge branch feature/clear-cache-button 2024-06-21 16:05:49 +03:00
vchikalkin
5c6b419452 merge branch release/dyn-4312_recalc-after-kk 2024-05-22 09:31:51 +03:00
vchikalkin
048d132da2 apps/web: increase proxyTimeout up to 90s 2024-05-13 12:24:47 +03:00
vchikalkin
dd154a3004 merge branch fix/dyn-4368_credit-rate 2024-05-04 11:08:59 +03:00
vchikalkin
8dbcf4bb7f build: fix antd generate css for production 2024-04-24 15:23:13 +03:00
vchikalkin
c0f825a587 merge branch release/dyn-4331_fingap-rat 2024-04-20 10:26:00 +03:00
vchikalkin
2b3dd5e7b9 merge branch release/dyn-4284_dangerous-goods 2024-04-15 10:18:13 +03:00
vchikalkin
5d1aa99d75 apps/web: globals.css: revert text-decoration: none for 'a' tag 2024-04-11 19:55:48 +03:00
vchikalkin
4be47fced5 apps/web: replace normalize.css -> modern-normalize 2024-04-11 16:32:30 +03:00
vchikalkin
66fc8ee70e merge branch release/dyn-4114_filter-models 2024-04-11 15:00:34 +03:00
vchikalkin
09fc6956d8 apps/web (Admin/Cache): temp remove ReloadButton 2024-04-11 14:58:36 +03:00
vchikalkin
501ed13f8a scripts: add antd generate css script 2024-04-11 14:09:29 +03:00
vchikalkin
61132928b5 apps/web: beautify Loading spinner 2024-04-11 11:08:15 +03:00
vchikalkin
21b009c833 apps/web: add loading spinner between pages 2024-04-11 10:47:24 +03:00
vchikalkin
c616b981f1 apps/web: disable Link prefetch 2024-04-11 09:39:30 +03:00
vchikalkin
93d16cf35c merge branch feature/cache-manager-admin 2024-04-11 00:53:31 +03:00
vchikalkin
bd7cf3284b merge branch release/dyn-3905_elt-osago 2024-04-08 21:09:28 +03:00
vchikalkin
a0e6c4ff2f apps/web: fix get fingap from kp 2024-03-24 11:43:48 +03:00
vchikalkin
88fbb616b5 apps/web: fix ELT Kasko sum check 2024-03-24 10:59:02 +03:00
vchikalkin
7efea2acc9 merge branch fix/ui-ux-2203 2024-03-24 10:56:29 +03:00
vchikalkin
907f288815 DYN-4145: apps/web: корректируем КПП и Адреса в методе OSAGO 2024-03-18 17:55:40 +03:00
vchikalkin
2fea400060 apps/api: do not cache graphql responses with errors 2024-03-18 17:19:54 +03:00
vchikalkin
f695835c61 apps/web: process/used-pl: add ( ) symbols to vin regex 2024-03-07 13:39:13 +03:00
vchikalkin
47d47a0bfd apps/web: create Content component to separate from error 2024-03-04 16:41:22 +03:00
vchikalkin
540d5642a1 apps/web: fix show statusCode error 2024-03-04 16:26:54 +03:00
vchikalkin
2ec2a14706 merge branch release/dyn-3987_early-redemption-sums 2024-03-04 14:23:09 +03:00
vchikalkin
f7110497d3 apps/web: show apollo error for user 2024-03-01 11:28:36 +03:00
vchikalkin
c9326697f6 axios: capture more error information to sentry 2024-03-01 10:29:06 +03:00
vchikalkin
c2982cf31b apps/web: fix apollo modifyDataLink: check values are exist 2024-02-29 17:03:34 +03:00
vchikalkin
8c69b3b9c4 apps/web: add extra data for sentry 2024-02-29 16:56:43 +03:00
vchikalkin
24987be78e apps/web: fix telematic/tracker reaction 2024-02-29 16:06:15 +03:00
vchikalkin
defc625324 apps/api: check graphql response status & forward to response 2024-02-29 16:01:23 +03:00
vchikalkin
eee91d5f2d build: actualize .dockerignore 2024-02-29 15:17:10 +03:00
vchikalkin
a65202c653 apps/web: fix tbxVehicleTaxInYear addonBefore 2024-02-28 16:58:47 +03:00
vchikalkin
ffaad142e8 Docker: apps/api: fix build 2024-02-28 16:38:44 +03:00
vchikalkin
9e40e5141c merge branch feature/migrate-yarn-to-pnpm 2024-02-28 16:33:15 +03:00
vchikalkin
79e707a232 apps/web: add sentry scope to set user on client 2024-02-24 17:14:06 +03:00
vchikalkin
d374208097 Sentry: disable mask inputs 2024-02-24 16:24:00 +03:00
vchikalkin
557053caa6 apps/web: fix sentry trpc capture exception 2024-02-23 12:47:12 +03:00
vchikalkin
e258873976 apps/web: reconfigure sentry 2024-02-22 15:36:20 +03:00
vchikalkin
59e45ddc63 project: upgrade packages 2024-02-22 14:37:37 +03:00
vchikalkin
eaf95c6007 apps/web: upgrade next@14.1.0 2024-02-22 13:16:25 +03:00
vchikalkin
f3518244ab apps/api: reduce TTL GetConfigurations, GetDealers, GetModels 2024-02-21 22:38:07 +03:00
vchikalkin
c5683f7edd apps/web: fix irrInfo empty after loadKP 2024-02-18 18:34:32 +03:00
vchikalkin
acdacbf18e apps/api: manual headers for fetching from graphql 2024-02-18 17:40:36 +03:00
vchikalkin
ef88c12e74 apps/api: configure queries ttl 2024-02-18 17:12:12 +03:00
vchikalkin
ab0d455afb apps/api: add seconds util 2024-02-18 15:23:36 +03:00
vchikalkin
e0d2836f4a app/api: add feature to disable cache for some queries 2024-02-18 15:15:53 +03:00
vchikalkin
4bc5234d09 apps/api: add query-ttl config
proxyController: pass all request headers to proxied server
2024-02-18 15:00:36 +03:00
vchikalkin
6041082fec apps/web: move initial data fetch to client 2024-02-18 12:23:50 +03:00
vchikalkin
f426ca7f23 apps/web: disable disable webpack for build 2024-02-17 20:36:27 +03:00
vchikalkin
5115551b0e apps/api: fix expose port 2024-02-17 20:06:13 +03:00
vchikalkin
5ce9484e43 docker: fix build api 2024-02-17 19:24:01 +03:00
vchikalkin
8a117e250c merge branch feature/optimize-graphql-queries 2024-02-17 17:39:31 +03:00
vchikalkin
8089f62c31 process/fingap: fix getFingapRisks 2024-02-16 17:24:45 +03:00
vchikalkin
bd775babf9 server: make getTarif public procedure 2024-02-16 13:53:41 +03:00
vchikalkin
e222a3093a process/fingap & process/supplier-agent: remove inline graphql queries 2024-02-16 13:32:16 +03:00
vchikalkin
53debf2cff server: combine miltiple quote query to single 2024-02-16 13:22:01 +03:00
vchikalkin
c092b8f47c init/get-main-data: disable some queries 2024-02-16 12:08:14 +03:00
vchikalkin
6b7c679a0d process/configurator: fix irr-addon reset loading indication 2024-02-14 16:01:14 +03:00
vchikalkin
7a00478727 Form/CreateKP: изменили расположение элементов 2024-02-12 15:26:52 +03:00
vchikalkin
677ab1d75b process/configurator: удалили реакцию заполнения tbxIRR_Perc из тарифа
добавили валидацию tbxIRR_Perc на диапазон irrInfo
2024-02-12 15:16:51 +03:00
vchikalkin
126302a10a merge branch feature/loading-tarif-indicator 2024-02-12 13:25:11 +03:00
vchikalkin
35ce71c149 process/configurator: abort prev getTarif query 2024-02-08 13:19:51 +03:00
vchikalkin
47b57c0c73 apps/web: get selectTarif options on load-kp 2024-02-08 10:56:30 +03:00
obarykina
46c0b305b6 [2] merge branch release/dyn-3974_full-price-nds 2024-02-08 09:46:56 +03:00
vchikalkin
eb6f0ef949 merge branch fix/dyn-3977_get-tarifs 2024-02-07 16:21:22 +03:00
obarykina
7ca4e41980 components/calulation/addons/product-addon: show cbxFloatingRate 2024-02-07 15:48:34 +03:00
obarykina
662b04be0f merge branch release/dyn-3965_filter-telematic-electric-car 2024-02-07 15:42:52 +03:00
obarykina
e2d37712ad merge branch release/dyn-3974_full-price-nds 2024-02-07 15:34:17 +03:00
vchikalkin
4c3d305e7f apps/web: hide cbxFloatingRate from product-addon
add cbxPartialVAT & cbxFloatingRate to unlimited
2024-02-01 09:30:02 +03:00
vchikalkin
1aaa84e31d merge branch release/dyn-3854_floating-rate_partial-nds 2024-02-01 09:26:44 +03:00
vchikalkin
5a290d5be9 merge branch release/dyn-3855_kasko-validation 2024-02-01 08:58:19 +03:00
vchikalkin
ac14a86c33 process/recalc: fix firstPaymentPerc validation 2024-01-23 16:58:48 +03:00
vchikalkin
175cb63515 apps/web: calculate: use values.disableChecks to DISABLE_CHECKS_RESULTS 2024-01-16 11:24:36 +03:00
vchikalkin
803b4a1cfb project: remove .npmrc 2024-01-14 16:31:55 +03:00
vchikalkin
709eda1b50 build: add SENTRYCLI_CDNURL to docker configs 2024-01-14 16:23:25 +03:00
vchikalkin
bdc6d032aa apps/web: remove sharp from packages 2024-01-14 15:29:23 +03:00
vchikalkin
3462395818 apps/web: fix Dockerfile 2023-12-24 01:28:00 +03:00
vchikalkin
9308417c4d docker-compose.yml: remove depends_on 2023-12-24 01:20:14 +03:00
vchikalkin
d0f11cbc63 project: add docker-compose.yml 2023-12-24 01:06:36 +03:00
vchikalkin
eb1eb44769 merge release/dyn-3749_promotion 2023-12-12 08:56:09 +03:00
vchikalkin
6c300c8688 Revert "merge refactor/grid-styles"
This reverts commit 0c0dc7de5333a9d3710a161ec6432408b38174d9.
2023-12-11 15:43:15 +03:00
vchikalkin
3898fc1bdf apps/web: fix manifest 401 2023-12-01 16:27:55 +03:00
vchikalkin
0c0dc7de53 merge refactor/grid-styles 2023-12-01 16:16:14 +03:00
vchikalkin
703cee467c merge release/dyn-3643_minor-updates-november-2023 2023-12-01 10:48:45 +03:00
vchikalkin
c154568bb4 merge release/dyn-2803_sale-post-pi 2023-11-27 11:06:14 +03:00
vchikalkin
8dd9627849 merge refactor/error-monitoring 2023-10-04 17:36:52 +03:00
vchikalkin
07c87d583f merge fix/evo_baseproducts-filter 2023-09-21 10:36:25 +03:00
vchikalkin
0ff6ba3f74 merge fix/elt-remove-caching 2023-09-18 10:21:18 +03:00
vchikalkin
ae0b72aebd merge fix/dyn-3363_osago-kasko-leaseback 2023-09-06 15:40:08 +03:00
vchikalkin
10f3760c63 merge release/dyn-3089_year-parallel-import 2023-08-28 10:25:03 +03:00
vchikalkin
db29a77d37 merge release/dyn-3268_result-bonus-dop-prod 2023-08-25 12:59:56 +03:00
vchikalkin
0936d1cdd7 merge fix/dyn-3291_safe-finance 2023-08-24 15:21:00 +03:00
vchikalkin
5c039597b3 constants/value: change RATE to 15.9 2023-08-23 13:38:05 +03:00
vchikalkin
17a9810878 merge branch fix/dyn-3267_elt 2023-08-23 13:33:16 +03:00
vchikalkin
438242ac31 eslint: specify next app rootDir 2023-08-01 14:14:18 +03:00
vchikalkin
78064edf81 merge project/eslint-rules 2023-08-01 13:41:44 +03:00
vchikalkin
565cabb9bd merge fix/dyn-3167_elt 2023-08-01 10:03:10 +03:00
vchikalkin
903b32084a [2] Calculation/config: increase Min/Max PriceChange 2023-07-19 14:49:27 +03:00
vchikalkin
0f61d4a399 merge branch fix/kp-data_price-change 2023-07-17 14:04:58 +03:00
vchikalkin
7df69f92e8 eslint: upgrade config-canonical 2023-07-12 12:27:49 +03:00
vchikalkin
58d7a05591 merge experimental/error-monitoring 2023-07-12 11:14:13 +03:00
vchikalkin
802885e726 Calculation/config: increase Min/Max PriceChange 2023-07-06 13:30:01 +03:00
vchikalkin
caeb304d15 process/configurator: format min, max validation number 2023-07-06 13:28:52 +03:00
vchikalkin
d4c70101f2 merge branch release/dyn-2954_elt-restriction 2023-07-06 11:18:06 +03:00
vchikalkin
6fd64a62bf merge branch release/dyn-3047_min-max-validation 2023-07-05 12:58:22 +03:00
vchikalkin
11820a7b5d process/insurance: add fingap validation 2023-07-04 11:19:43 +03:00
vchikalkin
f9eda3c6b0 merge branch fix/dyn-3021_fingap 2023-07-04 10:42:55 +03:00
vchikalkin
c77f60fdc3 merge branch upgrade/packages-2606 2023-07-04 10:40:25 +03:00
vchikalkin
84de7a84e8 fix: process/supplier-agent: get-kp-data 2023-06-20 14:52:14 +03:00
vchikalkin
498fa217fe process/leasing-object: fix importerRewardPerc, importerRewardRub (get-kp-data) 2023-06-15 10:28:06 +03:00
vchikalkin
0f32abc6b2 process/payments: fix degression & firstPaymentPerc 2023-06-09 15:13:16 +03:00
vchikalkin
7ce8104b87 process/fingap: pass insCost from kp 2023-06-08 17:14:46 +03:00
vchikalkin
0952ce12ee process/insurance: add fingap.policyType validation 2023-06-08 16:45:05 +03:00
vchikalkin
b90bfc74d9 process/fingap: fix reaction 2023-06-08 16:21:51 +03:00
vchikalkin
d06b6e20d5 process/insurance: add fingap.insCost validation 2023-06-08 14:05:55 +03:00
vchikalkin
654865e845 fix: Expected number, received null 2023-06-07 09:48:34 +03:00
vchikalkin
4fd5c3602e process/insurance: add fingap.insured validation 2023-06-06 18:30:00 +03:00
vchikalkin
4a65a39ba3 process:subsidy: fix subsidySum ,importProgramSum 2023-06-06 12:23:45 +03:00
vchikalkin
e9edf189e7 fix: insurance-table fingap row 2023-06-05 13:42:46 +03:00
vchikalkin
e6289b7174 [2] fix: recalc disabled agents fields 2023-06-05 11:55:35 +03:00
vchikalkin
aaa8cadc02 fix: reset result values after create-kp with recalc 2023-06-05 11:31:40 +03:00
vchikalkin
554a702e6c fix: recalc disabled agents fields 2023-06-05 10:28:48 +03:00
vchikalkin
8e65d864ef fix: при подгрузке КП если в Салоне 2 ЮЛ, то слетает выбранный второй из списка и указывается первый по списку и соответственно Ав второго из списка тоже слетает 2023-06-05 10:18:29 +03:00
vchikalkin
2090056b81 fix: при подгрузке КП отрабатывает реакция на изменение продукта и есть продукт с частичным НДС то БУ ставится автоматом Да 2023-06-05 10:09:58 +03:00
vchikalkin
159fb85e63 fix: selectDealerPerson evo_inn/evo_kpp 2023-06-02 15:44:52 +03:00
vchikalkin
56b6510933 Form/ELT: fix kasko empty error message 2023-06-01 17:42:15 +03:00
vchikalkin
6a828ce82b process/configurator: fix tbxIRR_Perc reset 2023-06-01 16:14:42 +03:00
vchikalkin
074de953c9 process/gibdd: add selectRegionRegistration filter 2023-06-01 15:57:22 +03:00
vchikalkin
fe614b2529 fix: resetValues after get calculate results 2023-06-01 15:13:31 +03:00
vchikalkin
b7ae72d4c0 server/calculate: fix preparedValues districtRate 2023-06-01 14:24:42 +03:00
vchikalkin
805dc4dba3 process/add-product: fix telematic/tracker, recalcWithRevision 2023-06-01 14:22:36 +03:00
vchikalkin
290526b933 fix: RetryButton & SupportButton 2023-06-01 12:34:35 +03:00
vchikalkin
fd972aadad ui: remove page margin for desktop 2023-06-01 11:54:58 +03:00
vchikalkin
78c2446945 Calculation/Settings: move btnCreateKP & linkDownloadKp elements 2023-05-31 17:22:24 +03:00
vchikalkin
9a7e074863 ui: fix Form/InsuranceTable on laptops 2023-05-31 16:53:42 +03:00
vchikalkin
9ea4eeb6ce Revert "process/create-kp: auto download kp file"
This reverts commit cadfec5fc2c5cf85697aa4218e6b0c95de93d7d2.
2023-05-31 12:39:26 +03:00
vchikalkin
cadfec5fc2 process/create-kp: auto download kp file 2023-05-31 11:56:16 +03:00
vchikalkin
5c8560e266 store: add useErrors hook
Calculation/render: override btnCalculate, btnCreateKP, btnCreateKPMini
2023-05-31 11:45:59 +03:00
vchikalkin
2d0117bc4c Calculation/Form: disable elements when Calculate || CreateKP 2023-05-31 11:02:03 +03:00
vchikalkin
05ed1b6aef Calculation/Form: disable elements when LoadKP 2023-05-31 10:26:50 +03:00
vchikalkin
4f509aefdf process/configurator: fix selectRate value when load-kp 2023-05-30 15:25:11 +03:00
vchikalkin
befa92fbce fix: download-kp url 2023-05-30 14:45:47 +03:00
vchikalkin
f4acf8c316 elements: fix linkDownloadKp 2023-05-30 14:03:44 +03:00
vchikalkin
c9523328da fix: exclude values reset results after create-kp 2023-05-30 13:45:25 +03:00
vchikalkin
0ed21711db Components/Common: fix extra error Buttons
config/users: add roles
2023-05-30 11:40:28 +03:00
vchikalkin
c98167e609 ui: Form/AddProduct: fix selectInsNSIB 2023-05-30 11:25:03 +03:00
vchikalkin
feb72a3021 ui: Output/Results: sort results values 2023-05-30 11:20:46 +03:00
vchikalkin
8e3fb86169 leaseObjectUsed improvements 2023-05-29 11:59:02 +03:00
vchikalkin
6a95fbb3f2 process/price: fix NaN 2023-05-25 16:39:56 +03:00
vchikalkin
ab5700dd92 merge fix/migration/elt 2023-05-25 13:46:47 +03:00
vchikalkin
383b70434f process: make elt error notifications silent 2023-05-24 15:12:12 +03:00
vchikalkin
f8adc2e8c7 Form/Settings: fix tbxIRR_Perc min/max validation 2023-05-24 12:58:23 +03:00
vchikalkin
0246916fca process/calculate: reset results on change form values 2023-05-24 12:55:53 +03:00
vchikalkin
4383bc54b3 Components/Output: set active key validation if hasErrors 2023-05-24 11:46:01 +03:00
vchikalkin
2800184cfe Components/Output: set active key after calculation results 2023-05-24 11:29:12 +03:00
vchikalkin
616313eb20 merge release/dyn-2846_pi 2023-05-19 13:11:42 +03:00
vchikalkin
96e792b91d fix PaymentsTable 2023-05-16 14:13:19 +03:00
vchikalkin
1d5328db73 beautify imports/exports after 4549448 2023-05-16 13:50:47 +03:00
vchikalkin
45494483a0 fix build 2023-05-16 13:42:46 +03:00
vchikalkin
7fb706cdaf ui: fix elements layout on mobile devices 2023-05-16 12:04:01 +03:00
vchikalkin
d620cf0c92 optimize package/ui/elements exports 2023-05-16 12:03:54 +03:00
vchikalkin
5940f4c048 elt/validation: remove supplierDiscountRub 2023-05-15 09:22:34 +03:00
vchikalkin
bb6a3986b1 elt: extend validation
extend reset reaction triggers
fix make-request/osago
2023-05-15 09:15:31 +03:00
vchikalkin
2bea56fa87 process/configurator: fix get IRR_Perc from kp 2023-05-12 12:38:35 +03:00
vchikalkin
d195530b15 tbxVIN: capitalize letters 2023-05-12 12:07:49 +03:00
vchikalkin
04b7def049 merge migration/elt 2023-05-12 11:34:09 +03:00
vchikalkin
a2471a0ca8 style: global scrollbar style 2023-04-21 13:26:21 +03:00
vchikalkin
c9a9f3a66d Components/Output/PaymentsTable: disable pagination 2023-04-21 13:01:05 +03:00
vchikalkin
910c5627d2 merge release/unlimited-1904 2023-04-21 11:13:58 +03:00
vchikalkin
860ecb9384 merge migration/fix-4 2023-04-19 22:34:50 +03:00
vchikalkin
87e4783dd5 merge migration/fix-3 & migration/upgrade-code-130423 2023-04-14 10:57:02 +03:00
vchikalkin
0d35042e41 merge migration/fix-2 2023-04-10 11:58:50 +03:00
vchikalkin
95040c5a10 06e7687: fix download-kp url 2023-04-04 15:10:14 +03:00
vchikalkin
06e7687fc1 trpc/create-kp: add downloadKp url to result 2023-04-04 13:27:07 +03:00
vchikalkin
79179c10f3 merge migration/fix-1 2023-04-03 12:50:47 +03:00
vchikalkin
5e2fd20520 eslint-staged: ignore /graphql 2023-04-03 12:50:23 +03:00
vchikalkin
3e7d350248 Revert "trpc: check unlimited in middleware"
This reverts commit 90c0ef481b03bfb7d1a822dfa9e667f59f2c27b2.
2023-04-03 10:16:46 +03:00
vchikalkin
618d8cca58 project: add lint-staged 2023-04-03 10:10:18 +03:00
vchikalkin
90c0ef481b trpc: check unlimited in middleware 2023-03-29 15:10:34 +03:00
vchikalkin
1ea11f8eff change support url 2023-03-29 12:24:16 +03:00
vchikalkin
e8d1057c69 merge feature/unlimited 2023-03-29 10:07:34 +03:00
vchikalkin
5442905966 merge migration/random-4 2023-03-28 09:33:17 +03:00
vchikalkin
9d45a7bff0 merge migration/random-3 2023-03-16 15:35:44 +03:00
vchikalkin
29460f4509 fix load-kp error 2023-02-28 15:28:39 +03:00
vchikalkin
44ccd85e86 upgrade packages #2 2023-02-28 10:39:17 +03:00
vchikalkin
57c28c741e packages/tools: remove radash 2023-02-28 10:30:32 +03:00
vchikalkin
d01047752f upgrade packages 2023-02-28 10:18:52 +03:00
vchikalkin
d1df501f86 merge migration/random-2 2023-02-28 09:46:31 +03:00
vchikalkin
b786f49aa1 vscode: fix client debug 2023-02-06 14:30:10 +03:00
vchikalkin
4f972a4cfe git: regenerate .gitignore 2023-02-06 12:50:34 +03:00
vchikalkin
b15de1ffe2 merge branch dev/eslint-config 2023-02-06 12:19:39 +03:00
vchikalkin
96c4a095b1 merge migration/random-1 2023-02-03 08:30:24 +03:00
vchikalkin
d6db50a582 merge migration/fix/agents 2023-01-18 15:44:15 +03:00
vchikalkin
fa3b6a2390 mocks: set default user akalinina 2023-01-18 15:40:56 +03:00
vchikalkin
24e4d8cd7d merge release/turborepo 2023-01-11 11:26:08 +03:00
vchikalkin
259a63f787 fix build 2022-12-27 13:59:50 +03:00
vchikalkin
e77a8e03d4 merge release/environment-variables 2022-12-27 13:10:09 +03:00
vchikalkin
e2bbbaaeab packages: upgrade to next@13 2022-12-21 13:50:41 +03:00
vchikalkin
ff6d7f57a2 packages/ui: fix layout imports 2022-12-21 12:26:06 +03:00
vchikalkin
3dbef34d33 next.config.js: use next-composed-plugins 2022-12-21 11:04:54 +03:00
vchikalkin
156c6c51b5 project: use yarn workspaces
(merge branch release/monorepo)
2022-12-20 22:04:45 +03:00
vchikalkin
af3b079a88 Project: migrate to Yarn 3 2022-12-19 13:28:00 +03:00
Chika
f1fe8e48bd process/load-kp: beautify types 2022-11-14 21:51:01 +03:00
Chika
102d3446e6 process/fingap: load data from kp 2022-11-14 21:33:18 +03:00
Chika
878ec1d62a process/payments: add get-kp-data 2022-11-11 11:54:16 +03:00
Chika
b0e7624cd9 commit for 58fdfb1 2022-11-09 13:38:26 +03:00
Chika
58fdfb1ed9 process/load-kp: typescript fixes 2022-11-09 13:31:12 +03:00
Chika
f03ba9d979 pages/index: revert getOwnerData back to server 2022-11-08 12:04:37 +03:00
Chika
32fa871238 process/load-kp: block selectQuote
show loading message
2022-11-08 09:36:26 +03:00
Chika
cc223234b9 Elements: fix message not showing 2022-11-08 09:29:40 +03:00
Chika
857a40af49 process/init: fix GetMainData query fetched twice 2022-11-08 09:18:38 +03:00
Chika
6f1cfab8f5 process/init: fix get-data fetched multiple times 2022-11-07 22:59:25 +03:00
Chika
222b0b0e74 pages: add CRM graphql connection error
pages: use fetch data hooks (fetch all gql data on client)
mocks: use process.env variables
2022-11-07 17:43:14 +03:00
Chika
85e1115de8 pages: use Elements/Result in error pages 2022-11-07 11:18:14 +03:00
Chika
f6c351d39e pages: create Error Component 2022-11-06 14:00:22 +03:00
Chika
11e60367ab process/agents: add rewardSumm validation :
* Если RewardSumm= 0 И в списке RewardCondition есть запись,
   *  у которой evo_reward_condition.evo_agency_agreementid. Обязательная выплата АВ (evo_required_reward) = True,
   *  то поле RewardSumm обводить красной рамкой
   * и выводить ошибку "Согласно Агентскому договору обязательна выплата АВ. Заложите АВ в расчет"
2022-11-05 13:30:13 +03:00
Chika
96ab658f93 typescript: replace graphql-codegen 'any' types 2022-11-05 13:10:07 +03:00
Chika
0569d45972 process: add type ReactionsContext 2022-11-03 12:25:32 +03:00
Chika
21c97ece2b stores/tables: refactor insurance methods 2022-11-03 11:55:36 +03:00
Chika
391f9039f7 merge branch migration/agents 2022-11-02 21:36:48 +03:00
Chika
34bab4f715 process/fingap: disable query refetching 2022-10-25 12:49:28 +03:00
Chika
4ae1212a65 process/agents: fix vscode apollo extension crash 2022-10-24 18:21:05 +03:00
Chika
b78d7002a2 packages: upgrade fetch libs 2022-10-24 12:31:18 +03:00
Chika
601e03da90 packages: upgrade radash 2022-10-19 13:49:31 +03:00
Chika
92badbba4b merge branch migration/payments-table 2022-10-17 16:01:02 +03:00
589 changed files with 54404 additions and 19115 deletions

2
.env
View File

@ -1,2 +0,0 @@
####### USERS ########
USERS_SUPER=["akalinina","vchikalkin"]

View File

@ -5,5 +5,8 @@ yarn.lock
package-lock.json
**/*.test.js
coverage
mocks
graphql
.eslintrc.js
**/*.config.js
**/scripts
**/package.json
turbo.json

11
.eslintrc.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
root: true,
settings: {
next: {
rootDir: ['apps/*/'],
},
react: {
version: 'detect',
},
},
};

View File

@ -1,86 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"next",
"prettier",
"airbnb",
"airbnb-typescript",
"plugin:@typescript-eslint/recommended",
"plugin:unicorn/recommended"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"parser": "@typescript-eslint/parser",
"plugins": ["react", "prettier", "@typescript-eslint", "unicorn", "testing-library"],
"rules": {
"linebreak-style": ["error", "windows"],
"comma-dangle": "off",
"@typescript-eslint/comma-dangle": ["off"],
"react/react-in-jsx-scope": "off",
"react/jsx-props-no-spreading": "off",
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
"react/forbid-prop-types": "off",
"react/require-default-props": [
"error",
{
"ignoreFunctionalComponents": true
}
],
"import/extensions": "off",
"object-curly-newline": [
"warn",
{
"ObjectExpression": "always",
"ObjectPattern": { "multiline": true },
"ImportDeclaration": "never",
"ExportDeclaration": { "multiline": true, "minProperties": 3 }
}
],
"lines-between-class-members": "off",
"@typescript-eslint/lines-between-class-members": ["off"],
"indent": "off",
"@typescript-eslint/indent": ["off"],
"react/jsx-no-bind": [
"error",
{
"ignoreDOMComponents": false,
"ignoreRefs": false,
"allowArrowFunctions": false,
"allowFunctions": true,
"allowBind": false
}
],
"newline-before-return": "warn",
"@typescript-eslint/consistent-type-imports": "error",
"react/prop-types": "off",
// Airbnb prefers forEach
"unicorn/no-array-for-each": "off",
"unicorn/prevent-abbreviations": "off",
"unicorn/no-null": "off",
"unicorn/prefer-node-protocol": "off",
"unicorn/no-array-reduce": "off",
"unicorn/prefer-module": "off",
"unicorn/text-encoding-identifier-case": "off",
"unicorn/filename-case": [
"error",
{
"case": "kebabCase",
"ignore": ["^.*.(jsx|tsx)$"]
}
],
"import/no-unresolved": "warn"
},
"overrides": [
// Only uses Testing Library lint rules in test files
{
"files": ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
"extends": ["plugin:testing-library/react"]
}
]
}

51
.gitignore vendored
View File

@ -1,19 +1,44 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
# Created by https://www.toptal.com/developers/gitignore/api/nextjs
# Edit at https://www.toptal.com/developers/gitignore?templates=nextjs
### NextJS ###
# dependencies
/node_modules
/.pnp
node_modules
.pnp
.pnp.js
# testing
/coverage
coverage
# next.js
/.next/
/out/
.next/
out/
# production
/build
build
# misc
.DS_Store
@ -33,3 +58,15 @@ yarn-error.log*
# typescript
*.tsbuildinfo
# End of https://www.toptal.com/developers/gitignore/api/nextjs
# Created by https://www.toptal.com/developers/gitignore/api/turbo
# Edit at https://www.toptal.com/developers/gitignore?templates=turbo
### Turbo ###
# Turborepo task cache
.turbo
# End of https://www.toptal.com/developers/gitignore/api/turbo
.pnpm

View File

@ -1,12 +0,0 @@
overwrite: true
schema: './graphql/crm.schema.graphql'
documents: '**/*.{graphql,js,ts,jsx,tsx}'
generates:
./graphql/crm.types.ts:
plugins:
- typescript
- typescript-operations
config:
onlyOperationTypes: true
useTypeImports: true
# exclude: './graphql/crm.schema.graphql'

View File

@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn precommit
npx lint-staged --concurrent false

View File

@ -1,3 +1,5 @@
.next
public
graphql
**/graphql/*.types.ts
**/graphql/*.schema.graphql
node_modules

View File

@ -4,7 +4,7 @@
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"insertPragma": false,
"jsxBracketSameLine": false,
"bracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 100,
"proseWrap": "preserve",

10
.vscode/launch.json vendored
View File

@ -9,9 +9,15 @@
},
{
"name": "Next.js: debug client-side",
"type": "pwa-chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/apps/web",
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack://_N_E/*": "${webRoot}/*"
},
"skipFiles": ["**/<node_internals>/**", "**/node_modules/**"]
},
{
"name": "Next.js: debug full stack",

18
.vscode/settings.json vendored
View File

@ -13,9 +13,19 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll.eslint": true,
"source.fixAll.format": true
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit",
"source.removeUnusedImports": "explicit"
},
"workbench.editor.labelFormat": "short"
"workbench.editor.labelFormat": "short",
"eslint.workingDirectories": [{ "directory": "apps/web", "changeProcessCWD": true }],
"eslint.validate": [
"javascript",
"javascriptreact",
"json",
"typescript",
"typescriptreact",
"yaml"
],
"eslint.lintTask.enable": true
}

View File

@ -1,13 +0,0 @@
/* eslint-disable object-curly-newline */
import type { FormTabRows } from '../../lib/render-rows';
export const id = 'create-kp';
export const title = 'Создание КП';
export const rows: FormTabRows = [
[['cbxPriceWithDiscount', 'cbxFullPriceWithDiscount', 'cbxCostIncrease']],
[['cbxInsurance', 'cbxRegistrationQuote', 'cbxTechnicalCardQuote']],
[['cbxNSIB', 'cbxQuoteRedemptionGraph', 'cbxShowFinGAP']],
[['tbxQuoteName', 'radioQuoteContactGender'], { gridTemplateColumns: '1fr 1fr' }],
[['btnCreateKP', 'linkDownloadKp'], { gridTemplateColumns: '1fr 1fr' }],
];

View File

@ -1,10 +0,0 @@
export type Risk = {
key: string;
riskId: string;
riskName: string;
calcType: number;
premiumPerc: number;
sum: number;
premium: number;
keys?: string[];
};

View File

@ -1,67 +0,0 @@
/* 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 { formatter, parser } from 'tools/number';
import { buildOptionComponent, buildValueComponent } from './builders';
import type * as Insurance from './types';
export const columns: ColumnsType<Insurance.RowValues> = [
{
key: 'policyType',
dataIndex: 'policyType',
title: 'Тип полиса',
},
{
key: 'insuranceCompany',
dataIndex: 'insuranceCompany',
title: 'Страховая компания',
width: 300,
render: (_, record) => {
const Component = buildOptionComponent(record.key, Select, 'insuranceCompany');
return <Component showSearch optionFilterProp="label" />;
},
},
{
key: 'insured',
dataIndex: 'insured',
title: 'Плательщик',
render: (_, record) => {
const Component = buildOptionComponent(record.key, Select, 'insured');
return <Component />;
},
},
{
key: 'insCost',
dataIndex: 'insCost',
title: 'Стоимость за 1-й период',
render: (_, record) => {
const Component = buildValueComponent(record.key, InputNumber, 'insCost');
return (
<Component
min={0}
max={MAX_INSURANCE}
step={1000}
precision={2}
parser={parser}
formatter={formatter}
addonAfter="₽"
/>
);
},
},
{
key: 'insTerm',
dataIndex: 'insTerm',
title: 'Срок страхования',
render: (_, record) => {
const Component = buildOptionComponent(record.key, Select, 'insTerm');
return <Component />;
},
},
];

View File

@ -1,28 +0,0 @@
/* 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];
}

View File

@ -1,20 +0,0 @@
import type { BaseOption, Status } from 'Elements/types';
export type Keys = 'osago' | 'kasko' | 'fingap';
export type RowValues = {
key: Keys;
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 Values = Exclude<keyof RowValues, 'key'>;
export type RowOptions = {
[ValueName in Values]?: BaseOption<RowValues[ValueName]>[];
};
export type RowStatuses = Record<Values, Status>;

View File

@ -1,12 +0,0 @@
import type { FormTabRows } from '../../lib/render-rows';
export const id = 'insurance';
export const title = 'Страхование';
export const rows: FormTabRows = [
[['tbxLeaseObjectYear', 'selectLeaseObjectUseFor', 'selectLegalClientRegion']],
[['selectEngineType', 'tbxInsFranchise', 'selectLegalClientTown']],
[['selectLeaseObjectCategory', 'tbxMileage', 'tbxINNForCalc']],
[['tbxLeaseObjectMotorPower', 'cbxWithTrailer', 'selectGPSBrand']],
[['tbxEngineVolume', 'cbxInsDecentral', 'selectGPSModel']],
];

View File

@ -1,12 +0,0 @@
import renderFormRows from '../../lib/render-rows';
import { id, rows, title } from './config';
function LeasingObject() {
return renderFormRows(rows);
}
export default {
id,
title,
Component: LeasingObject,
};

View File

@ -1,14 +0,0 @@
/* eslint-disable import/prefer-default-export */
import { observer } from 'mobx-react-lite';
import type { ComponentType } from 'react';
import { useRowStatus } from 'stores/tables/payments/hooks';
import { usePaymentValue } from './hooks';
export function buildValueComponent<T>(index: number, Component: ComponentType<T>) {
return observer((props: T) => {
const [value, setValue] = usePaymentValue(index);
const status = useRowStatus(index);
return <Component value={value} setValue={setValue} status={status} {...props} />;
});
}

View File

@ -1,31 +0,0 @@
/* eslint-disable import/prefer-default-export */
import type { ColumnsType } from 'antd/lib/table';
import InputNumber from 'Elements/InputNumber';
import { buildValueComponent } from './builders';
type Payment = {
key: number;
num: number;
paymentRelation: number;
};
export const columns: ColumnsType<Payment> = [
{
key: 'num',
dataIndex: 'num',
title: '#',
width: '7%',
render: (_value, payment) => payment.num + 1,
},
{
key: 'paymentRelation',
dataIndex: 'paymentRelation',
title: '% платежа',
render: (_value, payment) => {
const Component = buildValueComponent(payment.num, InputNumber);
return <Component min={0.01} max={100} step={1} precision={2} />;
},
},
];

View File

@ -1,28 +0,0 @@
/* eslint-disable import/prefer-default-export */
import { useEffect, useState } from 'react';
import { useRowValue } from 'stores/tables/payments/hooks';
import { useDebouncedCallback } from 'use-debounce';
export function usePaymentValue(index) {
const [storeValue, setStoreValue] = useRowValue(index);
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];
}

View File

@ -1,89 +0,0 @@
import Alert from 'Elements/Alert';
import Table from 'Elements/Table';
import { computed } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useStore } from 'stores/hooks';
import styled from 'styled-components';
import { Box, Flex } from 'UIKit/grid';
import { min } from 'UIKit/mq';
import { columns } from './config';
const Grid = styled(Flex)`
flex-direction: column;
`;
const TableWrapper = styled.div`
td > * {
margin: 0;
}
`;
const TablesGroupGrid = styled(Box)`
display: flex;
flex-direction: column;
gap: 10px;
${min('tablet')} {
display: grid;
grid-template-columns: repeat(5, 1fr);
}
`;
const Validation = observer(() => {
const store = useStore();
const { payments } = store.$tables;
const messages = payments.validation.getMessages();
if (messages?.length) {
return <Alert type="error" banner message={messages[0]} />;
}
return null;
});
const SPLIT_NUMBER = 12;
function TablePart({ num }) {
const store = useStore();
const { payments } = store.$tables;
const values = payments.values.slice(num * SPLIT_NUMBER, num * SPLIT_NUMBER + SPLIT_NUMBER);
const dataSource = values.map((value, index) => ({
key: index + num * SPLIT_NUMBER,
num: index + num * SPLIT_NUMBER,
paymentRelation: value,
}));
return (
<TableWrapper>
<Table size="small" columns={columns} dataSource={dataSource} pagination={false} />
</TableWrapper>
);
}
const TablesGroup = observer(() => {
const store = useStore();
const { payments } = store.$tables;
const valuesLength = computed(() => payments.values.length).get();
const tablesNumber = Math.ceil(valuesLength / SPLIT_NUMBER);
const tables = [];
for (let i = 0; i < tablesNumber; i += 1) {
tables.push(<TablePart key={i} num={i} />);
}
return <TablesGroupGrid>{tables}</TablesGroupGrid>;
});
export default function TablePayments() {
return (
<Grid>
<Validation />
<TablesGroup />
</Grid>
);
}

View File

@ -1,42 +0,0 @@
import { Box, Flex } from 'UIKit/grid';
import elementsRender from '../../config/elements-render';
import { elements, id, title } from './config';
import PaymentsTable from './PaymentsTable';
function Payments() {
const renderedElements = elements.map((elementName) => {
const render = elementsRender[elementName]?.render;
return render();
});
// eslint-disable-next-line operator-linebreak
const [radioGraphType, selectSeasonType, tbxParmentsDecreasePercent, selectHighSeasonStart] =
renderedElements;
return (
<Flex flexDirection="column">
<Box
sx={{
display: 'grid',
gridTemplateColumns: ['1fr', '1fr', '1fr 1fr'],
gap: '10px',
}}
>
{radioGraphType}
<Flex flexDirection="column">
{selectSeasonType}
{tbxParmentsDecreasePercent}
{selectHighSeasonStart}
</Flex>
</Box>
<PaymentsTable />
</Flex>
);
}
export default {
id,
title,
Component: Payments,
};

View File

@ -1,20 +0,0 @@
/* eslint-disable import/prefer-default-export */
/* eslint-disable object-curly-newline */
import type { FormTabRows } from '../lib/render-rows';
const defaultRowStyle = { gridTemplateColumns: '1fr' };
export const rows: FormTabRows = [
{ title: 'Выбор Интереса/ЛС' },
[['selectLead'], defaultRowStyle],
[['selectOpportunity'], defaultRowStyle],
[['cbxRecalcWithRevision'], defaultRowStyle],
[['selectQuote'], defaultRowStyle],
[['btnCalculate'], defaultRowStyle],
{ title: 'Параметры расчета' },
[['labelIrrInfo'], defaultRowStyle],
[['radioCalcType'], defaultRowStyle],
[['tbxIRR_Perc'], defaultRowStyle],
[['tbxTotalPayments'], defaultRowStyle],
];

View File

@ -1,21 +0,0 @@
import Background from 'Elements/layout/Background';
import styled from 'styled-components';
import { min } from 'UIKit/mq';
import renderFormRows from '../lib/render-rows';
import { rows } from './config';
const Wrapper = styled(Background)`
padding: 4px 10px;
${min('tablet')} {
min-height: 790px;
}
${min('laptop')} {
padding: 4px 18px 10px;
}
`;
export default function Settings() {
return <Wrapper>{renderFormRows(rows)}</Wrapper>;
}

View File

@ -1,32 +0,0 @@
import { gql, useQuery } from '@apollo/client';
import type * as CRMTypes from 'graphql/crm.types';
import { observer } from 'mobx-react-lite';
import { useStore } from 'stores/hooks';
const QUERY_GET_CURRENCY_SYMBOL = gql`
query GetCurrencySymbol($currencyid: Uuid!) {
transactioncurrency(transactioncurrencyid: $currencyid) {
currencysymbol
}
}
`;
const CurrencyAddon = observer(() => {
const { $calculation } = useStore();
const currencyid = $calculation.$values.getValue('supplierCurrency');
const { data } = useQuery<
CRMTypes.GetCurrencySymbolQuery,
CRMTypes.GetCurrencySymbolQueryVariables
>(QUERY_GET_CURRENCY_SYMBOL, {
variables: {
currencyid,
},
skip: !currencyid,
});
return <span>{data?.transactioncurrency?.currencysymbol}</span>;
});
export default <CurrencyAddon />;

View File

@ -1,27 +0,0 @@
/* eslint-disable react/jsx-no-bind */
import { observer } from 'mobx-react-lite';
import type { ComponentType } from 'react';
import { useStatus } from 'stores/calculation/statuses/hooks';
import type { Elements } from '../config/map/actions';
type BuilderProps = {
elementName: Elements;
valueName: string;
};
export default function buildAction<T>(
Component: ComponentType<T>,
{ elementName, valueName: actionName }: BuilderProps
) {
return observer((props: T) => {
const status = useStatus(elementName);
return (
<Component
status={status}
action={() => import(`process/${actionName}`).then((m) => m.default())}
{...props}
/>
);
});
}

View File

@ -1,37 +0,0 @@
import { observer } from 'mobx-react-lite';
import type { ComponentType } from 'react';
import { useOptions } from 'stores/calculation/options/hooks';
import { useStatus } from 'stores/calculation/statuses/hooks';
import { useValidation } from 'stores/calculation/validation/hooks';
import type { Values } from 'stores/calculation/values/types';
import type { Elements } from '../config/map/values';
import { useStoreValue } from './hooks';
type BuilderProps = {
elementName: Elements;
valueName: Values;
};
export default function buildOptions<T>(
Component: ComponentType<T>,
{ elementName, valueName }: BuilderProps
) {
return observer((props: T) => {
const [value, setValue] = useStoreValue(valueName);
const status = useStatus(elementName);
const { isValid, help } = useValidation(elementName);
const options = useOptions(elementName);
return (
<Component
value={value}
setValue={setValue}
options={options}
status={status}
isValid={isValid}
help={help}
{...props}
/>
);
});
}

View File

@ -1,23 +0,0 @@
import { observer } from 'mobx-react-lite';
import type { ComponentType } from 'react';
import { useStatus } from 'stores/calculation/statuses/hooks';
import { useValue } from 'stores/calculation/values/hooks';
import type { Values } from 'stores/calculation/values/types';
import type { Elements } from '../config/map/values';
type BuilderProps = {
elementName: Elements;
valueName: Values;
};
export default function buildReadonly<T>(
Component: ComponentType<T>,
{ elementName, valueName }: BuilderProps
) {
return observer((props: T) => {
const [value] = useValue(valueName);
const status = useStatus(elementName);
return <Component value={value} status={status} readOnly {...props} />;
});
}

View File

@ -1,153 +0,0 @@
import buildAction from '../builders/build-action';
import buildOptions from '../builders/build-options';
import buildReadonly from '../builders/build-readonly';
import buildValue from '../builders/build-value';
import type { Elements as ActionElements } from './map/actions';
import type { Elements as ValuesElements } from './map/values';
function wrapElementsBuilders<C, T extends Record<ValuesElements | ActionElements, C>>(arg: T) {
return arg;
}
const builders = wrapElementsBuilders({
cbxRecalcWithRevision: buildValue,
tbxLeaseObjectPrice: buildValue,
tbxLeaseObjectPriceWthtVAT: buildValue,
tbxVATInLeaseObjectPrice: buildValue,
tbxEngineHours: buildValue,
tbxSupplierDiscountRub: buildValue,
tbxSupplierDiscountPerc: buildValue,
tbxLeasingPeriod: buildValue,
tbxFirstPaymentPerc: buildValue,
tbxFirstPaymentRub: buildValue,
tbxLastPaymentPerc: buildValue,
tbxLastPaymentRub: buildValue,
selectImportProgram: buildOptions,
tbxImportProgramSum: buildReadonly,
tbxAddEquipmentPrice: buildValue,
tbxRedemptionPaymentSum: buildValue,
tbxParmentsDecreasePercent: buildValue,
tbxComissionPerc: buildValue,
tbxComissionRub: buildValue,
tbxSaleBonus: buildValue,
tbxIRR_Perc: buildValue,
tbxLeaseObjectCount: buildValue,
cbxWithTrailer: buildValue,
cbxLeaseObjectUsed: buildValue,
tbxMaxMass: buildValue,
tbxCountSeats: buildValue,
tbxMaxSpeed: buildValue,
tbxLeaseObjectYear: buildValue,
tbxLeaseObjectMotorPower: buildValue,
tbxEngineVolume: buildValue,
tbxDealerRewardSumm: buildValue,
tbxDealerBrokerRewardSumm: buildValue,
tbxIndAgentRewardSumm: buildValue,
tbxCalcDoubleAgentRewardSumm: buildValue,
tbxCalcBrokerRewardSum: buildValue,
tbxFinDepartmentRewardSumm: buildValue,
cbxInsDecentral: buildValue,
tbxInsFranchise: buildValue,
cbxInsUnlimitDrivers: buildValue,
tbxInsAgeDrivers: buildValue,
tbxInsExpDrivers: buildValue,
tbxINNForCalc: buildValue,
cbxLastPaymentRedemption: buildValue,
cbxPriceWithDiscount: buildValue,
cbxFullPriceWithDiscount: buildValue,
cbxCostIncrease: buildValue,
cbxInsurance: buildValue,
cbxRegistrationQuote: buildValue,
cbxTechnicalCardQuote: buildValue,
cbxNSIB: buildValue,
tbxQuoteName: buildValue,
cbxQuoteRedemptionGraph: buildValue,
cbxShowFinGAP: buildValue,
tbxCreditRate: buildValue,
tbxMaxPriceChange: buildValue,
tbxImporterRewardPerc: buildValue,
tbxImporterRewardRub: buildValue,
cbxDisableChecks: buildValue,
tbxMileage: buildValue,
tbxTotalPayments: buildValue,
tbxVehicleTaxInYear: buildValue,
tbxVehicleTaxInLeasingPeriod: buildValue,
tbxMinPriceChange: buildValue,
selectProduct: buildOptions,
selectClientRisk: buildOptions,
selectClientType: buildOptions,
selectSupplierCurrency: buildOptions,
selectSeasonType: buildOptions,
selectHighSeasonStart: buildOptions,
selectLeaseObjectType: buildOptions,
selectBrand: buildOptions,
selectModel: buildOptions,
selectConfiguration: buildOptions,
selectLeaseObjectUseFor: buildOptions,
selectLeaseObjectCategory: buildOptions,
selectEngineType: buildOptions,
selectDealer: buildOptions,
selectDealerPerson: buildOptions,
selectDealerRewardCondition: buildOptions,
selectDealerBroker: buildOptions,
selectDealerBrokerRewardCondition: buildOptions,
selectIndAgent: buildOptions,
selectIndAgentRewardCondition: buildOptions,
selectCalcDoubleAgent: buildOptions,
selectCalcDoubleAgentRewardCondition: buildOptions,
selectCalcBroker: buildOptions,
selectCalcBrokerRewardCondition: buildOptions,
selectCalcFinDepartment: buildOptions,
selectFinDepartmentRewardCondtion: buildOptions,
selectGPSBrand: buildOptions,
selectGPSModel: buildOptions,
selectRegionRegistration: buildOptions,
selectTownRegistration: buildOptions,
selectRegistration: buildOptions,
selectInsNSIB: buildOptions,
selectTracker: buildOptions,
selectTelematic: buildOptions,
selectTechnicalCard: buildOptions,
selectTarif: buildOptions,
selectRate: buildOptions,
selectLead: buildOptions,
selectOpportunity: buildOptions,
selectQuote: buildOptions,
selectObjectRegionRegistration: buildOptions,
selectObjectCategoryTax: buildOptions,
selectObjectTypeTax: buildOptions,
selectLegalClientRegion: buildOptions,
selectLegalClientTown: buildOptions,
selectSubsidy: buildOptions,
selectFuelCard: buildOptions,
radioBalanceHolder: buildOptions,
radioLastPaymentRule: buildOptions,
radioGraphType: buildOptions,
radioDeliveryTime: buildOptions,
radioInsKaskoType: buildOptions,
radioInfuranceOPF: buildOptions,
selectRequirementTelematic: buildOptions,
radioQuoteContactGender: buildOptions,
radioCalcType: buildOptions,
radioObjectRegistration: buildOptions,
radioTypePTS: buildOptions,
tbxBonusCoefficient: buildValue,
labelLeaseObjectRisk: buildReadonly,
tbxInsKaskoPriceLeasePeriod: buildReadonly,
labelIrrInfo: buildReadonly,
labelRegistrationDescription: buildReadonly,
labelDepreciationGroup: buildReadonly,
tbxSubsidySum: buildReadonly,
btnCreateKP: buildAction,
btnCalculate: buildAction,
linkDownloadKp: buildReadonly,
linkLeadUrl: buildReadonly,
linkOpportunityUrl: buildReadonly,
linkQuoteUrl: buildReadonly,
});
export default builders;

View File

@ -1,168 +0,0 @@
import Button from 'Elements/Button';
import Checkbox from 'Elements/Checkbox';
import Input from 'Elements/Input';
import InputNumber from 'Elements/InputNumber';
import Link from 'Elements/Link';
import Radio from 'Elements/Radio';
import Segmented from 'Elements/Segmented';
import Select from 'Elements/Select';
import Switch from 'Elements/Switch';
import Text from 'Elements/Text';
import type { ComponentProps } from 'react';
import type { Elements as ActionElements } from './map/actions';
import type { Elements as ValuesElements } from './map/values';
function wrapComponentsMap<C, T extends Record<ValuesElements | ActionElements, C>>(arg: T) {
return arg;
}
const components = wrapComponentsMap({
selectProduct: Select,
selectClientRisk: Select,
selectClientType: Select,
selectSupplierCurrency: Select,
tbxLeaseObjectPrice: InputNumber,
tbxLeaseObjectPriceWthtVAT: InputNumber,
tbxVATInLeaseObjectPrice: InputNumber,
tbxSupplierDiscountRub: InputNumber,
tbxSupplierDiscountPerc: InputNumber,
radioBalanceHolder: Radio,
tbxSaleBonus: InputNumber,
tbxFirstPaymentPerc: InputNumber,
tbxFirstPaymentRub: InputNumber,
radioLastPaymentRule: Segmented,
tbxLastPaymentPerc: InputNumber,
tbxLastPaymentRub: InputNumber,
selectImportProgram: Select,
tbxImportProgramSum: InputNumber,
tbxAddEquipmentPrice: InputNumber,
tbxRedemptionPaymentSum: InputNumber,
tbxLeasingPeriod: InputNumber,
radioGraphType: Radio,
tbxParmentsDecreasePercent: InputNumber,
selectSeasonType: Select,
selectHighSeasonStart: Select,
tbxComissionPerc: InputNumber,
tbxComissionRub: InputNumber,
selectLeaseObjectType: Select,
selectBrand: Select,
selectModel: Select,
selectConfiguration: Select,
cbxLeaseObjectUsed: Checkbox,
radioDeliveryTime: Segmented,
tbxLeaseObjectCount: InputNumber,
selectLeaseObjectUseFor: Select,
tbxLeaseObjectYear: InputNumber,
selectLeaseObjectCategory: Select,
selectEngineType: Select,
tbxLeaseObjectMotorPower: InputNumber,
tbxEngineVolume: InputNumber,
tbxMaxMass: InputNumber,
tbxCountSeats: InputNumber,
tbxMaxSpeed: InputNumber,
cbxWithTrailer: Checkbox,
selectDealer: Select,
selectDealerPerson: Select,
selectDealerRewardCondition: Select,
tbxDealerRewardSumm: InputNumber,
selectDealerBroker: Select,
selectDealerBrokerRewardCondition: Select,
tbxDealerBrokerRewardSumm: InputNumber,
selectIndAgent: Select,
selectIndAgentRewardCondition: Select,
tbxIndAgentRewardSumm: InputNumber,
selectCalcDoubleAgent: Select,
selectCalcDoubleAgentRewardCondition: Select,
tbxCalcDoubleAgentRewardSumm: InputNumber,
selectCalcBroker: Select,
selectCalcBrokerRewardCondition: Select,
tbxCalcBrokerRewardSum: InputNumber,
selectCalcFinDepartment: Select,
selectFinDepartmentRewardCondtion: Select,
tbxFinDepartmentRewardSumm: InputNumber,
cbxInsDecentral: Switch,
radioInsKaskoType: Radio,
tbxInsFranchise: InputNumber,
cbxInsUnlimitDrivers: Switch,
tbxInsAgeDrivers: InputNumber,
tbxInsExpDrivers: InputNumber,
tbxINNForCalc: InputNumber,
selectGPSBrand: Select,
selectGPSModel: Select,
selectRegionRegistration: Select,
selectTownRegistration: Select,
radioInfuranceOPF: Radio,
selectRegistration: Select,
selectInsNSIB: Select,
selectRequirementTelematic: Select,
selectTracker: Select,
selectTelematic: Select,
selectTechnicalCard: Select,
cbxLastPaymentRedemption: Switch,
cbxPriceWithDiscount: Switch,
cbxFullPriceWithDiscount: Switch,
cbxCostIncrease: Switch,
cbxInsurance: Switch,
cbxRegistrationQuote: Switch,
cbxTechnicalCardQuote: Switch,
cbxNSIB: Switch,
cbxQuoteRedemptionGraph: Switch,
cbxShowFinGAP: Switch,
tbxQuoteName: Input,
radioQuoteContactGender: Radio,
cbxDisableChecks: Switch,
selectTarif: Select,
tbxCreditRate: InputNumber,
selectRate: Select,
tbxMaxPriceChange: InputNumber,
tbxImporterRewardPerc: InputNumber,
tbxImporterRewardRub: InputNumber,
selectLead: Select,
selectOpportunity: Select,
selectQuote: Select,
cbxRecalcWithRevision: Checkbox,
tbxIRR_Perc: InputNumber,
tbxMileage: InputNumber,
tbxEngineHours: InputNumber,
radioCalcType: Segmented,
tbxTotalPayments: InputNumber,
radioObjectRegistration: Radio,
selectObjectRegionRegistration: Select,
tbxVehicleTaxInYear: InputNumber,
tbxVehicleTaxInLeasingPeriod: InputNumber,
selectObjectCategoryTax: Select,
selectObjectTypeTax: Select,
radioTypePTS: Radio,
selectLegalClientRegion: Select,
selectLegalClientTown: Select,
selectSubsidy: Select,
selectFuelCard: Select,
tbxMinPriceChange: InputNumber,
tbxBonusCoefficient: InputNumber,
/** Readonly Elements */
labelLeaseObjectRisk: Text,
tbxInsKaskoPriceLeasePeriod: InputNumber,
labelIrrInfo: Text,
labelRegistrationDescription: Text,
labelDepreciationGroup: Text,
tbxSubsidySum: InputNumber,
/** Button Elements */
btnCreateKP: Button,
btnCalculate: Button,
/** Link Elements */
linkDownloadKp: Link,
linkLeadUrl: Link,
linkOpportunityUrl: Link,
linkQuoteUrl: Link,
});
export default components;
type ComponentsTypes = typeof components;
export type ElementsProps = {
[Component in keyof ComponentsTypes]: ComponentProps<ComponentsTypes[Component]>;
};

View File

@ -1,167 +0,0 @@
/* eslint-disable object-curly-newline */
import { Container, Head } from 'Components/Layout/Element';
import Link from 'Elements/Link';
import Tooltip from 'Elements/Tooltip';
import type { ComponentProps } from 'react';
import buildReadonly from '../../builders/build-readonly';
import builders from '../elements-builders';
import components from '../elements-components';
import elementsProps from '../elements-props';
import titles from '../elements-titles';
import map from '../map';
import type { RenderProps } from './types';
const defaultLinkProps: ComponentProps<typeof Link> = {
text: 'Открыть в CRM',
type: 'link',
};
const overrideRender: Partial<Record<keyof typeof map, RenderProps>> = {
selectLead: {
render: () => {
const elementName = 'selectLead';
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,
valueName,
});
const LinkComponent = buildReadonly(Link, {
elementName: 'linkLeadUrl',
valueName: 'leadUrl',
});
return (
<Container key={elementName}>
<Head
htmlFor={elementName}
title={title}
addon={<LinkComponent {...defaultLinkProps} />}
/>
<Element {...props} id={elementName} />
</Container>
);
},
},
selectOpportunity: {
render: () => {
const elementName = 'selectOpportunity';
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,
valueName,
});
const LinkComponent = buildReadonly(Link, {
elementName: 'linkOpportunityUrl',
valueName: 'opportunityUrl',
});
return (
<Container key={elementName}>
<Head
htmlFor={elementName}
title={title}
addon={<LinkComponent {...defaultLinkProps} />}
/>
<Element {...props} id={elementName} />
</Container>
);
},
},
selectQuote: {
render: () => {
const elementName = 'selectQuote';
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,
valueName,
});
const LinkComponent = buildReadonly(Link, {
elementName: 'linkQuoteUrl',
valueName: 'quoteUrl',
});
return (
<Container key={elementName}>
<Head
htmlFor={elementName}
title={title}
addon={<LinkComponent {...defaultLinkProps} />}
/>
<Element {...props} id={elementName} />
</Container>
);
},
},
tbxVehicleTaxInYear: {
render: () => {
const elementName = 'tbxVehicleTaxInYear';
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,
valueName,
});
return (
<Tooltip title="Без учета налога на роскошь" placement="topLeft">
<Container>
<Head htmlFor={elementName} title={title} />
<Element {...props} id={elementName} />
</Container>
</Tooltip>
);
},
},
selectHighSeasonStart: {
render: () => {
const elementName = 'selectHighSeasonStart';
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,
valueName,
});
return (
<Tooltip title="С какого платежа начинается полный высокий сезон" placement="topLeft">
<Container>
<Head htmlFor={elementName} title={title} />
<Element {...props} id={elementName} />
</Container>
</Tooltip>
);
},
},
};
export default overrideRender;

View File

@ -1,2 +0,0 @@
export { default as Form } from './Form';
export { default as Settings } from './Settings';

View File

@ -1,41 +0,0 @@
import Image from 'next/image';
import logo from 'public/assets/images/logo-primary.svg';
import styled from 'styled-components';
import { Flex } from 'UIKit/grid';
import { min } from 'UIKit/mq';
const ImageWrapper = styled.div`
width: 100px;
${min('laptop')} {
width: 135px;
}
img {
filter: brightness(0) invert(1);
}
`;
const LogoText = styled.h3`
margin: 0;
text-transform: uppercase;
color: #fff;
font-size: 0.85rem;
font-family: 'Montserrat';
font-weight: 500;
${min('laptop')} {
font-size: 1.2rem;
}
`;
function Logo() {
return (
<Flex flexDirection="column" alignItems="flex-start" justifyContent="space-between">
<ImageWrapper>
<Image alt="logo" src={logo} layout="responsive" objectFit="contain" />
</ImageWrapper>
<LogoText>Лизинговый Калькулятор</LogoText>
</Flex>
);
}
export default Logo;

View File

@ -1,13 +0,0 @@
/* eslint-disable react/prop-types */
/* eslint-disable import/no-unresolved */
import { Flex } from 'UIKit/grid';
import Header from './Header';
export default function Layout({ children }) {
return (
<Flex flexDirection="column">
<Header />
<main>{children}</main>
</Flex>
);
}

View File

@ -1,40 +0,0 @@
/* eslint-disable import/prefer-default-export */
import type { ColumnsType } from 'antd/lib/table';
import type { Payment } from './types';
export const columns: ColumnsType<Payment> = [
{
key: 'num',
dataIndex: 'num',
title: '#',
width: '10%',
},
{
key: 'paymentSum',
dataIndex: 'paymentSum',
title: 'Сумма платежа',
render: Intl.NumberFormat('ru', {
style: 'currency',
currency: 'RUB',
}).format,
},
{
key: 'ndsCompensation',
dataIndex: 'ndsCompensation',
title: 'НДС к возмещению',
render: Intl.NumberFormat('ru', {
style: 'currency',
currency: 'RUB',
}).format,
},
{
key: 'redemptionAmount',
dataIndex: 'redemptionAmount',
title: 'Сумма досрочного выкупа',
render: Intl.NumberFormat('ru', {
style: 'currency',
currency: 'RUB',
}).format,
},
];

View File

@ -1,32 +0,0 @@
import { MAX_LEASING_PERIOD } from 'constants/values';
import Table from 'Elements/Table';
import { toJS } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useStore } from 'stores/hooks';
import { columns } from './config';
const PaymentsTable = observer(() => {
const { $results } = useStore();
return (
<Table
columns={columns}
dataSource={toJS($results.payments)}
size="small"
pagination={{
defaultPageSize: 12,
pageSizeOptions: [12, MAX_LEASING_PERIOD],
responsive: true,
}}
scroll={{
x: true,
}}
/>
);
});
export default {
id: 'payments-table',
title: 'Таблица платежей',
Component: PaymentsTable,
};

View File

@ -1,7 +0,0 @@
export type Payment = {
key: string;
num: number;
paymentSum: string;
ndsCompensation: string;
redemptionAmount: string;
};

View File

@ -1,76 +0,0 @@
/* eslint-disable object-curly-newline */
import type { Values } from 'stores/results/types';
export const id = 'output';
export const title = 'Результаты';
export const titles: Record<Values, string> = {
resultTotalGraphwithNDS: 'Итого по графику, с НДС',
resultPlPrice: 'Стоимость ПЛ с НДС',
resultPriceUpPr: 'Удорожание, год',
resultIRRGraphPerc: 'IRR по графику клиента, %',
resultIRRNominalPerc: 'IRR (номинал), %',
resultInsKasko: 'КАСКО, НС, ДГО в графике',
resultInsOsago: 'ОСАГО в графике',
resultDopProdSum: 'Общая сумма доп.продуктов',
resultFirstPayment: 'Первый платеж',
resultLastPayment: 'Последний платеж',
resultTerm: 'Срок, мес.',
resultAB_FL: 'АВ ФЛ, без НДФЛ.',
resultAB_UL: 'АВ ЮЛ, с НДС.',
resultBonusMPL: 'Бонус МПЛ за лизинг, без НДФЛ',
resultDopMPLLeasing: 'Доп.бонус МПЛ за лизинг, без НДФЛ',
resultBonusDopProd: 'Бонус МПЛ за доп.продукты, без НДФЛ',
resultBonusSafeFinance: 'Бонус за Safe Finance без НДФЛ',
resultFirstPaymentRiskPolicy: 'Первый платеж по риск политике, %',
};
const moneyFormatters = Object.fromEntries(
(
[
'resultTotalGraphwithNDS',
'resultPlPrice',
'resultInsKasko',
'resultInsOsago',
'resultDopProdSum',
'resultFirstPayment',
'resultLastPayment',
'resultAB_FL',
'resultAB_UL',
'resultBonusMPL',
'resultDopMPLLeasing',
'resultBonusDopProd',
'tbxSubsidySum',
'resultBonusSafeFinance',
'resultPriceUpPr',
] as Values[]
).map((a) => [
a,
// prettier-ignore
Intl.NumberFormat('ru', {
style: 'currency',
currency: 'RUB',
}).format,
])
);
const percentFormatters = Object.fromEntries(
(['resultIRRGraphPerc', 'resultIRRNominalPerc', 'resultFirstPaymentRiskPolicy'] as Values[]).map(
(a) => [
a,
// prettier-ignore
Intl.NumberFormat('ru', {
style: 'percent',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format,
]
)
);
const defaultFormatters = {
resultTerm: Intl.NumberFormat('ru').format,
};
export const formatters = Object.assign(moneyFormatters, percentFormatters, defaultFormatters);

View File

@ -1,46 +0,0 @@
import { Container, Head } from 'Components/Layout/Element';
import Text from 'Elements/Text';
import { toJS } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useStore } from 'stores/hooks';
import styled from 'styled-components';
import { Box } from 'UIKit/grid';
import { min } from 'UIKit/mq';
import { formatters, id, title, titles } from './config';
const Grid = styled(Box)`
display: grid;
grid-template-columns: 1fr;
${min('tablet')} {
grid-template-columns: 1fr 1fr;
}
`;
const Results = observer(() => {
const { $results } = useStore();
const values = toJS($results.values);
return (
<Grid>
{Object.keys(values).map((valueName) => {
const formatter = formatters[valueName];
const storeValue = values[valueName];
const value = formatter(storeValue);
return (
<Container key={valueName}>
<Head title={titles[valueName]} />
<Text>{value}</Text>
</Container>
);
})}
</Grid>
);
});
export default {
id,
title,
Component: Results,
};

View File

@ -1,77 +0,0 @@
import Alert from 'Elements/Alert';
import { observer } from 'mobx-react-lite';
import { useStore } from 'stores/hooks';
import styled from 'styled-components';
import { Box, Flex } from 'UIKit/grid';
import titles from '../Calculation/config/elements-titles';
const Bold = styled.span`
font-weight: bold;
`;
function Message(title, text) {
return (
<>
<Bold>{title}</Bold>
{': '}
{text}
</>
);
}
function getElementsErrors($calculation) {
const { elementsErrors } = $calculation.$validation;
const errors = Object.keys(elementsErrors).map((elementName) => {
const elementErrors = elementsErrors[elementName];
const elementTitle = titles[elementName];
return elementErrors.map((error) => (
<Alert key={error.name} type="error" showIcon message={Message(elementTitle, error.text)} />
));
});
return errors;
}
function getPaymentsTableErrors($tables) {
const { payments } = $tables;
const messages = payments.validation.getMessages();
const title = payments.validation.params.err_title;
return messages.map((text) => <Alert type="error" showIcon message={Message(title, text)} />);
}
function getInsuranceTableErrors($tables) {
const { insurance } = $tables;
const messages = insurance.validation.getMessages();
return messages.map((message) => <Alert type="error" showIcon message={message} />);
}
const Errors = observer(() => {
const { $calculation, $tables } = useStore();
const elementsErrors = getElementsErrors($calculation);
const paymentsErrors = getPaymentsTableErrors($tables);
const insuranceErrors = getInsuranceTableErrors($tables);
const errors = [...elementsErrors, ...paymentsErrors, ...insuranceErrors];
if (errors.length === 0) return <Alert type="success" showIcon message="Ошибок нет 🙂" />;
return <Flex flexDirection="column">{errors}</Flex>;
});
function Validation() {
return (
<Box>
<Errors />
</Box>
);
}
export default {
id: 'validation',
title: 'Ошибки',
Component: Validation,
};

View File

@ -1,35 +0,0 @@
import Background from 'Elements/layout/Background';
import Tabs from 'Elements/layout/Tabs';
import styled from 'styled-components';
import { min } from 'UIKit/mq';
import PaymentsTable from './PaymentsTable';
import Results from './Results';
import Validation from './Validation';
const outputTabs = [PaymentsTable, Results, Validation];
const Wrapper = styled(Background)`
padding: 4px 10px;
min-height: 200px;
${min('laptop')} {
padding: 4px 18px;
min-height: 641px;
}
`;
function Output() {
return (
<Wrapper>
<Tabs>
{outputTabs.map(({ id, title, Component }) => (
<Tabs.TabPane tab={title} key={id}>
<Component />
</Tabs.TabPane>
))}
</Tabs>
</Wrapper>
);
}
export default Output;

View File

@ -1,69 +0,0 @@
# Install dependencies only when needed
FROM node:16-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
# If using npm with a `package-lock.json` comment out above and use below instead
# COPY package.json package-lock.json ./
# RUN npm ci
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_BASE_PATH
ARG NEXT_PUBLIC_COLOR_PRIMARY
ARG NEXT_PUBLIC_COLOR_SECONDARY
ARG NEXT_PUBLIC_COLOR_TERTIARTY
ARG NEXT_PUBLIC_FAVICON
ARG NEXT_TELEMETRY_DISABLED
ARG NEXT_PUBLIC_URL_CRM_GRAPHQL_PROXY
ARG NEXT_PUBLIC_URL_CRM_GRAPHQL_DIRECT
ARG NEXT_PUBLIC_URL_GET_USER_PROXY
ARG NEXT_PUBLIC_URL_GET_USER_DIRECT
ARG NEXT_PUBLIC_URL_CORE_FINGAP_PROXY
ARG NEXT_PUBLIC_URL_CORE_FINGAP_DIRECT
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# If using npm comment out above and use below instead
# RUN npm run build
# Production image, copy all the files and run next
FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# You only need to copy next.config.js if you are NOT using the default configuration
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]

View File

@ -1,3 +0,0 @@
/* eslint-disable unicorn/filename-case */
/* eslint-disable no-restricted-exports */
export { Alert as default } from 'antd';

View File

@ -1,34 +0,0 @@
import { Button as AntButton } from 'antd';
import type { BaseButtonProps } from 'antd/lib/button/button';
import type { FC } from 'react';
import { useThrottledCallback } from 'use-debounce';
import type { BaseElementProps } from './types';
type ElementProps = {
action: () => void;
text: string;
};
type ButtonProps = BaseButtonProps & Pick<ElementProps, 'text'>;
export default (function Button({
status,
action,
text,
...props
}: BaseElementProps<never> & ElementProps) {
const throttledAction = useThrottledCallback(action, 1200, {
trailing: false,
});
return (
<AntButton
disabled={status === 'Disabled'}
loading={status === 'Loading'}
onClick={throttledAction}
{...props}
>
{text}
</AntButton>
);
} as FC<ButtonProps>);

View File

@ -1,40 +0,0 @@
import type { CheckboxProps as AntCheckboxProps } from 'antd';
import { Checkbox as AntCheckbox, Form } from 'antd';
import type { CheckboxChangeEvent } from 'antd/lib/checkbox';
import type { FC } from 'react';
import type { BaseElementProps } from './types';
const { Item: FormItem } = Form;
type ElementProps = {
text: string;
};
type CheckboxProps = AntCheckboxProps & ElementProps;
export default (function Checkbox({
value,
setValue,
status,
isValid,
help,
text,
...props
}: BaseElementProps<boolean> & ElementProps) {
function handleChange(e: CheckboxChangeEvent) {
setValue(e.target.checked);
}
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntCheckbox
checked={value}
onChange={handleChange}
disabled={status === 'Disabled'}
{...props}
>
{text}
</AntCheckbox>
</FormItem>
);
} as FC<CheckboxProps>);

View File

@ -1,25 +0,0 @@
import type { InputProps } from 'antd';
import { Form, Input as AntInput } from 'antd';
import type { ChangeEvent, FC } from 'react';
import type { BaseElementProps } from './types';
const { Item: FormItem } = Form;
export default (function Input({
value,
setValue,
status,
isValid,
help,
...props
}: BaseElementProps<string>) {
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value);
}
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntInput value={value} onChange={handleChange} disabled={status === 'Disabled'} {...props} />
</FormItem>
);
} as FC<InputProps>);

View File

@ -1,29 +0,0 @@
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;
type InputNumberProps = AntInputNumberProps<number>;
export default (function InputNumber({
setValue,
status,
isValid,
help,
...props
}: BaseElementProps<number>) {
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntInputNumber
onChange={setValue}
disabled={status === 'Disabled'}
style={{
width: '100%',
}}
{...props}
/>
</FormItem>
);
} as FC<InputNumberProps>);

View File

@ -1,30 +0,0 @@
import { Button as AntButton } from 'antd';
import type { BaseButtonProps } from 'antd/lib/button/button';
import type { FC } from 'react';
import type { BaseElementProps } from './types';
type ElementProps = {
text: string;
};
type LinkProps = BaseButtonProps & ElementProps;
export default (function Link({
value,
status,
text,
...props
}: BaseElementProps<string> & ElementProps) {
return (
<AntButton
rel="noopener"
target="_blank"
href={value}
disabled={status === 'Disabled' || !value}
loading={status === 'Loading'}
{...props}
>
{text}
</AntButton>
);
} as FC<LinkProps>);

View File

@ -1,49 +0,0 @@
import type { RadioChangeEvent, RadioGroupProps, SpaceProps } from 'antd';
import { Form, Radio as AntRadio, Space } from 'antd';
import type { FC } from 'react';
import type { BaseElementProps, BaseOption } from './types';
const { Item: FormItem } = Form;
type ElementProps = BaseElementProps<string | null> & {
options: BaseOption[];
spaceProps?: SpaceProps;
};
type RadioProps = RadioGroupProps & {
spaceProps?: SpaceProps;
};
export default (function Radio({
value = null,
setValue,
options,
status,
isValid,
help,
spaceProps,
...props
}: ElementProps) {
function handleChange(e: RadioChangeEvent) {
setValue(e.target.value);
}
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntRadio.Group
value={value}
onChange={handleChange}
disabled={status === 'Disabled'}
{...props}
>
<Space {...spaceProps}>
{options.map((option) => (
<AntRadio key={option.value} value={option.value}>
{option.label}
</AntRadio>
))}
</Space>
</AntRadio.Group>
</FormItem>
);
} as FC<RadioProps>);

View File

@ -1,32 +0,0 @@
import type { SegmentedProps } from 'antd';
import { Form, Segmented as AntSegmented } from 'antd';
import type { FC } from 'react';
import type { BaseElementProps, BaseOption } from './types';
const { Item: FormItem } = Form;
type ElementProps = BaseElementProps<string | number> & {
options: BaseOption[];
};
export default (function Segmented({
value,
setValue,
options,
status,
isValid,
help,
...props
}: ElementProps) {
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntSegmented
value={value}
onChange={setValue}
disabled={status === 'Disabled'}
options={options}
{...props}
/>
</FormItem>
);
} as FC<Partial<SegmentedProps>>);

View File

@ -1,46 +0,0 @@
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';
const { Item: FormItem } = Form;
type ElementProps = {
options: BaseOption[];
};
export default (function Select({
value = null,
setValue,
options = [],
status,
isValid,
help,
...props
}: BaseElementProps<string | null> & ElementProps) {
const optionsWithNull = useMemo(
() => [
{
label: 'Не выбрано',
value: null,
},
...options,
],
[options]
);
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntSelect
value={value}
onChange={setValue}
disabled={status === 'Disabled'}
loading={status === 'Loading'}
optionFilterProp="children"
options={optionsWithNull}
{...props}
/>
</FormItem>
);
} as FC<SelectProps>);

View File

@ -1,21 +0,0 @@
import type { SwitchProps } from 'antd';
import { Form, Switch as AntSwitch } from 'antd';
import type { FC } from 'react';
import type { BaseElementProps } from './types';
const { Item: FormItem } = Form;
export default (function Switch({
value,
setValue,
status,
isValid,
help,
...props
}: BaseElementProps<boolean>) {
return (
<FormItem hasFeedback validateStatus={isValid === false ? 'error' : ''} help={help}>
<AntSwitch checked={value} onChange={setValue} disabled={status === 'Disabled'} {...props} />
</FormItem>
);
} as FC<SwitchProps>);

View File

@ -1,4 +0,0 @@
/* eslint-disable unicorn/filename-case */
/* eslint-disable no-restricted-exports */
export { Table as default } from 'antd';

View File

@ -1,16 +0,0 @@
import type { FC, ReactNode } from 'react';
import styled from 'styled-components';
const Span = styled.span`
margin-bottom: 18px;
font-size: 0.85rem;
`;
type TextProps = {
value: any;
children: ReactNode;
};
export default (function Text({ value, ...props }: TextProps) {
return <Span {...props}>{value || props.children}</Span>;
} as FC<TextProps>);

View File

@ -1,3 +0,0 @@
/* eslint-disable unicorn/filename-case */
/* eslint-disable no-restricted-exports */
export { Tooltip as default } from 'antd';

View File

@ -1,3 +0,0 @@
import DownloadOutlined from '@ant-design/icons/lib/icons/DownloadOutlined';
export default <DownloadOutlined />;

View File

@ -1,4 +0,0 @@
/* eslint-disable unicorn/filename-case */
/* eslint-disable no-restricted-exports */
export { Divider as default } from 'antd';

View File

@ -1,4 +0,0 @@
/* eslint-disable unicorn/filename-case */
/* eslint-disable no-restricted-exports */
export { Tabs as default } from 'antd';

View File

@ -1,8 +0,0 @@
/* eslint-disable unicorn/prefer-export-from */
import { notification } from 'antd';
notification.config({
placement: 'bottomRight',
});
export default notification;

View File

@ -1,14 +0,0 @@
export type Status = 'Default' | 'Disabled' | 'Loading' | 'Hidden';
export type BaseElementProps<Value> = {
value: Value;
setValue: (value: Value) => void;
status?: Status;
isValid?: boolean;
help?: string;
};
export type BaseOption<Value = any> = {
label: string;
value: Value;
};

View File

@ -1,34 +1,81 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# Turborepo starter
## Getting Started
This is an official starter Turborepo.
First, run the development server:
## Using this example
```bash
npm run dev
# or
yarn dev
Run the following command:
```sh
npx create-turbo@latest
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## What's inside?
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
This Turborepo includes the following packages/apps:
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
### Apps and Packages
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
- `docs`: a [Next.js](https://nextjs.org/) app
- `web`: another [Next.js](https://nextjs.org/) app
- `@repo/ui`: a stub React component library shared by both `web` and `docs` applications
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `@repo/tsconfig`: `tsconfig.json`s used throughout the monorepo
## Learn More
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).
To learn more about Next.js, take a look at the following resources:
### Utilities
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
This Turborepo has some additional tools already setup for you:
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [ESLint](https://eslint.org/) for code linting
- [Prettier](https://prettier.io) for code formatting
## Deploy on Vercel
### Build
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
To build all apps and packages, run the following command:
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
```
cd my-turborepo
pnpm build
```
### Develop
To develop all apps and packages, run the following command:
```
cd my-turborepo
pnpm dev
```
### Remote Caching
Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands:
```
cd my-turborepo
npx turbo login
```
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:
```
npx turbo link
```
## Useful Links
Learn more about the power of Turborepo:
- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)

View File

@ -1,11 +0,0 @@
/* eslint-disable import/prefer-default-export */
import { createGlobalStyle } from 'styled-components';
export const GlobalStyle = createGlobalStyle`
:root {
--color-background: rgb(240, 240, 240);
--color-primary: ${process.env.NEXT_PUBLIC_COLOR_PRIMARY};
--color-secondary: ${process.env.NEXT_PUBLIC_COLOR_SECONDARY};
--color-tertiarty: ${process.env.NEXT_PUBLIC_COLOR_TERTIARTY};
}
`;

View File

@ -1,20 +0,0 @@
const screens = {
tablet: 768,
laptop: 1024,
'laptop-hd': 1280,
desktop: 1680,
'desktop-xl': 1921,
};
const threshold = 0;
export function min(breakpoint: keyof typeof screens) {
return `@media (min-width: calc(${screens[breakpoint]}px + ${threshold}px))`;
}
export function max(breakpoint: keyof typeof screens) {
return `@media (max-width: calc(${screens[breakpoint]}px))`;
}
export const mediaQuery = {
breakpoints: Object.values(screens).map((value) => `${value + threshold}px`),
};

View File

@ -1,7 +0,0 @@
import { mediaQuery } from './mq';
const theme = {
...mediaQuery,
};
export default theme;

View File

@ -1,9 +0,0 @@
/* eslint-disable import/prefer-default-export */
import axios from 'axios';
import type { RequestFinGAP, ResponseFinGAP } from './types';
export async function calculateFinGAP(payload: RequestFinGAP, signal?: AbortSignal) {
return axios.post<ResponseFinGAP>(process.env.NEXT_PUBLIC_URL_CORE_FINGAP_PROXY!, payload, {
signal,
});
}

View File

@ -1,16 +0,0 @@
/* eslint-disable import/prefer-default-export */
import type { AxiosRequestConfig } from 'axios';
import axios from 'axios';
import { love } from './tools';
import type { User } from './types';
// prettier-ignore
const uri = typeof window === 'undefined'
? process.env.NEXT_PUBLIC_URL_GET_USER_DIRECT
: process.env.NEXT_PUBLIC_URL_GET_USER_PROXY;
export async function getUser(config: AxiosRequestConfig) {
const user = await axios.get<User>(uri!, config).then((res) => love(res.data));
return user;
}

View File

@ -1,10 +0,0 @@
/* eslint-disable import/prefer-default-export */
import type { User } from './types';
export function love(user: User) {
const superUsers: string[] = JSON.parse(process.env.USERS_SUPER || '');
// eslint-disable-next-line no-param-reassign
if (superUsers?.includes(user.username)) user.displayName += '🧡';
return user;
}

View File

@ -1,12 +0,0 @@
/** @type {import('apollo').ApolloConfig} */
module.exports = {
client: {
service: {
name: 'crmgraphql',
url: process.env.NEXT_PUBLIC_URL_CRM_GRAPHQL_DIRECT,
localSchemaFile: './graphql/crm.schema.graphql',
},
excludes: ['graphql/**/*'],
includes: ['pages/**/*', 'process/**/*', 'Components/**/*'],
},
};

View File

@ -1,35 +0,0 @@
/* eslint-disable no-underscore-dangle */
/* eslint-disable @typescript-eslint/naming-convention */
import { ApolloClient, InMemoryCache } from '@apollo/client';
/** @type {import('@apollo/client').ApolloClient<NormalizedCacheObject>} */
let apolloClient;
// prettier-ignore
const uri = typeof window === 'undefined'
? process.env.NEXT_PUBLIC_URL_CRM_GRAPHQL_DIRECT
: process.env.NEXT_PUBLIC_URL_CRM_GRAPHQL_PROXY;
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
uri,
cache: new InMemoryCache(),
});
}
export default function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState);
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}

7
apps/api/.dockerignore Normal file
View File

@ -0,0 +1,7 @@
.git
Dockerfile
.dockerignore
node_modules
*.log
dist
README.md

13
apps/api/.eslintrc.js Normal file
View File

@ -0,0 +1,13 @@
const { createConfig } = require('@vchikalkin/eslint-config-awesome');
module.exports = createConfig('typescript', {
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
ignorePatterns: ['*.config.js', '.eslintrc.js'],
rules: {
'import/no-duplicates': 'off',
'import/consistent-type-specifier-style': 'off',
},
});

56
apps/api/.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
# compiled output
/dist
/node_modules
/build
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# temp directory
.temp
.tmp
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

4
apps/api/.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}

45
apps/api/Dockerfile Normal file
View File

@ -0,0 +1,45 @@
# This Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker.
# Make sure you update both files!
FROM node:alpine AS builder
RUN corepack enable && corepack prepare pnpm@8.9.0 --activate
ENV PNPM_HOME=/usr/local/bin
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
RUN apk update
# Set working directory
WORKDIR /app
RUN pnpm add -g turbo@1.12.4 dotenv-cli
COPY . .
RUN turbo prune --scope=api --docker
# Add lockfile and package.json's of isolated subworkspace
FROM node:alpine AS installer
RUN corepack enable && corepack prepare pnpm@latest --activate
ENV PNPM_HOME=/usr/local/bin
RUN apk add --no-cache libc6-compat
RUN apk update
WORKDIR /app
# First install dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=builder /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
RUN pnpm install
# Build the project and its dependencies
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
RUN pnpm dotenv -e .env turbo run build --filter=api...
FROM node:alpine AS runner
WORKDIR /app
# Don't run production as root
RUN addgroup --system --gid 1001 nestjs
RUN adduser --system --uid 1001 nestjs
USER nestjs
COPY --from=installer /app .
CMD node apps/api/dist/main.js

73
apps/api/README.md Normal file
View File

@ -0,0 +1,73 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ pnpm install
```
## Running the app
```bash
# development
$ pnpm run start
# watch mode
$ pnpm run start:dev
# production mode
$ pnpm run start:prod
```
## Test
```bash
# unit tests
$ pnpm run test
# e2e tests
$ pnpm run test:e2e
# test coverage
$ pnpm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).

8
apps/api/nest-cli.json Normal file
View File

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}

74
apps/api/package.json Normal file
View File

@ -0,0 +1,74 @@
{
"name": "api",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/cache-manager": "^2.2.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-fastify": "^10.3.3",
"cache-manager": "^5.4.0",
"cache-manager-ioredis": "^2.1.0",
"ioredis": "^5.3.2",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@vchikalkin/eslint-config-awesome": "^1.1.6",
"eslint": "^8.52.0",
"fastify": "^4.26.1",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"shared": "workspace:*",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "29.1.1",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.3.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

View File

@ -0,0 +1,16 @@
import { ProxyModule } from './proxy/proxy.module';
import { Global, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Global()
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
ProxyModule,
],
providers: [],
})
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class AppModule {}

View File

@ -0,0 +1,3 @@
import { seconds } from 'src/utils/time';
export const DEFAULT_CACHE_TTL = seconds().fromMinutes(15);

View File

@ -0,0 +1,3 @@
import envSchema from './schema/env';
export const env = envSchema.parse(process.env);

View File

@ -0,0 +1,21 @@
import { DEFAULT_CACHE_TTL } from '../constants';
import { z } from 'zod';
const envSchema = z.object({
CACHE_TTL: z
.string()
.transform((val) => Number.parseInt(val, 10))
.default(DEFAULT_CACHE_TTL.toString()),
PORT: z
.string()
.transform((val) => Number.parseInt(val, 10))
.default('3001'),
REDIS_HOST: z.string(),
REDIS_PORT: z
.string()
.transform((val) => Number.parseInt(val, 10))
.default('6379'),
URL_CRM_GRAPHQL_DIRECT: z.string(),
});
export default envSchema;

15
apps/api/src/main.ts Normal file
View File

@ -0,0 +1,15 @@
import { AppModule } from './app.module';
import { env } from './config/env';
import { NestFactory } from '@nestjs/core';
import type { NestFastifyApplication } from '@nestjs/platform-fastify';
import { FastifyAdapter } from '@nestjs/platform-fastify';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
await app.listen(env.PORT, '0.0.0.0');
}
bootstrap();

View File

@ -0,0 +1,64 @@
import { seconds } from 'src/utils/time';
export const queryTTL: Record<string, number | false> = {
GetAddProductType: seconds().fromHours(12),
GetAddproductTypes: seconds().fromHours(12),
GetAgent: seconds().fromHours(12),
GetBrand: seconds().fromHours(3),
GetBrands: seconds().fromHours(3),
GetCoefficients: seconds().fromHours(12),
GetConfiguration: seconds().fromHours(3),
GetConfigurations: seconds().fromMinutes(15),
GetCurrencyChanges: seconds().fromHours(1),
GetDealer: seconds().fromHours(1),
GetDealerPerson: seconds().fromHours(1),
GetDealerPersons: seconds().fromHours(1),
GetDealers: seconds().fromMinutes(15),
GetEltInsuranceRules: seconds().fromHours(12),
GetFuelCards: seconds().fromHours(12),
GetGPSBrands: seconds().fromHours(24),
GetGPSModels: seconds().fromHours(24),
GetImportProgram: seconds().fromHours(12),
GetInsNSIBTypes: seconds().fromHours(12),
GetInsuranceCompanies: seconds().fromHours(12),
GetInsuranceCompany: seconds().fromHours(12),
GetLead: false,
GetLeadUrl: seconds().fromHours(12),
GetLeads: false,
GetLeaseObjectType: seconds().fromHours(24),
GetLeaseObjectTypes: seconds().fromHours(24),
GetLeasingWithoutKaskoTypes: seconds().fromHours(12),
GetModel: seconds().fromHours(3),
GetModels: seconds().fromMinutes(15),
GetOpportunities: false,
GetOpportunity: false,
GetOpportunityUrl: seconds().fromHours(12),
GetOsagoAddproductTypes: seconds().fromHours(12),
GetProduct: seconds().fromHours(12),
GetProducts: seconds().fromHours(12),
GetQuote: false,
GetQuoteData: false,
GetQuoteUrl: seconds().fromHours(12),
GetQuotes: false,
GetRate: seconds().fromHours(12),
GetRates: seconds().fromHours(12),
GetRegion: seconds().fromHours(24),
GetRegions: seconds().fromHours(24),
GetRegistrationTypes: seconds().fromHours(12),
GetRewardCondition: seconds().fromHours(1),
GetRewardConditions: seconds().fromHours(1),
GetRoles: seconds().fromHours(12),
GetSotCoefficientType: seconds().fromHours(12),
GetSubsidies: seconds().fromHours(12),
GetSubsidy: seconds().fromHours(12),
GetSystemUser: seconds().fromHours(12),
GetTarif: seconds().fromHours(12),
GetTarifs: seconds().fromHours(12),
GetTechnicalCards: seconds().fromHours(12),
GetTelematicTypes: seconds().fromHours(12),
GetTown: seconds().fromHours(24),
GetTowns: seconds().fromHours(24),
GetTrackerTypes: seconds().fromHours(12),
GetTransactionCurrencies: seconds().fromHours(12),
GetTransactionCurrency: seconds().fromHours(12),
};

View File

@ -0,0 +1,127 @@
import { queryTTL } from './lib/config';
import type { GQLRequest, GQLResponse } from './types';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import {
All,
Controller,
Delete,
Get,
HttpException,
HttpStatus,
Inject,
Query,
Req,
Res,
} from '@nestjs/common';
import type { Cache } from 'cache-manager';
import { FastifyReply, FastifyRequest } from 'fastify';
import type { QueryItem } from 'shared/types/cache';
import { env } from 'src/config/env';
type RedisStore = Omit<Cache, 'set'> & {
set: (key: string, value: unknown, { ttl }: { ttl: number }) => Promise<void>;
};
@Controller('proxy')
export class ProxyController {
constructor(
@Inject(CACHE_MANAGER) private readonly cacheManager: RedisStore,
) {}
@All('/graphql')
public async graphql(@Req() req: FastifyRequest, @Res() reply: FastifyReply) {
const { operationName, query, variables } = req.body as GQLRequest;
const key = `${operationName} ${JSON.stringify(variables)}`;
const cached = await this.cacheManager.get(key);
if (cached) return reply.send(cached);
const response = await fetch(env.URL_CRM_GRAPHQL_DIRECT, {
body: JSON.stringify({ operationName, query, variables }),
headers: {
Authorization: req.headers.authorization,
'Content-Type': 'application/json',
Cookie: req.headers.cookie,
},
method: req.method,
});
const data = (await response.json()) as GQLResponse;
if (!response.ok || data?.error || data?.errors?.length)
throw new HttpException(
response.statusText,
response.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
const ttl = queryTTL[operationName];
if (data && ttl !== false)
await this.cacheManager.set(key, data, { ttl: ttl || env.CACHE_TTL });
return reply.send(data);
}
@Get('/get-queries')
public async getQueriesList(@Res() reply: FastifyReply) {
const res = await this.getAllQueries();
return reply.send(res);
}
private async getAllQueries() {
const list = await this.cacheManager.store.keys('*');
return (Object.keys(queryTTL) as Array<keyof typeof queryTTL>).reduce(
(acc, queryName) => {
const queries = list.filter((x) => x.split(' ').at(0) === queryName);
if (queries.length) {
const ttl = queryTTL[queryName];
acc[queryName] = { queries, ttl };
}
return acc;
},
{} as Record<string, QueryItem>,
);
}
@Delete('/delete-query')
public async deleteQuery(
@Query('queryKey') queryKey: string,
@Res() reply: FastifyReply,
) {
try {
await this.cacheManager.del(queryKey);
return reply.send('ok');
} catch (error) {
throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Delete('/reset')
public async reset(@Res() reply: FastifyReply) {
try {
await this.cacheManager.reset();
return reply.send('ok');
} catch (error) {
throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Get('/get-query')
public async getQueryValue(
@Query('queryKey') queryKey: string,
@Res() reply: FastifyReply,
) {
try {
const value = await this.cacheManager.get(queryKey);
return reply.send(value);
} catch (error) {
throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -0,0 +1,20 @@
import { ProxyController } from './proxy.controller';
import { CacheModule } from '@nestjs/cache-manager';
import { Module } from '@nestjs/common';
import * as redisStore from 'cache-manager-ioredis';
import type { RedisOptions } from 'ioredis';
import { env } from 'src/config/env';
@Module({
controllers: [ProxyController],
imports: [
CacheModule.register<RedisOptions>({
host: env.REDIS_HOST,
port: env.REDIS_PORT,
store: redisStore,
ttl: env.CACHE_TTL,
}),
],
})
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class ProxyModule {}

View File

@ -0,0 +1,11 @@
export type GQLRequest = {
operationName: string;
query: string;
variables: string;
};
export type GQLResponse = {
data: unknown;
error?: unknown;
errors?: unknown[];
};

View File

@ -0,0 +1,13 @@
export function seconds() {
return {
fromDays(days: number) {
return days * 24 * 60 * 60;
},
fromHours(hours: number) {
return hours * 60 * 60;
},
fromMinutes(minutes: number) {
return minutes * 60;
},
};
}

View File

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}

22
apps/api/tsconfig.json Normal file
View File

@ -0,0 +1,22 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
},
"exclude": ["node_modules"]
}

View File

@ -1,7 +1,8 @@
.git
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
*.log
dist
.next
.git
README.md

8
apps/web/.eslintignore Normal file
View File

@ -0,0 +1,8 @@
.next
public
apollo.config.js
mocks
graphql/crm.schema.graphql
graphql/crm.types.ts
package.json
next-env.d.ts

14
apps/web/.eslintrc.js Normal file
View File

@ -0,0 +1,14 @@
const { createConfig } = require('@vchikalkin/eslint-config-awesome');
module.exports = createConfig('next-typescript', {
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'import/no-duplicates': 'off',
'react/forbid-component-props': 'off',
'import/consistent-type-specifier-style': 'off',
},
ignorePatterns: ['*.config.js', '.eslintrc.js'],
});

4
apps/web/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Sentry Config File
.sentryclirc
/styles/antd.min.css

22
apps/web/.graphqlrc.yml Normal file
View File

@ -0,0 +1,22 @@
overwrite: true
schema: './graphql/crm.schema.graphql'
documents: [./**/!(*.schema).graphql]
generates:
./graphql/crm.types.ts:
plugins:
- typescript
- typescript-operations
- typed-document-node
config:
onlyOperationTypes: true
useTypeImports: true
avoidOptionals:
field: true
inputValue: false
object: true
defaultValue: true
scalars:
UUID: string
Decimal: number
DateTime: string
# exclude: './graphql/crm.schema.graphql'

19
apps/web/@types/errors.ts Normal file
View File

@ -0,0 +1,19 @@
import elementsToValues from '@/Components/Calculation/config/map/values';
export const ERR_ELT_KASKO = 'ERR_ELT_KASKO';
export const ERR_ELT_OSAGO = 'ERR_ELT_OSAGO';
export const ERR_FINGAP_TABLE = 'ERR_FINGAP_TABLE';
export const ERR_INSURANCE_TABLE = 'ERR_INSURANCE_TABLE';
export const ERR_PAYMENTS_TABLE = 'ERR_PAYMENTS_TABLE';
export const ERROR_TABLE_KEYS = [
ERR_ELT_KASKO,
ERR_ELT_OSAGO,
ERR_FINGAP_TABLE,
ERR_INSURANCE_TABLE,
ERR_PAYMENTS_TABLE,
];
export const ERROR_ELEMENTS_KEYS = Object.keys(elementsToValues);
export const ERROR_KEYS = [...ERROR_ELEMENTS_KEYS, ...ERROR_TABLE_KEYS];

View File

@ -0,0 +1,77 @@
import * as cacheApi from '@/api/cache/query';
import { min } from '@/styles/mq';
import { useQuery } from '@tanstack/react-query';
import { memo, useState } from 'react';
import styled from 'styled-components';
import { Button, Collapse } from 'ui/elements';
import { Flex } from 'ui/grid';
type QueryProps = {
readonly onDeleteQuery: () => Promise<void>;
readonly queryKey: string;
};
const StyledPre = styled.pre`
max-height: 300px;
overflow-y: auto;
${min('desktop')} {
max-height: 800px;
}
`;
export const Query = memo(({ onDeleteQuery, queryKey }: QueryProps) => {
const { data, refetch } = useQuery({
enabled: false,
queryFn: ({ signal }) => signal && cacheApi.getQueryValue(queryKey, { signal }),
queryKey: ['admin', 'cache', 'query', queryKey],
refetchOnWindowFocus: false,
});
const [activeKey, setActiveKey] = useState<string | undefined>(undefined);
const [deletePending, setDeletePending] = useState(false);
const content = (
<>
<StyledPre>{JSON.stringify(data, null, 2)}</StyledPre>
<Flex justifyContent="flex-end">
<Button
type="primary"
danger
disabled={deletePending}
onClick={() => {
setDeletePending(true);
onDeleteQuery().finally(() => {
setDeletePending(false);
});
}}
>
Удалить
</Button>
</Flex>
</>
);
return (
<Collapse
bordered={false}
activeKey={activeKey}
items={[
{
children: data ? content : 'Загрузка...',
key: queryKey,
label: queryKey,
},
]}
onChange={() => {
if (activeKey) {
setActiveKey(undefined);
} else {
setActiveKey(queryKey);
refetch();
}
}}
/>
);
});

View File

@ -0,0 +1,22 @@
import { Query } from './Query';
import * as cacheApi from '@/api/cache/query';
import { useState } from 'react';
import type { QueryItem } from 'shared/types/cache';
type QueryListProps = QueryItem;
export const QueryList = ({ queries }: QueryListProps) => {
const [deletedQueries, setDeletedQueries] = useState<QueryItem['queries']>([]);
function handleDeleteQuery(queryKey: string) {
return cacheApi
.deleteQuery(queryKey)
.then(() => setDeletedQueries([...deletedQueries, queryKey]));
}
const activeQueries = queries.filter((queryKey) => !deletedQueries.includes(queryKey));
return activeQueries.map((queryKey) => (
<Query key={queryKey} queryKey={queryKey} onDeleteQuery={() => handleDeleteQuery(queryKey)} />
));
};

View File

@ -0,0 +1,24 @@
import { useState } from 'react';
import { Button } from 'ui/elements';
import { ReloadOutlined } from 'ui/elements/icons';
export function ReloadButton({ onClick }: { readonly onClick: () => Promise<unknown> }) {
const [pending, setPending] = useState(false);
return (
<Button
loading={pending}
onClick={() => {
setPending(true);
onClick().finally(() => {
setTimeout(() => {
setPending(false);
}, 1000);
});
}}
icon={<ReloadOutlined rev="" />}
>
Обновить
</Button>
);
}

Some files were not shown because too many files have changed in this diff Show More