diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..7a9dfa0
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "pwa-chrome",
+ "request": "launch",
+ "name": "Launch Chrome against localhost",
+ "url": "http://localhost:8080",
+ "webRoot": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6ac579c..7492d7b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3163,6 +3163,84 @@
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
},
+ "@emotion/cache": {
+ "version": "11.6.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.6.0.tgz",
+ "integrity": "sha512-ElbsWY1KMwEowkv42vGo0UPuLgtPYfIs9BxxVrmvsaJVvktknsHYYlx5NQ5g6zLDcOTyamlDc7FkRg2TAcQDKQ==",
+ "requires": {
+ "@emotion/memoize": "^0.7.4",
+ "@emotion/sheet": "^1.1.0",
+ "@emotion/utils": "^1.0.0",
+ "@emotion/weak-memoize": "^0.2.5",
+ "stylis": "^4.0.10"
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
+ },
+ "@emotion/memoize": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
+ "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
+ },
+ "@emotion/react": {
+ "version": "11.7.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.0.tgz",
+ "integrity": "sha512-WL93hf9+/2s3cA1JVJlz8+Uy6p6QWukqQFOm2OZO5ki51hfucHMOmbSjiyC3t2Y4RI8XUmBoepoc/24ny/VBbA==",
+ "requires": {
+ "@babel/runtime": "^7.13.10",
+ "@emotion/cache": "^11.6.0",
+ "@emotion/serialize": "^1.0.2",
+ "@emotion/sheet": "^1.1.0",
+ "@emotion/utils": "^1.0.0",
+ "@emotion/weak-memoize": "^0.2.5",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.16.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
+ "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ }
+ }
+ },
+ "@emotion/serialize": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+ "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+ "requires": {
+ "@emotion/hash": "^0.8.0",
+ "@emotion/memoize": "^0.7.4",
+ "@emotion/unitless": "^0.7.5",
+ "@emotion/utils": "^1.0.0",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+ "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
+ "@emotion/utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
+ "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
+ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
+ },
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@@ -3376,6 +3454,11 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
},
+ "@popperjs/core": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
+ "integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
+ },
"@sheerun/mutationobserver-shim": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz",
@@ -3577,6 +3660,8 @@
},
"@testing-library/jest-dom": {
"version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-4.2.4.tgz",
+ "integrity": "sha512-j31Bn0rQo12fhCWOUWy9fl7wtqkp7In/YP2p5ZFyRuiiB9Qs3g+hS4gAmDWONbAHcRmVooNJ5eOHQDCOmUFXHg==",
"requires": {
"@babel/runtime": "^7.5.1",
"chalk": "^2.4.1",
@@ -3601,6 +3686,8 @@
},
"@testing-library/react": {
"version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.5.0.tgz",
+ "integrity": "sha512-di1b+D0p+rfeboHO5W7gTVeZDIK5+maEgstrZbWZSSvxDyfDRkkyBE1AJR5Psd6doNldluXlCWqXriUfqu/9Qg==",
"requires": {
"@babel/runtime": "^7.8.4",
"@testing-library/dom": "^6.15.0",
@@ -3608,7 +3695,9 @@
}
},
"@testing-library/user-event": {
- "version": "7.2.1"
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz",
+ "integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA=="
},
"@types/babel__core": {
"version": "7.1.10",
@@ -3735,6 +3824,14 @@
"@types/react": "*"
}
},
+ "@types/react-transition-group": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
+ "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -5017,6 +5114,11 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
+ "bootstrap": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
+ "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -5472,6 +5574,11 @@
}
}
},
+ "classnames": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+ "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+ },
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -6472,6 +6579,15 @@
"utila": "~0.4"
}
},
+ "dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "requires": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -8116,6 +8232,14 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
+ "history": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.1.0.tgz",
+ "integrity": "sha512-zPuQgPacm2vH2xdORvGGz1wQMuHSIB56yNAy5FnLuwOwgSYyPKptJtcMm6Ev+hRGeS+GzhbmRacHzvlESbFwDg==",
+ "requires": {
+ "@babel/runtime": "^7.7.6"
+ }
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -9842,6 +9966,11 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
+ "memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
+ },
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -12214,6 +12343,8 @@
},
"react": {
"version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
+ "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -12420,6 +12551,8 @@
},
"react-dom": {
"version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
+ "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -12432,11 +12565,30 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
"integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA=="
},
+ "react-fast-compare": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
+ "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
+ },
+ "react-hook-form": {
+ "version": "7.20.4",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.20.4.tgz",
+ "integrity": "sha512-Nvy6TnNMlBoR+qS8FpA8lrqtGJ4uoi/MRYEgMEdBMY0HwHVuC7wB1sk6wTjg7FjOUt7QqMAP2W/vOhTWbKrtkQ=="
+ },
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "react-popper": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz",
+ "integrity": "sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==",
+ "requires": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ }
+ },
"react-redux": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz",
@@ -12449,8 +12601,27 @@
"react-is": "^16.9.0"
}
},
+ "react-router": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.0.2.tgz",
+ "integrity": "sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q==",
+ "requires": {
+ "history": "^5.1.0"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.2.tgz",
+ "integrity": "sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA==",
+ "requires": {
+ "history": "^5.1.0",
+ "react-router": "6.0.2"
+ }
+ },
"react-scripts": {
"version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz",
+ "integrity": "sha512-oSnoWmii/iKdeQiwaO6map1lUaZLmG0xIUyb/HwCVFLT7gNbj8JZ9RmpvMCZ4fB98ZUMRfNmp/ft8uy/xD1RLA==",
"requires": {
"@babel/core": "7.9.0",
"@svgr/webpack": "4.3.3",
@@ -12528,6 +12699,64 @@
}
}
},
+ "react-select": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.2.1.tgz",
+ "integrity": "sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==",
+ "requires": {
+ "@babel/runtime": "^7.12.0",
+ "@emotion/cache": "^11.4.0",
+ "@emotion/react": "^11.1.1",
+ "@types/react-transition-group": "^4.4.0",
+ "memoize-one": "^5.0.0",
+ "prop-types": "^15.6.0",
+ "react-transition-group": "^4.3.0"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.16.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
+ "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ }
+ }
+ },
+ "react-transition-group": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+ "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ }
+ },
+ "reactstrap": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.0.1.tgz",
+ "integrity": "sha512-89VOv7SRlAlpS7RwXhzOQkSWkuhBR8LBsPGfNHifNL3WhtNP9y1sBdTcTYyH1ee2QtI8zRdwD0T5I/blHiwemg==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "@popperjs/core": "^2.6.0",
+ "classnames": "^2.2.3",
+ "prop-types": "^15.5.8",
+ "react-popper": "^2.2.4",
+ "react-transition-group": "^4.4.2"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.16.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
+ "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ }
+ }
+ },
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -12880,6 +13109,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
+ "reselect": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
+ "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
+ },
"resolve": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
@@ -14223,6 +14457,11 @@
}
}
},
+ "stylis": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz",
+ "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg=="
+ },
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@@ -14961,6 +15200,14 @@
"makeerror": "1.0.x"
}
},
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"watchpack": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
diff --git a/package.json b/package.json
index e00a0d2..e8b578c 100644
--- a/package.json
+++ b/package.json
@@ -7,11 +7,17 @@
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"axios": "^0.20.0",
+ "bootstrap": "^5.1.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-hook-form": "^7.20.4",
"react-redux": "^7.2.1",
+ "react-router-dom": "^6.0.2",
"react-scripts": "3.4.3",
- "redux": "^4.0.5"
+ "react-select": "^5.2.1",
+ "reactstrap": "^9.0.1",
+ "redux": "^4.0.5",
+ "reselect": "^4.1.5"
},
"scripts": {
"start": "react-scripts start",
diff --git a/public/images/ecobel.png b/public/images/ecobel.png
new file mode 100644
index 0000000..6c19e02
Binary files /dev/null and b/public/images/ecobel.png differ
diff --git a/src/application/actions/collection.js b/src/application/actions/collection.js
new file mode 100644
index 0000000..5fcd5ca
--- /dev/null
+++ b/src/application/actions/collection.js
@@ -0,0 +1,49 @@
+export const LOAD_All = '[all] load';
+export const LOAD_ALL_SUCCESS = '[all] load success';
+export const LOAD_ITEM_SUCCESS = '[one item] load success';
+export const LOAD_ALL_FAILURE = '[all] load failure';
+export const LOAD_ITEM = '[one item] load';
+export const SAVE_ITEM ='[one item] save';
+
+export const TOGGLE_TAB= '[toggle tab of project] toggle ';
+
+
+export const loadAll = collection => ({
+ type: LOAD_All,
+ payload:collection,
+});
+
+export const load = (items)=> ({
+ type: LOAD_ITEM,
+ payload: {id:items.id, items:items.table},
+});
+
+export const loadItemSuccess = item => ({
+ type: LOAD_ITEM_SUCCESS,
+ payload: item,
+});
+
+
+export const loadAllSuccess = items => ({
+ type: LOAD_ALL_SUCCESS,
+ payload: items,
+});
+
+export const loadAllFailure = error => ({
+ type: LOAD_ALL_FAILURE,
+ payload: error,
+});
+
+export const toggleTab = tabId => ({
+ type: TOGGLE_TAB,
+ payload: tabId,
+});
+
+
+export const save = data => ({
+ type: SAVE_ITEM,
+ payload: data,
+});
+
+
+
diff --git a/src/application/actions/permitActions.js b/src/application/actions/permitActions.js
new file mode 100644
index 0000000..e436dcb
--- /dev/null
+++ b/src/application/actions/permitActions.js
@@ -0,0 +1,46 @@
+export const LOAD_PERMIT = '[load permit]';
+export const LOAD_PERMIT_SUCCESS = '[permit loaded successfully]';
+export const SPLIT_PERMIT_SUCCESS = '[permit splitted successfully]';
+export const SPLIT_PERMIT = '[split Permit]';
+export const SPLIT_AND_SAVE = '[split and save]';
+export const SPLIT_AND_SAVE_SUCCESS = '[splited and saved successfylly]';
+export const SAVE_PERMIT = '[save permit]';
+
+export const TOGGLE_TAB = '[change permit tab]';
+
+export const load = id => ({
+ type: LOAD_PERMIT,
+ payload: id,
+});
+
+
+export const loadPermitSuccess = permit=> ({
+ type: LOAD_PERMIT_SUCCESS,
+ payload: permit,
+});
+
+export const split = payload => ({
+ type: SPLIT_PERMIT,
+ payload: payload,
+});
+
+export const splitPermitSuccess = permitItems=> ({
+ type: SPLIT_PERMIT_SUCCESS,
+ payload: permitItems,
+});
+
+export const splitAndSave = payload => ({
+ type: SPLIT_AND_SAVE,
+ payload: payload,
+});
+
+export const splitAndSavePermitSuccess = permitItems=> ({
+ type: SPLIT_AND_SAVE_SUCCESS,
+ payload: permitItems,
+});
+
+export const toggleTab = tabId => ({
+ type: TOGGLE_TAB,
+ payload: tabId,
+});
+
diff --git a/src/application/actions/projectActions.js b/src/application/actions/projectActions.js
new file mode 100644
index 0000000..1574733
--- /dev/null
+++ b/src/application/actions/projectActions.js
@@ -0,0 +1,13 @@
+export const CREATE_ENVIRONMENT_MODULE = '[create environment module]';
+export const CREATE_SECURITY_MODULE = '[create security module]';
+
+
+export const createEnvironmentModule = payload => ({
+ type: CREATE_ENVIRONMENT_MODULE,
+ payload: payload,
+});
+
+export const createSecurityModule = payload => ({
+ type: CREATE_SECURITY_MODULE,
+ payload: payload,
+});
\ No newline at end of file
diff --git a/src/application/actions/ricActions.js b/src/application/actions/ricActions.js
new file mode 100644
index 0000000..1e29744
--- /dev/null
+++ b/src/application/actions/ricActions.js
@@ -0,0 +1,28 @@
+export const LOAD_RIC = '[load ric]';
+export const LOAD_RIC_SUCCESS = '[load ric success]';
+
+export const UPDATE_RIC_STATUS = '[update ric status]';
+export const UPDATE_RIC_STATUS_SUCCESS = '[update ric status success]'
+
+export const loadRic = projectid => ({
+ type: LOAD_RIC,
+ payload: projectid,
+});
+
+export const loadRicSuccess = ric=> ({
+ type: LOAD_RIC_SUCCESS,
+ payload: ric,
+});
+
+export const updateRicStatus =ric=>({
+ type: UPDATE_RIC_STATUS,
+ payload: ric,
+});
+
+export const updateRicStatusSuccess =ric=>({
+ type: UPDATE_RIC_STATUS_SUCCESS,
+ payload: ric,
+});
+
+
+
diff --git a/src/application/actions/todos.js b/src/application/actions/todos.js
deleted file mode 100644
index 2168a4e..0000000
--- a/src/application/actions/todos.js
+++ /dev/null
@@ -1,29 +0,0 @@
-export const LOAD_TODOS = '[todos] load';
-export const LOAD_TODOS_SUCCESS = '[todos] load success';
-export const LOAD_TODOS_FAILURE = '[todos] load failure';
-export const SET_TODOS = '[todos] set';
-export const PUT_TODO = '[todos] put';
-
-export const loadTodos = {
- type: LOAD_TODOS,
-};
-
-export const loadTodosSuccess = todos => ({
- type: LOAD_TODOS_SUCCESS,
- payload: todos,
-});
-
-export const loadTodosFailure = error => ({
- type: LOAD_TODOS_FAILURE,
- payload: error,
-});
-
-export const setTodos = todos => ({
- type: SET_TODOS,
- payload: todos,
-});
-
-export const putTodo = todo => ({
- type: PUT_TODO,
- payload: todo,
-});
\ No newline at end of file
diff --git a/src/application/actions/ui.js b/src/application/actions/ui.js
index 497d866..b8f7392 100644
--- a/src/application/actions/ui.js
+++ b/src/application/actions/ui.js
@@ -9,4 +9,4 @@ export const pageLoaded = {
export const setLoading = isLoading => ({
type: isLoading ? SET_LOADING_ON : SET_LOADING_OFF,
payload: isLoading,
-});
\ No newline at end of file
+});
diff --git a/src/application/middleware/collection.js b/src/application/middleware/collection.js
new file mode 100644
index 0000000..46844c1
--- /dev/null
+++ b/src/application/middleware/collection.js
@@ -0,0 +1,64 @@
+import { loadAllFailure, loadAllSuccess,loadItemSuccess, LOAD_All, SAVE_ITEM, LOAD_ITEM, } from "../actions/collection";
+import * as uiActions from '../actions/ui';
+
+const loadFlow = ({ api }) => ({ dispatch }) => next => async (action) => {
+ next(action);
+
+const loadAll = async ()=>{
+ try {
+ //log('blabla');
+ dispatch(uiActions.setLoading(true));
+ const items = await api[action.payload].getAll();
+ dispatch(loadAllSuccess(items));
+ dispatch(uiActions.setLoading(false));
+ } catch (error) {
+ dispatch(loadAllFailure(error));
+ }
+}
+
+const load = async ()=>{
+ try {
+ dispatch(uiActions.setLoading(true));
+ const item = await api[action.payload.items].get(action.payload.id);
+ dispatch(loadItemSuccess(item));
+ dispatch(uiActions.setLoading(false));
+ } catch (error) {
+ dispatch(loadAllFailure(error));
+ }
+}
+
+const save = async ()=>{
+ try {
+ await api[action.payload.items].save(action.payload.data);
+ //dispatch(saveSuccess)
+ } catch (error) {
+ dispatch(loadAllFailure(error));
+ }
+}
+
+
+switch(action.type) {
+ case LOAD_All:
+ loadAll();
+ break;
+ case LOAD_ITEM:
+ load();
+ break;
+ case SAVE_ITEM:
+ save();
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*const putProjectsFlow = () => ({ dispatch, getState }) => next => action => {
+ next(action);
+}*/
+
+
+
+export default [
+ loadFlow,
+]
\ No newline at end of file
diff --git a/src/application/middleware/companies.test.js b/src/application/middleware/companies.test.js
new file mode 100644
index 0000000..264fda9
--- /dev/null
+++ b/src/application/middleware/companies.test.js
@@ -0,0 +1,43 @@
+// import { loadCompaniesSuccess, LOAD_COMPANIES } from '../actions/collection';
+// import companiesMiddleware from './companies';
+
+// describe('companies middleware', () => {
+// describe('load companies flow', () => {
+// const [ loadCompaniesFlow ] = companiesMiddleware;
+
+// const dummyCompany = {
+// id: '1',
+// title: 'Dummy company',
+// completed: true,
+// };
+// const api = {
+// companies: {
+// getAll: jest.fn().mockImplementationOnce(() => new Promise((resolve) => resolve([dummyCompany])))
+// }
+// }
+// const dispatch = jest.fn();
+// const next = jest.fn();
+// const action = {
+// type: LOAD_COMPANIES
+// }
+
+
+// it('passes action to next middleware', async () => {
+// await loadCompaniesFlow({ api })({ dispatch })(next)(action);
+
+// expect(next).toHaveBeenCalledWith(action);
+// });
+
+// it('calls api.companies.getAll at least once', async () => {
+// await loadCompaniesFlow({ api })({ dispatch })(next)(action);
+
+// expect(api.companies.getAll).toHaveBeenCalled();
+// });c
+
+// it('calls api.companies.getAll at least once', async () => {
+// await loadCompaniesFlow({ api })({ dispatch })(next)(action);
+
+// expect(dispatch).toHaveBeenCalledWith(loadCompaniesSuccess([dummyCompany]));
+// });
+// });
+// });
\ No newline at end of file
diff --git a/src/application/middleware/index.js b/src/application/middleware/index.js
index fcce8f7..35c1f04 100644
--- a/src/application/middleware/index.js
+++ b/src/application/middleware/index.js
@@ -1,7 +1,11 @@
import ui from './ui';
-import todos from './todos';
+import collection from './collection';
+import permit from './permitMiddleware';
+import ric from './ricMiddleware'
export default [
...ui,
- ...todos,
+ ...collection,
+ ...permit,
+ ...ric
]
\ No newline at end of file
diff --git a/src/application/middleware/permitMiddleware.js b/src/application/middleware/permitMiddleware.js
new file mode 100644
index 0000000..ea5db51
--- /dev/null
+++ b/src/application/middleware/permitMiddleware.js
@@ -0,0 +1,71 @@
+import * as uiActions from '../actions/ui';
+import { loadPermitSuccess, splitPermitSuccess,splitAndSavePermitSuccess,
+ LOAD_PERMIT,SPLIT_PERMIT, SAVE_PERMIT, SPLIT_AND_SAVE } from "../actions/permitActions";
+
+
+
+const permitFlow = ({ api }) => ({ dispatch }) => next => async (action) => {
+ next(action);
+
+const load = async ()=>{
+ try {
+ dispatch(uiActions.setLoading(true))
+ const permit = await api.permits.get(action.payload);
+ dispatch(loadPermitSuccess(permit));
+ dispatch(uiActions.setLoading(false));
+ } catch (error) {
+ //dispatch(loadAllFailure(error));
+ }
+}
+
+const save = async ()=>{
+ try {
+ await api.permits.save(action.payload.data);
+ //dispatch (Saved)
+ } catch (error) {
+ // dispatch(loadAllFailure(error));
+ }
+}
+
+const split = async ()=>{
+ try {
+ const permitItems= await api.permits.split(action.payload);
+ dispatch(splitPermitSuccess(permitItems));
+
+ } catch (error) {
+ // dispatch(loadAllFailure(error));
+ }
+}
+
+const splitAndSave = async ()=>{
+ try {
+ const permitItems= await api.permits.splitAndSave(action.payload);
+ dispatch(splitAndSavePermitSuccess(permitItems));
+
+ } catch (error) {
+ // dispatch(loadAllFailure(error));
+ }
+}
+
+
+switch(action.type) {
+ case LOAD_PERMIT:
+ load();
+ break;
+ case SPLIT_PERMIT:
+ split();
+ break;
+ case SPLIT_AND_SAVE:
+ splitAndSave();
+ break;
+ case SAVE_PERMIT:
+ save();
+ break;
+ default:
+ break;
+ }
+}
+
+export default [
+ permitFlow
+]
\ No newline at end of file
diff --git a/src/application/middleware/ricMiddleware.js b/src/application/middleware/ricMiddleware.js
new file mode 100644
index 0000000..688219c
--- /dev/null
+++ b/src/application/middleware/ricMiddleware.js
@@ -0,0 +1,48 @@
+
+import { loadRicSuccess, updateRicStatusSuccess, LOAD_RIC, UPDATE_RIC_STATUS } from '../actions/ricActions';
+import * as uiActions from '../actions/ui';
+
+
+
+const ricFlow = ({ api }) => ({ dispatch }) => next => async (action) => {
+ next(action);
+
+ const loadRic = async ()=>{
+ try {
+ dispatch(uiActions.setLoading(true))
+ const ric = await api.projects.getRic(action.payload);
+ dispatch(loadRicSuccess(ric));
+ dispatch(uiActions.setLoading(false));
+ } catch (error) {
+ //dispatch(loadAllFailure(error));
+ }
+ }
+
+ const updateStatus = async()=>{
+ try{
+ await api.ric.updateStatus(action.payload);
+ dispatch(updateRicStatusSuccess(action.payload));
+ }
+ catch (error) {
+ //dispatch(loadAllFailure(error));
+ }
+ }
+
+ switch(action.type) {
+ case LOAD_RIC:
+ loadRic();
+ break;
+ case UPDATE_RIC_STATUS:
+ updateStatus();
+ break;
+ default:
+ break;
+ }
+
+
+
+}
+
+export default [
+ ricFlow
+]
\ No newline at end of file
diff --git a/src/application/middleware/todos.js b/src/application/middleware/todos.js
deleted file mode 100644
index 4f1e5cb..0000000
--- a/src/application/middleware/todos.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { loadTodosFailure, loadTodosSuccess, LOAD_TODOS, PUT_TODO, setTodos } from "../actions/todos";
-import * as uiActions from '../actions/ui';
-
-const loadTodosFlow = ({ api }) => ({ dispatch }) => next => async (action) => {
- next(action);
-
- if (action.type === LOAD_TODOS) {
- try {
- dispatch(uiActions.setLoading(true));
- const todos = await api.todos.getAll();
- dispatch(loadTodosSuccess(todos));
- dispatch(uiActions.setLoading(false));
- } catch (error) {
- dispatch(loadTodosFailure(error));
- }
- }
-}
-
-const putTodoFlow = () => ({ dispatch, getState }) => next => action => {
-
- if (action.type === PUT_TODO) {
- const oldTodosClone = getState().todos.allTodos.map(todo => ({...todo}));
-
- const newTodo = action.payload;
-
- const index = oldTodosClone.findIndex(todo => todo.id === newTodo.id);
-
- oldTodosClone[index] = newTodo;
-
- dispatch(setTodos(oldTodosClone));
- }
-
- next(action);
-}
-
-export default [
- loadTodosFlow,
- putTodoFlow,
-]
\ No newline at end of file
diff --git a/src/application/middleware/todos.test.js b/src/application/middleware/todos.test.js
deleted file mode 100644
index 3ff7717..0000000
--- a/src/application/middleware/todos.test.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { loadTodosSuccess, LOAD_TODOS } from '../actions/todos';
-import todosMiddleware from './todos';
-
-describe('todos middleware', () => {
- describe('load todos flow', () => {
- const [ loadTodosFlow ] = todosMiddleware;
-
- const dummyTodo = {
- id: '1',
- title: 'Dummy todo',
- completed: true,
- };
- const api = {
- todos: {
- getAll: jest.fn().mockImplementationOnce(() => new Promise((resolve) => resolve([dummyTodo])))
- }
- }
- const dispatch = jest.fn();
- const next = jest.fn();
- const action = {
- type: LOAD_TODOS
- }
-
-
- it('passes action to next middleware', async () => {
- await loadTodosFlow({ api })({ dispatch })(next)(action);
-
- expect(next).toHaveBeenCalledWith(action);
- });
-
- it('calls api.todos.getAll at least once', async () => {
- await loadTodosFlow({ api })({ dispatch })(next)(action);
-
- expect(api.todos.getAll).toHaveBeenCalled();
- });
-
- it('calls api.todos.getAll at least once', async () => {
- await loadTodosFlow({ api })({ dispatch })(next)(action);
-
- expect(dispatch).toHaveBeenCalledWith(loadTodosSuccess([dummyTodo]));
- });
- });
-});
\ No newline at end of file
diff --git a/src/application/middleware/ui.js b/src/application/middleware/ui.js
index d5a3a4e..a23492f 100644
--- a/src/application/middleware/ui.js
+++ b/src/application/middleware/ui.js
@@ -1,12 +1,11 @@
import { PAGE_LOADED } from "../actions/ui";
-import * as todosActions from '../actions/todos';
+
const pageLoadedFlow = ({ log }) => ({ dispatch }) => next => action => {
next(action);
if (action.type === PAGE_LOADED) {
log('page loaded');
- dispatch(todosActions.loadTodos);
}
}
diff --git a/src/application/reducers/collectionReducer.js b/src/application/reducers/collectionReducer.js
new file mode 100644
index 0000000..511fc8c
--- /dev/null
+++ b/src/application/reducers/collectionReducer.js
@@ -0,0 +1,23 @@
+import { LOAD_ALL_SUCCESS,LOAD_ITEM_SUCCESS, TOGGLE_TAB } from "../actions/collection";
+
+const initialState = {
+ all: [],
+ selected:null,
+ activeTab:"general",
+ error: null
+};
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case LOAD_ALL_SUCCESS:
+ return {...state, all: action.payload, error: null, selected:null };
+ case LOAD_ITEM_SUCCESS:
+ return {...state, selected: action.payload, error: null, activeTab:"general"};
+ case TOGGLE_TAB:
+ return {...state,activeTab:action.payload};
+ default:
+ return state;
+ }
+}
+
+export default reducer;
\ No newline at end of file
diff --git a/src/application/reducers/index.js b/src/application/reducers/index.js
index a916dd1..290896c 100644
--- a/src/application/reducers/index.js
+++ b/src/application/reducers/index.js
@@ -1,8 +1,12 @@
import { combineReducers } from 'redux';
import ui from './ui';
-import todos from './todos';
+import collectionReducer from './collectionReducer';
+import permitReducer from './permitReducer';
+import ricReducer from './ricReducer';
export default combineReducers({
ui,
- todos,
+ collectionReducer,
+ permitReducer,
+ ricReducer,
})
\ No newline at end of file
diff --git a/src/application/reducers/permitReducer.js b/src/application/reducers/permitReducer.js
new file mode 100644
index 0000000..26946ee
--- /dev/null
+++ b/src/application/reducers/permitReducer.js
@@ -0,0 +1,36 @@
+import { LOAD_PERMIT_SUCCESS, TOGGLE_TAB,SPLIT_PERMIT_SUCCESS, SPLIT_AND_SAVE_SUCCESS } from "../actions/permitActions";
+
+const initialState = {
+ permit:null,
+ contextOptions:null,
+ contexts:null,
+ authorities:null,
+
+ activeTab:"general",
+ error: null,
+ items:null
+};
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case LOAD_PERMIT_SUCCESS:
+ return {...state,
+ permit: action.payload.permit,
+ contextOptions:action.payload.contextOptions,
+ contexts:action.payload.contexts,
+ authorities:action.payload.authorities,
+ items : action.payload.permitItems,
+
+ error: null, activeTab:"general"};
+ case SPLIT_PERMIT_SUCCESS:
+ return {...state, error: null,items:action.payload.permitItems, activeTab:"detail"};
+ case SPLIT_AND_SAVE_SUCCESS:
+ return {...state, error: null,items:action.payload, activeTab:"detail"};
+ case TOGGLE_TAB:
+ return {...state,activeTab:action.payload};
+ default:
+ return state;
+ }
+}
+
+export default reducer;
\ No newline at end of file
diff --git a/src/application/reducers/ricReducer.js b/src/application/reducers/ricReducer.js
new file mode 100644
index 0000000..3eec09e
--- /dev/null
+++ b/src/application/reducers/ricReducer.js
@@ -0,0 +1,33 @@
+
+import {LOAD_RIC_SUCCESS, UPDATE_RIC_STATUS_SUCCESS} from '../actions/ricActions'
+
+const initialState = {
+ ric:[],
+ statusOptions:null,
+ error: null,
+};
+
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case LOAD_RIC_SUCCESS:
+ return {...state,
+ ric: action.payload.ric,
+ statusOptions:action.payload.statusOptions,
+ //error: null,
+ }
+ case UPDATE_RIC_STATUS_SUCCESS:
+ const newRic = [...state.ric]
+ const item = newRic.find(s => s.codeId == action.payload.codeId);
+ if(item.statusId !==action.payload.statusId)
+ {
+ item.statusId=action.payload.statusId;
+ return {...state, ric:newRic}
+ }
+ return state;
+ default:
+ return state;
+ }
+}
+
+export default reducer;
\ No newline at end of file
diff --git a/src/application/reducers/todos.js b/src/application/reducers/todos.js
deleted file mode 100644
index ccb0f50..0000000
--- a/src/application/reducers/todos.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { LOAD_TODOS_SUCCESS, SET_TODOS } from "../actions/todos";
-
-const initialState = {
- allTodos: [],
- error: null
-};
-
-const reducer = (state = initialState, action) => {
- switch (action.type) {
- case LOAD_TODOS_SUCCESS:
- return { allTodos: action.payload, error: null };
- case SET_TODOS:
- return { allTodos: action.payload, error: null };
- default:
- return state;
- }
-}
-
-export default reducer;
\ No newline at end of file
diff --git a/src/application/reducers/ui.js b/src/application/reducers/ui.js
index b761b02..e14455f 100644
--- a/src/application/reducers/ui.js
+++ b/src/application/reducers/ui.js
@@ -10,6 +10,6 @@ export default (state = initialState, action) => {
case (uiActions.SET_LOADING_OFF):
return { ...state, loading: action.payload };
default:
- return state;
+ return state
}
}
\ No newline at end of file
diff --git a/src/application/selectors/collection.js b/src/application/selectors/collection.js
new file mode 100644
index 0000000..abe7402
--- /dev/null
+++ b/src/application/selectors/collection.js
@@ -0,0 +1,3 @@
+export const getItemsSelector = state => state.collectionReducer.all;
+export const getItemSelector = state => state.collectionReducer.selected;
+export const getActiveTabSelector = state => state.collectionReducer.activeTab;
\ No newline at end of file
diff --git a/src/application/selectors/permits.js b/src/application/selectors/permits.js
new file mode 100644
index 0000000..c53eb56
--- /dev/null
+++ b/src/application/selectors/permits.js
@@ -0,0 +1,8 @@
+export const getPermitItems = state => state.permitReducer.items;
+export const getPermit = state => state.permitReducer.permit;
+export const getActiveTabSelector= state=> state.permitReducer.activeTab;
+export const getAuthorities = state => state.permitReducer.authorities;
+export const getContextOptions = state => state.permitReducer.contextOptions;
+export const getContexts = state => state.permitReducer.contexts;
+
+
diff --git a/src/application/selectors/ricSelector.js b/src/application/selectors/ricSelector.js
new file mode 100644
index 0000000..0c298f1
--- /dev/null
+++ b/src/application/selectors/ricSelector.js
@@ -0,0 +1,15 @@
+
+import { createSelector } from 'reselect'
+
+export const getStatusOptions = state => state.ricReducer.statusOptions;
+export const getRic = state => state.ricReducer.ric;
+export const getError = state => state.ricReducer.error;
+
+const selectItems = state => state.ricReducer.ric;
+const selectItemId = (_,itemId) => itemId
+
+
+export const selectItemById= createSelector(
+ selectItems, selectItemId,
+ (items, id) => {return items[id];}
+ )
\ No newline at end of file
diff --git a/src/application/selectors/todos.js b/src/application/selectors/todos.js
deleted file mode 100644
index d43f6f0..0000000
--- a/src/application/selectors/todos.js
+++ /dev/null
@@ -1 +0,0 @@
-export const getTodos = state => state.todos.allTodos;
\ No newline at end of file
diff --git a/src/assets/env.png b/src/assets/env.png
new file mode 100644
index 0000000..6bc10b0
Binary files /dev/null and b/src/assets/env.png differ
diff --git a/src/assets/secu.png b/src/assets/secu.png
new file mode 100644
index 0000000..450d542
Binary files /dev/null and b/src/assets/secu.png differ
diff --git a/src/components/Permit/Detail.js b/src/components/Permit/Detail.js
new file mode 100644
index 0000000..091febc
--- /dev/null
+++ b/src/components/Permit/Detail.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import { Table } from 'reactstrap';
+import './Detail.module.css';
+
+export default (props)=>{
+
+const {permitItems} = props
+ const Head = () => (
+ | Sequence |
+ Description |
+ Comment |
+ Aspect |
+ Codes |
+
);
+ return (permitItems !== undefined &&
+
+
+
+
+
+ {permitItems.map((item, index) => {
+ return (
+
+
+ | {item.sequence} |
+
+
+ {item.headers.map((header, key) => {
+ return (
+ React.createElement("h"+ (header.level +1),'',header.content)
+
+ )
+ })}
+
+
+ {item.description}
+ |
+ {item.comment} |
+ {item.aspect} |
+
)
+ })}
+
+
+
+ );
+}
diff --git a/src/components/Permit/Detail.module.css b/src/components/Permit/Detail.module.css
new file mode 100644
index 0000000..2edfed5
--- /dev/null
+++ b/src/components/Permit/Detail.module.css
@@ -0,0 +1,6 @@
+h1 {
+ color: rgba(4, 83, 15, 0.959);
+ font-size: 10px;
+}
+
+
diff --git a/src/components/Permit/General.js b/src/components/Permit/General.js
new file mode 100644
index 0000000..bd96097
--- /dev/null
+++ b/src/components/Permit/General.js
@@ -0,0 +1,130 @@
+import React, {useEffect} from 'react'
+import { DropDown } from '../../components/boxes/DropDown';
+import Multiselect from "../../components/boxes/MultiSelect";
+import * as itemsActions from '../../application/actions/collection';
+import * as permitActions from '../../application/actions/permitActions';
+
+export default (props)=> {
+
+ const {dispatch,
+ permit,
+ authorities,
+ contextOptions,
+ contexts,
+ register,
+ handleSubmit,
+ setValue,
+ getValues,
+ control }= props;
+
+ const onSubmit = data => {
+ dispatch(itemsActions.save({data:data, items:'permits'}));
+ }
+
+ const onSplit = data => {
+ dispatch(permitActions.split(data));
+ }
+
+ const onSplitAndSave = data =>{
+ dispatch(permitActions.splitAndSave(data));
+ }
+
+ useEffect(()=>{
+ if(permit!==null)
+ {
+ //use this:
+ // reset(permit);
+ //or this, but not both!!
+ setValue('description', permit.description, )
+ setValue('contexts', contexts.map(p=>p.value))
+ setValue('authorityId', permit.authorityId)
+ setValue('subject', permit.subject)
+ setValue('projectId',permit.projectId)
+ setValue('permitId',permit.id)
+ setValue('rawContent', permit.rawContent)
+ };
+ },[setValue,permit, contexts]);
+
+
+ return(<>
+
+ >)
+
+}
diff --git a/src/components/Project/General.js b/src/components/Project/General.js
new file mode 100644
index 0000000..e7718b4
--- /dev/null
+++ b/src/components/Project/General.js
@@ -0,0 +1,98 @@
+import React from 'react'
+
+import { DropDown } from '../../components/boxes/DropDown';
+import * as itemsActions from '../../application/actions/collection';
+
+
+
+export default (props)=>{
+
+const {
+ register,
+ project,
+ handleSubmit,
+ dispatch
+
+}=props
+
+const onSubmit = d => {
+ dispatch(itemsActions.save({data:d, items:'projects'}));
+}
+
+return(<>
+
+
+>)
+}
\ No newline at end of file
diff --git a/src/components/Project/Installations.js b/src/components/Project/Installations.js
new file mode 100644
index 0000000..f65e15d
--- /dev/null
+++ b/src/components/Project/Installations.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import { Table } from 'reactstrap';
+
+export default (props) => {
+
+ const Head = () => {
+ return (
+
+ | Code |
+ Type |
+
);
+ }
+
+ const { collection } = props
+ return (collection !== undefined &&
+
+
+
+
+
+ {collection.map((item, key) => {
+ return (
+
+ | {item.codeRef} |
+ Environment |
+
)
+ })}
+
+
+
+ );
+ }
\ No newline at end of file
diff --git a/src/components/Project/Module.js b/src/components/Project/Module.js
new file mode 100644
index 0000000..ed213fb
--- /dev/null
+++ b/src/components/Project/Module.js
@@ -0,0 +1,89 @@
+import React, { useEffect } from 'react'
+import { useForm } from "react-hook-form";
+
+import { DropDown } from '../../components/boxes/DropDown';
+import * as itemsActions from '../../application/actions/collection';
+
+export default (props)=>{
+
+ const {module, watchFrequencies, dispatch, }= props;
+ const {register, handleSubmit,setValue}= useForm({ mode: 'onBlur' });
+
+ const onSubmit = d => {
+ //dispatch(itemsActions.save({data:d, items:'modules'}));
+ console.log(d)
+ }
+
+ useEffect(()=>{
+ setValue('watchFrequencyId',module.watchFrequencyId);
+ setValue('activeYear', module.activeYear);
+ setValue('watchActive', module.watchActive );
+ setValue('multiSites', module.multiSites );
+ },[module])
+
+return (<>
+
+
+>)
+}
diff --git a/src/components/Project/Permits.js b/src/components/Project/Permits.js
new file mode 100644
index 0000000..f1ff989
--- /dev/null
+++ b/src/components/Project/Permits.js
@@ -0,0 +1,57 @@
+import React from 'react';
+import {Link} from 'react-router-dom';
+import { Table } from 'reactstrap';
+
+import * as uiActions from '../../application/actions/ui';
+
+export default (props) => {
+
+ const Head = () => {
+ return (
+
+ | Description |
+ Encoding time |
+ Subject |
+ Delivery |
+ Term |
+ Authority |
+ rawContent |
+ |
+ |
+
);
+ }
+
+ const { collection, dispatch } = props
+ return (collection !== undefined &&
+ );
+ }
\ No newline at end of file
diff --git a/src/components/Project/Sites.js b/src/components/Project/Sites.js
new file mode 100644
index 0000000..f6faf43
--- /dev/null
+++ b/src/components/Project/Sites.js
@@ -0,0 +1,5 @@
+import React from "react"
+
+export default ()=>{
+ return (Sites
)
+}
\ No newline at end of file
diff --git a/src/components/boxes/DropDown.js b/src/components/boxes/DropDown.js
new file mode 100644
index 0000000..8f6f795
--- /dev/null
+++ b/src/components/boxes/DropDown.js
@@ -0,0 +1,27 @@
+import React from 'react'
+
+export const DropDown = (props) => {
+
+ const {
+ collection,
+ placeholder,
+ _name,
+ register,
+
+ } = props
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/boxes/MultiSelect.js b/src/components/boxes/MultiSelect.js
new file mode 100644
index 0000000..59c899c
--- /dev/null
+++ b/src/components/boxes/MultiSelect.js
@@ -0,0 +1,39 @@
+import React from "react";
+import SelectMultiple from "react-select";
+import { Controller } from "react-hook-form";
+
+const Multiselect = ({ label, name, values = [], control }) => {
+ const options = values.map((value) => ({
+ label: value,
+ value: value
+ }));
+
+ return (
+
+
+ {
+ return (
+
+ onChange(options?.map((option) => option.value))
+ }
+ onBlur={onBlur}
+ value={options.filter((option) => value?.includes(option.value))}
+ defaultValue={options.filter((option) =>
+ value?.includes(option.value)
+ )}
+ />
+ );
+ }}
+ />
+
+ );
+};
+
+export default Multiselect;
diff --git a/src/components/containers/Collection.js b/src/components/containers/Collection.js
new file mode 100644
index 0000000..4c3cc19
--- /dev/null
+++ b/src/components/containers/Collection.js
@@ -0,0 +1,33 @@
+import React, { Component } from 'react';
+import { Table } from 'reactstrap';
+
+export default (props) => {
+ const { handleAddPermit, handleDeletePermit, collection, Head } = props
+ return (collection !== undefined &&
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/components/layout/MainNavigation.js b/src/components/layout/MainNavigation.js
new file mode 100644
index 0000000..f6a5d9c
--- /dev/null
+++ b/src/components/layout/MainNavigation.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import {Link} from 'react-router-dom';
+import classes from './MainNavigation.module.css';
+
+
+function MainNavigation(){
+return
+
+
+

+
+
+
+
+
+}
+
+export default MainNavigation;
diff --git a/src/components/layout/MainNavigation.module.css b/src/components/layout/MainNavigation.module.css
new file mode 100644
index 0000000..49c6cba
--- /dev/null
+++ b/src/components/layout/MainNavigation.module.css
@@ -0,0 +1,47 @@
+.header {
+
+ height: 5rem;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background-color: #464c97;
+ padding: 0 10%;
+ }
+
+ .logo {
+ font-size: 2rem;
+ color: white;
+ font-weight: bold;
+ }
+
+ .header ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ align-items: baseline;
+ }
+
+ .header li {
+ margin-left: 3rem;
+ }
+
+ .header a {
+ text-decoration: none;
+ font-size: 1.5rem;
+ color: #fcb8d2;
+ }
+
+ .header a:hover,
+ .header a:active,
+ .header a.active {
+ color: white;
+ }
+
+ .badge {
+ background-color: #cc2062;
+ color: white;
+ border-radius: 12px;
+ padding: 0 1rem;
+ margin-left: 0.5rem;
+ }
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 45c0b7e..22b561b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,13 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import {BrowserRouter} from 'react-router-dom';
import { Provider } from 'react-redux';
+
import { configureStore } from './application/store';
import services from './infrastructure/services';
import App from './views';
+import 'bootstrap/dist/css/bootstrap.css';
+import './views/Ric.css'
ReactDOM.render(
+
-
+
+
+
,
document.getElementById('root')
);
\ No newline at end of file
diff --git a/src/infrastructure/services/api/todos/index.js b/src/infrastructure/services/api/companies/index.js
similarity index 55%
rename from src/infrastructure/services/api/todos/index.js
rename to src/infrastructure/services/api/companies/index.js
index 13b4ebe..e147e1a 100644
--- a/src/infrastructure/services/api/todos/index.js
+++ b/src/infrastructure/services/api/companies/index.js
@@ -2,8 +2,9 @@ import axios from 'axios';
export default {
getAll: async () => {
- const response = await axios.get('https://jsonplaceholder.typicode.com/todos');
-
+ const response = await axios.get('https://localhost:44351/api/Companies/All');
return response.data
}
+
+
}
\ No newline at end of file
diff --git a/src/infrastructure/services/api/index.js b/src/infrastructure/services/api/index.js
index bf56e09..daf08d1 100644
--- a/src/infrastructure/services/api/index.js
+++ b/src/infrastructure/services/api/index.js
@@ -1,5 +1,11 @@
-import todos from './todos';
+import companies from './companies';
+import projects from './projects';
+import permits from './permits';
+import ric from './ric';
export default {
- todos,
+ companies,
+ projects,
+ permits,
+ ric,
};
\ No newline at end of file
diff --git a/src/infrastructure/services/api/permits/index.js b/src/infrastructure/services/api/permits/index.js
new file mode 100644
index 0000000..c5e2871
--- /dev/null
+++ b/src/infrastructure/services/api/permits/index.js
@@ -0,0 +1,27 @@
+import axios from 'axios';
+const endpoint='https://localhost:44351/api/permits/';
+export default {
+
+
+ get: async function(id) {
+ const response = await axios.get(endpoint+ 'get/'+id);
+ return response.data
+ },
+
+ save: async function(paload){
+ const response = await axios.put(endpoint+ 'update', paload);
+ return response.data
+ },
+
+ split: async payload =>{
+ ///api/permits/split/
+ const response = await axios.post(endpoint+'split',payload);
+ return response.data
+ },
+
+ splitAndSave: async payload =>{
+ ///api/permits/split/
+ const response = await axios.post(endpoint+'splitAndSave',payload);
+ return response.data
+ }
+}
diff --git a/src/infrastructure/services/api/projects/index.js b/src/infrastructure/services/api/projects/index.js
new file mode 100644
index 0000000..91a198b
--- /dev/null
+++ b/src/infrastructure/services/api/projects/index.js
@@ -0,0 +1,24 @@
+import axios from 'axios';
+const endpoint='https://localhost:44351/api/projects/';
+export default {
+
+ getAll: async () => {
+ const response = await axios.get(endpoint+'all');
+ return response.data
+ },
+ get: async function(id) {
+ const response = await axios.get(endpoint+ 'Get/'+id);
+ return response.data
+ },
+
+ save: async function(data){
+ const response = await axios.put(endpoint+ 'update', data);
+ return response.data
+ },
+
+ getRic:async function(projectid) {
+ const response = await axios.get(endpoint+ 'GetRic/'+projectid);
+ return response.data
+ },
+}
+
diff --git a/src/infrastructure/services/api/ric/index.js b/src/infrastructure/services/api/ric/index.js
new file mode 100644
index 0000000..34c4f8f
--- /dev/null
+++ b/src/infrastructure/services/api/ric/index.js
@@ -0,0 +1,9 @@
+import axios from 'axios';
+const endpoint='https://localhost:44351/api/projects/';
+export default {
+
+ updateStatus: async (ric) => {
+ const response = await axios.put(endpoint+'UpdateRicStatus/',ric);
+ return response.data
+ },
+}
\ No newline at end of file
diff --git a/src/views/Companies.js b/src/views/Companies.js
new file mode 100644
index 0000000..17f8815
--- /dev/null
+++ b/src/views/Companies.js
@@ -0,0 +1,58 @@
+import React, { useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+
+import { getItemsSelector } from '../application/selectors/collection';
+import { pageLoaded } from '../application/actions/ui';
+import { getLoading } from '../application/selectors/ui';
+
+import * as companyActions from '../application/actions/collection';
+
+export default () => {
+
+ const dispatch = useDispatch();
+ const companies = useSelector(getItemsSelector);
+ const loading = useSelector(getLoading);
+ useEffect(() => {
+ dispatch(pageLoaded);
+ dispatch(companyActions.loadAll('companies'));
+ }, [dispatch]);
+
+ return (
+ <>
+ Companies
+ {loading ? 'Loading companies...' : (
+
+
+
+
+ | Name |
+ Adr |
+ Adr |
+ City |
+ Country |
+
+
+ {companies.map(company => (
+
+ dispatch(putCompany({...company, completed: !company.completed }))}
+ >
+ | {company.name} |
+ {company.address1} |
+ {company.address2} |
+ {company.countryId} |
+
+ ))}
+
+
+
+ )}
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/views/Installations.js b/src/views/Installations.js
new file mode 100644
index 0000000..8e4c83b
--- /dev/null
+++ b/src/views/Installations.js
@@ -0,0 +1,6 @@
+import React from "react";
+
+export default ()=>{
+ return (Installations
);
+
+}
\ No newline at end of file
diff --git a/src/views/NotFoundError.js b/src/views/NotFoundError.js
new file mode 100644
index 0000000..ad1db75
--- /dev/null
+++ b/src/views/NotFoundError.js
@@ -0,0 +1,10 @@
+import React from 'react';
+//import './NotFound.css'
+
+export const NotFound = (props) => {
+ return (
+
+ "404 SORRY COULDN'T FIND IT!!!"
+
+ )
+}
\ No newline at end of file
diff --git a/src/views/Permit.js b/src/views/Permit.js
new file mode 100644
index 0000000..5e4fef7
--- /dev/null
+++ b/src/views/Permit.js
@@ -0,0 +1,125 @@
+import React, {useEffect} from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import {useParams,Link} from "react-router-dom";
+import { useForm } from "react-hook-form";
+
+
+import classnames from 'classnames';
+import { getPermitItems, getPermit,getAuthorities, getContextOptions, getContexts, getActiveTabSelector} from '../application/selectors/permits';
+import * as permitActions from '../application/actions/permitActions';
+import { getLoading } from '../application/selectors/ui';
+import { pageLoaded } from '../application/actions/ui';
+import PermitGeneral from '../components/Permit/General';
+import PermitDetail from '../components/Permit/Detail';
+import * as uiActions from '../application/actions/ui';
+
+import { Alert,TabContent, TabPane, Nav, NavItem, NavLink, Row, Col } from 'reactstrap';
+
+
+export default ()=>{
+ const { register, handleSubmit, setValue,reset,getValues, control} = useForm({ mode: 'onBlur' });
+ const dispatch = useDispatch();
+ const permit = useSelector(getPermit);
+ const authorities = useSelector(getAuthorities);
+ const contextOptions = useSelector(getContextOptions);
+ const contexts = useSelector(getContexts);
+ const permitItems=useSelector(getPermitItems);
+ const tab = useSelector(getActiveTabSelector);
+ const loading = useSelector(getLoading);
+ let { id } = useParams();
+
+ useEffect(() => {
+ dispatch(pageLoaded);
+ dispatch(permitActions.load(id));
+ }, [dispatch,id]);
+
+ const toggle = tab=>{
+ dispatch(permitActions.toggleTab(tab))
+ }
+ const General = () => {
+ return (
+ { toggle("general"); }}
+ >
+ General
+
+
+ )
+ }
+
+ const Detail = () => {
+ return (
+ { toggle("detail"); }}
+ >
+ Detail
+
+
+ )
+ }
+
+ const Tabs = () => {
+
+ return(
+
+
+ )
+ }
+
+ return (
+ <>
+ {loading ? 'loading permit...' : (
+ <>
+
+ dispatch(uiActions.setLoading(true))}
+ to={"/Projects/" + permit.projectId}>
+ {"Back to "+ permit.projectName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >)}
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/views/Project.js b/src/views/Project.js
new file mode 100644
index 0000000..ce6cc42
--- /dev/null
+++ b/src/views/Project.js
@@ -0,0 +1,168 @@
+import React, {useEffect} from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import {useParams} from "react-router-dom";
+import { useForm } from "react-hook-form";
+import { useNavigate } from 'react-router-dom';
+import { Alert,TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';
+import * as uiActions from '../application/actions/ui';
+import * as itemsActions from '../application/actions/collection';
+import * as projectActions from '../application/actions/projectActions';
+import { getItemSelector, getActiveTabSelector} from '../application/selectors/collection';
+
+import { getLoading } from '../application/selectors/ui';
+import { pageLoaded } from '../application/actions/ui';
+import classnames from 'classnames';
+
+import Permits from '../components/Project/Permits';
+
+import General from '../components/Project/General'
+import Module from '../components/Project/Module'
+import Sites from '../components/Project/Sites'
+
+export default () =>{
+ const { register, handleSubmit, setValue} = useForm({ mode: 'onBlur' });
+
+ let { id } = useParams();
+ const dispatch = useDispatch();
+ const project = useSelector(getItemSelector);
+ const tab = useSelector(getActiveTabSelector);
+ const loading = useSelector(getLoading);
+
+ useEffect(() => {
+ dispatch(pageLoaded);
+ dispatch(itemsActions.load({id:id, table:'projects'}));
+ }, [dispatch, id]);
+
+ let navigate = useNavigate();
+ const navigateTo = (link) => {
+ dispatch(uiActions.setLoading(true));
+ navigate(link);
+
+ }
+
+ useEffect(()=>{
+ if(project!==null)
+ {
+ setValue('name', project.name );
+ setValue('address', project.address);
+ setValue('regionId', project.regionId);
+ setValue('activityAreaId', project.activityAreaId, )
+ setValue('description', project.description, )
+ setValue('id', project.id, )
+ //setValue('companyId', project.companyId)
+
+ };
+ },[setValue, project]);
+
+ const toggle = tab=>{
+ dispatch(itemsActions.toggleTab(tab))
+ }
+
+ const Tabs = () => {
+
+ return(
+ <>
+ >)
+ };
+
+return (
+ <>
+ {loading ? 'Loading project...' : (
+ <>
+
+
+
+
{project.name }
+
+ {!project.hasEnvironmentModule &&
+
+ dispatch(projectActions.createEnvironmentModule(project.id))}/>
+
}
+ {!project.hasSecurityModule &&
+
+ dispatch(projectActions.createSecurityModule(project.id))}/>
+
}
+
+
+
+
+
+
+
+
+
+
+
+ {(project.modules === undefined?[]:project.modules).map((module, i) =>
+
+
+ )
+ }
+
+
+
+
+
+ >
+ )}
+
+ >
+ )
+}
+
diff --git a/src/views/Projects.js b/src/views/Projects.js
new file mode 100644
index 0000000..4fc640d
--- /dev/null
+++ b/src/views/Projects.js
@@ -0,0 +1,66 @@
+import React, { useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import { getItemsSelector } from '../application/selectors/collection';
+import { getLoading } from '../application/selectors/ui';
+import * as uiActions from '../application/actions/ui';
+import * as projectActions from '../application/actions/collection';
+import classes from './Projects.module.css';
+import env from '../assets/env.png';
+import secu from '../assets/secu.png';
+
+export default () => {
+
+ const dispatch = useDispatch();
+ const projects = useSelector(getItemsSelector);
+ const loading = useSelector(getLoading);
+
+ useEffect(() => {
+ dispatch(uiActions.pageLoaded);
+ dispatch(projectActions.loadAll('projects'));
+ },[dispatch]);
+
+ let navigate = useNavigate();
+ const navigateTo = (link) => {
+ dispatch(uiActions.setLoading(true));
+ navigate(link);
+
+ }
+
+ return (
+ <>
+ {loading ? 'Loading projects...' : (
+
+ {projects.map(project => (
+
navigateTo('/projects/'+project.id)}
+ >
+
{project.name}
+
{project.consultantName}
+ {/*
{project.description}
*/}
+
+
+

+
+
+

+
+
+
+
+
+ ))}
+
+
+ )}
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/views/Projects.module.css b/src/views/Projects.module.css
new file mode 100644
index 0000000..0f1b71f
--- /dev/null
+++ b/src/views/Projects.module.css
@@ -0,0 +1,75 @@
+body{
+ margin:0;
+ padding: 0;
+
+}
+.listContainer{
+ padding: 0 2%;
+ display: grid;
+ grid-template-columns: repeat(
+ auto-fill,
+ minmax(300px, 1fr)
+ );
+ grid-template-rows: 1fr;
+ grid-gap: 20px;
+ cursor: pointer;
+ margin-top: 30px;
+}
+
+.listContainer :hover {
+ box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
+ background-color:rgb(231, 231, 240);
+ border-radius: 10px;
+}
+
+.item{
+ min-height: 200px;
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
+ transition: 0.3s;
+ border-radius: 15px;
+ position: relative;
+
+}
+
+.consultantName{
+ font-style: italic;
+ color: gray;
+ font-size: 0.9rem;
+ ;
+}
+
+
+
+.projectFooter{
+ position: absolute;
+ bottom:0;
+ left: 0;
+ padding: 10px;
+ font-style: italic;
+ color: gray;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ align-items: center;
+}
+.item :hover{
+ box-shadow:none;
+
+}
+
+
+.fit_picture{
+ display: flex;
+ opacity: 100%;
+ margin:10px;
+ width:35px;
+ height:35px;
+}
+
+.hasOpac{
+
+ margin:10px;
+ opacity: 15%;
+ width:35px;
+ height:35px;
+}
diff --git a/src/views/Publications.js b/src/views/Publications.js
new file mode 100644
index 0000000..92b7eb5
--- /dev/null
+++ b/src/views/Publications.js
@@ -0,0 +1,6 @@
+import React from "react";
+
+export default ()=>{
+ return (Publications
);
+
+}
\ No newline at end of file
diff --git a/src/views/Ric.css b/src/views/Ric.css
new file mode 100644
index 0000000..e5001ac
--- /dev/null
+++ b/src/views/Ric.css
@@ -0,0 +1,105 @@
+/* html {
+ background: #222;
+ color: #FFF;
+} */
+
+.board {
+ display: flex;
+ justify-content: flex-start;
+}
+
+.card {
+ background: #3F4447;
+ padding: 10px;
+ margin-bottom: 10px;
+ border-radius: 3px;
+ cursor: pointer;
+ min-width: 200px;
+ width: auto;
+ height: 120px;
+ cursor: grab;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+ position:relative;
+ box-sizing: content-box;
+}
+
+
+
+.card:active {
+ cursor: grabbing;
+}
+
+.card.dragging {
+ opacity: .5;
+ transform: scale(.8);
+}
+
+.status {
+ padding: 10px;
+ background: #2c2c2c;
+ border: 2px solid #2c2c2c;
+ transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
+
+}
+
+.status.status {
+ margin: 0 2px;
+ min-width: 300px;
+ /* box-sizing:content-box;*/
+ width: 100%;
+}
+
+.status.status-idle h2 {
+ background: #3498DB;
+}
+
+.status.status-info h2 {
+ background:grey;
+}
+
+.status.status-ncpl h2 {
+ background: hsl(12, 90%, 51%);
+}
+
+.status.status-tbf h2 {
+ background: hsl(251, 90%, 51%);
+}
+
+.status.status-tbv h2 {
+ background: rgb(202, 243, 18);
+}
+
+.status.status-ok h2 {
+ background: rgb(132, 223, 29);
+}
+
+
+
+.status.drop {
+ border: 2px dashed #FFF;
+}
+
+.status.drop article {
+ pointer-events: none;
+}
+
+.card h3 {
+ font-size: 12px;
+ font-family: Helvetica;
+ margin: 0;
+ color: white;
+ position:absolute;
+
+}
+
+.card:last-child {
+ margin-bottom: 0;
+}
+
+.board h2 {
+ /*background: green;*/
+ margin: -10px -10px 10px;
+ padding: 4px 10px;
+ font-family: Helvetica;
+ font-size: 21px;
+}
\ No newline at end of file
diff --git a/src/views/Ric.js b/src/views/Ric.js
new file mode 100644
index 0000000..c108aaf
--- /dev/null
+++ b/src/views/Ric.js
@@ -0,0 +1,133 @@
+import React, {useEffect}from "react";
+import {useParams} from "react-router-dom";
+import { useDispatch, useSelector } from 'react-redux';
+
+
+import * as ricActions from '../application/actions/ricActions';
+import { getRic, getStatusOptions, getError} from '../application/selectors/ricSelector';
+
+import { getLoading } from '../application/selectors/ui';
+import { pageLoaded } from '../application/actions/ui';
+
+
+//import classes from './Ric.css';
+
+
+
+export default()=>{
+
+
+
+
+
+ let { id } = useParams();
+ const dispatch = useDispatch();
+ const ric = useSelector(getRic);
+ const statusOptions = useSelector(getStatusOptions);
+ const loading = useSelector(getLoading);
+ const error= useSelector(getError);
+
+
+ const dragStart = target => {
+ target.classList.add('dragging');
+ };
+
+
+ const dragEnd = target => {
+ target.classList.remove('dragging');
+ };
+
+ const dragEnter = event => {
+ event.currentTarget.classList.add('drop');
+ };
+
+ const dragLeave = event => {
+ event.currentTarget.classList.remove('drop');
+ };
+
+ const drag = event => {
+ event.dataTransfer.setData('text/html', event.currentTarget.outerHTML);
+ event.dataTransfer.setData('text/plain', event.currentTarget.dataset.id);
+ var source = id ? document.getElementById(event.target.id).parentNode.id : '';
+ event.dataTransfer.setData("source", source);
+ };
+
+ const drop = event => {
+
+ const codeId= event.dataTransfer.getData('text/plain');
+ const statusId=event.currentTarget.id;
+ var source = event.dataTransfer.getData("source");
+
+ // remove drop style from target
+ document.querySelectorAll('.status').forEach(status => status.classList.remove('drop'));
+ if(source!==statusId)
+ dispatch(ricActions.updateRicStatus({codeId:codeId, projectId:id,statusId:statusId}));
+
+ };
+
+ const allowDrop = event => {
+ event.preventDefault();
+ };
+
+ useEffect(() => {
+ dispatch(pageLoaded);
+ dispatch(ricActions.loadRic(id));
+ }, [dispatch, id]);
+
+ useEffect(()=>{
+
+ document.querySelectorAll('.status').forEach(status => {
+ status.addEventListener('dragenter', dragEnter);
+ status.addEventListener('dragleave', dragLeave);
+ });
+
+ document.addEventListener('dragstart', e => {
+ try{
+ if (e.target.className.includes('card')) {
+ dragStart(e.target);
+ }
+ }
+ catch{}
+ });
+
+ document.addEventListener('dragend', e => {
+ try{
+ if (e.target.className.includes('card')) {
+ dragEnd(e.target);
+ }
+ }
+ catch{}
+ }, []);
+
+})
+ return (
+ <>{loading ? 'Loading ric...' : (
+ <>
+ Registre des installations classées {id}
+ {error}
+
+ {statusOptions.map(option =>
+ (
+ drop(e)} onDragOver={e=>allowDrop(e)}>
+
{option.description +"("+ric.filter(s=>s.statusId===option.name).length+")"}
+ {ric.filter(s=>s.statusId===option.name).map(c=>
+ (
+
drag(e)}
+ data-id={c.codeId}
+ id={c.codeId}>
+ {c.code.codeRef}
+
+ )
+ )}
+
+ )
+
+ )}
+
+ >)}
+
+ >);
+}
\ No newline at end of file
diff --git a/src/views/index.js b/src/views/index.js
index ace5530..5059580 100644
--- a/src/views/index.js
+++ b/src/views/index.js
@@ -1,36 +1,37 @@
-import React, { useEffect } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
-import { getTodos } from '../application/selectors/todos';
-import { pageLoaded } from '../application/actions/ui';
-import { putTodo } from '../application/actions/todos';
-import { getLoading } from '../application/selectors/ui';
+import React from 'react';
+import {Route, Routes} from 'react-router-dom';
+
+import MainNavigation from '../components/layout/MainNavigation';
+
+import ProjectsPage from './Projects';
+import CompaniesPage from './Companies';
+import ProjectPage from './Project';
+import PermitPage from './Permit';
+import PublicationsPage from './Publications';
+import InstallationsPage from './Installations';
+import RicPage from './Ric';
+import { NotFound } from './NotFoundError';
+
export default () => {
- const dispatch = useDispatch();
- const todos = useSelector(getTodos);
- const loading = useSelector(getLoading);
- useEffect(() => {
- dispatch(pageLoaded);
- }, [dispatch]);
+
return (
<>
- Essential Todos
- {loading ? 'Loading todos...' : (
-
- {todos.map(todo => (
- - dispatch(putTodo({...todo, completed: !todo.completed }))}
- >
- {todo.title}
-
- ))}
-
- )}
+
+
+ Home} />
+ Home} />
+ } />
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+ }/>
+
+ }/>
+
+
>
)
}
\ No newline at end of file