Browse Source

Connect factories to api

merge-requests/7/head
Kurmansky 3 years ago
parent
commit
e38ea3cc37
  1. 65
      package-lock.json
  2. 7
      package.json
  3. 2
      src/api/auth/requests.ts
  4. 11
      src/api/factories/requests.interface.ts
  5. 26
      src/api/factories/requests.ts
  6. 0
      src/api/factories/responses.interfaces.ts
  7. 7
      src/api/index.ts
  8. 51
      src/components/Fields/TreeSelectField.tsx
  9. 10
      src/config/index.js
  10. 105
      src/config/rest-client.js
  11. 9
      src/containers/App/router/routes.config.ts
  12. 129
      src/containers/Factory/components/DataFactory/index.jsx
  13. 9
      src/containers/Factory/components/DataFactory/style.scss
  14. 119
      src/containers/Factory/components/FormFactory/index.jsx
  15. 167
      src/containers/Factory/components/FormFactory/index.tsx
  16. 69
      src/containers/Factory/components/ModalForm/index.jsx
  17. 57
      src/containers/Factory/components/ModalForm/index.tsx
  18. 167
      src/containers/Factory/components/TreeFactory/index.jsx
  19. 151
      src/containers/Factory/components/TreeFactory/index.tsx
  20. 3
      src/containers/Factory/components/TreeFactory/style.scss
  21. 1
      src/containers/Factory/hooks/index.ts
  22. 69
      src/containers/Factory/hooks/use-factory.hook.ts
  23. 103
      src/containers/Factory/index.jsx
  24. 74
      src/containers/Factory/index.tsx
  25. 68
      src/containers/Factory/reducer.js
  26. 24
      src/containers/auth/screens/sign-in.screen.tsx
  27. 80
      src/lib/helper.ts
  28. 3
      src/services/domain/auth.service.ts
  29. 65
      src/services/domain/factories.service.ts
  30. 5
      src/services/domain/index.ts
  31. 3
      src/shared/components/fields/index.ts
  32. 29
      src/shared/components/fields/input-field.component.tsx
  33. 54
      src/shared/components/fields/select-field.component.tsx
  34. 83
      src/shared/components/fields/select-multi-field.component.tsx
  35. 7
      src/shared/components/index.ts
  36. 15
      src/shared/interfaces/factories.interfaces.ts
  37. 13
      src/shared/interfaces/index.ts
  38. 65
      src/shared/interfaces/taxonomy.interfaces.ts
  39. 3
      src/store/factories/index.ts
  40. 19
      src/store/factories/reducer.ts
  41. 4
      src/store/factories/selectors.ts
  42. 10
      src/store/factories/types.ts
  43. 54
      src/store/index.tsx
  44. 1
      tsconfig.json
  45. 30362
      yarn.lock

65
package-lock.json generated

