diff --git a/package-lock.json b/package-lock.json index a6b784ab..0290f4ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "cron": "^2.3.1", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.57", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.66", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", @@ -1061,14 +1061,14 @@ } }, "node_modules/@next/env": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.10.tgz", - "integrity": "sha512-3G1yD/XKTSLdihyDSa8JEsaWOELY+OWe08o0LUYzfuHp1zHDA8SObQlzKt+v+wrkkPcnPweoLH1ImZeUa0A1NQ==" + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.12.tgz", + "integrity": "sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.10.tgz", - "integrity": "sha512-4bsdfKmmg7mgFGph0UorD1xWfZ5jZEw4kKRHYEeTK9bT1QnMbPVPlVXQRIiFPrhoDQnZUoa6duuPUJIEGLV1Jg==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.12.tgz", + "integrity": "sha512-deUrbCXTMZ6ZhbOoloqecnUeNpUOupi8SE2tx4jPfNS9uyUR9zK4iXBvH65opVcA/9F5I/p8vDXSYbUlbmBjZg==", "cpu": [ "arm64" ], @@ -1081,9 +1081,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.10.tgz", - "integrity": "sha512-ngXhUBbcZIWZWqNbQSNxQrB9T1V+wgfCzAor2olYuo/YpaL6mUYNUEgeBMhr8qwV0ARSgKaOp35lRvB7EmCRBg==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.12.tgz", + "integrity": "sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==", "cpu": [ "x64" ], @@ -1096,9 +1096,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.10.tgz", - "integrity": "sha512-SjCZZCOmHD4uyM75MVArSAmF5Y+IJSGroPRj2v9/jnBT36SYFTORN8Ag/lhw81W9EeexKY/CUg2e9mdebZOwsg==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.12.tgz", + "integrity": "sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==", "cpu": [ "arm64" ], @@ -1111,9 +1111,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.10.tgz", - "integrity": "sha512-F+VlcWijX5qteoYIOxNiBbNE8ruaWuRlcYyIRK10CugqI/BIeCDzEDyrHIHY8AWwbkTwe6GRHabMdE688Rqq4Q==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.12.tgz", + "integrity": "sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==", "cpu": [ "arm64" ], @@ -1126,9 +1126,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.10.tgz", - "integrity": "sha512-WDv1YtAV07nhfy3i1visr5p/tjiH6CeXp4wX78lzP1jI07t4PnHHG1WEDFOduXh3WT4hG6yN82EQBQHDi7hBrQ==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.12.tgz", + "integrity": "sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==", "cpu": [ "x64" ], @@ -1141,9 +1141,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.10.tgz", - "integrity": "sha512-zFkzqc737xr6qoBgDa3AwC7jPQzGLjDlkNmt/ljvQJ/Veri5ECdHjZCUuiTUfVjshNIIpki6FuP0RaQYK9iCRg==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.12.tgz", + "integrity": "sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==", "cpu": [ "x64" ], @@ -1156,9 +1156,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.10.tgz", - "integrity": "sha512-IboRS8IWz5mWfnjAdCekkl8s0B7ijpWeDwK2O8CdgZkoCDY0ZQHBSGiJ2KViAG6+BJVfLvcP+a2fh6cdyBr9QQ==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.12.tgz", + "integrity": "sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==", "cpu": [ "arm64" ], @@ -1171,9 +1171,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.10.tgz", - "integrity": "sha512-bSA+4j8jY4EEiwD/M2bol4uVEu1lBlgsGdvM+mmBm/BbqofNBfaZ2qwSbwE2OwbAmzNdVJRFRXQZ0dkjopTRaQ==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.12.tgz", + "integrity": "sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==", "cpu": [ "ia32" ], @@ -1186,9 +1186,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.10.tgz", - "integrity": "sha512-g2+tU63yTWmcVQKDGY0MV1PjjqgZtwM4rB1oVVi/v0brdZAcrcTV+04agKzWtvWroyFz6IqtT0MoZJA7PNyLVw==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.12.tgz", + "integrity": "sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==", "cpu": [ "x64" ], @@ -1493,9 +1493,9 @@ } }, "node_modules/@types/node": { - "version": "18.17.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.0.tgz", - "integrity": "sha512-GXZxEtOxYGFchyUzxvKI14iff9KZ2DI+A6a37o6EQevtg6uO9t+aUZKcaC1Te5Ng1OnLM7K9NVVj+FbecD9cJg==" + "version": "18.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz", + "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==" }, "node_modules/@types/node-fetch": { "version": "2.6.4", @@ -2049,9 +2049,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001516", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", - "integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", "funding": [ { "type": "opencollective", @@ -2335,9 +2335,9 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/cron": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/cron/-/cron-2.3.1.tgz", - "integrity": "sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.4.0.tgz", + "integrity": "sha512-Cx77ic1TyIAtUggr0oAhtS8MLzPBUqGNIvdDM7jE3oFIxfe8LXWI9q3iQN/H2CebAiMir53LQKWOhEKnzkJTAQ==", "dependencies": { "luxon": "^3.2.1" } @@ -2467,9 +2467,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.461", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", - "integrity": "sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==", + "version": "1.4.473", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.473.tgz", + "integrity": "sha512-aVfC8+440vGfl06l8HKKn8/PD5jRfSnLkTTD65EFvU46igbpQRri1gxSzW9/+TeUlwYzrXk1sw867T96zlyECA==", "dev": true }, "node_modules/emittery": { @@ -3193,17 +3193,17 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps": { @@ -3244,9 +3244,9 @@ "dev": true }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -3972,7 +3972,7 @@ } }, "node_modules/le-coffre-resources": { - "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#dffc2429ba6ceb4129deb56e308962799b9b572c", + "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#e7916d516fe434c93ef13e765d8d63a7ce3c56b2", "license": "MIT", "dependencies": { "class-transformer": "^0.5.1", @@ -3990,9 +3990,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.37", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.37.tgz", - "integrity": "sha512-Z10PCaOCiAxbUxLyR31DNeeNugSVP6iv/m7UrSKS5JHziEMApJtgku4e9Q69pzzSC9LnQiM09sqsGf2ticZnMw==" + "version": "1.10.38", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.38.tgz", + "integrity": "sha512-4NjVXVUmpZ9Zsqq6FXa2+MKI+KAI3tOqA0pxXgXGluhpj4ge5didmbWJpMBqGB3AVGv1SnEtKdGTbxjSEG1kCQ==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -4066,20 +4066,53 @@ } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4386,11 +4419,11 @@ } }, "node_modules/next": { - "version": "13.4.10", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.10.tgz", - "integrity": "sha512-4ep6aKxVTQ7rkUW2fBLhpBr/5oceCuf4KmlUpvG/aXuDTIf9mexNSpabUD6RWPspu6wiJJvozZREhXhueYO36A==", + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.12.tgz", + "integrity": "sha512-eHfnru9x6NRmTMcjQp6Nz0J4XH9OubmzOa7CkWL+AUrUxpibub3vWwttjduu9No16dug1kq04hiUUpo7J3m3Xw==", "dependencies": { - "@next/env": "13.4.10", + "@next/env": "13.4.12", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -4406,15 +4439,15 @@ "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.10", - "@next/swc-darwin-x64": "13.4.10", - "@next/swc-linux-arm64-gnu": "13.4.10", - "@next/swc-linux-arm64-musl": "13.4.10", - "@next/swc-linux-x64-gnu": "13.4.10", - "@next/swc-linux-x64-musl": "13.4.10", - "@next/swc-win32-arm64-msvc": "13.4.10", - "@next/swc-win32-ia32-msvc": "13.4.10", - "@next/swc-win32-x64-msvc": "13.4.10" + "@next/swc-darwin-arm64": "13.4.12", + "@next/swc-darwin-x64": "13.4.12", + "@next/swc-linux-arm64-gnu": "13.4.12", + "@next/swc-linux-arm64-musl": "13.4.12", + "@next/swc-linux-x64-gnu": "13.4.12", + "@next/swc-linux-x64-musl": "13.4.12", + "@next/swc-win32-arm64-msvc": "13.4.12", + "@next/swc-win32-ia32-msvc": "13.4.12", + "@next/swc-win32-x64-msvc": "13.4.12" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -5661,9 +5694,9 @@ } }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/type-detect": { "version": "4.0.8", diff --git a/package.json b/package.json index 7467bbf0..fd68c5be 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "cron": "^2.3.1", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.63", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.66", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", diff --git a/src/app/api/super-admin/AppointmentsController.ts b/src/app/api/super-admin/AppointmentsController.ts new file mode 100644 index 00000000..f2790755 --- /dev/null +++ b/src/app/api/super-admin/AppointmentsController.ts @@ -0,0 +1,76 @@ +import { Response, Request } from "express"; +import { Controller, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import AppointmentsService from "@Services/super-admin/AppointmentsService/AppointmentsService"; +import { Service } from "typedi"; +import { Appointment } from "le-coffre-resources/dist/SuperAdmin"; +import authHandler from "@App/middlewares/AuthHandler"; + +@Controller() +@Service() +export default class AppointmentsController extends ApiController { + constructor(private appointmentsService: AppointmentsService) { + super(); + } + + /** + * @description Get all appointments + */ + @Get("/api/v1/super-admin/appointments", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + //call service to get prisma entity + const appointmentsEntities = await this.appointmentsService.get(query); + + //Hydrate ressource with prisma entity + const appointments = Appointment.hydrateArray(appointmentsEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, appointments); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific appointment by uid + */ + @Get("/api/v1/super-admin/appointments/:uid", [authHandler]) + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + const appointmentEntity = await this.appointmentsService.getByUid(uid, query); + + if (!appointmentEntity) { + this.httpNotFoundRequest(response, "appointment not found"); + return; + } + + //Hydrate ressource with prisma entity + const appointment = Appointment.hydrate(appointmentEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, appointment); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/super-admin/LiveVoteController.ts b/src/app/api/super-admin/LiveVoteController.ts new file mode 100644 index 00000000..0e3307f4 --- /dev/null +++ b/src/app/api/super-admin/LiveVoteController.ts @@ -0,0 +1,94 @@ +import authHandler from "@App/middlewares/AuthHandler"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Post } from "@ControllerPattern/index"; +import { EAppointmentStatus } from "@prisma/client"; +import AppointmentService from "@Services/super-admin/AppointmentsService/AppointmentsService"; +import LiveVoteService from "@Services/super-admin/LiveVoteService/LiveVoteService"; +import UsersService from "@Services/super-admin/UsersService/UsersService"; +import VotesService from "@Services/super-admin/VotesService/VotesService"; +import { validateOrReject } from "class-validator"; +import { Request, Response } from "express"; +import { Vote } from "le-coffre-resources/dist/SuperAdmin"; +import { Service } from "typedi"; + +@Controller() +@Service() +export default class LiveVoteController extends ApiController { + constructor( + private liveVoteService: LiveVoteService, + private votesService: VotesService, + private usersService: UsersService, + private appointmentService: AppointmentService, + ) { + super(); + } + + /** + * @description Create a new vote + */ + @Post("/api/v1/super-admin/live-votes", [authHandler]) + protected async post(req: Request, response: Response) { + try { + const userId = req.body.user.userId; + //init IUser resource with request body values + const voteEntity = Vote.hydrate(req.body); + //validate user + await validateOrReject(voteEntity, { groups: ["createVote"] }); + + let voteFound = []; + if (voteEntity.appointment.uid) { + const appointment = await this.appointmentService.getByUid(voteEntity.appointment.uid); + if (!appointment) { + this.httpNotFoundRequest(response, "Appointment not found"); + return; + } + if (appointment.status === EAppointmentStatus.CLOSED) { + this.httpBadRequest(response, "Appointment is closed"); + return; + } + voteFound = await this.votesService.get({ + where: { AND: [{ appointment: { uid: voteEntity.appointment.uid } }, { voter: { uid: userId } }] }, + }); + } else { + voteFound = await this.votesService.get({ + where: { + AND: [ + { + appointment: { + AND: [{ user_uid: voteEntity.appointment.targeted_user.uid }, { status: EAppointmentStatus.OPEN }], + }, + }, + { voter: { uid: userId } }, + ], + }, + }); + } + + if (voteFound.length) { + this.httpBadRequest(response, "Voter already voted for this appointment"); + return; + } + + const voter = await this.usersService.getByUid(userId); + + voteEntity.voter = voter!; + //call service to get prisma entity + const voteEntityCreated = await this.liveVoteService.create(voteEntity); + + if (!voteEntityCreated) { + this.httpBadRequest(response, "Appointment choice is not valid"); + return; + } + //Hydrate ressource with prisma entity + const vote = Vote.hydrate(voteEntityCreated, { + strategy: "excludeAll", + }); + + //success + this.httpCreated(response, vote); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/super-admin/UsersController.ts b/src/app/api/super-admin/UsersController.ts index a96b3f5a..71d97022 100644 --- a/src/app/api/super-admin/UsersController.ts +++ b/src/app/api/super-admin/UsersController.ts @@ -90,12 +90,13 @@ export default class UsersController extends ApiController { protected async put(req: Request, response: Response) { try { const uid = req.params["uid"]; + if (!uid) { this.httpBadRequest(response, "No uid provided"); return; } - const userFound = await this.usersService.getByUid(uid); + const userFound = await this.usersService.getByUid(uid, {role: true, votes: true}); if (!userFound) { this.httpNotFoundRequest(response, "user not found"); @@ -104,12 +105,13 @@ export default class UsersController extends ApiController { //init IUser resource with request body values const userEntity = User.hydrate(req.body); + const userFoundEntity = User.hydrate(userFound, { strategy: "excludeAll" }); //validate user await validateOrReject(userEntity, { groups: ["updateUser"] }); - + //call service to get prisma entity - const userEntityUpdated = await this.usersService.update(uid, userEntity); + const userEntityUpdated = await this.usersService.update(uid, userFoundEntity); //Hydrate ressource with prisma entity const user = User.hydrate(userEntityUpdated, { diff --git a/src/app/api/super-admin/VotesController.ts b/src/app/api/super-admin/VotesController.ts new file mode 100644 index 00000000..4f08ccf8 --- /dev/null +++ b/src/app/api/super-admin/VotesController.ts @@ -0,0 +1,110 @@ +import { Response, Request } from "express"; +import { Controller, Delete, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import VotesService from "@Services/super-admin/VotesService/VotesService"; +import { Service } from "typedi"; +import { Vote } from "le-coffre-resources/dist/SuperAdmin"; +import authHandler from "@App/middlewares/AuthHandler"; +import { Votes } from "@prisma/client"; + +@Controller() +@Service() +export default class VotesController extends ApiController { + constructor(private votesService: VotesService) { + super(); + } + + /** + * @description Get all votes + */ + @Get("/api/v1/super-admin/votes", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + //call service to get prisma entity + const votesEntities = await this.votesService.get(query); + + //Hydrate ressource with prisma entity + const votes = Vote.hydrateArray(votesEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, votes); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific vote by uid + */ + @Get("/api/v1/super-admin/votes/:uid", [authHandler]) + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + const voteEntity = await this.votesService.getByUid(uid, query); + + if (!voteEntity) { + this.httpNotFoundRequest(response, "vote not found"); + return; + } + + //Hydrate ressource with prisma entity + const vote = Vote.hydrate(voteEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, vote); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Delete a specific folder + */ + @Delete("/api/v1/super-admin/votes/:uid", [authHandler]) + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const voteFound = await this.votesService.getByUid(uid); + + if (!voteFound) { + this.httpNotFoundRequest(response, "vote not found"); + return; + } + + //call service to get prisma entity + const votetEntity: Votes = await this.votesService.delete(uid); + + //Hydrate ressource with prisma entity + const vote = Vote.hydrate(votetEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, vote); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/index.ts b/src/app/index.ts index 22f4d1f3..f550461c 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -40,6 +40,9 @@ import RolesControllerNotary from "./api/notary/RolesController"; import OfficeRolesControllerNotary from "./api/notary/OfficeRolesController"; import FilesControllerCustomer from "./api/customer/FilesController"; import DocumentsControllerCustomer from "./api/customer/DocumentsController"; +import AppointmentsController from "./api/super-admin/AppointmentsController"; +import VotesController from "./api/super-admin/VotesController"; +import LiveVoteController from "./api/super-admin/LiveVoteController"; /** @@ -56,6 +59,9 @@ export default { Container.get(DeedTypesControllerSuperAdmin); Container.get(DocumentsControllerSuperAdmin); Container.get(DocumentTypesControllerSuperAdmin); + Container.get(AppointmentsController); + Container.get(VotesController); + Container.get(LiveVoteController); Container.get(IdNotUserController); Container.get(FranceConnectCustomerController); Container.get(FilesControllerSuperAdmin); diff --git a/src/common/databases/migrations/20230713145026_v12/migration.sql b/src/common/databases/migrations/20230713145026_v12/migration.sql new file mode 100644 index 00000000..c5dac8d4 --- /dev/null +++ b/src/common/databases/migrations/20230713145026_v12/migration.sql @@ -0,0 +1,38 @@ +-- DropForeignKey +ALTER TABLE "users" DROP CONSTRAINT "users_contact_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "users" DROP CONSTRAINT "users_office_role_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "users" DROP CONSTRAINT "users_office_uid_fkey"; + +-- DropForeignKey +ALTER TABLE "users" DROP CONSTRAINT "users_roles_uid_fkey"; + +-- CreateTable +CREATE TABLE "votes" ( + "uid" TEXT NOT NULL, + "user_uid" VARCHAR(255) NOT NULL, + "voters" TEXT[], + + CONSTRAINT "votes_pkey" PRIMARY KEY ("uid") +); + +-- CreateIndex +CREATE UNIQUE INDEX "votes_uid_key" ON "votes"("uid"); + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_contact_uid_fkey" FOREIGN KEY ("contact_uid") REFERENCES "contacts"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_roles_uid_fkey" FOREIGN KEY ("roles_uid") REFERENCES "roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_office_role_uid_fkey" FOREIGN KEY ("office_role_uid") REFERENCES "office_roles"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_office_uid_fkey" FOREIGN KEY ("office_uid") REFERENCES "offices"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "votes" ADD CONSTRAINT "votes_user_uid_fkey" FOREIGN KEY ("user_uid") REFERENCES "users"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/common/databases/migrations/20230725084826_v12/migration.sql b/src/common/databases/migrations/20230725084826_v12/migration.sql new file mode 100644 index 00000000..0b7f1455 --- /dev/null +++ b/src/common/databases/migrations/20230725084826_v12/migration.sql @@ -0,0 +1,45 @@ +/* + Warnings: + + - You are about to drop the column `user_uid` on the `votes` table. All the data in the column will be lost. + - You are about to drop the column `voters` on the `votes` table. All the data in the column will be lost. + - Added the required column `appointment_uid` to the `votes` table without a default value. This is not possible if the table is not empty. + - Added the required column `voter_uid` to the `votes` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "EVote" AS ENUM ('NOMINATE', 'DISMISS'); + +-- CreateEnum +CREATE TYPE "EAppointmentStatus" AS ENUM ('OPEN', 'CLOSED'); + +-- DropForeignKey +ALTER TABLE "votes" DROP CONSTRAINT "votes_user_uid_fkey"; + +-- AlterTable +ALTER TABLE "votes" DROP COLUMN "user_uid", +DROP COLUMN "voters", +ADD COLUMN "appointment_uid" VARCHAR(255) NOT NULL, +ADD COLUMN "choice" "EVote" NOT NULL DEFAULT 'NOMINATE', +ADD COLUMN "voter_uid" VARCHAR(255) NOT NULL; + +-- CreateTable +CREATE TABLE "appointments" ( + "uid" TEXT NOT NULL, + "user_uid" VARCHAR(255) NOT NULL, + "status" "EAppointmentStatus" NOT NULL DEFAULT 'OPEN', + + CONSTRAINT "appointments_pkey" PRIMARY KEY ("uid") +); + +-- CreateIndex +CREATE UNIQUE INDEX "appointments_uid_key" ON "appointments"("uid"); + +-- AddForeignKey +ALTER TABLE "appointments" ADD CONSTRAINT "appointments_user_uid_fkey" FOREIGN KEY ("user_uid") REFERENCES "users"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "votes" ADD CONSTRAINT "votes_appointment_uid_fkey" FOREIGN KEY ("appointment_uid") REFERENCES "appointments"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "votes" ADD CONSTRAINT "votes_voter_uid_fkey" FOREIGN KEY ("voter_uid") REFERENCES "users"("uid") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/common/databases/migrations/20230725151748_v13/migration.sql b/src/common/databases/migrations/20230725151748_v13/migration.sql new file mode 100644 index 00000000..b8c667d7 --- /dev/null +++ b/src/common/databases/migrations/20230725151748_v13/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - You are about to drop the column `choice` on the `votes` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "appointments" ADD COLUMN "choice" "EVote" NOT NULL DEFAULT 'NOMINATE'; + +-- AlterTable +ALTER TABLE "votes" DROP COLUMN "choice"; diff --git a/src/common/databases/migrations/20230726095252_v25/migration.sql b/src/common/databases/migrations/20230726095252_v25/migration.sql new file mode 100644 index 00000000..389c4749 --- /dev/null +++ b/src/common/databases/migrations/20230726095252_v25/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[user_uid,choice,status]` on the table `appointments` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "appointments_user_uid_choice_status_key" ON "appointments"("user_uid", "choice", "status"); diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 53eae7d8..98ff92ea 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -65,6 +65,8 @@ model Users { office_uid String @db.VarChar(255) notifications Notifications[] @relation("UserHasNotifications") office_folders OfficeFolders[] @relation("OfficeFolderHasStakeholders") + appointment Appointments[] + votes Votes[] @@map("users") } @@ -287,6 +289,28 @@ model Emails { @@map("email") } +model Appointments { + uid String @id @unique @default(uuid()) + user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) + user_uid String @db.VarChar(255) + choice EVote @default(NOMINATE) + status EAppointmentStatus @default(OPEN) + votes Votes[] + + @@unique([user_uid, choice, status]) + @@map("appointments") +} + +model Votes { + uid String @id @unique @default(uuid()) + appointment Appointments @relation(fields: [appointment_uid], references: [uid], onDelete: Cascade) + appointment_uid String @db.VarChar(255) + voter Users @relation(fields: [voter_uid], references: [uid]) + voter_uid String @db.VarChar(255) + + @@map("votes") +} + enum ECivility { MALE FEMALE @@ -321,3 +345,13 @@ enum EDocumentStatus { ANCHORED REFUSED } + +enum EVote { + NOMINATE + DISMISS +} + +enum EAppointmentStatus { + OPEN + CLOSED +} diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index cf0129f4..941942db 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -1,18 +1,7 @@ +import { ECivility, ECustomerStatus, EFolderStatus, EOfficeStatus, Prisma, PrismaClient } from "@prisma/client"; +import User, { Address, Contact, Customer, Deed, DeedType, DocumentType, Office, OfficeFolder, OfficeRole, Role, Rule } from "le-coffre-resources/dist/SuperAdmin"; + import "module-alias/register"; -import { EFolderStatus, EOfficeStatus, ECivility, ECustomerStatus, PrismaClient, Prisma } from "@prisma/client"; -import User, { - Address, - Contact, - Customer, - Deed, - DeedType, - Office, - OfficeFolder, - OfficeRole, - Role, - Rule, - DocumentType, -} from "le-coffre-resources/dist/SuperAdmin"; export default async function main() { try{ @@ -605,7 +594,7 @@ export default async function main() { updated_at: new Date(), }, { - name: "PUT deedtypes", + name: "PUT deed-types", created_at: new Date(), updated_at: new Date(), }, @@ -665,7 +654,7 @@ export default async function main() { updated_at: new Date(), }, { - name: "POST deedtypes", + name: "POST deed-types", created_at: new Date(), updated_at: new Date(), }, @@ -862,26 +851,19 @@ export default async function main() { rules: rules.slice(0, 40), }, { - name: "notary", + name: "Notaire", created_at: new Date(), updated_at: new Date(), office: offices[0]!, rules: rules.slice(0, 33), }, { - name: "deputy", + name: "Collaborateur", created_at: new Date(), updated_at: new Date(), office: offices[0]!, rules: rules.slice(0, 22), - }, - { - name: "listener", - created_at: new Date(), - updated_at: new Date(), - office: offices[0]!, - rules: rules.slice(0, 11), - }, + } ]; const users: User[] = [ diff --git a/src/common/repositories/AppointmentsRepository.ts b/src/common/repositories/AppointmentsRepository.ts new file mode 100644 index 00000000..62b74d34 --- /dev/null +++ b/src/common/repositories/AppointmentsRepository.ts @@ -0,0 +1,76 @@ +import Database from "@Common/databases/database"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Service } from "typedi"; +import { Appointments, EAppointmentStatus, Prisma } from "@prisma/client"; + +@Service() +export default class AppointmentsRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().appointments; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Find many appointments + */ + public async findMany(query: Prisma.AppointmentsFindManyArgs) { + query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); + return this.model.findMany(query); + } + + /** + * @description : Update data of a appointment + */ + public async update(uid: string, status: EAppointmentStatus): Promise { + const updateArgs: Prisma.AppointmentsUpdateArgs = { + where: { + uid: uid, + }, + data: { + status: status, + }, + }; + + return this.model.update(updateArgs); + } + + /** + * @description : Find one appointment + */ + public async findOneByUid(uid: string, query?: Prisma.AppointmentsInclude) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: query, + }); + } + + /** + * @description : Find one appointment with votes + */ + public async findOneByUidWithVotes(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: {votes: true}, + }); + } + + /** + * @description : delete a appointment + */ + public async delete(uid: string): Promise { + return this.model.delete({ + where: { + uid: uid, + }, + }); + } +} diff --git a/src/common/repositories/UsersRepository.ts b/src/common/repositories/UsersRepository.ts index b472a8ec..49673146 100644 --- a/src/common/repositories/UsersRepository.ts +++ b/src/common/repositories/UsersRepository.ts @@ -2,7 +2,7 @@ import Database from "@Common/databases/database"; import BaseRepository from "@Repositories/BaseRepository"; import { Service } from "typedi"; import { ECivility, Prisma, Users } from "@prisma/client"; -import User from "le-coffre-resources/dist/SuperAdmin"; +import User, { Role } from "le-coffre-resources/dist/SuperAdmin"; @Service() export default class UsersRepository extends BaseRepository { @@ -196,6 +196,18 @@ export default class UsersRepository extends BaseRepository { }); } + /** + * @description : Find one user with office + */ + public async findOneByUidWithRole(uid: string): Promise<((Users & {role: Role} )| null)> { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { role: true }, + }); + } + /** * @description : Find one user */ diff --git a/src/common/repositories/VotesRepository.ts b/src/common/repositories/VotesRepository.ts new file mode 100644 index 00000000..a9bf7746 --- /dev/null +++ b/src/common/repositories/VotesRepository.ts @@ -0,0 +1,89 @@ +import Database from "@Common/databases/database"; +import { EAppointmentStatus, EVote, Prisma, Votes } from "@prisma/client"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Vote } from "le-coffre-resources/dist/SuperAdmin"; +import { Service } from "typedi"; + +@Service() +export default class VotesRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().votes; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Find many votes + */ + public async findMany(query: Prisma.VotesFindManyArgs) { + query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); + return this.model.findMany(query); + } + + /** + * @description : Create new vote + */ + public async create(vote: Vote): Promise { + let whereArg: Prisma.AppointmentsWhereUniqueInput; + if(vote.appointment.targeted_user.uid) { + whereArg = { + user_uid_choice_status: { + user_uid: vote.appointment.targeted_user.uid, + choice: EVote[vote.appointment.choice as keyof typeof EVote], + status: EAppointmentStatus.OPEN, + } + }; + } else { + whereArg = { + uid: vote.appointment.uid, + }; + } + const createArgs: Prisma.VotesCreateArgs = { + data: { + appointment: { + connectOrCreate: { + where: whereArg, + create: { + choice: EVote[vote.appointment.choice as keyof typeof EVote], + user_uid: vote.appointment.targeted_user.uid!, + } + }, + }, + voter: { + connect: { + uid: vote.voter.uid, + }, + }, + } + }; + + return this.model.create({...createArgs, include: {appointment: {include: {votes: true}}}}); + } + + /** + * @description : Find one vote + */ + public async findOneByUid(uid: string, query?: Prisma.VotesInclude): Promise { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: query, + }); + } + + /** + * @description : delete a vote + */ + public async delete(uid: string): Promise { + return this.model.delete({ + where: { + uid: uid, + }, + }); + } +} diff --git a/src/services/super-admin/AppointmentsService/AppointmentsService.ts b/src/services/super-admin/AppointmentsService/AppointmentsService.ts new file mode 100644 index 00000000..45d8b481 --- /dev/null +++ b/src/services/super-admin/AppointmentsService/AppointmentsService.ts @@ -0,0 +1,53 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import AppointmentsRepository from "@Repositories/AppointmentsRepository"; +import { Prisma, Appointments, EAppointmentStatus } from "@prisma/client"; + +@Service() +export default class AppointmentService extends BaseService { + constructor(private appointmentRepository: AppointmentsRepository) { + super(); + } + + /** + * @description : Get all appointments + * @throws {Error} If appointments cannot be get + */ + public get(query: Prisma.AppointmentsFindManyArgs) { + return this.appointmentRepository.findMany(query); + } + + /** + * @description : Modify a appointment + * @throws {Error} If appointment cannot be modified + */ + public async update(uid: string, status: EAppointmentStatus): Promise { + return this.appointmentRepository.update(uid, status); + } + + /** + * @description : Get a appointment by uid + * @throws {Error} If appointment cannot be get by uid + */ + public getByUid(uid: string, query?: Prisma.AppointmentsInclude): Promise { + return this.appointmentRepository.findOneByUid(uid, query); + } + + /** + * @description : Get a appointment by uid + * @throws {Error} If appointment cannot be get by uid + */ + public getByUidWithVotes(uid: string) { + return this.appointmentRepository.findOneByUidWithVotes(uid); + } + + /** + * @description : delete a appointment by uid + * @throws {Error} If appointment cannot be get by uid + */ + public delete(uid: string) { + return this.appointmentRepository.delete(uid); + } + + +} diff --git a/src/services/super-admin/LiveVoteService/LiveVoteService.ts b/src/services/super-admin/LiveVoteService/LiveVoteService.ts new file mode 100644 index 00000000..5576bb28 --- /dev/null +++ b/src/services/super-admin/LiveVoteService/LiveVoteService.ts @@ -0,0 +1,97 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import User, { Appointment, Role, Vote } from "le-coffre-resources/dist/SuperAdmin"; +import VotesRepository from "@Repositories/VotesRepository"; +import { Appointments, EAppointmentStatus, EVote, Votes } from "@prisma/client"; +import AppointmentService from "../AppointmentsService/AppointmentsService"; +import UsersService from "../UsersService/UsersService"; +import RolesService from "../RolesService/RolesService"; + +@Service() +export default class LiveVoteService extends BaseService { + constructor( + private voteRepository: VotesRepository, + private appointmentService: AppointmentService, + private userService: UsersService, + private roleService: RolesService, + ) { + super(); + } + + public async verifyVoterChoice(vote: Vote): Promise { + const userWithRole = await this.userService.getByUidWithRole(vote.appointment.targeted_user.uid!); + if (userWithRole!.role.name === "super-admin" && vote.appointment.choice === EVote.DISMISS) { + return true; + } + if (userWithRole!.role.name !== "super-admin" && vote.appointment.choice === EVote.NOMINATE) { + return true; + } + return false; + } + + public async getAppointmentWithVotes(vote: Vote): Promise { + if (vote.appointment.uid) { + return this.appointmentService.getByUidWithVotes(vote.appointment.uid); + } + const appointmentByUser = await this.appointmentService.get({ + where: { + AND: [ + { user_uid: vote.appointment.targeted_user.uid }, + { status: EAppointmentStatus.OPEN }, + { choice: EVote[vote.appointment.choice as keyof typeof EVote] }, + ], + }, + include: { votes: true }, + }); + if (appointmentByUser.length >= 1) { + return this.appointmentService.getByUidWithVotes(appointmentByUser[0]!.uid); + } + return null; + } + + private async closeVote(appointment: Appointments, vote: Votes) { + await this.appointmentService.update(vote.appointment_uid, EAppointmentStatus.CLOSED); + const user = await this.userService.getByUid(appointment.user_uid, { role: true }); + const userEntity = User.hydrate(user!, { strategy: "excludeAll" }); + + return await this.updateRole(appointment, userEntity, vote); + } + + private async updateRole(appointment: Appointments, userEntity: User, vote: Votes) { + if (appointment.choice === EVote.DISMISS) { + const roles = await this.roleService.get({ where: { name: "default" } }); + const roleEntity = Role.hydrate(roles[0]!, { strategy: "excludeAll" }); + userEntity.role = roleEntity; + await this.userService.update(appointment!.user_uid, userEntity); + return vote; + } else if (appointment.choice === EVote.NOMINATE) { + const roles = await this.roleService.get({ where: { name: "super-admin" } }); + const roleEntity = Role.hydrate(roles[0]!, { strategy: "excludeAll" }); + userEntity!.role = roleEntity; + await this.userService.update(appointment!.user_uid, userEntity); + return vote; + } + return vote; + } + + /** + * @description : Create a vote + * @throws {Error} If vote couldn't be created + */ + public async create(vote: Vote): Promise { + const appointment = await this.getAppointmentWithVotes(vote); + + if (appointment) { + const appointmentEntity = Appointment.hydrate(appointment, { strategy: "excludeAll" }); + if (appointmentEntity?.votes && appointmentEntity.votes.length >= 2) { + const voteCreated = await this.voteRepository.create(vote); + return this.closeVote(appointment, voteCreated); + } + } + + const approvedChoice = await this.verifyVoterChoice(vote); + if(!approvedChoice) return null; + + return this.voteRepository.create(vote); + } +} diff --git a/src/services/super-admin/UsersService/UsersService.ts b/src/services/super-admin/UsersService/UsersService.ts index da350fea..241cb1e4 100644 --- a/src/services/super-admin/UsersService/UsersService.ts +++ b/src/services/super-admin/UsersService/UsersService.ts @@ -51,6 +51,14 @@ export default class UsersService extends BaseService { return this.userRepository.findOneByUidWithOffice(uid); } + /** + * @description : Get a user by uid with role + * @throws {Error} If user cannot be get by uid + */ + public getByUidWithRole(uid: string) { + return this.userRepository.findOneByUidWithRole(uid); + } + /** * @description : Get a user by uid * @throws {Error} If user cannot be get by uid diff --git a/src/services/super-admin/VotesService/VotesService.ts b/src/services/super-admin/VotesService/VotesService.ts new file mode 100644 index 00000000..fa32fb4d --- /dev/null +++ b/src/services/super-admin/VotesService/VotesService.ts @@ -0,0 +1,35 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import VotesRepository from "@Repositories/VotesRepository"; +import { Prisma } from "@prisma/client"; + +@Service() +export default class VoteService extends BaseService { + constructor(private voteRepository: VotesRepository) { + super(); + } + + /** + * @description : Get all votes + * @throws {Error} If votes cannot be get + */ + public get(query: Prisma.VotesFindManyArgs) { + return this.voteRepository.findMany(query); + } + + /** + * @description : Get a vote by uid + * @throws {Error} If vote cannot be get by uid + */ + public getByUid(uid: string, query?: Prisma.VotesInclude) { + return this.voteRepository.findOneByUid(uid, query); + } + + /** + * @description : delete a vote by uid + * @throws {Error} If vote cannot be get by uid + */ + public delete(uid: string) { + return this.voteRepository.delete(uid); + } +}