@ -161,6 +161,7 @@ @@ -161,6 +161,7 @@
"devDependencies": {
"@babel/helper-call-delegate": "^7.12.13",
"@babel/helper-regex": "^7.10.5",
"@types/lodash": "^4.14.175",
"@types/react-router-dom": "^5.1.5",
"babel-core": "^6.26.3",
"babel-runtime": "6.26.0",
@ -7864,6 +7865,7 @@ @@ -7864,6 +7865,7 @@
},
"node_modules/@babel/preset-env/node_modules/@babel/preset-modules/node_modules/@babel/plugin-proposal-unicode-property-regex/node_modules/@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"peer": true,
"dependencies": {
@ -7924,6 +7926,7 @@ @@ -7924,6 +7926,7 @@
},
"node_modules/@babel/preset-env/node_modules/@babel/preset-modules/node_modules/@babel/plugin-transform-dotall-regex/node_modules/@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"peer": true,
"dependencies": {
@ -11558,6 +11561,12 @@ @@ -11558,6 +11561,12 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.175",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz",
"integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==",
"dev": true
},
"node_modules/@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@ -36502,6 +36511,7 @@ @@ -36502,6 +36511,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -36745,6 +36755,7 @@ @@ -36745,6 +36755,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -37453,6 +37464,7 @@ @@ -37453,6 +37464,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -37679,6 +37691,7 @@ @@ -37679,6 +37691,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -37917,6 +37930,7 @@ @@ -37917,6 +37930,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -38135,6 +38149,7 @@ @@ -38135,6 +38149,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -38376,6 +38391,7 @@ @@ -38376,6 +38391,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -38602,6 +38618,7 @@ @@ -38602,6 +38618,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -38820,6 +38837,7 @@ @@ -38820,6 +38837,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -39038,6 +39056,7 @@ @@ -39038,6 +39056,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -39256,6 +39275,7 @@ @@ -39256,6 +39275,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -39474,6 +39494,7 @@ @@ -39474,6 +39494,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -39692,6 +39713,7 @@ @@ -39692,6 +39713,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -39910,6 +39932,7 @@ @@ -39910,6 +39932,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -40412,6 +40435,7 @@ @@ -40412,6 +40435,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -41044,6 +41068,7 @@ @@ -41044,6 +41068,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -42135,6 +42160,8 @@ @@ -42135,6 +42160,8 @@
"dependencies": {
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@ -42183,6 +42210,8 @@ @@ -42183,6 +42210,8 @@
"dependencies": {
"@babel/core": {
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@ -42411,6 +42440,7 @@ @@ -42411,6 +42440,7 @@
},
"@babel/core": {
"version": "7.15.5",
"integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==",
"peer": true,
"requires": {
"@babel/code-frame": "^7.14.5",
@ -45019,6 +45049,12 @@ @@ -45019,6 +45049,12 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/lodash": {
"version": "4.14.175",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz",
"integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==",
"dev": true
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@ -47773,7 +47809,8 @@ @@ -47773,7 +47809,8 @@
},
"dependencies": {
"bn.js": {
"version": "5.2.0"
"version": "5.2.0",
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw=="
}
}
},
@ -47843,6 +47880,7 @@ @@ -47843,6 +47880,7 @@
},
"semver": {
"version": "7.3.5",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"requires": {
"lru-cache": "^6.0.0"
}
@ -49827,6 +49865,7 @@ @@ -49827,6 +49865,7 @@
},
"domhandler": {
"version": "2.1.0",
"integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=",
"requires": {
"domelementtype": "1"
}
@ -49969,7 +50008,8 @@ @@ -49969,7 +50008,8 @@
},
"dependencies": {
"bn.js": {
"version": "4.12.0"
"version": "4.12.0",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
}
}
},
@ -51826,6 +51866,7 @@ @@ -51826,6 +51866,7 @@
"dependencies": {
"combined-stream": {
"version": "1.0.6",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "~1.0.0"
}
@ -52508,6 +52549,7 @@ @@ -52508,6 +52549,7 @@
},
"hmac-drbg": {
"version": "1.0.1",
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
"requires": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
@ -52687,6 +52729,7 @@ @@ -52687,6 +52729,7 @@
"dependencies": {
"domutils": {
"version": "1.1.6",
"integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=",
"requires": {
"domelementtype": "1"
}
@ -52698,6 +52741,7 @@ @@ -52698,6 +52741,7 @@
},
"readable-stream": {
"version": "1.0.34",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
@ -53440,7 +53484,8 @@ @@ -53440,7 +53484,8 @@
},
"dependencies": {
"has-symbols": {
"version": "1.0.2"
"version": "1.0.2",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
}
}
},
@ -57076,6 +57121,7 @@ @@ -57076,6 +57121,7 @@
},
"postcss-calc": {
"version": "6.0.2",
"integrity": "sha512-fiznXjEN5T42Qm7qqMCVJXS3roaj9r4xsSi+meaBVe7CJBl8t/QLOXu02Z2E6oWAMWIvCuF6JrvzFekmVEbOKA==",
"requires": {
"css-unit-converter": "^1.1.1",
"postcss": "^7.0.2",
@ -57193,6 +57239,7 @@ @@ -57193,6 +57239,7 @@
},
"postcss-colormin": {
"version": "4.0.2",
"integrity": "sha512-1QJc2coIehnVFsz0otges8kQLsryi4lo19WD+U5xCWvXd0uw/Z+KKYnbiNDCnO9GP+PvErPHCG0jNvWTngk9Rw==",
"requires": {
"browserslist": "^4.0.0",
"color": "^3.0.0",
@ -57287,6 +57334,7 @@ @@ -57287,6 +57334,7 @@
},
"postcss-discard-comments": {
"version": "4.0.1",
"integrity": "sha512-Ay+rZu1Sz6g8IdzRjUgG2NafSNpp2MSMOQUb+9kkzzzP+kh07fP0yNbhtFejURnyVXSX3FYy2nVNW1QTnNjgBQ==",
"requires": {
"postcss": "^7.0.0"
}
@ -57518,6 +57566,7 @@ @@ -57518,6 +57566,7 @@
},
"postcss-merge-longhand": {
"version": "4.0.6",
"integrity": "sha512-JavnI+V4IHWsaUAfOoKeMEiJQGXTraEy1nHM0ILlE6NIQPEZrJDAnPh3lNGZ5HAk2mSSrwp66JoGhvjp6SqShA==",
"requires": {
"css-color-names": "0.0.4",
"postcss": "^7.0.0",
@ -57527,6 +57576,7 @@ @@ -57527,6 +57576,7 @@
},
"postcss-merge-rules": {
"version": "4.0.2",
"integrity": "sha512-UiuXwCCJtQy9tAIxsnurfF0mrNHKc4NnNx6NxqmzNNjXpQwLSukUxELHTRF0Rg1pAmcoKLih8PwvZbiordchag==",
"requires": {
"browserslist": "^4.0.0",
"caniuse-api": "^3.0.0",
@ -57559,6 +57609,7 @@ @@ -57559,6 +57609,7 @@
},
"postcss-minify-gradients": {
"version": "4.0.1",
"integrity": "sha512-pySEW3E6Ly5mHm18rekbWiAjVi/Wj8KKt2vwSfVFAWdW6wOIekgqxKxLU7vJfb107o3FDNPkaYFCxGAJBFyogA==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"is-color-stop": "^1.0.0",
@ -57568,6 +57619,7 @@ @@ -57568,6 +57619,7 @@
},
"postcss-minify-params": {
"version": "4.0.1",
"integrity": "sha512-h4W0FEMEzBLxpxIVelRtMheskOKKp52ND6rJv+nBS33G1twu2tCyurYj/YtgU76+UDCvWeNs0hs8HFAWE2OUFg==",
"requires": {
"alphanum-sort": "^1.0.0",
"browserslist": "^4.0.0",
@ -57579,6 +57631,7 @@ @@ -57579,6 +57631,7 @@
},
"postcss-minify-selectors": {
"version": "4.0.1",
"integrity": "sha512-8+plQkomve3G+CodLCgbhAKrb5lekAnLYuL1d7Nz+/7RANpBEVdgBkPNwljfSKvZ9xkkZTZITd04KP+zeJTJqg==",
"requires": {
"alphanum-sort": "^1.0.0",
"has": "^1.0.0",
@ -57834,6 +57887,7 @@ @@ -57834,6 +57887,7 @@
},
"postcss-normalize-display-values": {
"version": "4.0.1",
"integrity": "sha512-R5mC4vaDdvsrku96yXP7zak+O3Mm9Y8IslUobk7IMP+u/g+lXvcN4jngmHY5zeJnrQvE13dfAg5ViU05ZFDwdg==",
"requires": {
"cssnano-util-get-match": "^4.0.0",
"postcss": "^7.0.0",
@ -57842,6 +57896,7 @@ @@ -57842,6 +57896,7 @@
},
"postcss-normalize-positions": {
"version": "4.0.1",
"integrity": "sha512-GNoOaLRBM0gvH+ZRb2vKCIujzz4aclli64MBwDuYGU2EY53LwiP7MxOZGE46UGtotrSnmarPPZ69l2S/uxdaWA==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"has": "^1.0.0",
@ -57851,6 +57906,7 @@ @@ -57851,6 +57906,7 @@
},
"postcss-normalize-repeat-style": {
"version": "4.0.1",
"integrity": "sha512-fFHPGIjBUyUiswY2rd9rsFcC0t3oRta4wxE1h3lpwfQZwFeFjXFSiDtdJ7APCmHQOnUZnqYBADNRPKPwFAONgA==",
"requires": {
"cssnano-util-get-arguments": "^4.0.0",
"cssnano-util-get-match": "^4.0.0",
@ -57860,6 +57916,7 @@ @@ -57860,6 +57916,7 @@
},
"postcss-normalize-string": {
"version": "4.0.1",
"integrity": "sha512-IJoexFTkAvAq5UZVxWXAGE0yLoNN/012v7TQh5nDo6imZJl2Fwgbhy3J2qnIoaDBrtUP0H7JrXlX1jjn2YcvCQ==",
"requires": {
"has": "^1.0.0",
"postcss": "^7.0.0",
@ -57868,6 +57925,7 @@ @@ -57868,6 +57925,7 @@
},
"postcss-normalize-timing-functions": {
"version": "4.0.1",
"integrity": "sha512-1nOtk7ze36+63ONWD8RCaRDYsnzorrj+Q6fxkQV+mlY5+471Qx9kspqv0O/qQNMeApg8KNrRf496zHwJ3tBZ7w==",
"requires": {
"cssnano-util-get-match": "^4.0.0",
"postcss": "^7.0.0",
@ -58172,6 +58230,7 @@ @@ -58172,6 +58230,7 @@
"dependencies": {
"css-tree": {
"version": "1.1.3",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"requires": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"

7
package.json

@ -3,9 +3,9 @@ @@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^4.0.6",
"@babel/core": "7.12.3",
"@bitalikrty/redux-create-reducer": "^1.0.0",
"@ant-design/icons": "^4.0.6",
"@material-ui/core": "^4.9.9",
"@material-ui/icons": "^4.9.1",
"@mdi/font": "^3.5.95",
@ -49,9 +49,9 @@ @@ -49,9 +49,9 @@
"eslint-webpack-plugin": "^2.5.2",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "6.1.1",
"fs-extra": "^9.0.1",
"file-saver": "^2.0.2",
"firebase": "^7.14.5",
"fs-extra": "^9.0.1",
"highcharts": "^7.0.3",
"html-webpack-plugin": "4.5.0",
"i18next": "^15.0.5",
@ -159,9 +159,10 @@ @@ -159,9 +159,10 @@
"test": "node scripts/test.js"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.5",
"@babel/helper-call-delegate": "^7.12.13",
"@babel/helper-regex": "^7.10.5",
"@types/lodash": "^4.14.175",
"@types/react-router-dom": "^5.1.5",
"babel-core": "^6.26.3",
"babel-runtime": "6.26.0",
"eslint-config-airbnb": "^17.1.0",

2
src/api/auth/requests.ts

@ -18,7 +18,7 @@ const sendRefreshToken = ( @@ -18,7 +18,7 @@ const sendRefreshToken = (
const logout = (
params: Req.ILogoutPayload,
): ApiResponse<void> => {
return http.post<void>('auth/logout', params)
return http.post<void>('/auth/logout', params)
}
export const authApi = {

11
src/api/factories/requests.interface.ts

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
export interface ICreateFactory {
parentId?: number;
description?: string;
directorId: number;
name: string;
shortName: string;
}
export interface IUpdateFactory extends ICreateFactory {
id: number;
}

26
src/api/factories/requests.ts

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
import { ICreateFactory, IUpdateFactory } from "./requests.interface";
import { ApiResponse } from "../http.types";
import http from "../http.service";
import { IFactory } from "@/shared";
const fetchFactories = (): ApiResponse<IFactory[]> => {
return http.get<IFactory[]>("factories", null);
};
const createFactory = (payload: ICreateFactory): ApiResponse<IFactory> => {
return http.post<IFactory>("factories", payload);
};
const updateFactory = (payload: IUpdateFactory): ApiResponse<IFactory> => {
return http.put<IFactory>("factories", payload);
};
const deleteFactory = (id: number): ApiResponse<void> => {
return http.delete<void>(`factories/${id}`);
};
export const factoriesApi = {
fetchFactories,
createFactory,
updateFactory,
deleteFactory,
};

0
src/api/factories/responses.interfaces.ts

7
src/api/index.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
export * from './auth/requests'
export * from './account/requests'
export * from './taxonomies/requests'
export * from "./auth/requests";
export * from "./account/requests";
export * from "./taxonomies/requests";
export * from "./factories/requests";

51
src/components/Fields/TreeSelectField.tsx

@ -1,18 +1,16 @@ @@ -1,18 +1,16 @@
import React from 'react'
import { TreeSelect } from 'antd';
import './TreeSelectField.scss'
import './SelectField.scss';
import { ITaxonomy } from "@/shared";
import React from "react";
import { TreeSelect } from "antd";
import "./TreeSelectField.scss";
import "./SelectField.scss";
interface IProps {
register: any
label: string
tree: ITaxonomy[]
placeholder?: string
multiple?: boolean
touched?: boolean
error?: string
register: any;
label: string;
tree: any[];
placeholder?: string;
multiple?: boolean;
touched?: boolean;
error?: string;
}
export const TreeSelectField = (props: IProps) => {
@ -20,23 +18,25 @@ export const TreeSelectField = (props: IProps) => { @@ -20,23 +18,25 @@ export const TreeSelectField = (props: IProps) => {
const renderTree = (tree) => {
if (Array.isArray(tree)) {
return tree.map(item => <TreeNode value={item['' || "id"]} title={item.name} key={item.id}>
{item.children && renderTree(item.children)}
</TreeNode>)
return tree.map((item) => (
<TreeNode value={item["" || "id"]} title={item.name} key={item.id}>
{item.children && renderTree(item.children)}
</TreeNode>
));
}
}
};
return (
<div className="form__form-group tree-select-field">
<span className="form__form-group-label">{props.label}</span>
<div className="form__form-group-field">
<div className="form__form-group-input-wrap">
<div style={{ position: 'relative' }}>
<div style={{ position: "relative" }}>
<TreeSelect
showSearch
treeNodeFilterProp='title'
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeNodeFilterProp="title"
style={{ width: "100%" }}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
placeholder={props.placeholder}
allowClear
defaultValue={null}
@ -46,9 +46,12 @@ export const TreeSelectField = (props: IProps) => { @@ -46,9 +46,12 @@ export const TreeSelectField = (props: IProps) => {
{renderTree(props.tree)}
</TreeSelect>
</div>
{props.touched && props.error && <span className="form__form-group-error">{props.error}</span>}
{props.touched &&
props.error && (
<span className="form__form-group-error">{props.error}</span>
)}
</div>
</div>
</div>
)
}
);
};

10
src/config/index.js

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
export const config = {
apiUrl: 'http://localhost:3000/admin',
socketUrl: 'http://localhost:3000',
}
apiUrl: "http://185.69.154.136:5000/admin/",
socketUrl: "http://localhost:3000",
};
export default config
export default config;

105
src/config/rest-client.js

@ -1,64 +1,75 @@ @@ -1,64 +1,75 @@
import config from './index';
import moment from 'moment';
var restClient = require('../lib/rest-client');
import config from "./index";
import moment from "moment";
var restClient = require("../lib/rest-client");
function checkHttpStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response
} else {
// let error = new Error(response ? (response.statusText || 'Error') : 'Error');
// error.response = response;
return Promise.resolve().then(() => {
return response.json();
}).then(res => {
res.status = response.status;
return Promise.reject(res)
});
if (response.status >= 200 && response.status < 300) {
return response;
} else {
// let error = new Error(response ? (response.statusText || 'Error') : 'Error');
// error.response = response;
return Promise.resolve()
.then(() => {
return response.json();
})
.then((res) => {
res.status = response.status;
return Promise.reject(res);
});
return Promise.reject(response)
// throw error;
}
return Promise.reject(response);
// throw error;
}
}
function makeRequest({ url, method, headers, bodyJSObject }) {
let header_tmp = Object.assign({}, this.headers, headers);
let header_tmp = Object.assign({}, this.headers, headers);
let body = bodyJSObject ? JSON.stringify(bodyJSObject) : undefined;
if (header_tmp.hasOwnProperty('Content-Type') && header_tmp['Content-Type'] == 'multipart/form-data') {
body = bodyJSObject;
delete header_tmp['Accept'];
delete header_tmp['Content-Type'];
}
return fetch(url,
{
method: method || "GET",
headers: header_tmp,
body: body,
mode: 'cors'
}).then(checkHttpStatus).then(response => {
return response.json();
});
let body = bodyJSObject ? JSON.stringify(bodyJSObject) : undefined;
if (
header_tmp.hasOwnProperty("Content-Type") &&
header_tmp["Content-Type"] == "multipart/form-data"
) {
body = bodyJSObject;
delete header_tmp["Accept"];
delete header_tmp["Content-Type"];
}
return fetch(url, {
method: method || "GET",
headers: header_tmp,
body: body,
mode: "cors",
})
.then(checkHttpStatus)
.then((response) => {
return response.json();
});
}
let token = '';
let token = "";
if (localStorage.getItem('remember_me') && localStorage.getItem('remember_me') == 'yes') {
token = localStorage.getItem('token')
} else if (localStorage.getItem('expiret_date') && moment(localStorage.getItem('expiret_date')) > moment()) {
token = localStorage.getItem('token');
if (
localStorage.getItem("remember_me") &&
localStorage.getItem("remember_me") == "yes"
) {
token = localStorage.getItem("token");
} else if (
localStorage.getItem("expiret_date") &&
moment(localStorage.getItem("expiret_date")) > moment()
) {
token = localStorage.getItem("token");
} else {
token = ''
token = "";
}
restClient.config({
baseUrl: config.apiUrl + '/api',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
requestMethod: makeRequest
baseUrl: config.apiUrl + "/api",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
requestMethod: makeRequest,
});
export default restClient;

9
src/containers/App/router/routes.config.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import { SignIn } from "@/containers/auth"
import { Factory } from "@/containers/Factory"
import Task from "@/containers/Task"
import { Taxonomy } from "@/containers/Taxonomy"
import { RouteEnum } from "./routes.enum"
@ -28,10 +29,10 @@ const privateRoutes = [ @@ -28,10 +29,10 @@ const privateRoutes = [
// path: RouteEnum.Pages,
// component: Pages
// },
// {
// path: RouteEnum.Factory,
// component: Factory
// },
{
path: RouteEnum.Factory,
component: Factory
},
// {
// path: RouteEnum.User,
// component: User

129
src/containers/Factory/components/DataFactory/index.jsx

@ -1,129 +0,0 @@ @@ -1,129 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Table, Popconfirm } from 'antd';
import './style.scss';
import { setItemFactory, showModal, deleteFactory } from '../../actions';
import { getPermissionCheck } from '../../../../lib/helper';
import TableGrid from '../../../../components/TableGrid/TableGrig';
class DataFactory extends Component {
constructor(props) {
super(props)
}
render() {
let {data, active_factory, profile} = this.props;
let permissions = profile.permissions || {};
const columns = [
{
name: 'Назва',
dataIndex: 'name',
key: 'name',
resizable: true,
sortable: true,
filter: true,
},
{
name: 'Дії',
key: 'action',
width: 100,
formatter: ({row}) => (
<span>
{getPermissionCheck('factory', 'update', profile) ? <a className="lnr lnr-pencil" href='javascript:;' onClick={() => {
this.props.setItemFactory(row);
this.props.showModal(true);
}}></a> : null}
<span style={{padding: '10px'}}> </span>
{getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm
title="Ви справді хочете видалити?"
onConfirm={() => {this.props.deleteFactory(row.id)}}
>
<a className="lnr lnr-trash" href="javascript:;"></a>
</Popconfirm> : null}
</span>
),
}
];
let clientHeight = document.body.clientHeight - 128 - 52 - 90;
let collumsList = [
{title: 'Назва', key: 'name'},
{title: 'Дії', key: 'action'},
];
let defaultCollumsActive = ['name', 'action'];
return <div><TableGrid
data_table={data.filter(item => item.parent_factory == active_factory)}
columns={columns}
tableName={'log_users'}
tableHeight={clientHeight}
collumsList={collumsList}
defaultCollumsActive={defaultCollumsActive}
onRow={(record, rowIndex) => {
return {
onDoubleClick: (event) => {
// history.push('/profile/' + record.id)
if(getPermissionCheck('factory', 'update', profile)){
this.props.setItemFactory(record);
this.props.showModal(true);
}
},
};
}}
/></div>
return (
<div>
<Table
dataSource={data.filter(item => item.parent_factory == active_factory)}
bordered
pagination={false}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {}, // click row
onDoubleClick: (event) => {
// history.push('/profile/' + record.id)
if(getPermissionCheck('factory', 'update', profile)){
this.props.setItemFactory(record);
this.props.showModal(true);
}
}, // double click row
onContextMenu: (event) => {}, // right button click row
onMouseEnter: (event) => {}, // mouse enter row
onMouseLeave: (event) => {}, // mouse leave row
};
}}
columns={columns}
rowKey={(record) => record.id}
/>
</div>
)
}
}
DataFactory.propTypes = {
}
const mapStateToProps = (state, ownProps) => ({
is_load: state.factory.is_load,
data: state.factory.data,
active_factory: state.factory.active_factory,
profile: state.auth.profile,
})
const mapDispatchToProps = {
setItemFactory,
deleteFactory,
showModal
}
export default connect(mapStateToProps, mapDispatchToProps)(DataFactory)

9
src/containers/Factory/components/DataFactory/style.scss

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
.factory {
.ant-table-wrapper{
.ant-empty-image{
img{
width: auto;
}
}
}
}

119
src/containers/Factory/components/FormFactory/index.jsx

@ -1,119 +0,0 @@ @@ -1,119 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import validationFields from '../../../../lib/validate';
import { Field, reduxForm } from 'redux-form';
import InputField from '../../../../components/Fields/InputField';
import {ButtonToolbar, Button} from 'reactstrap'
import TreeSelectField from '../../../../components/Fields/TreeSelectField';
import { genTree } from '../../../../lib/helper';
import _ from 'lodash'
import SelectField from '../../../../components/Fields/SelectField';
import { createFactory, updateFactory, showModal } from '../../actions';
import SelectMultyField from '../../../../components/Fields/SelectMultyField';
class FormFactory extends Component {
constructor(props) {
super(props)
}
submit = (values) => {
if(values.id){
this.props.updateFactory(values.id, values);
} else {
this.props.createFactory(values);
}
this.props.showModal(false);
}
render() {
let {handleSubmit, factory, users} = this.props;
return (
<form className="form" onSubmit={handleSubmit(this.submit)}>
<Field
name='name'
component={InputField}
type="text"
placeholder="Назва"
label="Назва"
/>
<Field
name='short_name'
component={InputField}
type="text"
placeholder="Коротка назва"
label="Коротка назва"
/>
<Field
name='parent_factory'
component={TreeSelectField}
placeholder="Батьківська категорія"
label="Батьківська категорія"
tree={genTree(_.cloneDeep(factory), undefined, undefined, 'parent_factory')}
/>
<Field
name='director'
component={SelectField}
placeholder="Директор"
label="Директор"
options={users.map(item => ({title: item.name, value: item.id}))}
/>
<Field
name='director_permissions'
component={SelectMultyField}
placeholder="Права доступу на підприємство"
label="Права доступу на підприємство"
options={[
{title: 'Перегляд задач', value: 'find'},
{title: 'Створення задач', value: 'create'},
{title: 'Редагування задач', value: 'update'},
{title: 'Видалення задач', value: 'destroy'},
]}
/>
<ButtonToolbar className="form__button-toolbar">
<Button color="primary" type="submit">Зберегти</Button>
</ButtonToolbar>
</form>
)
}
}
FormFactory.propTypes = {
}
FormFactory = reduxForm({
form: 'form_factory',
validate: (values) => {
let fields = [
{name: 'name', rules: [{name: 'required', message: 'Заповнити обовязково'}]},
// {name: 'password', rules: [{name: 'required', message: 'Заповнити обовязково'}]},
]
let error = validationFields(fields, values);
if(values.id == values.parent_factory && values.parent_factory){
error.parent_factory = 'Невірна батьківська категорія'
}
return error;
}
})(FormFactory);
const mapStateToProps = (state, ownProps) => ({
factory: state.factory.data,
initialValues: Object.assign({}, state.factory.item_factory) ,
users: state.factory.users,
})
const mapDispatchToProps = {
createFactory,
updateFactory,
showModal,
}
export default connect(mapStateToProps, mapDispatchToProps)(FormFactory)

167
src/containers/Factory/components/FormFactory/index.tsx

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
import React, { FC } from "react";
import { ButtonToolbar, Button } from "reactstrap";
import _ from "lodash";
import {
IFactory,
InputField,
IUser,
SelectField,
SelectMultiField,
} from "@/shared";
import { Controller, useForm } from "react-hook-form";
import { TreeSelectField } from "@/components/Fields";
import { genTree } from "@/lib/helper";
import {
ICreateFactory,
IUpdateFactory,
} from "@/api/factories/requests.interface";
interface IProps {
selectedFactory: IFactory;
factories: IFactory[];
users: IUser[];
updateFactory: (factory: IUpdateFactory) => void;
createFactory: (factory: ICreateFactory) => void;
closeModal: () => void;
}
export const FormFactory: FC<IProps> = ({
selectedFactory,
factories,
users,
updateFactory,
createFactory,
closeModal,
}) => {
const { control, handleSubmit } = useForm<Omit<IFactory, "children">>({
defaultValues: { ...selectedFactory },
});
const filterParentsTree = (
factories: IFactory[],
selectedFactory: IFactory
) => {
const filteredFactories = factories.filter((it) => {
return it.id !== selectedFactory.id && it.id !== selectedFactory.parentId;
});
for (let i; i < filteredFactories.length; i++) {
if (filteredFactories[i].children.length) {
filterParentsTree(filteredFactories[i].children, selectedFactory);
}
}
return filteredFactories;
};
const getParentsTree = () => {
let parentsTree: any;
if (selectedFactory) {
const filteredFactories = filterParentsTree(factories, selectedFactory);
parentsTree = genTree(
filteredFactories,
undefined,
undefined,
"parentId"
);
} else {
parentsTree = genTree(factories, undefined, undefined, "parentId");
}
return parentsTree;
};
const submit = (values) => {
if (values.id) {
updateFactory(values);
} else {
createFactory(values);
}
closeModal();
};
const parentCategoryTree = getParentsTree();
return (
<form className="form" onSubmit={handleSubmit(submit)}>
<Controller
name="name"
control={control}
render={({ field: { value, onChange } }) => (
<InputField
placeholder={"Назва"}
label={"Назва"}
onChange={onChange}
value={value}
/>
)}
/>
<Controller
name="shortName"
control={control}
render={({ field: { value, onChange } }) => (
<InputField
placeholder={"Коротка назва"}
label={"Коротка назва"}
onChange={onChange}
value={value}
/>
)}
/>
<Controller
name="parentId"
control={control}
render={({ field: { onChange, onBlur, value, ref } }) => (
<TreeSelectField
register={{ onChange, onBlur, ref, value }}
label="Батьківська категорія"
placeholder="Батьківська категорія"
tree={parentCategoryTree}
/>
)}
/>
<Controller
name="directorId"
control={control}
defaultValue={5}
render={({ field: { onChange } }) => (
<SelectField
onChange={onChange}
label="Директор"
placeholder="Директор"
options={users}
/>
)}
/>
<Controller
name="usersAccessPermission"
control={control}
render={({ field: { onChange } }) => (
<SelectMultiField
onChange={onChange}
label="Права доступу на підприємство"
placeholder="Права доступу на підприємство"
options={[
{ title: "Перегляд задач", value: "find" },
{ title: "Створення задач", value: "create" },
{ title: "Редагування задач", value: "update" },
{ title: "Видалення задач", value: "destroy" },
]}
/>
)}
/>
<ButtonToolbar className="form__button-toolbar">
<Button color="primary" type="submit">
Зберегти
</Button>
</ButtonToolbar>
</form>
);
};

69
src/containers/Factory/components/ModalForm/index.jsx

@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { connect } from 'react-redux'
import { showModal } from '../../actions';
import FormFactory from '../FormFactory'
class ModalForm extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
componentWillUnmount() {
}
toggle = () => {
this.props.showModal(false)
}
render() {
let {show_modal} = this.props;
let color = 'primary';
return (
<Modal
isOpen={show_modal}
toggle={this.toggle}
className={`modal-dialog--${color} modal-dialog--header`}
>
<div className="modal__header">
<button className="lnr lnr-cross modal__close-btn" type="button" onClick={this.toggle} />
<h4 className="bold-text modal__title" style={{color: 'white'}}>Підприємство</h4>
</div>
<div className="modal__body">
<FormFactory/>
</div>
</Modal>
)
}
}
ModalForm.propTypes = {
}
const mapStateToProps = (state, ownProps) => ({
show_modal: state.factory.show_modal,
})
const mapDispatchToProps = {
showModal
}
export default connect(mapStateToProps, mapDispatchToProps)(ModalForm)

57
src/containers/Factory/components/ModalForm/index.tsx

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
import {
ICreateFactory,
IUpdateFactory,
} from "@/api/factories/requests.interface";
import { IFactory, Loader } from "@/shared";
import React, { FC } from "react";
import { Modal } from "reactstrap";
import { FormFactory } from "../FormFactory";
interface IProps {
isOpen: boolean;
selectedFactory: IFactory;
factories: IFactory[];
closeModal: () => void;
createFactory: (factory: ICreateFactory) => void;
updateFactory: (factory: IUpdateFactory) => void;
}
export const ModalForm: FC<IProps> = ({
isOpen,
selectedFactory,
factories,
closeModal,
createFactory,
updateFactory,
}) => {
return (
<Modal
isOpen={isOpen}
toggle={closeModal}
className={`modal-dialog--primary modal-dialog--header`}
>
<div className="modal__header">
<button
className="lnr lnr-cross modal__close-btn"
type="button"
onClick={closeModal}
/>
<h4 className="bold-text modal__title" style={{ color: "white" }}>
Підприємство
</h4>
</div>
<div className="modal__body">
<FormFactory
selectedFactory={selectedFactory}
factories={factories}
users={[]}
closeModal={closeModal}
updateFactory={updateFactory}
createFactory={createFactory}
/>
</div>
</Modal>
);
};

167
src/containers/Factory/components/TreeFactory/index.jsx

@ -1,167 +0,0 @@ @@ -1,167 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Tree, Popconfirm, Input } from 'antd';
import { connect } from 'react-redux';
import { genTree, getPermissionCheck } from '../../../../lib/helper';
import _ from 'lodash';
import { selectFactory, setItemFactory, showModal, deleteFactory } from '../../actions';
import './style.scss';
const { Search } = Input;
const getParentKey = (id, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.id === id)) {
parentKey = node.id;
} else if (getParentKey(id, node.children)) {
parentKey = getParentKey(id, node.children);
}
}
}
return parentKey;
};
class TreeFactory extends Component {
constructor(props) {
super(props)
this.state = {
autoExpandParent: false,
searchValue: '',
expandedKeys: []
}
}
onSelect = (value) => {
// if(value[0]){
// this.props.selectFactory(value[0]);
// } else {
// this.props.selectFactory(null);
// }
}
onExpand = expandedKeys => {
this.setState({
expandedKeys,
autoExpandParent: false,
});
};
renderTitle = (item) => {
let {profile} = this.props;
const {searchValue} = this.state;
const index = item.name.indexOf(searchValue);
const beforeStr = item.name.substr(0, index);
const afterStr = item.name.substr(index + searchValue.length);
const title = index > -1 ? (
<span>
{beforeStr}
<span style={{color: 'white', background: '#0cbf00'}}>{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.name}</span>
);
return <div className="tree-title" onDoubleClick={() => {
console.log("TreeFactory -> renderTitle -> item", item)
this.props.setItemFactory(item);
this.props.showModal(true);
}}>
{(item.children && item.children.length) && <i className="fas fa-folder"></i>}
{(!item.children || !item.children.length) && <i className="lnr lnr-apartment"></i>}
<div className="text">{title}</div>
{getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm
title="Ви справді хочете видалити?"
onConfirm={() => {this.props.deleteFactory(item.id)}}
>
<a className="lnr lnr-trash" href="javascript:;"></a>
</Popconfirm> : null}
</div>
}
renderTree = (tree) => {
if(Array.isArray(tree)){
return tree.map(item => <Tree.TreeNode title={this.renderTitle(item)} key={item.id}>
{item.children ? this.renderTree(item.children) : null}
</Tree.TreeNode>)
}
}
onChange = (e) => {
const { value } = e.target;
let {data} = this.props;
if(!value){
this.setState({
searchValue: value,
expandedKeys: [],
autoExpandParent: true,
});
return;
}
let tree = genTree(_.cloneDeep(data));
const expandedKeys = data
.map(item => {
if (item.name.indexOf(value) > -1) {
return getParentKey(item.id, tree);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
this.setState({
searchValue: value,
expandedKeys,
autoExpandParent: true,
});
}
render() {
let {data} = this.props;
let tree = genTree(_.cloneDeep(data));
const { autoExpandParent, expandedKeys } = this.state;
return (
<div className="TreeFactory">
<Search style={{ marginBottom: 8, maxWidth: 200 }} placeholder="Search" onChange={this.onChange} />
<Tree
onSelect={this.onSelect}
showArrow
autoExpandParent={autoExpandParent}
onExpand={this.onExpand}
expandedKeys={expandedKeys}
>
{this.renderTree(tree)}
</Tree>
</div>
)
}
}
TreeFactory.propTypes = {
}
const mapStateToProps = (state, ownProps) => ({
is_load: state.factory.is_load,
data: state.factory.data,
profile: state.auth.profile,
})
const mapDispatchToProps = {
selectFactory,
setItemFactory,
showModal,
deleteFactory,
}
export default connect(mapStateToProps, mapDispatchToProps)(TreeFactory)

151
src/containers/Factory/components/TreeFactory/index.tsx

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
import { Input, Popconfirm, Tree } from "antd";
import React, { FC, useMemo, useState, useCallback, useEffect } from "react";
import { genTree, getPermissionCheck } from "@/lib/helper";
import _ from "lodash";
import { IFactory, IUser } from "@/shared";
import "./style.scss";
interface IProps {
factories: IFactory[];
profile: IUser;
openModal: () => void;
setSelectedFactory: (factory: IFactory) => void;
deleteFactory: (id: number) => void;
}
export const TreeFactory: FC<IProps> = ({
factories,
profile,
openModal,
deleteFactory,
setSelectedFactory,
}) => {
const [searchVal, setSearchVal] = useState<string>("");
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const [autoExpandParent, setAutoExpandParent] = useState<boolean>(false);
const preparedTree = genTree(_.cloneDeep(factories));
const getParentKey = (id, tree) => {
let parentKey;
tree.forEach((el) => {
if (el.children) {
if (el.children.some((it) => it.id === id)) {
parentKey = el.id;
} else if (getParentKey(id, el.children)) {
parentKey = getParentKey(id, el.children);
}
}
});
return parentKey;
};
const findParentKeys = (arr: IFactory[], str: string): string[] =>
arr
.map((item) => {
if (item.name.toLowerCase().indexOf(str.toLowerCase()) > -1) {
const parentKey = getParentKey(item.id, preparedTree);
return parentKey;
}
if (item.children.length) return findParentKeys(item.children, str);
})
.filter((item, i, self) => {
return item && self.indexOf(item) === i;
});
const onExpend = (expandedKeys: string[]) => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onChange = (value: string) => {
if (!value) {
setSearchVal(value);
setExpandedKeys([]);
setAutoExpandParent(true);
return;
}
const expandedKeys = findParentKeys(factories, value);
setSearchVal(value);
setAutoExpandParent(true);
setExpandedKeys(expandedKeys);
};
const renderTitle = (item) => {
const index = item.name.indexOf(searchVal);
const beforeStr = item.name.substr(0, index);
const afterStr = item.name.substr(index + searchVal.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span style={{ color: "white", background: "#0cbf00" }}>
{searchVal}
</span>
{afterStr}
</span>
) : (
<span>{item.name}</span>
);
return (
<div
className="tree-title"
onClick={() => {
setSelectedFactory(item);
}}
>
<div style={{ display: "flex" }} onClick={openModal}>
{item.children.length ? <i className="fas fa-folder" /> : null}
{!item.children.length ? <i className="lnr lnr-apartment" /> : null}
<div className="text">{title}</div>
</div>
{getPermissionCheck("factory", "destroy", profile) && (
<Popconfirm
title="Ви справді хочете видалити?"
onConfirm={() => deleteFactory(item.id)}
>
<a className="lnr lnr-trash" href="javascript:;" />
</Popconfirm>
)}
</div>
);
};
const renderTree = useCallback(
(preparedTree) => {
if (Array.isArray(preparedTree)) {
return preparedTree.map((item) => (
<Tree.TreeNode title={renderTitle(item)} key={item.id}>
{item.children ? renderTree(item.children) : null}
</Tree.TreeNode>
));
}
},
[factories, searchVal]
);
return (
<div className="TreeFactory">
<Input.Search
style={{ marginBottom: 8, maxWidth: 200 }}
placeholder="Search"
onChange={(e) => onChange(e.target.value)}
/>
<Tree
onExpand={onExpend}
autoExpandParent={autoExpandParent}
expandedKeys={expandedKeys}
>
{renderTree(preparedTree)}
</Tree>
</div>
);
};

3
src/containers/Factory/components/TreeFactory/style.scss

@ -5,10 +5,9 @@ @@ -5,10 +5,9 @@
}
.tree-title{
width: 100%;
display: flex;
align-items: center;
flex-wrap: nowrap;
.fa-folder{
margin-right: 5px;

1
src/containers/Factory/hooks/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from "./use-factory.hook";

69
src/containers/Factory/hooks/use-factory.hook.ts

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
import { useSelector } from "react-redux";
import { IUpdateFactory } from "./../../../api/factories/requests.interface";
import { useEffect, useState } from "react";
import { factoriesService } from "@/services/domain";
import { IFactory } from "@/shared";
import { ICreateFactory } from "@/api/factories/requests.interface";
import { isLoadingFactories } from "@/store/factories";
export const useFactory = () => {
const [factories, setFactories] = useState<IFactory[]>([]);
const [needRefetch, setRefetch] = useState<boolean>(false);
const isLoading = useSelector(isLoadingFactories);
const fetchFactories = async () => {
const factories = await factoriesService.fetchFactories();
setFactories(factories);
};
useEffect(() => {
fetchFactories();
}, []);
useEffect(
() => {
if (needRefetch) {
fetchFactories();
setRefetch(false);
}
},
[needRefetch]
);
const createFactory = async (factory: ICreateFactory) => {
const createdFactory = await factoriesService.createFactory(factory);
setRefetch(true);
};
const updateFactory = async (factory: IUpdateFactory) => {
const updatedFactory = await factoriesService.updateFactory(factory);
factories.forEach((it) => {
if (it.id !== factory.id) return it;
else return updatedFactory;
});
setFactories(factories);
setRefetch(true);
};
const deleteFactory = async (id: number) => {
await factoriesService.deleteFactory(id);
factories.filter((it) => it.id !== id);
setFactories(factories);
setRefetch(true);
};
return {
factories,
isLoading,
createFactory,
updateFactory,
deleteFactory,
};
};

103
src/containers/Factory/index.jsx

@ -1,103 +0,0 @@ @@ -1,103 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Col, Container, Row, Card, CardBody, Button } from 'reactstrap';
import { connect } from 'react-redux'
import TreeFactory from './components/TreeFactory';
import { getFactory, showModal, setItemFactory, deleteFactory } from './actions';
import DataFactory from './components/DataFactory';
import ModalForm from './components/ModalForm'
import { Popconfirm } from 'antd';
import { getPermissionCheck } from '../../lib/helper';
import histoty from '../../lib/histoty';
import './style.scss';
class Factory extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
}
componentDidMount() {
this.props.getFactory()
}
componentWillReceiveProps(nextProps) {
}
componentWillUnmount() {
}
render() {
let {active_factory, profile} = this.props;
let permissions = profile.permissions || {};
if(!getPermissionCheck('factory', 'find', profile)){
histoty.push('/');
}
return (
<Container className="factory">
<Row>
<Col md={12}>
<Card>
<CardBody>
<ModalForm/>
<Row>
<div className='col-md-12 justify-content-end flex'>
{getPermissionCheck('factory', 'create', profile) ? <Button color="primary" size='sm' onClick={() => {
this.props.setItemFactory({});
this.props.showModal(true);
}}><i className="fal fa-building"></i> Створити</Button> : null}
{/* {getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm
title="Ви справді хочете видалити?"
onConfirm={() => {
this.props.deleteFactory(active_factory)
}}
disabled={!active_factory}
>
<Button color="danger" disabled={!active_factory} size='sm'> <i className="fal fa-trash-alt"></i> Видалити</Button>
</Popconfirm> : null} */}
</div>
</Row>
<Row>
<Col md={12}>
<TreeFactory/>
</Col>
</Row>
</CardBody>
</Card>
</Col>
</Row>
</Container>
)
}
}
Factory.propTypes = {
}
const mapStateToProps = (state, ownProps) => ({
is_load: state.factory.is_load,
active_factory: state.factory.active_factory,
data: state.factory.data,
profile: state.auth.profile,
})
const mapDispatchToProps = {
getFactory,
setItemFactory,
deleteFactory,
showModal,
}
export default connect(mapStateToProps, mapDispatchToProps)(Factory)

74
src/containers/Factory/index.tsx

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
import { getPermissionCheck } from "@/lib/helper";
import { IFactory, Loader } from "@/shared";
import { getProfile } from "@/store/account";
import { Button, Card, Col, Row } from "antd";
import React, { FC, useState } from "react";
import { useSelector } from "react-redux";
import { Container } from "reactstrap";
import { ModalForm } from "./components/ModalForm";
import { TreeFactory } from "./components/TreeFactory";
import { useFactory } from "./hooks";
export const Factory: FC = () => {
const [isOpenModal, setOpenModal] = useState<boolean>(false);
const [selectedFactory, setFactory] = useState<IFactory>();
const profile = useSelector(getProfile);
const {
isLoading,
factories,
createFactory,
updateFactory,
deleteFactory,
} = useFactory();
return (
<Container className="factory">
<Row>
<Col md={12} />
<Card>
<ModalForm
isOpen={isOpenModal}
selectedFactory={selectedFactory}
factories={factories}
closeModal={() => setOpenModal(false)}
createFactory={createFactory}
updateFactory={updateFactory}
/>
<Row>
<div className="col-md-12 justify-content-end flex">
{getPermissionCheck("factory", "create", profile) ? (
<Button
type="primary"
onClick={() => {
setOpenModal(true);
setFactory(null);
}}
>
<i className="fal fa-building" /> Створити
</Button>
) : null}
</div>
</Row>
<Row>
<Col md={12}>
{isLoading ? (
<Loader />
) : (
<TreeFactory
factories={factories}
profile={profile}
openModal={() => setOpenModal(true)}
setSelectedFactory={setFactory}
deleteFactory={deleteFactory}
/>
)}
</Col>
</Row>
</Card>
</Row>
</Container>
);
};

68
src/containers/Factory/reducer.js

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import _ from 'lodash'
import _ from "lodash";
const initialState = {
is_load: false,
@ -6,40 +6,52 @@ const initialState = { @@ -6,40 +6,52 @@ const initialState = {
active_factory: null,
show_modal: false,
item_factory: {},
users: []
users: [],
};
export default function (state = initialState, action) {
export default function(state = initialState, action) {
let tmp_data = _.cloneDeep(state.data);
switch (action.type) {
case 'GET_FACTORY':
return {...state, data: action.data, is_load: true, active_factory: null}
case 'GET_FACTORY_USERS':
return {...state, users: action.data}
case 'FACTORY_SHOW_MODAL':
return {...state, show_modal: action.status}
case 'SELECT_FACTORY':
return {...state, active_factory: action.active_factory}
case 'SET_FACTORY_FACTORY':
return {...state, item_factory: action.factory}
case 'CREATE_FACTORY':
return {...state, data: [...state.data, action.data]}
case 'UPDATE_FACTORY':
return {...state, data: tmp_data.map(item => {
if(item.id == action.id){
return action.data;
}
return item;
})}
case 'DELETE_FACTORY':
tmp_data = tmp_data.filter(item => {
if(item.id == action.id){
case "GET_FACTORY":
return {
...state,
data: action.data,
is_load: true,
active_factory: null,
};
case "GET_FACTORY_USERS":
return { ...state, users: action.data };
case "FACTORY_SHOW_MODAL":
return { ...state, show_modal: action.status };
case "SELECT_FACTORY":
return { ...state, active_factory: action.active_factory };
case "SET_FACTORY_FACTORY":
return { ...state, item_factory: action.factory };
case "CREATE_FACTORY":
return { ...state, data: [...state.data, action.data] };
case "UPDATE_FACTORY":
return {
...state,
data: tmp_data.map((item) => {
if (item.id == action.id) {
return action.data;
}
return item;
}),
};
case "DELETE_FACTORY":
tmp_data = tmp_data.filter((item) => {
if (item.id == action.id) {
return false;
}
return true;
})
return {...state, data: tmp_data, active_factory: action.id == state.active_factory ? null : state.active_factory}
});
return {
...state,
data: tmp_data,
active_factory:
action.id == state.active_factory ? null : state.active_factory,
};
default:
return state;
}

24
src/containers/auth/screens/sign-in.screen.tsx

@ -1,27 +1,31 @@ @@ -1,27 +1,31 @@
import React from 'react';
import React from "react";
import { SignInForm } from "../components";
import logo from '@/assets/img/logo_task2.png'
import logo from "@/assets/img/logo_task2.png";
interface IProps { }
interface IProps {}
export const SignIn = (props: IProps) => {
return (
<div className="account">
<div className="account__wrapper">
<div className="account__card">
<div className="account__head" style={{ border: 'none', paddingLeft: 0 }}>
<div
className="account__head"
style={{ border: "none", paddingLeft: 0 }}
>
<h3 className="account__title">
<span className="account__logo">
<img style={{ width: '140px', marginLeft: '-5px' }} src={logo} />
<img
style={{ width: "140px", marginLeft: "-5px" }}
src={logo}
/>
</span>
</h3>
<h4 className="account__subhead subhead"></h4>
<h4 className="account__subhead subhead" />
</div>
<SignInForm />
</div>
</div>
</div>
)
}
);
};

80
src/lib/helper.ts

@ -1,68 +1,67 @@ @@ -1,68 +1,67 @@
import { EUserRole } from "@/shared";
import _ from 'lodash'
import { IUser } from '../shared/interfaces/user.interfaces';
import _ from "lodash";
import { IUser } from "../shared/interfaces/user.interfaces";
export function genTree(array, parent, tree, field) {
export function genTree(
array: Array<any>,
parent?: any,
tree?: any,
field?: any
) {
tree = typeof tree !== "undefined" ? tree : [];
parent = typeof parent !== "undefined" ? parent : { id: null };
field = typeof field !== "undefined" ? field : "parentId";
tree = typeof tree !== 'undefined' ? tree : [];
parent = typeof parent !== 'undefined' ? parent : { id: null };
field = typeof field !== 'undefined' ? field : 'parent_factory';
var children = _.filter(array, function (child) {
// return child.parent_factory == parent.id || (child.parent_factory && child.parent_factory.id == parent.id);
return child[field] == parent.id;
});
const children = _.filter(array, (child) => child[field] == parent.id);
if (!_.isEmpty(children)) {
if (parent.id == null) {
tree = children;
} else {
parent['children'] = children
parent["children"] = children;
}
_.each(children, function (child) { genTree(array, child, undefined, field) });
_.each(children, function(child) {
genTree(array, child, undefined, field);
});
}
return tree;
}
export function disableRouter(exclude, url) {
let routes = url.split('/').filter(item => !!item);
let routes = url.split("/").filter((item) => !!item);
if (!routes.length) {
return false
return false;
}
let exlude_arr = exclude.filter(item => item.indexOf(routes[0]) > -1);
let exclude_arr = exclude.filter((item) => item.indexOf(routes[0]) > -1);
let result = true;
exlude_arr.some(item => {
exclude_arr.some((item) => {
if (validRouter(item, routes)) {
result = false;
return true;
}
;
})
});
function validRouter(data, routes) {
let data_array = data.split('/').filter(item => !!item);
let data_array = data.split("/").filter((item) => !!item);
if (data_array.length != routes.length) {
return false;
}
return !routes.some((item, index) => {
if (data_array[index] == '{integer}') {
return isNaN(parseInt(item))
} else if (data_array[index] == '{*}') {
if (data_array[index] == "{integer}") {
return isNaN(parseInt(item));
} else if (data_array[index] == "{*}") {
return false;
} else {
return data_array[index] != item
return data_array[index] != item;
}
})
});
}
return result;
@ -76,29 +75,33 @@ export function getImageThumb(image, thumb) { @@ -76,29 +75,33 @@ export function getImageThumb(image, thumb) {
return image;
}
let img = image.split('/')[image.split('/').length - 1];
let img = image.split("/")[image.split("/").length - 1];
if (!img || img.split('.').length != 2) {
if (!img || img.split(".").length != 2) {
return image;
}
let thumb_name = img.split('.')[0] + '_' + thumb + '.' + img.split('.')[1];
return image.replace(img, thumb_name)
let thumb_name = img.split(".")[0] + "_" + thumb + "." + img.split(".")[1];
return image.replace(img, thumb_name);
}
export const getPermissionCheck = (controller: any, action: any, user: IUser) => {
export const getPermissionCheck = (
controller: any,
action: any,
user: IUser
) => {
if (user.role === EUserRole.Admin) {
return true;
}
let permissions = user.permissions || [];
const permissions = user.permissions.tabs;
let index_controller = _.findIndex(permissions, ['tabs', controller]);
//@ts-ignore
const index_controller = _.findIndex(permissions, ["tabs", controller]);
if (index_controller < 0) {
return false
return false;
}
let actions = permissions[index_controller].actions || [];
@ -107,6 +110,5 @@ export const getPermissionCheck = (controller: any, action: any, user: IUser) => @@ -107,6 +110,5 @@ export const getPermissionCheck = (controller: any, action: any, user: IUser) =>
return false;
}
return true;
}
};

3
src/services/domain/auth.service.ts

@ -8,8 +8,6 @@ import moment from "moment" @@ -8,8 +8,6 @@ import moment from "moment"
import { GlobalContainerService } from "../system"
import { accountService } from "./account.service"
interface ISignIn {
login: string
password: string
@ -20,6 +18,7 @@ const signIn = async (payload: ISignIn) => { @@ -20,6 +18,7 @@ const signIn = async (payload: ISignIn) => {
simpleDispatch(new isLoading({ isLoading: true }))
try {
const { data } = await authApi.signIn({ ...payload, deviceName: 'web' })
if (data) await _saveTokens(data)
await accountService.getAccount()

65
src/services/domain/factories.service.ts

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
import { simpleDispatch } from "@/store/store-helpers";
import { IFactory } from "@/shared";
import { factoriesApi } from "@/api";
import {
ICreateFactory,
IUpdateFactory,
} from "@/api/factories/requests.interface";
import { FactoriesIsLoading } from "@/store/factories";
const fetchFactories = async (): Promise<IFactory[]> => {
simpleDispatch(new FactoriesIsLoading({ isLoading: true }));
try {
const { data: factoriesList } = await factoriesApi.fetchFactories();
return factoriesList;
} catch (err) {
console.log("FETCH FACTORIES ERROR: ", err);
} finally {
simpleDispatch(new FactoriesIsLoading({ isLoading: false }));
}
};
const createFactory = async (payload: ICreateFactory): Promise<IFactory> => {
simpleDispatch(new FactoriesIsLoading({ isLoading: true }));
try {
const { data: newFactory } = await factoriesApi.createFactory(payload);
return newFactory;
} catch (err) {
console.log("CREATE FACTORY ERROR: ", err);
} finally {
simpleDispatch(new FactoriesIsLoading({ isLoading: false }));
}
};
const updateFactory = async (payload: IUpdateFactory): Promise<IFactory> => {
simpleDispatch(new FactoriesIsLoading({ isLoading: true }));
try {
const { data: updatedFactory } = await factoriesApi.updateFactory(payload);
return updatedFactory;
} catch (err) {
console.log("UPDATE FACTORY ERROR: ", err);
} finally {
simpleDispatch(new FactoriesIsLoading({ isLoading: false }));
}
};
const deleteFactory = async (id: number): Promise<void> => {
simpleDispatch(new FactoriesIsLoading({ isLoading: true }));
try {
await factoriesApi.deleteFactory(id);
} catch (err) {
console.log("DELETE FACTORY ERROR: ", err);
} finally {
simpleDispatch(new FactoriesIsLoading({ isLoading: false }));
}
};
export const factoriesService = {
fetchFactories,
createFactory,
updateFactory,
deleteFactory,
};

5
src/services/domain/index.ts

@ -1 +1,4 @@ @@ -1 +1,4 @@
export * from './auth.service'
export * from "./auth.service";
export * from "./factories.service";
export * from "./account.service";
export * from "./taxonomies.service";

3
src/shared/components/fields/index.ts

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export * from "./input-field.component";
export * from "./select-field.component";
export * from "./select-multi-field.component";

29
src/shared/components/fields/input-field.component.tsx

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
import React, { FC, CSSProperties } from "react";
import { Input } from "antd";
interface IProps {
label: string;
value: string | number;
placeholder: string;
style?: CSSProperties;
onChange: (val: string) => void;
}
export const InputField: FC<IProps> = ({
label,
value,
placeholder,
style,
onChange,
}) => {
return (
<div style={{ ...style, width: "100%", marginBottom: 20 }}>
<span>{label}</span>
<Input
placeholder={placeholder}
value={value}
onChange={({ target: { value } }) => onChange(value)}
/>
</div>
);
};

54
src/shared/components/fields/select-field.component.tsx

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
import React, { FC } from "react";
import { Select } from "antd";
interface IProps {
options: any[];
label: string;
placeholder: string;
isDisabled?: boolean;
touched?: boolean;
error?: string;
onChange: (val: string) => void;
}
export const SelectField: FC<IProps> = ({
options,
label,
placeholder,
isDisabled,
touched,
error,
onChange,
}) => {
return (
<div className="form__form-group select-field">
{label ? <span className="form__form-group-label">{label}</span> : null}
<div className="form__form-group-field">
<div className="form__form-group-input-wrap">
<Select
showSearch
style={{ width: "100%" }}
placeholder={placeholder}
optionFilterProp="children"
onChange={onChange}
disabled={isDisabled}
allowClear
filterOption={(input, { props }) =>
props.title.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{options.map((item, index) => (
<Select.Option key={item.value + "-" + index} value={item.value}>
{item.title}
</Select.Option>
))}
</Select>
{touched &&
error && <span className="form__form-group-error">{error}</span>}
</div>
</div>
</div>
);
};

83
src/shared/components/fields/select-multi-field.component.tsx

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import { Select } from "antd";
import React, { FC } from "react";
interface IProps {
label?: string;
placeholder: string;
options: any[];
touched?: boolean;
error?: string;
input?: any;
onChange: () => void;
}
export const SelectMultiField: FC<IProps> = ({
label,
placeholder,
options,
touched,
error,
input,
onChange,
}) => {
return (
<div className="form__form-group select-field">
{label ? <span className="form__form-group-label">{label}</span> : null}
<div className="form__form-group-field">
<div className="form__form-group-input-wrap">
<div style={{ position: "relative" }}>
<Select
showSearch
style={{ width: "100%" }}
placeholder={placeholder}
optionFilterProp="children"
mode="multiple"
onChange={onChange}
allowClear
filterOption={(input, { props }) =>
props.title.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{options.map((item, index) => (
<Select.Option
key={item.value + "-" + index}
value={item.value}
>
{item.title}
</Select.Option>
))}
</Select>
{!input?.value || !input?.value.length ? (
<span
className="ant-select-arrow"
style={{ outline: "currentcolor none medium" }}
>
<i
aria-label="icon: down"
className="anticon anticon-down ant-select-arrow-icon"
>
<svg
viewBox="64 64 896 896"
className=""
data-icon="down"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
>
<path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z" />
</svg>
</i>
</span>
) : null}
</div>
{touched &&
error && <span className="form__form-group-error">{error}</span>}
</div>
</div>
</div>
);
};

7
src/shared/components/index.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
export * from './form'
export * from './layouts'
export * from './elements'
export * from "./form";
export * from "./layouts";
export * from "./elements";
export * from "./fields";

15
src/shared/interfaces/factories.interfaces.ts

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
export interface IFactory {
id: number;
name: string;
shortName: string;
description: string;
path?: string;
directorId: number;
authorId: number;
parentId?: number;
children?: IFactory[];
usersAccessPermission: any[];
createdAt: string;
updatedAt: string;
};

13
src/shared/interfaces/index.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
export * from './token-pair.interfaces'
export * from './user.interfaces'
export * from './permissions.interface'
export * from './comment.interfaces'
export * from './task.interfaces'
export * from './taxonomy.interfaces'
export * from "./token-pair.interfaces";
export * from "./user.interfaces";
export * from "./permissions.interface";
export * from "./comment.interfaces";
export * from "./task.interfaces";
export * from "./taxonomy.interfaces";
export * from "./factories.interfaces";

65
src/shared/interfaces/taxonomy.interfaces.ts

@ -1,54 +1,53 @@ @@ -1,54 +1,53 @@
import { ETaxonomyType } from "../enums";
export interface ITaxonomy {
/** Ідентифікатор */
id: number
/** Ідентифікатор */
id: number;
/** Ідентифікатор батьківської таксономії */
parentId?: number
/** Ідентифікатор батьківської таксономії */
parentId?: number;
/** Дочерні таксономії */
children?: ITaxonomy[]
/** Дочерні таксономії */
children?: ITaxonomy[];
/** Назва */
name?: string
/** Назва */
name?: string;
/** Тип класифікації */
type: ETaxonomyType
/** Тип класифікації */
type: ETaxonomyType;
/** Чи видалено */
isDeleted?: boolean
/** Чи видалено */
isDeleted?: boolean;
/** Чи за замовчуванням */
isDefault?: boolean
/** Чи за замовчуванням */
isDefault?: boolean;
/** Посилання на зображення */
iconUrl?: string
/** Посилання на зображення */
iconUrl?: string;
}
export interface ICreateOrUpdateTaxonomy {
/** Ідентифікатор */
id?: number
/** Ідентифікатор */
id?: number;
/** Ідентифікатор батьківської таксономії */
parentId?: number
/** Ідентифікатор батьківської таксономії */
parentId?: number;
/** Дочерні таксономії */
children?: ITaxonomy[]
/** Дочерні таксономії */
children?: ITaxonomy[];
/** Назва */
name?: string
/** Назва */
name?: string;
/** Тип класифікації */
type: ETaxonomyType
/** Тип класифікації */
type: ETaxonomyType;
/** Чи видалено */
isDeleted?: boolean
/** Чи видалено */
isDeleted?: boolean;
/** Чи за замовчуванням */
isDefault?: boolean
/** Посилання на зображення */
iconUrl?: string
/** Чи за замовчуванням */
isDefault?: boolean;
/** Посилання на зображення */
iconUrl?: string;
}

3
src/store/factories/index.ts

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export * from "./reducer";
export * from "./selectors";
export * from "./types";

19
src/store/factories/reducer.ts

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
import { TFactoriesActions } from "./types";
import { createReducer } from "@bitalikrty/redux-create-reducer";
export interface IFactoriesState {
isLoading: boolean;
}
const initialState: IFactoriesState = {
isLoading: false,
};
export const factoriesReducer = createReducer<
IFactoriesState,
TFactoriesActions
>(initialState, {
IS_LOADING_FACTORIES: (state, action) => {
return { ...state, isLoading: action.payload.isLoading };
},
});

4
src/store/factories/selectors.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
import { RootState } from "@/store";
export const isLoadingFactories = (state: RootState) =>
state.factories.isLoading;

10
src/store/factories/types.ts

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
export class FactoriesIsLoading {
readonly type = "IS_LOADING_FACTORIES";
constructor(
public readonly payload: {
isLoading: boolean;
}
) {}
}
export type TFactoriesActions = FactoriesIsLoading;

54
src/store/index.tsx

@ -1,37 +1,31 @@ @@ -1,37 +1,31 @@
import {
createStore,
combineReducers,
} from 'redux'
import { GlobalContainerService } from '@/services/system'
import { authReducer, IAuthState } from './auth'
import { composeWithDevTools } from 'redux-devtools-extension'
import { accountReducer, IAccountState } from "./account"
import { ISharedState, sharedReducer } from "./shared"
import { ITaxonomiesState, TaxonomiesReducer } from './taxonomies/reducer';
import { createStore, combineReducers } from "redux";
import { GlobalContainerService } from "@/services/system";
import { authReducer, IAuthState } from "./auth";
import { composeWithDevTools } from "redux-devtools-extension";
import { accountReducer, IAccountState } from "./account";
import { ISharedState, sharedReducer } from "./shared";
import { ITaxonomiesState, TaxonomiesReducer } from "./taxonomies/reducer";
import { factoriesReducer, IFactoriesState } from "./factories";
const rootReducer = combineReducers<{
auth: IAuthState
account: IAccountState
taxonomies: ITaxonomiesState
shared: ISharedState
auth: IAuthState;
account: IAccountState;
taxonomies: ITaxonomiesState;
shared: ISharedState;
factories: IFactoriesState;
}>({
auth: authReducer,
account: accountReducer,
taxonomies: TaxonomiesReducer,
// task: taskReducer,
shared: sharedReducer,
})
export type RootState = ReturnType<typeof rootReducer>
auth: authReducer,
account: accountReducer,
taxonomies: TaxonomiesReducer,
// task: taskReducer,
shared: sharedReducer,
factories: factoriesReducer,
});
const store = createStore(
rootReducer,
composeWithDevTools()
)
export type RootState = ReturnType<typeof rootReducer>;
const store = createStore(rootReducer, composeWithDevTools());
GlobalContainerService.set('store', store)
GlobalContainerService.set("store", store);
export default store
export default store;

1
tsconfig.json

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": false,
"strictNullChecks": false,
"noEmit": true,
"jsx": "react",
"baseUrl": "./",

30362
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save