diff --git a/config/config-dev.json b/config/config-dev.json index 3320942c82b8a0ff6ae8e347c87e619f1ab91685..e154cc463bc6600168812652f7d706e0b587c0fb 100644 --- a/config/config-dev.json +++ b/config/config-dev.json @@ -3,6 +3,9 @@ "url": "https://kong-dev.alpha.grandlyon.com/organizations/" }, "resources": { - "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/" + "url": "https://kong-dev.alpha.grandlyon.com/resources/" + }, + "mediaLibrary": { + "url": "https://kong-dev.alpha.grandlyon.com/media-library/" } -} \ No newline at end of file +} diff --git a/config/config-rec.json b/config/config-rec.json index 4955a9a3ca1fb9c3cc45c9fc867b4ff120df5e06..99666bacf0611bdb0dc575d1877ca8fd1161b41f 100644 --- a/config/config-rec.json +++ b/config/config-rec.json @@ -3,6 +3,9 @@ "url": "https://kong-rec.alpha.grandlyon.com/organizations/" }, "resources": { - "url": "https://kong-rec.alpha.grandlyon.com/resources/resources/" + "url": "https://kong-rec.alpha.grandlyon.com/resources/" + }, + "mediaLibrary": { + "url": "https://kong-rec.alpha.grandlyon.com/media-library/" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 407e9f82fe0cc07b92a531131648fed6ca8d87f1..4880633268b69e163a910ab76ab2c76cce10bede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -930,6 +930,46 @@ } } }, + "@fimbul/bifrost": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.17.0.tgz", + "integrity": "sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q==", + "dev": true, + "requires": { + "@fimbul/ymir": "^0.17.0", + "get-caller-file": "^2.0.0", + "tslib": "^1.8.1", + "tsutils": "^3.5.0" + }, + "dependencies": { + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@fimbul/ymir": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.17.0.tgz", + "integrity": "sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA==", + "dev": true, + "requires": { + "inversify": "^5.0.0", + "reflect-metadata": "^0.1.12", + "tslib": "^1.8.1" + } + }, "@ngtools/webpack": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.1.tgz", @@ -1489,14 +1529,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1546,7 +1584,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1756,7 +1793,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, "requires": { "chalk": "^1.1.3", "esutils": "^2.0.2", @@ -1766,14 +1802,12 @@ "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "chalk": { "version": "1.1.3", "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -1785,8 +1819,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } }, @@ -1882,8 +1915,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -2075,7 +2107,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2118,7 +2149,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2155,7 +2186,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2209,7 +2240,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2261,8 +2292,7 @@ "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, "builtin-status-codes": { "version": "3.0.0", @@ -2340,7 +2370,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "optional": true, @@ -2591,7 +2621,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -2599,8 +2628,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.1.2", @@ -2629,8 +2657,7 @@ "commander": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" }, "commondir": { "version": "1.0.1", @@ -2689,8 +2716,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -2882,7 +2908,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2895,7 +2921,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3208,12 +3234,11 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3256,6 +3281,30 @@ "buffer-indexof": "^1.0.0" } }, + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "^1.1.6", + "isarray": "0.0.1" + }, + "dependencies": { + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, "dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -3520,8 +3569,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint-scope": { "version": "4.0.0", @@ -3551,8 +3599,7 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "etag": { "version": "1.8.1", @@ -4022,7 +4069,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4198,8 +4245,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.4", @@ -4242,14 +4288,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4269,8 +4313,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -4418,7 +4461,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4815,7 +4857,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4989,7 +5030,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5020,8 +5060,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-unicode": { "version": "2.0.1", @@ -5180,7 +5219,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -5407,7 +5446,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5416,8 +5454,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -5523,6 +5560,12 @@ "loose-envify": "^1.0.0" } }, + "inversify": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz", + "integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==", + "dev": true + }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -6067,14 +6110,12 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6083,8 +6124,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" } } }, @@ -6391,7 +6431,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6769,7 +6809,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -6874,7 +6914,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6882,8 +6921,7 @@ "minimist": { "version": "0.0.8", "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.3.5", @@ -7104,7 +7142,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true, "optional": true @@ -7196,7 +7234,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "optional": true, @@ -7459,7 +7497,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -7486,7 +7523,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" @@ -7495,8 +7531,7 @@ "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" } } }, @@ -7523,7 +7558,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "optional": true, @@ -7868,8 +7903,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -7886,8 +7920,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -8689,7 +8722,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", - "dev": true, "requires": { "path-parse": "^1.0.5" } @@ -8788,6 +8820,30 @@ "tslib": "^1.9.0" } }, + "rxjs-tslint": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/rxjs-tslint/-/rxjs-tslint-0.1.7.tgz", + "integrity": "sha512-NnOfqutNfdT7VQnQm32JLYh2gDZjc0gdWZFtrxf/czNGkLKJ1nOO6jbKAFI09W0f9lCtv6P2ozxjbQH8TSPPFQ==", + "requires": { + "chalk": "^2.4.0", + "optimist": "^0.6.1", + "tslint": "^5.9.1", + "tsutils": "^2.25.0", + "typescript": ">=2.8.3" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -8852,7 +8908,7 @@ }, "sax": { "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz", "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", "dev": true }, @@ -8931,8 +8987,7 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "semver-dsl": { "version": "1.0.1", @@ -9057,7 +9112,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -9587,8 +9642,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.15.1", @@ -9754,7 +9808,6 @@ "version": "3.0.1", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -9848,7 +9901,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -10260,7 +10312,6 @@ "version": "5.9.1", "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", - "dev": true, "requires": { "babel-code-frame": "^6.22.0", "builtin-modules": "^1.1.1", @@ -10280,7 +10331,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -10289,11 +10339,80 @@ } } }, + "tslint-config-airbnb": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/tslint-config-airbnb/-/tslint-config-airbnb-5.11.1.tgz", + "integrity": "sha512-hkaittm2607vVMe8eotANGN1CimD5tor7uoY3ypg2VTtEcDB/KGWYbJOz58t8LI4cWSyWtgqYQ5F0HwKxxhlkQ==", + "dev": true, + "requires": { + "tslint-consistent-codestyle": "^1.14.1", + "tslint-eslint-rules": "^5.4.0", + "tslint-microsoft-contrib": "~5.2.1" + } + }, + "tslint-consistent-codestyle": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.15.1.tgz", + "integrity": "sha512-38Y3Dz4zcABe/PlPAQSGNEWPGVq0OzcIQR7SEU6dNujp/SgvhxhJOhIhI9gY4r0I3/TNtvVQwARWor9O9LPZWg==", + "dev": true, + "requires": { + "@fimbul/bifrost": "^0.17.0", + "tslib": "^1.7.1", + "tsutils": "^2.29.0" + } + }, + "tslint-eslint-rules": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", + "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "^3.0.0" + }, + "dependencies": { + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tslint-microsoft-contrib": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz", + "integrity": "sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA==", + "dev": true, + "requires": { + "tsutils": "^2.27.2 <2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, "requires": { "tslib": "^1.8.1" } @@ -10338,8 +10457,7 @@ "typescript": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", - "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", - "dev": true + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==" }, "uglify-js": { "version": "3.4.9", @@ -11133,8 +11251,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xml2js": { "version": "0.4.19", diff --git a/package.json b/package.json index 44e295b09d709f0807cae53bd0e4fbed2c2ed20d..d9459cdc73c1364adc05fc7b717eda481ce7812f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "admin-gui", - "version": "0.0.0", + "version": "1.0.0", "scripts": { "ng": "ng", "start": "ng serve", @@ -26,6 +26,7 @@ "bulma": "^0.7.4", "core-js": "^2.6.4", "rxjs": "^6.4.0", + "rxjs-tslint": "^0.1.7", "sass-recursive-map-merge": "^1.0.1", "zone.js": "^0.8.29" }, @@ -48,6 +49,7 @@ "protractor": "^5.4.2", "ts-node": "~5.0.1", "tslint": "~5.9.1", + "tslint-config-airbnb": "^5.11.1", "typescript": "~3.2.4" } } diff --git a/src/app/app.component.html b/src/app/app.component.html index f752e2e749bef88d778a4a25316c122aa1536965..3043831d86a8dae809137666c003e22eeb3ad9cc 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,23 +1,23 @@ <div class="grid"> - <header class="main-header"> - <div class="hamburger" - (click)="sidebarOpened = !sidebarOpened" - [ngClass]="{'expanded': sidebarOpened}"> + <header class="main-header"> + <div class="hamburger" (click)="sidebarOpened = !sidebarOpened" [ngClass]="{'expanded': sidebarOpened}"> - </div> - <h4>Plateforme Data Admin</h4> - <div class="logo"> - <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole"> + </div> + <h4>Plateforme Data Admin</h4> + <div class="logo"> + <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole"> - </div> - </header> - <nav class="main-nav" [ngClass]="{'is-active': sidebarOpened}"> - <app-menu [expanded]="sidebarOpened"></app-menu> - </nav> - <div class="main-content" [ngClass]="{'wide': !sidebarOpened}"> - <router-outlet></router-outlet> + </div> + </header> + <nav class="main-nav" [ngClass]="{'is-active': sidebarOpened}"> + <app-menu [expanded]="sidebarOpened"></app-menu> + </nav> + <div class="main-content" [ngClass]="{'wide': !sidebarOpened}"> + <router-outlet></router-outlet> - </div> + </div> </div> + +<app-notifications></app-notifications> \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 75e19beeeef3748f190ce47d71bba6935be957f6..d00c62e939d0fa8b9677eab2569861298897d7ec 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,8 +1,5 @@ import { Component, OnInit } from '@angular/core'; - - - @Component({ selector: 'app-root', templateUrl: './app.component.html', @@ -14,13 +11,9 @@ export class AppComponent implements OnInit { sidebarOpened: boolean; constructor( - ) { - } - - + ) {} ngOnInit(): void { this.sidebarOpened = false; } - } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c5e0ccd4a906c771333afc79d90f75d50cdf6476..9b843b6f527b5aebbae62eec91003795da9b875f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -41,6 +41,6 @@ export function initAppConfig(appConfigService: AppConfigService) { multi: true, }, ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule { } diff --git a/src/app/app.routing.module.ts b/src/app/app.routing.module.ts index 23e3d791e5f1e9615cc870ed1b5260219d76c007..2e229688a10fc14059435ecf98a68a990b6f7dc4 100644 --- a/src/app/app.routing.module.ts +++ b/src/app/app.routing.module.ts @@ -7,7 +7,7 @@ import { OrganizationFormComponent } from './components/organizations/edit/organ import { ResourcesComponent } from './components/resources/list/resources.component'; import { ResourceFormComponent } from './components/resources/edit/resource-form.component'; import { ResourceDetailComponent } from './components/resources/detail/resource-detail.component'; - +import { FormatsComponent, FormatDetailComponent, FormatFormComponent } from './components'; const appRoutes: Routes = [ { @@ -73,6 +73,34 @@ const appRoutes: Routes = [ title: 'Ressource', }, }, + { + path: 'formats', + component: FormatsComponent, + data: { + title: 'Formats', + }, + }, + { + path: 'formats/new', + component: FormatFormComponent, + data: { + title: 'Nouveau format', + }, + }, + { + path: 'formats/:id/edit', + component: FormatFormComponent, + data: { + title: 'Modifier le format', + }, + }, + { + path: 'formats/:id', + component: FormatDetailComponent, + data: { + title: 'Format', + }, + }, ]; @NgModule({ diff --git a/src/app/components/back-button/back-button.component.html b/src/app/components/back-button/back-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..64c049e03ec9b0b68670c52df1dfe65aa8dbe544 --- /dev/null +++ b/src/app/components/back-button/back-button.component.html @@ -0,0 +1,8 @@ +<div class="back-button"> + <a [routerLink]="route" [title]="title"> + <span class="icon is-medium"> + <i class="fas fa-arrow-left"></i> + </span> + Retour + </a> +</div> diff --git a/src/app/components/back-button/back-button.component.scss b/src/app/components/back-button/back-button.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e477cc426f1ad8b385c0b434647395db2399529a --- /dev/null +++ b/src/app/components/back-button/back-button.component.scss @@ -0,0 +1,14 @@ +.back-button { + a { + color: unset; // Remove default link color + display: flex; + align-items: center; + } + padding: 1rem 1.5rem; + a:hover { + color: #d5232a; + span, a { + color: #d5232a; + } + } +} \ No newline at end of file diff --git a/src/app/components/back-button/back-button.component.ts b/src/app/components/back-button/back-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc592862d5aa1e1a21ace689ea34303c7ba33bef --- /dev/null +++ b/src/app/components/back-button/back-button.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit, Input } from '@angular/core'; + +@Component({ + selector: 'app-back-button', + templateUrl: './back-button.component.html', + styleUrls: ['./back-button.component.scss'], +}) +export class BackButtonComponent implements OnInit { + + @Input() route: string; + @Input() title: string; + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/components/crud-buttons/crud-buttons.component.html b/src/app/components/crud-buttons/crud-buttons.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d81c63b54d8e4ffbbc9f902e0111589b4ee96257 --- /dev/null +++ b/src/app/components/crud-buttons/crud-buttons.component.html @@ -0,0 +1,11 @@ +<div class="crud-buttons-container"> + <span class="icon"> + <a [routerLink]="[id]"><i class="fas fa-eye"></i></a> + </span> + <span class="icon"> + <a [routerLink]="[id, 'edit']"> <i class="fas fa-edit"></i></a> + </span> + <span class="icon has-text-danger" (click)="onDelete()"> + <a> <i class="fas fa-trash"></i></a> + </span> +</div> diff --git a/src/app/components/crud-buttons/crud-buttons.component.scss b/src/app/components/crud-buttons/crud-buttons.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..327d4a70a9de5587a9bca70f677c69b904e890f1 --- /dev/null +++ b/src/app/components/crud-buttons/crud-buttons.component.scss @@ -0,0 +1,23 @@ +.crud-buttons-container { + display: flex; + align-items: center; + justify-content: space-around; +} + +.icon { + cursor: pointer; + + i { + color: #4a4a4a; + } + + &:hover { + .fa-eye, .fa-edit { + color: blue; + } + + .fa-trash { + color: #d5232a; + } + } +} diff --git a/src/app/components/crud-buttons/crud-buttons.component.ts b/src/app/components/crud-buttons/crud-buttons.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2c660fee6700844e460b114711754ddc473b7ae --- /dev/null +++ b/src/app/components/crud-buttons/crud-buttons.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { identifierModuleUrl } from '@angular/compiler'; + +@Component({ + selector: 'app-crud-buttons', + templateUrl: './crud-buttons.component.html', + styleUrls: ['./crud-buttons.component.scss'], +}) +export class CrudButtonsComponent implements OnInit { + + @Output() delete = new EventEmitter<string>(); + @Input() id; + + constructor() { } + + ngOnInit() { + } + + onDelete() { + this.delete.emit(this.id); + } + +} diff --git a/src/app/components/formats/detail/format-detail.component.html b/src/app/components/formats/detail/format-detail.component.html new file mode 100644 index 0000000000000000000000000000000000000000..064569e10d7d0053978eb236b6998d0667cefdc2 --- /dev/null +++ b/src/app/components/formats/detail/format-detail.component.html @@ -0,0 +1,30 @@ +<ng-container *ngIf="format"> + + <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button> + + <section class="section"> + <div class="columns is-centered"> + <div class="column is-8"> + <div class="card"> + <header class="card-header"> + <p class="card-header-title has-text-centered"> + {{format.name}} + </p> + </header> + <div class="card-content"> + <div class="content"> + <p> + <span class="has-text-weight-bold">Id: </span> + <span>{{format.id}}</span> + </p> + <p> + <span class="has-text-weight-bold">Type MapServer: </span> + <span>{{format.mapServerType}}</span> + </p> + </div> + </div> + </div> + </div> + </div> + </section> +</ng-container> \ No newline at end of file diff --git a/src/app/components/formats/detail/format-detail.component.scss b/src/app/components/formats/detail/format-detail.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..163f99f474dc5696a1670be5273eef97a2efbd39 --- /dev/null +++ b/src/app/components/formats/detail/format-detail.component.scss @@ -0,0 +1,3 @@ +.card-header-title { + justify-content: center; +} \ No newline at end of file diff --git a/src/app/components/formats/detail/format-detail.component.ts b/src/app/components/formats/detail/format-detail.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..52b8bf8ce53e15d473d61a1639262df97609b87b --- /dev/null +++ b/src/app/components/formats/detail/format-detail.component.ts @@ -0,0 +1,28 @@ + +import { switchMap } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { FormatService } from 'src/app/services'; +import { Format } from 'src/app/models/format.model'; + +@Component({ + selector: 'app-format-detail', + templateUrl: './format-detail.component.html', + styleUrls: ['./format-detail.component.scss'], +}) +export class FormatDetailComponent implements OnInit { + + format: Format; + + constructor( + private _route: ActivatedRoute, + private _formatService: FormatService, + ) { + } + + ngOnInit(): void { + this._route.paramMap.pipe( + switchMap((params: ParamMap) => this._formatService.findById(params.get('id')))) + .subscribe((format: Format) => this.format = format); + } +} diff --git a/src/app/components/formats/edit/format-form.component.html b/src/app/components/formats/edit/format-form.component.html new file mode 100644 index 0000000000000000000000000000000000000000..dc9588a857641d4e0dcaff8b6343ca0fc2d66df4 --- /dev/null +++ b/src/app/components/formats/edit/format-form.component.html @@ -0,0 +1,41 @@ +<ng-container *ngIf="format"> + + <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button> + + <h1>{{ title }}</h1> + + <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless"> + <div class="column is-7"> + <input type="hidden" formControlName="id" value="{{format.id}}"> + + <div class="field"> + <label class="label required" for="name">Nom</label> + <div class="control"> + <input class="input" type="text" [value]="format.name" formControlName="name" id="name" required> + </div> + <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> + <p *ngIf="name.errors['required']" class="help is-danger"> + Le nom du format est obligatoire. + </p> + </div> + </div> + + <div class="field"> + <label class="label required" for="mapServerType">Type MapServer</label> + <div class="control"> + <input class="input" type="text" [value]="format.mapServerType" formControlName="mapServerType" + id="mapServerType"> + </div> + <div *ngIf="mapServerType.invalid && (mapServerType.dirty || mapServerType.touched)" class="alert alert-danger"> + <p *ngIf="mapServerType.errors['required']" class="help is-danger"> + Le nom du type MapServer est obligatoire. + </p> + </div> + </div> + + <div class="has-text-right"> + <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button> + </div> + </div> + </form> +</ng-container> \ No newline at end of file diff --git a/src/app/components/formats/edit/format-form.component.scss b/src/app/components/formats/edit/format-form.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..5bea26fe55b4ab7bb07da39d6140df0c988864d4 --- /dev/null +++ b/src/app/components/formats/edit/format-form.component.scss @@ -0,0 +1,19 @@ +.full-width { + width: 100%; +} + +h1 { + text-align: center +} + +.icon { + cursor: pointer; + &:hover { + .fa-plus { + color: lightblue; + } + .fa-trash { + color: #d5232a; + } + } +} \ No newline at end of file diff --git a/src/app/components/formats/edit/format-form.component.ts b/src/app/components/formats/edit/format-form.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..73665239d0f197242529f11b22649a127bd5d4e8 --- /dev/null +++ b/src/app/components/formats/edit/format-form.component.ts @@ -0,0 +1,78 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { filter, switchMap } from 'rxjs/operators'; +import { Format } from 'src/app/models/format.model'; +import { FormatService } from 'src/app/services'; + +@Component({ + selector: 'app-format-form', + templateUrl: './format-form.component.html', + styleUrls: ['./format-form.component.scss'], +}) +export class FormatFormComponent implements OnInit { + + format: Format = new Format(); + form: FormGroup; + title: string; + + constructor( + private _formatService: FormatService, + private _route: ActivatedRoute, + private _router: Router, + private _fb: FormBuilder, + ) { + } + + ngOnInit() { + this.title = this._route.snapshot.data.title; + this.initForm(); + + this._route.paramMap.pipe( + filter((paramMap: ParamMap) => (paramMap.get('id') !== null)), + switchMap((paramMap: ParamMap) => this._formatService.findById(paramMap.get('id')))) + .subscribe((format: Format) => { + + this.format = format; + + this.initForm(); + + }); + } + + initForm() { + this.form = this._fb.group({ + id: [this.format.id], + name: [this.format.name, Validators.required], + mapServerType: [this.format.mapServerType, Validators.required], + }); + } + + onSubmit() { + if (!this.formInvalid) { + this.format = new Format(this.form.value); + this._formatService.replaceOrCreate(this.format) + .subscribe( + (formatCreated) => { + this._router.navigate(['/formats', formatCreated.id]); + }, + (err) => { + alert(err.message); + }, + ); + } + } + + // Getters for each property + get name() { + return this.form.controls['name']; + } + + get mapServerType() { + return this.form.controls['mapServerType']; + } + + get formInvalid() { + return this.form.invalid; + } +} diff --git a/src/app/components/formats/list/formats.component.html b/src/app/components/formats/list/formats.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9bb68d7be561e6f54b47659456ac6e85f63b8a16 --- /dev/null +++ b/src/app/components/formats/list/formats.component.html @@ -0,0 +1,59 @@ +<ng-container *ngIf="formats"> + <div> + <div class="section"> + <div class="columns is-centered is-marginless"> + <div class="column has-text-centered"> + <h2>{{ totalElement }} formats trouvés</h2> + </div> + </div> + <div class="add-item has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> + Ajouter + </a> + </div> + <div class="table"> + <div class="header columns is-marginless"> + <div class="column is-2 has-text-centered"> + <span (click)="sortBy('name')" class="is-sortable"> + <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span> + <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> + <i class="fas sort-order-icon" + [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> + </span> + </span> + </div> + <div class="column is-2 has-text-centered"> + <span class="column-title">Type MapServer</span> + </div> + <div class="column is-offset-7 is-1 has-text-centered"> + <span class="column-title">Actions</span> + </div> + </div> + <div class="data-list"> + <div class="data columns is-multiline is-vcentered is-marginless" + *ngFor="let format of formats; let i=index; let odd=odd; let even=even;" + [ngClass]="{ odd: odd, even: even }"> + <div class="column is-2 has-text-centered"> + {{ format.name}} + </div> + <div class="column is-2 has-text-centered"> + {{ format.mapServerType}} + </div> + <div class="column is-offset-7 is-1 has-text-centered actions"> + <app-crud-buttons [id]="format.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> + </div> + </div> + </div> + <div class="columns is-marginless"> + <div class="column"> + <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" + [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" + [showFirstLastButtons]="true" (page)="changePagination($event)" + (pageSizeChanged)="changePageSize($event)"> + </app-paginator> + </div> + </div> + </div> + </div> + </div> +</ng-container> diff --git a/src/app/components/formats/list/formats.component.scss b/src/app/components/formats/list/formats.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..087d2f4242ed39d8f3505d06012bf823f015572e --- /dev/null +++ b/src/app/components/formats/list/formats.component.scss @@ -0,0 +1,53 @@ +section { + padding: 20px; +} +.section { + padding-top: 0; +} + +.table { + background-color: white; + border: 1px solid lightgray; +} + +.center { + text-align: center; +} + +img { + max-width: 100px; +} + +.header { + border-bottom: 1px solid lightgray; + background-color: #fafafa; + span { + color: #999; + + } +} + +.is-sortable .column-title { + cursor: pointer; +} + +.table .columns { + border-bottom: 1px solid lightgray +} + +i { + + &.title-icon { + margin-right: 0.75rem; + } + + &.sort-order-icon { + padding-left: 0.75rem; + cursor: pointer; + } +} + + +.add-item { + margin-bottom: 20px; +} diff --git a/src/app/components/formats/list/formats.component.ts b/src/app/components/formats/list/formats.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0efba29653deeaa91189cc967d4cfe9a8edea5bb --- /dev/null +++ b/src/app/components/formats/list/formats.component.ts @@ -0,0 +1,99 @@ +import { Component, OnInit } from '@angular/core'; +import { FormatService } from 'src/app/services/format.service'; +import { Subscription } from 'rxjs'; +import { PaginatorOptions } from 'src/app/models/paginator-options.model'; +import { Format, FormatRO } from 'src/app/models/format.model'; + +@Component({ + selector: 'app-formats', + templateUrl: './formats.component.html', + styleUrls: ['./formats.component.scss'], +}) +export class FormatsComponent implements OnInit { + + formats: Format[]; + searchChangeSub: Subscription; + + // Paginator options + paginator: PaginatorOptions; + + sortValue: string; + + totalElement: number; + pageSize = 10; + pageSizeOptions = [5, 10, 25, 100]; + filters = { + name: '', + }; + where = {}; + + constructor( + private formatsService: FormatService, + ) { + this.paginator = { + pageIndex: this.formatsService.pageNumber, + length: 0, + limit: this.formatsService.limit, + pageSizeOptions: [5, 10, 20], + }; + } + + ngOnInit(): void { + this.formatsService.sortOptions = { + value: 'name', + order: 'asc', + }; + this.search(); + + this.searchChangeSub = this.formatsService.searchChange$.subscribe( + () => { + this.search(); + }, + ); + } + + private search() { + this.formatsService.getFormats() + .subscribe((items: FormatRO) => { + this.formats = items.formats; + this.totalElement = items.totalCount; + + this.paginator.limit = this.formatsService.limit; + this.paginator.pageIndex = this.formatsService.pageNumber; + this.paginator.length = items.totalCount; + }); + } + + // When pagination is changed by user, we update datasetList with new pagination options + changePagination(pageIndex) { + this.formatsService.paginationChanged(this.paginator.limit, pageIndex); + } + + changePageSize(pageSize) { + this.formatsService.paginationChanged(pageSize, 1); + } + + sortBy(key: string) { + if (this.formatsService.sortOptions.value === key) { + this.formatsService.reverseSortOrder(); + } else { + this.formatsService.sortOptions.value = key; + this.formatsService.sortOptions.order = 'asc'; + } + this.search(); + } + + get sortOptions() { + return this.formatsService.sortOptions; + } + + displayDeletePopup(formatId) { + const pop = confirm('Etes vous sûr de vouloir supprimer ce format ?'); + if (pop) { + this.formatsService.delete(formatId).subscribe(() => { + this.formatsService.pageNumber = 1; + this.search(); + }); + } + } +} diff --git a/src/app/components/image-upload/image-upload.component.html b/src/app/components/image-upload/image-upload.component.html new file mode 100644 index 0000000000000000000000000000000000000000..72e3400a5b9ffda47784a569bdcaca09f18d99ff --- /dev/null +++ b/src/app/components/image-upload/image-upload.component.html @@ -0,0 +1,11 @@ +<div class="field"> + <label class="label" [for]="fieldParams.inputName">{{ fieldParams.label }}</label> + <div class="image-preview-container" *ngIf="fieldParams.existingImageUrl && !removeImageBtnClicked"> + <button class="button" (click)="removeImage()"><i class="far fa-times-circle"></i></button> + <img [src]="fieldParams.existingImageUrl" alt="" class="image-preview"> + </div> + <div class="control"> + <input *ngIf="!fieldParams.existingImageUrl || removeImageBtnClicked" class="input" type="file" + [name]="fieldParams.inputName" [id]="fieldParams.inputName" (change)="setFile($event)"> + </div> +</div> \ No newline at end of file diff --git a/src/app/components/image-upload/image-upload.component.scss b/src/app/components/image-upload/image-upload.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..d4a9d5613206de3aa75a786ce5adcc2bb26cfa48 --- /dev/null +++ b/src/app/components/image-upload/image-upload.component.scss @@ -0,0 +1,17 @@ +.image-preview-container { + display: inline-block; + position: relative; + max-height: 5rem; +} + +button { + position: absolute; + top: 0; + right: 0; + border: none; + background: transparent; +} + +.field { + margin-bottom: 0.75rem; +} diff --git a/src/app/components/image-upload/image-upload.component.ts b/src/app/components/image-upload/image-upload.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..476ad0fffdc9e7b2634d8def54e4c4d9e4826780 --- /dev/null +++ b/src/app/components/image-upload/image-upload.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core'; +import { IImageUploadFieldParams } from 'src/app/models/image-upload.model'; + +@Component({ + selector: 'app-image-upload', + templateUrl: './image-upload.component.html', + styleUrls: ['./image-upload.component.scss'] +}) +export class ImageUploadComponent implements OnInit { + + removeImageBtnClicked: boolean = false; + + @Input() fieldParams: IImageUploadFieldParams; + @Output() fileChanged = new EventEmitter<FileList>(); + @Output() imageRemoved = new EventEmitter<boolean>(); + + constructor() { } + + ngOnInit() { + } + + setFile(event) { + const files = event.srcElement.files; + if (!files) { + return; + } + + this.fileChanged.emit(files); + } + + removeImage() { + this.removeImageBtnClicked = true; + this.imageRemoved.emit(true); + } +} diff --git a/src/app/components/index.ts b/src/app/components/index.ts index c1207017b83f5bfc5cfb9855c460632a50d73a78..5b89ff87ce224523dc22b61a682d4166c34024d6 100644 --- a/src/app/components/index.ts +++ b/src/app/components/index.ts @@ -7,6 +7,13 @@ import { ResourceDetailComponent } from './resources/detail/resource-detail.comp import { ResourceFormComponent } from './resources/edit/resource-form.component'; import { ResourcesComponent } from './resources/list/resources.component'; import { WelcomeComponent } from './welcome/welcome.component'; +import { BackButtonComponent } from './back-button/back-button.component'; +import { CrudButtonsComponent } from './crud-buttons/crud-buttons.component'; +import { FormatsComponent } from './formats/list/formats.component'; +import { FormatDetailComponent } from './formats/detail/format-detail.component'; +import { FormatFormComponent } from './formats/edit/format-form.component'; +import { ImageUploadComponent } from './image-upload/image-upload.component'; +import { NotificationsComponent } from './notifications/notifications.component'; export { MenuComponent, @@ -18,6 +25,13 @@ export { ResourceFormComponent, ResourcesComponent, WelcomeComponent, + BackButtonComponent, + CrudButtonsComponent, + FormatsComponent, + FormatDetailComponent, + FormatFormComponent, + ImageUploadComponent, + NotificationsComponent, }; // tslint:disable-next-line:variable-name @@ -31,4 +45,11 @@ export const AppComponents = [ ResourceFormComponent, ResourcesComponent, WelcomeComponent, + BackButtonComponent, + CrudButtonsComponent, + FormatsComponent, + FormatDetailComponent, + FormatFormComponent, + ImageUploadComponent, + NotificationsComponent, ]; diff --git a/src/app/components/menu/menu.component.html b/src/app/components/menu/menu.component.html index 63c9727b0315c031bea447c498e166b48b913e8a..5652d6a7c9467e4a64f59bbcf700726943dbb027 100644 --- a/src/app/components/menu/menu.component.html +++ b/src/app/components/menu/menu.component.html @@ -22,5 +22,12 @@ <span class="label-menu">Ressources</span> </a> </li> + <li><a [routerLink]="['/', 'formats']" routerLinkActive="active-link"> + <span class="icon"> + <i class="far fa-file"></i> + </span> + <span class="label-menu">Formats</span> + </a> + </li> </ul> </aside> diff --git a/src/app/components/menu/menu.component.scss b/src/app/components/menu/menu.component.scss index c00f4bee1b2f45e079bb923d6cf96ea308b37dcd..a3172b6b6b674406c5a483a2355a059682eae417 100644 --- a/src/app/components/menu/menu.component.scss +++ b/src/app/components/menu/menu.component.scss @@ -40,6 +40,8 @@ li a { border-left: 3px solid #333745; + padding-left: calc(0.75em - 3px) ; + &.active-link { border-color:#d5232a; } diff --git a/src/app/components/notifications/notifications.component.html b/src/app/components/notifications/notifications.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8c0b6dbd5b8e3ca86bb68010e18fbe0c8b448a92 --- /dev/null +++ b/src/app/components/notifications/notifications.component.html @@ -0,0 +1,21 @@ +<div *ngIf="notifications.length > 0" class="app-notifications"> + <div *ngFor="let notification of notifications; index as i" class="app-notification" [class.success]="notification.type === 'success'" + [class.warning]="notification.type === 'warning'" [class.error]="notification.type === 'error'"> + <ng-container [ngSwitch]="notification.type"> + <div class="app-notification-type" *ngSwitchCase="'success'"> + <img src="./assets/img/notif_ok.svg" alt="Ok icon"> + <span>Ok!</span> + </div> + <div class="app-notification-type" *ngSwitchCase="'warning'"> + <img src="./assets/img/notif_warning.svg" alt="Warning icon"> + <span i18n="@@notification.oops">Oops!</span> + </div> + <div class="app-notification-type" *ngSwitchCase="'error'"> + <img src="./assets/img/notif_error.svg" alt="Error icon"> + <span i18n="@@notification.ouch">Ouch!</span> + </div> + </ng-container> + <p class="app-notification-content">{{ notification.message }}</p> + <button class="close-button" (click)="hide(i)"><i class="fas fa-times"></i></button> + </div> +</div> diff --git a/src/app/components/notifications/notifications.component.scss b/src/app/components/notifications/notifications.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..2c212c343bd7e3ad437add33026feaba335856b9 --- /dev/null +++ b/src/app/components/notifications/notifications.component.scss @@ -0,0 +1,118 @@ +@import '../../../../node_modules/bulma/sass/utilities/_all.sass'; + +$app-notification-width: 29rem; + +.app-notifications { + position: fixed; + z-index: 10; + bottom: 0.5rem; + right: 0.5rem; + + @media screen and (max-width: $tablet) { + bottom: 0; + right: 0; + width: 100%; + } +} + +.app-notification { + position: relative; + min-width: $app-notification-width; + max-width: $app-notification-width; + padding: 1rem; + margin-top: 0.5rem; + border: 2px solid white; + border-radius: 4px; + + @media screen and (max-width: $tablet) { + min-width: 100%; + max-width: 100%; + } + + // Adding slideFromRight animation on notification classe + // The div come from the right and go back after a while + // The animation lasts 5s and is runned only one time + animation: slideFromRight 8s 1; + + @media screen and (max-width: $tablet) { + animation: slideFromRightMobile 8s 1; + } + + span, + p, + button { + color: white; + } + + div.app-notification-type { + display: flex; + margin-bottom: 0.75rem; + + img { + width: 1.125rem; + height: 1.125rem; + margin-right: 0.5rem; + } + + span { + font-size: 1rem; + font-weight: bold; + } + } + + p.app-notification-content { + font-size: 0.875rem; + } +} + +.success { + background-color: rgba(84, 180, 51, 0.9); +} + +.warning { + background-color: rgba(239, 114, 23, 0.9); +} + +.error { + background-color: rgba(217, 3, 3, 0.9); +} + +.close-button { + position: absolute; + top: 0; + right: 0; + border: none; + background-color: transparent; + cursor: pointer; +} + +// Animation +@keyframes slideFromRight { + 0% { + transform: translateX($app-notification-width); + } + + 15%, + 85% { + transform: translateX(0); + } + + 100% { + transform: translateX($app-notification-width); + } +} + +@keyframes slideFromRightMobile { + 0% { + transform: translateX(100%); + } + + 15%, + 85% { + transform: translateX(0); + } + + 100% { + transform: translateX(100%); + } +} diff --git a/src/app/components/notifications/notifications.component.ts b/src/app/components/notifications/notifications.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc9bf93d5aa81ec68fc9e804b58fb67ce506e34c --- /dev/null +++ b/src/app/components/notifications/notifications.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { NotificationService } from '../../services'; +import { Notification } from '../../models/notification.model'; + +@Component({ + selector: 'app-notifications', + templateUrl: './notifications.component.html', + styleUrls: ['./notifications.component.scss'], +}) +export class NotificationsComponent implements OnInit { + notifications: Notification[] = []; + + constructor( + private _notificationService: NotificationService, + ) { } + + ngOnInit() { + this._notificationService.notification$.subscribe( + (notification) => { + if (notification) { + this.notifications.push(notification); + // Remove notification from array after 5 seconds + // IMPORTANT (if you change this value also change the value of the css animation) + setTimeout(() => this.notifications.shift(), 8000); + } + }, + ); + } + + hide(index) { + this.notifications.splice(index, 1); + } + +} diff --git a/src/app/components/organizations/detail/organization-detail.component.html b/src/app/components/organizations/detail/organization-detail.component.html index cd7687506be21813e2a2d2a9c411fde647abc0d6..3d17686ee4cdaecaee0b39828686063c00b39507 100644 --- a/src/app/components/organizations/detail/organization-detail.component.html +++ b/src/app/components/organizations/detail/organization-detail.component.html @@ -1,14 +1,6 @@ <ng-container *ngIf="organization"> - <div class="back-button"> - <a routerLink="/organizations" title="Retourner à la liste des organisations"> - <span class="icon is-medium"> - <i class="fas fa-arrow-left fa-lg"></i> - </span> - Retour - </a> - </div> - + <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button> <section class="section"> <div class="columns is-centered"> @@ -21,14 +13,18 @@ </header> <div class="card-image"> <figure class="image"> - <img src="{{organization.logo}}" alt="Logo de l'organization"> + <img [src]="organization.logo" alt="Logo de l'organization"> </figure> </div> <div class="card-content"> <div class="content"> <div> - {{organization.description}} + <p>{{organization.description}}</p> + </div> + <br> + <div> + <p><span class="has-text-weight-bold">Id:</span> {{ organization.id}}</p> </div> <br> <div> @@ -39,10 +35,10 @@ <div> <p class="has-text-weight-bold">Liens</p> <p *ngFor="let link of organization.links"> - <a href="{{ link.url }}" target="_blank">{{link.url}}</a> - </p> + <a href="{{ link.url }}" target="_blank">{{link.url}}</a> + </p> </div> - + </div> </div> </div> @@ -53,4 +49,4 @@ </section> -</ng-container> +</ng-container> \ No newline at end of file diff --git a/src/app/components/organizations/detail/organization-detail.component.scss b/src/app/components/organizations/detail/organization-detail.component.scss index f7cc6fc699db9733d186dc7455c50eb990a195b9..dff286c3a01ee11891ef45a0378e9fa7e35f4172 100644 --- a/src/app/components/organizations/detail/organization-detail.component.scss +++ b/src/app/components/organizations/detail/organization-detail.component.scss @@ -1,17 +1,3 @@ - -.back-button { - a { - color: unset; // Remove default link color - } - padding: 1rem 1.5rem; - a:hover { - color: #d5232a; - span, a { - color: #d5232a; - } - } -} - figure { text-align: center; } diff --git a/src/app/components/organizations/edit/organization-form.component.html b/src/app/components/organizations/edit/organization-form.component.html index a3639683bfa080bf2df4a0109d57c7fb0fd917b5..d00a3415c96010844093813d85249076880e9878 100644 --- a/src/app/components/organizations/edit/organization-form.component.html +++ b/src/app/components/organizations/edit/organization-form.component.html @@ -1,13 +1,6 @@ <ng-container *ngIf="organization"> - <div class="back-button"> - <a routerLink="/organizations" title="Retourner à la liste des organisations"> - <span class="icon is-medium"> - <i class="fas fa-arrow-left fa-lg"></i> - </span> - Retour - </a> - </div> + <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button> <h1>{{ title }}</h1> @@ -27,12 +20,9 @@ </div> </div> - <div class="field"> - <label class="label" for="logo">Logo</label> - <div class="control"> - <input class="input" type="text" [value]="organization.logo" formControlName="logo" id="logo"> - </div> - </div> + <app-image-upload [fieldParams]="logoFieldParams" (fileChanged)="logoChanged($event)" + (imageRemoved)="removeLogo()"> + </app-image-upload> <div class="field"> <label class="label required" for="description">Description</label> @@ -51,7 +41,8 @@ <div class="field"> <label class="label" for="elasticSearchName">Nom ElasticSearch de l'organisation</label> <div class="control"> - <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName" id="elasticSearchName"> + <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName" + id="elasticSearchName"> </div> </div> @@ -69,13 +60,13 @@ <div formArrayName="links"> <div *ngFor="let link of formLinks.controls; let i = index;" [formGroupName]="i" - class="columns is-multiline field"> + class="columns is-multiline field"> <div class="column is-11"> <div class="control"> - <input class="input" type="text" formControlName="url" required> + <input class="input" type="text" formControlName="url" required> </div> - <div *ngIf="link['controls'].url.invalid && (link['controls'].url.dirty || link['controls'].url.touched)" - class="alert alert-danger"> + <div *ngIf="link['controls'].url.invalid && (link['controls'].url.dirty || link['controls'].url.touched)" + class="alert alert-danger"> <p *ngIf="link.hasError('required', 'url')" class="help is-danger"> L'URL du lien est obligatoire. </p> @@ -90,15 +81,10 @@ </div> </div> </div> - - <div class="field"> - <div class="control"> - <button class="button validate" [disabled]="formInvalid == true">Valider</button> - </div> + <br> + <div class="has-text-right"> + <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button> </div> - </div> </form> - - -</ng-container> +</ng-container> \ No newline at end of file diff --git a/src/app/components/organizations/edit/organization-form.component.scss b/src/app/components/organizations/edit/organization-form.component.scss index 8522a9ab88fb48e0ae39d622940fa851978291f8..5bea26fe55b4ab7bb07da39d6140df0c988864d4 100644 --- a/src/app/components/organizations/edit/organization-form.component.scss +++ b/src/app/components/organizations/edit/organization-form.component.scss @@ -16,22 +16,4 @@ h1 { color: #d5232a; } } -} - -.validate { - background-color: #168f48; - color: white; -} - -.back-button { - a { - color: unset; // Remove default link color - } - padding: 1rem 1.5rem; - a:hover { - color: #d5232a; - span, a { - color: #d5232a; - } - } -} +} \ No newline at end of file diff --git a/src/app/components/organizations/edit/organization-form.component.ts b/src/app/components/organizations/edit/organization-form.component.ts index 3c2ce77e6618f345bdf34710f8783202e94f06bc..6952b5acf2765a670c81d8a822bc13353d8917a8 100644 --- a/src/app/components/organizations/edit/organization-form.component.ts +++ b/src/app/components/organizations/edit/organization-form.component.ts @@ -3,7 +3,9 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { Organization } from 'src/app/models/organization.model'; import { OrganizationService } from 'src/app/services/organization.service'; import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms'; -import { tap, filter, switchMap } from 'rxjs/operators'; +import { filter, switchMap, mergeMap } from 'rxjs/operators'; +import { IImageUploadFieldParams } from 'src/app/models/image-upload.model'; +import { NotificationService } from 'src/app/services'; @Component({ selector: 'app-organization-form', @@ -14,45 +16,54 @@ export class OrganizationFormComponent implements OnInit { organization: Organization; form: FormGroup; + logoFile: File; + logoFieldParams: IImageUploadFieldParams = { + inputName: 'logo', + label: 'Logo', + existingImageUrl: null, + }; + logo: File; title: string; constructor( - private organizationService: OrganizationService, - private route: ActivatedRoute, - private router: Router, + private _organizationService: OrganizationService, + private _notificationService: NotificationService, + private _route: ActivatedRoute, + private _router: Router, private _fb: FormBuilder, ) { } ngOnInit() { - this.title = this.route.snapshot.data.title; + this.title = this._route.snapshot.data.title; this.initForm(); - this.route.paramMap.pipe( + this._route.paramMap.pipe( filter((paramMap: ParamMap) => (paramMap.get('id') !== null)), - switchMap((paramMap: ParamMap) => this.organizationService.findById(paramMap.get('id')))) + switchMap((paramMap: ParamMap) => this._organizationService.findById(paramMap.get('id')))) .subscribe((organization: Organization) => { this.organization = organization; const arr = new FormArray([]); - this.organization.links.forEach(y => { + this.organization.links.forEach((y) => { arr.push(this._fb.group({ url: y.url, organizationId: y.organizationId, })); }); + this.logoFieldParams.existingImageUrl = organization.logo; + this.form = this._fb.group( { id: [this.organization.id], name: [organization.name, Validators.required], description: [organization.description, Validators.required], - logo: [organization.logo], elasticSearchName: [organization.elasticSearchName], + logo: [organization.logo], links: arr, }); - }); } @@ -60,13 +71,14 @@ export class OrganizationFormComponent implements OnInit { initForm() { this.organization = new Organization(); const arr = new FormArray([]); + this.logoFieldParams.existingImageUrl = null; this.form = this._fb.group( { id: [this.organization.id], name: [this.organization.name, Validators.required], description: [this.organization.description, Validators.required], - logo: [this.organization.logo], elasticSearchName: [this.organization.elasticSearchName], + logo: [this.organization.logo], links: arr, }); } @@ -83,9 +95,12 @@ export class OrganizationFormComponent implements OnInit { this.organization.links = []; } (this.form.controls.links as FormArray).push(this._fb.group( - { id: null, name: '', - url: ['', Validators.required], - organizationId: this.organization.id })); + { + id: null, name: '', + url: ['', Validators.required], + organizationId: this.organization.id, + }), + ); } removeLink(index) { @@ -97,19 +112,31 @@ export class OrganizationFormComponent implements OnInit { } onSubmit() { - if (! this.formInvalid) { + if (!this.formInvalid) { this.organization = new Organization(this.form.value); - this.organizationService.replaceOrCreate(this.organization) - .subscribe( - (organizationCreated) => { - this.router.navigate(['/organizations', organizationCreated.id]) - .then(() => { - }); - }, - (err) => { - alert(err.message); - }, - ); + let action; + + if (this.logoFile) { + action = this._organizationService.uploadLogoAndSaveOrganization(this.logoFile, this.organization); + } else { + action = this._organizationService.replaceOrCreate(this.organization); + } + + action.subscribe( + (organizationCreated) => { + this._notificationService.notify({ + message: 'L\'organisation a été créée avec succès.', + type: 'success', + }); + this._router.navigate(['/organizations', organizationCreated.id]); + }, + () => { + this._notificationService.notify({ + message: 'Une erreur est survenue lors de la création de l\'organisation.', + type: 'error', + }); + }, + ); } } @@ -123,6 +150,17 @@ export class OrganizationFormComponent implements OnInit { } get formInvalid() { - return this.form.invalid; + return this.form.invalid || + (!this.organization.logo && !this.logoFile); + } + + logoChanged(fileList: FileList) { + if (fileList && fileList.length > 0) { + this.logoFile = fileList[0]; + } + } + + removeLogo() { + this.form.get('logo').setValue(null); } } diff --git a/src/app/components/organizations/list/organizations.component.html b/src/app/components/organizations/list/organizations.component.html index e0c22f35203ad5145dfa7dd76d09f0cad0fbde41..cd06cbfebf61d7545861c6e923a256096f5ff6e3 100644 --- a/src/app/components/organizations/list/organizations.component.html +++ b/src/app/components/organizations/list/organizations.component.html @@ -6,21 +6,13 @@ <h2>{{ totalElement }} organisations trouvées</h2> </div> </div> - <div class="add-item"> - <a class="button is-link" [routerLink]="['new']"> + <div class="add-item has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> Ajouter </a> </div> <div class="table"> <div class="header columns is-marginless"> - <div class="column is-1 has-text-centered"> - <span (click)="sortBy('id')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === 'id'}">Id</span> - <span *ngIf="sortOptions.value === 'id'" class="has-text-danger"> - <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> - </span> - </span> - </div> <div class="column is-2 has-text-centered"> <span (click)="sortBy('name')" class="is-sortable"> <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span> @@ -35,7 +27,7 @@ <div class="column is-4 has-text-centered"> <span class="column-title">Description</span> </div> - <div class="column is-2 has-text-centered"> + <div class="column is-3 has-text-centered"> <span class="column-title">Liens</span> </div> <div class="column is-1 has-text-centered"> @@ -46,9 +38,6 @@ <div class="data columns is-multiline is-vcentered is-marginless" *ngFor="let organization of organizations; let i=index; let odd=odd; let even=even;" [ngClass]="{ odd: odd, even: even }"> - <div class="column is-1 has-text-centered"> - {{ organization.id }} - </div> <div class="column is-2 has-text-centered"> {{ organization.name}} </div> @@ -58,24 +47,13 @@ <div class="column is-4 has-text-justified"> {{ organization.description | slice:0:200}}... </div> - <div class="column is-2 has-text-centered"> + <div class="column is-3 has-text-centered"> <p *ngFor="let link of organization.links"> <a href="{{ link.url }}" target="_blank">{{link.url}}</a> </p> </div> <div class="column is-1 has-text-centered actions"> - <div class="columns is-marginless is-centered"> - <span class="icon column is-narrow"> - <a [routerLink]="[organization.id]"><i class="fas fa-eye"></i></a> - </span> - <span class="icon column is-narrow"> - <a [routerLink]="[organization.id, 'edit']"> <i class="fas fa-edit"></i></a> - </span> - <span class="icon has-text-danger column is-narrow" (click)="displayDeletePopup(organization.id)"> - <a> <i class="fas fa-trash"></i></a> - </span> - </div> - + <app-crud-buttons [id]="organization.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> </div> </div> diff --git a/src/app/components/organizations/list/organizations.component.scss b/src/app/components/organizations/list/organizations.component.scss index 5b91182b06c8b66f96e30646c55767d96bc0a805..087d2f4242ed39d8f3505d06012bf823f015572e 100644 --- a/src/app/components/organizations/list/organizations.component.scss +++ b/src/app/components/organizations/list/organizations.component.scss @@ -35,40 +35,6 @@ img { border-bottom: 1px solid lightgray } -.actions .columns { - border: none; -} - -.actions { - vertical-align: middle; - - .icon.column { - padding: 0; - } - - span { - display: inline-block; - } -} - -.icon { - height: unset; - cursor: pointer; - i { - color: #4a4a4a; - } - &:hover { - .fa-eye, .fa-edit { - color: blue; - } - - .fa-trash { - color: #d5232a; - } - - } -} - i { &.title-icon { diff --git a/src/app/components/organizations/list/organizations.component.ts b/src/app/components/organizations/list/organizations.component.ts index 17d063f7da2902ff8f6a68cef367260d148850ca..60020afb5dd4c14df58fccfe84cb081347bc9620 100644 --- a/src/app/components/organizations/list/organizations.component.ts +++ b/src/app/components/organizations/list/organizations.component.ts @@ -1,7 +1,7 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Organization, OrganizationRO } from 'src/app/models/organization.model'; import { OrganizationService } from 'src/app/services/organization.service'; -import { BehaviorSubject, Subscription } from 'rxjs'; +import { Subscription } from 'rxjs'; import { PaginatorOptions } from 'src/app/models/paginator-options.model'; @Component({ @@ -13,7 +13,6 @@ export class OrganizationsComponent implements OnInit { organizations: Organization[]; searchChangeSub: Subscription; - displayedColumns = ['name', 'logo', 'description', 'liens', 'actions']; // Paginator options paginator: PaginatorOptions; @@ -42,7 +41,7 @@ export class OrganizationsComponent implements OnInit { ngOnInit(): void { this.organizationsService.sortOptions = { value: 'name', - order: 'asc' + order: 'asc', }; this.search(); @@ -56,7 +55,6 @@ export class OrganizationsComponent implements OnInit { private search() { this.organizationsService.getOrganizations() .subscribe((items: OrganizationRO) => { - console.log(items); this.organizations = items.organizations; this.totalElement = items.totalCount; @@ -66,7 +64,6 @@ export class OrganizationsComponent implements OnInit { }); } - // When pagination is changed by user, we update datasetList with new pagination options changePagination(pageIndex) { this.organizationsService.paginationChanged(this.paginator.limit, pageIndex); @@ -92,7 +89,7 @@ export class OrganizationsComponent implements OnInit { displayDeletePopup(organizationId) { const pop = confirm('Etes vous sûr de vouloir supprimer cette organisation ?'); - if (pop === true) { + if (pop) { this.organizationsService.delete(organizationId).subscribe(() => { this.organizationsService.pageNumber = 1; this.search(); diff --git a/src/app/components/resources/detail/resource-detail.component.html b/src/app/components/resources/detail/resource-detail.component.html index fac0e690cee7db63941a75eb4d472c7aa9a9cbf2..60a5b0114ad29ff66798be9b4543a00585edf42a 100644 --- a/src/app/components/resources/detail/resource-detail.component.html +++ b/src/app/components/resources/detail/resource-detail.component.html @@ -1,14 +1,6 @@ <ng-container *ngIf="resource"> - <div class="back-button"> - <a routerLink="/resources" title="Retourner à la liste des organisations"> - <span class="icon is-medium"> - <i class="fas fa-arrow-left fa-lg"></i> - </span> - Retour - </a> - </div> - + <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button> <section class="section"> <div class="columns is-centered"> @@ -20,45 +12,94 @@ </p> </header> <div class="card-content"> - <div class="content"> <p> - <span class="has-text-weight-bold">Type: </span> - <span>{{resource.type}}</span> + <span class="has-text-weight-bold">Id: </span> + <span>{{resource.id}}</span> + </p> + <p> + <span class="has-text-weight-bold">Acronyme: </span> + <span *ngIf="resource.acronym; else emptyAcronymWarning">{{resource.acronym}}</span> + <ng-template #emptyAcronymWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </p> + + <p> + <span class="has-text-weight-bold">Type: </span> + <span *ngIf="resource.type; else emptyTypeWarning">{{resource.type}}</span> + <ng-template #emptyTypeWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> </p> - <p *ngIf="resource.description"> - <span class="has-text-weight-bold">Description</span> <br> - <span>{{resource.description}}</span> + <div *ngIf="resource.description"> + <span class="has-text-weight-bold">Description: </span> + <span *ngIf="resource.description; else emptyDescriptionWarning">{{resource.description}}</span> + <ng-template #emptyDescriptionWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> + + <p> + <span class="has-text-weight-bold">Requêtable: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isQueryable, 'has-text-danger': !resource.isQueryable}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isQueryable, 'fa-times-circle': !resource.isQueryable}"></i> + </span> </p> <p> - <span class="has-text-weight-bold">Requêtable</span> - <span class="icon has-text-success" *ngIf="resource.queryable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.queryable"> - <i class="far fa-times-circle"></i> - </span> + <span class="has-text-weight-bold">Téléchargeable: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isDownloadable, 'has-text-danger': !resource.isDownloadable}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isDownloadable, 'fa-times-circle': !resource.isDownloadable}"></i> + </span> </p> <p> - <span class="has-text-weight-bold">Téléchargeable</span> - <span class="icon has-text-success" *ngIf="resource.downloadable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.downloadable"> - <i class="far fa-times-circle"></i> - </span> + <span class="has-text-weight-bold">Standardisé: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isStandard, 'has-text-danger': !resource.isStandard}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isStandard, 'fa-times-circle': !resource.isStandard}"></i> + </span> </p> + <div *ngIf="resource.parametersUrl"> + <span class="has-text-weight-bold">Parametres URL: </span> + <span *ngIf="resource.parametersUrl; else emptyParameterUrlWarning">{{resource.parametersUrl}}</span> + <ng-template #emptyParameterUrlWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> + + <div *ngIf="resource.messageWarning"> + <span class="has-text-weight-bold">Message d'alerte: </span> + <span *ngIf="resource.messageWarning; else emptyMessageWarning">{{resource.messageWarning}}</span> + <ng-template #emptyMessageWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> + <br> + <div> <span class="has-text-weight-bold">Formats de sortie: </span> - <span *ngFor="let format of resource.outputFormats; let isLast=last"> - {{ format }}{{ isLast ? '' : ',' }} - </span> + <ul + *ngIf="resource.resourceFormats && resource.resourceFormats.length > 0; else noResourceFormatTemplate"> + <li *ngFor="let resourceFormat of resource.resourceFormats"> + {{ resourceFormat.format.name }}<span *ngIf="resourceFormat.isCuttable">, découpable</span><span + *ngIf="resourceFormat.isProjectable">, projectable</span> + </li> + </ul> + <ng-template #noResourceFormatTemplate> + <span class="empty-property">Non renseigné</span> + </ng-template> </div> - + + </div> </div> </div> @@ -69,4 +110,4 @@ </section> -</ng-container> +</ng-container> \ No newline at end of file diff --git a/src/app/components/resources/detail/resource-detail.component.scss b/src/app/components/resources/detail/resource-detail.component.scss index 9198904fe9916e79864faf708db4350f7d80bae6..05012fa666610636fa011ff7250fcdc78764b468 100644 --- a/src/app/components/resources/detail/resource-detail.component.scss +++ b/src/app/components/resources/detail/resource-detail.component.scss @@ -1,17 +1,8 @@ - -.back-button { - a { - color: unset; // Remove default link color - } - padding: 1rem 1.5rem; - a:hover { - color: #d5232a; - span, a { - color: #d5232a; - } - } -} - .card-header-title { justify-content: center; -} \ No newline at end of file +} + +.empty-property { + font-style: italic; + color: #818080; +} diff --git a/src/app/components/resources/edit/resource-form.component.html b/src/app/components/resources/edit/resource-form.component.html index 984aa5d6b31947a1e6594b3e3edf0b18cbbbe54a..9e24f8e03eb9db223d6f6fd09aaab963021582af 100644 --- a/src/app/components/resources/edit/resource-form.component.html +++ b/src/app/components/resources/edit/resource-form.component.html @@ -1,13 +1,6 @@ <ng-container *ngIf="resource"> - <div class="back-button"> - <a routerLink="/resources" title="Retourner à la liste des organisations"> - <span class="icon is-medium"> - <i class="fas fa-arrow-left fa-lg"></i> - </span> - Retour - </a> - </div> + <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button> <h1>{{ title }}</h1> @@ -18,7 +11,7 @@ <div class="field"> <label class="label required" for="name">Nom</label> <div class="control"> - <input class="input" type="text" formControlName="name" id="name" required> + <input class="input" type="text" formControlName="name" id="name"> </div> <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> <p *ngIf="name.errors['required']" class="help is-danger"> @@ -27,10 +20,17 @@ </div> </div> + <div class="field"> + <label class="label" for="acronym">Acronyme</label> + <div class="control"> + <input class="input" type="text" formControlName="acronym" id="acronym"> + </div> + </div> + <div class="field"> <label class="label required" for="type">Type</label> <div class="control"> - <input class="input" type="text" formControlName="type" id="type" required> + <input class="input" type="text" formControlName="type" id="type"> </div> <div *ngIf="type.invalid && (type.dirty || type.touched)" class="alert alert-danger"> <p *ngIf="type.errors['required']" class="help is-danger"> @@ -40,28 +40,42 @@ </div> <div class="field"> - <label class="label required" for="queryable">Requêtable</label> + <label class="label required" for="isQueryable">Requêtable</label> + <div class="control"> + <label class="radio"> + <input type="radio" formControlName="isQueryable" [value]="1"> + Oui + </label> + <label class="radio"> + <input type="radio" formControlName="isQueryable" [value]="0"> + Non + </label> + </div> + </div> + + <div class="field"> + <label class="label required" for="isDownloadable">Téléchargeable</label> <div class="control"> <label class="radio"> - <input type="radio" formControlName="queryable" [value]="1"> + <input type="radio" formControlName="isDownloadable" [value]="1"> Oui </label> <label class="radio"> - <input type="radio" formControlName="queryable" [value]="0"> + <input type="radio" formControlName="isDownloadable" [value]="0"> Non </label> </div> </div> <div class="field"> - <label class="label required" for="downloadable">Téléchargeable</label> + <label class="label required" for="isStandard">Standardisé</label> <div class="control"> <label class="radio"> - <input type="radio" formControlName="downloadable" [value]="1"> + <input type="radio" formControlName="isStandard" [value]="1"> Oui </label> <label class="radio"> - <input type="radio" formControlName="downloadable" [value]="0"> + <input type="radio" formControlName="isStandard" [value]="0"> Non </label> </div> @@ -71,26 +85,97 @@ <label class="label" for="description">Description</label> <div class="control"> <textarea class="textarea" formControlName="description" id="description"> - {{ resource.description }} - </textarea> + </textarea> </div> </div> <div class="field"> - <label class="label" for="outputFormats">Formats de sortie (Séparer chaque format par une virgule)</label> + <label class="label required" for="parametersUrl">Paramètres URL</label> <div class="control"> - <input class="input" type="text" formControlName="outputFormats" id="outputFormats"> + <input class="input" type="text" formControlName="parametersUrl" id="parametersUrl"> </div> - </div> + <div class="field"> + <label class="label required" for="messageWarning">Message d'alerte</label> <div class="control"> - <button class="button validate" [disabled]="formInvalid == true">Valider</button> + <input class="input" type="text" formControlName="messageWarning" id="messageWarning"> </div> </div> - </div> - </form> + <div class="field links"> + <div class="columns"> + <div class="column is-11"> + <label class="label">Formats</label> + </div> + <div class="column is-1"> + <span class="icon" (click)="addResourceFormat()" title="Ajouter un format"> + <i class="fas fa-plus"></i> + </span> + </div> + </div> + <div formArrayName="resourceFormats"> + <div *ngFor="let resourceFormat of formResourceFormats.controls; let i = index;" [formGroupName]="i" + class="columns is-multiline"> + <div class="field column is-5"> + <label class="label required" for="format">Format</label> + <div class="control"> + <div class="select"> + <select formControlName="formatId"> + <option [ngValue]="format.id" *ngFor="let format of formatsList"> {{format.name}}</option> + </select> + </div> + </div> + <div + *ngIf="resourceFormat['controls'].formatId.invalid && (resourceFormat['controls'].formatId.dirty || resourceFormat['controls'].formatId.touched)" + class="alert alert-danger"> + <p *ngIf="resourceFormat.hasError('required', 'format')" class="help is-danger"> + Le format est requis. + </p> + </div> + </div> + <div class="field column is-3"> + <label class="label required" for="isProjectable">Projectable</label> + <div class="control"> + <label class="radio"> + <input type="radio" formControlName="isProjectable" [value]="1"> + Oui + </label> + <label class="radio"> + <input type="radio" formControlName="isProjectable" [value]="0"> + Non + </label> + </div> + </div> + <div class="field column is-3"> + <label class="label required" for="isCuttable">Découpable</label> + <div class="control"> + <label class="radio"> + <input type="radio" formControlName="isCuttable" [value]="1"> + Oui + </label> + <label class="radio"> + <input type="radio" formControlName="isCuttable" [value]="0"> + Non + </label> + </div> + </div> + <div class="column is-1"> + <span class="icon" (click)="removeResourceFormat(i)" title="Supprimer le format"> + <i class="fas fa-trash"></i> + </span> + </div> + </div> + </div> + </div> -</ng-container> + + <br> + <div class="has-text-right"> + <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button> + </div> + + </div> + </form> +</ng-container> \ No newline at end of file diff --git a/src/app/components/resources/edit/resource-form.component.scss b/src/app/components/resources/edit/resource-form.component.scss index 8522a9ab88fb48e0ae39d622940fa851978291f8..c540d1b2697b6ce03cfbbdb19454eb365d6e2a4a 100644 --- a/src/app/components/resources/edit/resource-form.component.scss +++ b/src/app/components/resources/edit/resource-form.component.scss @@ -22,16 +22,3 @@ h1 { background-color: #168f48; color: white; } - -.back-button { - a { - color: unset; // Remove default link color - } - padding: 1rem 1.5rem; - a:hover { - color: #d5232a; - span, a { - color: #d5232a; - } - } -} diff --git a/src/app/components/resources/edit/resource-form.component.ts b/src/app/components/resources/edit/resource-form.component.ts index 4b77a4d0c857e8d33a260d0b5cdaa199e8839346..b40283eb2dbed3b4d2148c60ebd4bb3e6e048052 100644 --- a/src/app/components/resources/edit/resource-form.component.ts +++ b/src/app/components/resources/edit/resource-form.component.ts @@ -1,9 +1,12 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Resource } from 'src/app/models/resource.model'; +import { Resource, IResource } from 'src/app/models/resource.model'; import { ResourceService } from 'src/app/services/resource.service'; import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms'; -import { filter, switchMap } from 'rxjs/operators'; +import { filter, switchMap, mergeMap, merge, concatMap, map } from 'rxjs/operators'; +import { Format } from 'src/app/models/format.model'; +import { FormatService } from 'src/app/services'; +import { from, forkJoin, Observable, of } from 'rxjs'; @Component({ selector: 'app-resource-form', @@ -12,75 +15,134 @@ import { filter, switchMap } from 'rxjs/operators'; }) export class ResourceFormComponent implements OnInit { - resource: Resource; + resource: Resource = new Resource(); + formatsList: Format[] = []; form: FormGroup; title: string; constructor( - private resourceService: ResourceService, - private route: ActivatedRoute, - private router: Router, + private _resourceService: ResourceService, + private _formatService: FormatService, + private _route: ActivatedRoute, + private _router: Router, private _fb: FormBuilder, ) { } ngOnInit() { - this.title = this.route.snapshot.data.title; + this.title = this._route.snapshot.data.title; this.initForm(); - this.route.paramMap.pipe( + this._formatService.getAllFormats().subscribe((res) => { + this.formatsList = res; + }); + + this._route.paramMap.pipe( filter((paramMap: ParamMap) => (paramMap.get('id') !== null)), - switchMap((paramMap: ParamMap) => this.resourceService.findById(paramMap.get('id')))) + switchMap((paramMap: ParamMap) => this._resourceService.findById(paramMap.get('id')))) .subscribe((resource: Resource) => { - this.resource = resource; - - this.form = this._fb.group( - { - id: [this.resource.id], - name: [resource.name, Validators.required], - type: [resource.type, Validators.required], - queryable: [resource.queryable, Validators.required], - downloadable: [this.resource.downloadable, Validators.required], - description: [resource.description], - outputFormats: [this.resource.outputFormats.join(',')], - }); - + this.initForm(); }); } initForm() { - this.resource = new Resource(); - const arr = new FormArray([]); + const resourceFormats = new FormArray([]); + this.resource.resourceFormats.forEach((resourceFormat) => { + resourceFormats.push(this._fb.group({ + formatId: [resourceFormat.format.id, Validators.required], + isProjectable: resourceFormat.isProjectable, + isCuttable: resourceFormat.isCuttable, + id: resourceFormat.id, + })); + }); + this.form = this._fb.group( { + resourceFormats, id: [this.resource.id], name: [this.resource.name, Validators.required], + acronym: [this.resource.acronym], type: [this.resource.type, Validators.required], - queryable: [this.resource.queryable, Validators.required], - downloadable: [this.resource.downloadable, Validators.required], + isQueryable: [this.resource.isQueryable, Validators.required], + isDownloadable: [this.resource.isDownloadable, Validators.required], + isStandard: [this.resource.isStandard, Validators.required], description: [this.resource.description], - outputFormats: [this.resource.outputFormats.join(',')], + parametersUrl: [this.resource.parametersUrl], + messageWarning: [this.resource.messageWarning], }); } onSubmit() { if (!this.formInvalid) { - this.resource = new Resource(this.form.value); - this.resource.outputFormats = this.form.value.outputFormats.split(','); - this.resourceService.replaceOrCreate(this.resource) - .subscribe( - (resourceCreated) => { - this.router.navigate(['/resources', resourceCreated.id]) - .then(() => { - }); - }, - (err) => { - alert(err.message); - }, - ); + const resourceFormats = this.form.controls.resourceFormats['controls'].filter(e => e.dirty).map(e => e.value); + const newResource = new Resource(this.form.value); + let savedResource: Resource; + this._resourceService.replaceOrCreate(newResource).pipe( + mergeMap((resource) => { + savedResource = resource; + let actions: Observable<any>[] = []; + + const toAdd = resourceFormats.filter(e => !e.id); + const toUpdate = resourceFormats.filter(e => e.id) + const toDelete = this.resource.resourceFormats.map(e => e.id).filter((id) => { + return !this.form.controls.resourceFormats['controls'].map(e => e.value.id).find(rfId => rfId === id); + }); + + if (toAdd.length > 0) { + actions.push(this._resourceService.createResourceFormats(resource.id, toAdd)); + } + + if (toUpdate.length > 0) { + actions.push(this._resourceService.updateResourceFormats(resource.id, toUpdate)); + } + + if (toDelete.length > 0) { + actions.push(this._resourceService.deleteResourceFormats(resource.id, toDelete)); + } + + let res; + (actions.length > 0) ? res = forkJoin(actions) : res = of(null); + + return res; + }), + map(() => savedResource.id), + ).subscribe( + (resourceCreatedId) => { + this._router.navigate(['/resources', resourceCreatedId]); + }, + (err) => { + }, + ); + } + } + + arrayToString(array: string[]) { + let val = ''; + if (array && array.length) { + val = array.join(','); } + return val; + } + + addResourceFormat() { + // if (!this.form.controls.links) { + // this.organization.links = []; + // } + (this.form.controls.resourceFormats as FormArray).push(this._fb.group({ + formatId: [null, Validators.required], + isProjectable: 0, + isCuttable: 0, + })); + } + + removeResourceFormat(index) { + (this.form.controls.resourceFormats as FormArray).removeAt(index); + } + + compareFormats(format1: Format, format2: Format) { + return format1 && format2 ? format1.id === format2.id : false; } // Getters for each property @@ -92,18 +154,22 @@ export class ResourceFormComponent implements OnInit { return this.form.controls['type']; } - get queryable() { - return this.form.controls['queryable']; + get isQueryable() { + return this.form.controls['isQueryable']; } get description() { return this.form.controls['description']; } - get outputFormats() { - return this.form.controls['outputFormats']; + get formResourceFormats() { + return this.form.controls.resourceFormats as FormArray; } + // get outputFormats() { + // return this.form.controls['outputFormats']; + // } + get formInvalid() { return this.form.invalid; } diff --git a/src/app/components/resources/list/resources.component.html b/src/app/components/resources/list/resources.component.html index 9606966c5198e8fe8198338de2a52f5ebf0d2bd7..67f84ebc6389a162554ef6300e57f97b2ccc1b1c 100644 --- a/src/app/components/resources/list/resources.component.html +++ b/src/app/components/resources/list/resources.component.html @@ -6,110 +6,100 @@ <h2>{{ totalElement }} ressources trouvées</h2> </div> </div> - <div class="add-item"> - <a class="button is-link" [routerLink]="['new']"> + <div class="add-item has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> Ajouter </a> </div> <div class="table"> - <div class="header columns is-marginless"> - <div class="column is-1 has-text-centered"> - <span (click)="sortBy('id')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === 'id'}">Id</span> - <span *ngIf="sortOptions.value === 'id'" class="has-text-danger"> - <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> - </span> - </span> - </div> - <div class="column is-2 has-text-centered"> - <span (click)="sortBy('name')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span> - <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> - <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> - </span> - </span> - </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Type</span> - </div> - <div class="column is-3 has-text-centered"> - <span class="column-title">Description</span> - </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Requêtable</span> - </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Téléchargeable</span> - </div> - <div class="column is-2 has-text-centered"> - <span class="column-title">Formats</span> - </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Actions</span> - </div> + <div class="header columns is-marginless"> + <div class="column is-2 has-text-centered"> + <span (click)="sortBy('name')" class="is-sortable"> + <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span> + <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> + <i class="fas sort-order-icon" + [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> + </span> + </span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Acronyme</span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Type</span> + </div> + <div class="column is-4 has-text-centered"> + <span class="column-title">Description</span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Requêtable</span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Téléchargeable</span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Standard</span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Actions</span> + </div> + </div> + <div class="data-list"> + <div class="data columns is-multiline is-vcentered is-marginless" + *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;" + [ngClass]="{ odd: odd, even: even }"> + <div class="column is-2 has-text-centered"> + {{ resource.name}} + </div> + <div class="column is-1 has-text-centered"> + {{ resource.acronym}} + </div> + <div class="column is-1 has-text-centered"> + {{ resource.type}} + </div> + <div class="column is-4 has-text-centered"> + {{ resource.description | slice:0:300}} </div> - <div class="data-list"> - <div class="data columns is-multiline is-vcentered is-marginless" - *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;" - [ngClass]="{ odd: odd, even: even }"> - <div class="column is-1 has-text-centered"> - {{ resource.id }} - </div> - <div class="column is-2 has-text-centered"> - {{ resource.name}} - </div> - <div class="column is-1 has-text-centered"> - {{ resource.type}} - </div> - <div class="column is-3 has-text-centered"> - {{ resource.description | slice:0:300}} - </div> - <div class="column is-1 has-text-centered"> - <span class="icon has-text-success" *ngIf="resource.queryable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.queryable"> - <i class="far fa-times-circle"></i> - </span> - </div> - <div class="column is-1 has-text-centered"> - <span class="icon has-text-success" *ngIf="resource.downloadable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.downloadable"> - <i class="far fa-times-circle"></i> - </span> - </div> - <div class="column is-2 has-text-centered"> - {{ resource.outputFormats}} - </div> - <div class="column is-1 has-text-centered actions"> - <div class="columns is-marginless is-centered"> - <span class="icon column is-narrow"> - <a [routerLink]="[resource.id]"><i class="fas fa-eye"></i></a> - </span> - <span class="icon column is-narrow"> - <a [routerLink]="[resource.id, 'edit']"> <i class="fas fa-edit"></i></a> - </span> - <span class="icon has-text-danger column is-narrow" (click)="displayDeletePopup(resource.id)"> - <a> <i class="fas fa-trash"></i></a> - </span> - </div> - - </div> - - </div> + <div class="column is-1 has-text-centered"> + <span class="icon has-text-success" *ngIf="resource.isQueryable"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isQueryable"> + <i class="far fa-times-circle"></i> + </span> </div> - <div class="columns is-marginless"> - <div class="column"> - <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" - [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" - [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)"> - </app-paginator> - </div> + <div class="column is-1 has-text-centered"> + <span class="icon has-text-success" *ngIf="resource.isDownloadable"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isDownloadable"> + <i class="far fa-times-circle"></i> + </span> </div> + <div class="column is-1 has-text-centered"> + <span class="icon has-text-success" *ngIf="resource.isStandard"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isStandard"> + <i class="far fa-times-circle"></i> + </span> + </div> + <div class="column is-1 has-text-centered"> + <app-crud-buttons [id]="resource.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> + </div> + </div> + </div> + <div class="columns is-marginless"> + <div class="column"> + <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" + [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" + [showFirstLastButtons]="true" (page)="changePagination($event)" + (pageSizeChanged)="changePageSize($event)"> + </app-paginator> + </div> + </div> </div> </div> </div> -</ng-container> +</ng-container> \ No newline at end of file diff --git a/src/app/components/resources/list/resources.component.scss b/src/app/components/resources/list/resources.component.scss index aa337b2be447c0ed3974f37cb692ae0ef133d19b..c746b911909d67f2432690682808294ac825224e 100644 --- a/src/app/components/resources/list/resources.component.scss +++ b/src/app/components/resources/list/resources.component.scss @@ -35,37 +35,6 @@ img { border-bottom: 1px solid lightgray } -.actions .columns { - border: none; -} - -.actions { - vertical-align: middle; - - .icon.column { - padding: 0; - } - - span { - display: inline-block; - } -} - -.actions .icon { - cursor: pointer; - - i { - color: #4a4a4a; - } - &:hover { - .fa-eye, .fa-edit { - color: blue; - } - .fa-trash { - color: #d5232a; - } - } -} .icon { height: unset; } diff --git a/src/app/components/resources/list/resources.component.ts b/src/app/components/resources/list/resources.component.ts index a8d9357aa537cee3e71e6f1b72186adf159138f8..a56ed6ddeeaf05512edcd33cddbfa12247c45bb7 100644 --- a/src/app/components/resources/list/resources.component.ts +++ b/src/app/components/resources/list/resources.component.ts @@ -41,7 +41,7 @@ export class ResourcesComponent implements OnInit { ngOnInit(): void { this.resourcesService.sortOptions = { value: 'name', - order: 'asc' + order: 'asc', }; this.search(); @@ -64,7 +64,6 @@ export class ResourcesComponent implements OnInit { }); } - // When pagination is changed by user, we update datasetList with new pagination options changePagination(pageIndex) { this.resourcesService.paginationChanged(this.paginator.limit, pageIndex); @@ -89,8 +88,8 @@ export class ResourcesComponent implements OnInit { } displayDeletePopup(resourceId) { - const pop = confirm('Etes vous sûr de vouloir supprimer cette organisation ?'); - if (pop === true) { + const pop = confirm('Etes vous sûr de vouloir supprimer cette ressource ?'); + if (pop) { this.resourcesService.delete(resourceId).subscribe(() => { this.resourcesService.pageNumber = 1; this.search(); diff --git a/src/app/models/format.model.ts b/src/app/models/format.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebef0b2408621fcc803d6c67e724cf9c3ec13748 --- /dev/null +++ b/src/app/models/format.model.ts @@ -0,0 +1,34 @@ +export class Format { + id?: number; + name: string; + mapServerType: string; + + constructor(format?: IFormat) { + if (format) { + if (format.id) { + this.id = format.id; + } + this.name = format.name; + this.mapServerType = format.mapServerType; + } else { + this.name = ''; + this.mapServerType = ''; + } + } +} + +export class FormatRO { + formats: Format[]; + totalCount: number; + + constructor(formats, totalCount) { + this.formats = formats; + this.totalCount = totalCount; + } +} + +export interface IFormat { + id: number; + name: string; + mapServerType: string; +} diff --git a/src/app/models/image-upload.model.ts b/src/app/models/image-upload.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c4a768dc92888a6ef1f8ee5f8f0ceba92f32b4e --- /dev/null +++ b/src/app/models/image-upload.model.ts @@ -0,0 +1,5 @@ +export interface IImageUploadFieldParams { + label: string; + inputName: string; + existingImageUrl?: string; +} diff --git a/src/app/models/notification.model.ts b/src/app/models/notification.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..8fdf5fcff77594e5963c191719c2bc0990403e76 --- /dev/null +++ b/src/app/models/notification.model.ts @@ -0,0 +1,16 @@ +type errorType = 'error' | 'warning' | 'success'; + +export interface INotification { + message: string; + type: errorType; +} + +export class Notification { + message: string; + type: errorType; + + constructor(notification: INotification) { + this.message = notification.message; + this.type = notification.type; + } +} diff --git a/src/app/models/organization.model.ts b/src/app/models/organization.model.ts index 53adbd907ffd027f9924c0d4b9376bcae8364a02..54074cb77548abbd77d3452fe686f213512f5f73 100644 --- a/src/app/models/organization.model.ts +++ b/src/app/models/organization.model.ts @@ -8,17 +8,25 @@ export class Organization { constructor(organization?: IOrganization) { if (organization) { - this.id = organization.id; + if (organization.id) { + this.id = organization.id; + } this.name = organization.name; this.description = organization.description; this.elasticSearchName = organization.elasticSearchName; this.links = organization.links; + + this.links.forEach((link) => { + if (!link.id) { + delete link.id; + } + }); this.logo = organization.logo; } else { - this.name = ''; - this.description = ''; - this.logo = ''; - this.elasticSearchName = ''; + this.name = null; + this.description = null; + this.logo = null; + this.elasticSearchName = null; this.links = []; } diff --git a/src/app/models/resource.model.ts b/src/app/models/resource.model.ts index 7fb24fa24833f9c6553c65955df1c017b10db4c2..2d28cdda3fc135178cc0515efd361079a1f286ed 100644 --- a/src/app/models/resource.model.ts +++ b/src/app/models/resource.model.ts @@ -1,30 +1,46 @@ +import { Format } from './format.model'; + export class Resource { - id: number; + id?: string; name: string; + acronym: string; type: string; - description?: string; - queryable: number; - downloadable: number; - outputFormats: string[]; + description: string; + isQueryable: number; + isDownloadable: number; + isStandard: number; + parametersUrl: string; + messageWarning: string; + resourceFormats: IResourceFormat[]; constructor(resource?: IResource) { + if (resource) { - this.id = resource.id; + if (resource.id) { + this.id = resource.id; + } this.name = resource.name; + this.acronym = resource.acronym; this.type = resource.type; this.description = resource.description; - this.queryable = resource.queryable; - this.downloadable = resource.downloadable; - this.outputFormats = resource.outputFormats; + this.isQueryable = resource.isQueryable; + this.isDownloadable = resource.isDownloadable; + this.isStandard = resource.isStandard; + this.parametersUrl = resource.parametersUrl; + this.messageWarning = resource.messageWarning; + this.resourceFormats = resource.resourceFormats ? resource.resourceFormats : []; } else { this.name = ''; - this.description = ''; + this.acronym = ''; this.type = ''; - this.queryable = 0; - this.downloadable = 0; - this.outputFormats = []; + this.description = ''; + this.isQueryable = 0; + this.isDownloadable = 0; + this.isStandard = 0; + this.parametersUrl = ''; + this.messageWarning = ''; + this.resourceFormats = []; } - } } @@ -39,18 +55,23 @@ export class ResourceRO { } export interface IResource { - id: number; + id: string; name: string; + acronym: string; type: string; description: string; - queryable: number; - downloadable: number; - outputFormats: string[]; + isQueryable: number; + isDownloadable: number; + isStandard: number; + parametersUrl: string; + messageWarning: string; + resourceFormats: IResourceFormat[]; } -interface ILink { - id?: number; - name: string; - url: string; - organizationId: number; +export interface IResourceFormat { + format: Format; + formatId: string; + isProjectable: number; + isCuttable: number; + id?: string; } diff --git a/src/app/services/app-config.service.ts b/src/app/services/app-config.service.ts index edabf90b5de6509faaa179008aaa0cac92a70606..66e9fd31fad98efbde56154a7c9f346e8cf1dcb4 100644 --- a/src/app/services/app-config.service.ts +++ b/src/app/services/app-config.service.ts @@ -7,6 +7,9 @@ export class AppConfig { resources: { url: string; }; + mediaLibrary: { + url: string; + }; } export let APP_CONFIG: AppConfig; diff --git a/src/app/services/format.service.ts b/src/app/services/format.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..722f25a8a0188f9c85742e343b50e66f59a167c7 --- /dev/null +++ b/src/app/services/format.service.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { APP_CONFIG } from './app-config.service'; +import { FormatRO, IFormat, Format } from '../models/format.model'; + +@Injectable() +export class FormatService { + + formatServiceUrl: string; + limit: number; + pageNumber: number; + sortOptions: { + value: string, + order: string, + }; + + private _searchChangeSubject: Subject<any>; + + constructor( + private _httpClient: HttpClient) { + this.formatServiceUrl = `${APP_CONFIG.resources.url}formats/`; + this._searchChangeSubject = new Subject<any>(); + this.limit = 10; + this.pageNumber = 1; + } + + getFormats(): Observable<FormatRO> { + let query = '?'; + query += `limit=${(this.limit ? this.limit : 20)}`; + query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`; + query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`; + + return this._httpClient.get<IFormat[]>(this.formatServiceUrl + query, { observe: 'response' }).pipe( + map((response) => { + const totalCount = response.headers.get('Content-Range'); + const formats = []; + response.body.forEach((format) => { + formats.push(new Format(format)); + }); + return new FormatRO(formats, parseInt(totalCount, 10)); + })); + } + + getAllFormats(): Observable<Format[]> { + return this._httpClient.get<IFormat[]>(this.formatServiceUrl).pipe( + map(body => body.map(format => new Format(format))), + ); + } + + findById(id): Observable<Format> { + return this._httpClient.get<IFormat>(this.formatServiceUrl + id).pipe( + map((response) => { + return new Format(response); + }), + ); + } + + delete(id) { + return this._httpClient.delete(this.formatServiceUrl + id); + } + + replaceOrCreate(data): Observable<Format> { + if (data.id) { + return this._httpClient.put<IFormat>(this.formatServiceUrl + data.id, data).pipe( + map((response) => { + return new Format(response); + }), + ); + } + return this._httpClient.post<IFormat>(this.formatServiceUrl, data).pipe( + map((response) => { + return new Format(response); + }), + ); + } + + /* PAGINATION */ + paginationChanged(limit: number, pageNumber: number) { + this.limit = limit; + this.pageNumber = pageNumber; + this._searchChangeSubject.next(); + } + + reverseSortOrder(): void { + if (this.sortOptions.order === 'asc') { + this.sortOptions.order = 'desc'; + } else { + this.sortOptions.order = 'asc'; + } + } + + get searchChange$(): Observable<string> { + return this._searchChangeSubject.asObservable(); + } +} diff --git a/src/app/services/index.ts b/src/app/services/index.ts index 24455cc0269d221c37faf7bb2c66792a0ec257b8..34d4ee52b65425a4c8e071ecb732d74c314446ec 100644 --- a/src/app/services/index.ts +++ b/src/app/services/index.ts @@ -1,11 +1,15 @@ import { AppConfigService } from './app-config.service'; import { OrganizationService } from './organization.service'; import { ResourceService } from './resource.service'; +import { FormatService } from './format.service'; +import { NotificationService } from './notification.service'; export { AppConfigService, OrganizationService, ResourceService, + FormatService, + NotificationService, }; // tslint:disable-next-line:variable-name @@ -13,4 +17,6 @@ export const AppServices = [ AppConfigService, OrganizationService, ResourceService, + FormatService, + NotificationService, ]; diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a1a532e14cd3da0e832564d46e40aa03f3f447b --- /dev/null +++ b/src/app/services/notification.service.ts @@ -0,0 +1,21 @@ + +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { Notification } from '../models/notification.model'; + +@Injectable() +export class NotificationService { + + private _notification: BehaviorSubject<Notification> = new BehaviorSubject(null); + + constructor() { } + + notify(notification: Notification) { + this._notification.next(notification); + } + + get notification$() { + return this._notification; + } + +} diff --git a/src/app/services/organization.service.ts b/src/app/services/organization.service.ts index c5360560f0a3ef99269166b974109ef92beef31d..d5a82d7ce9d00a5b3a3aeebd765a1a93fca73923 100644 --- a/src/app/services/organization.service.ts +++ b/src/app/services/organization.service.ts @@ -1,11 +1,10 @@ import { Injectable } from '@angular/core'; -import { Observable , Subject } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Observable, Subject } from 'rxjs'; +import { map, mergeMap } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Organization, IOrganization, OrganizationRO } from '../models/organization.model'; import { APP_CONFIG } from './app-config.service'; - @Injectable() export class OrganizationService { @@ -20,16 +19,16 @@ export class OrganizationService { constructor( private _httpClient: HttpClient) { - this._searchChangeSubject = new Subject<any>(); - this.limit = 10; - this.pageNumber = 1; + this._searchChangeSubject = new Subject<any>(); + this.limit = 10; + this.pageNumber = 1; } getOrganizations(options?): Observable<OrganizationRO> { let query = '?'; - query += 'limit=' + (this.limit ? this.limit : 20); - query += '&offset=' + (this.pageNumber ? (this.pageNumber - 1) * this.limit : 0); - query += '&sort_by=' + this.sortOptions.value + '.' + this.sortOptions.order; + query += `limit=${(this.limit ? this.limit : 20)}`; + query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`; + query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`; return this._httpClient.get<IOrganization[]>(APP_CONFIG.organizations.url + query, { observe: 'response' }).pipe( map((response) => { @@ -46,49 +45,66 @@ export class OrganizationService { return this._httpClient.get<IOrganization>(APP_CONFIG.organizations.url + id).pipe( map((response) => { return new Organization(response); - } - )); + }), + ); } delete(id) { return this._httpClient.delete(APP_CONFIG.organizations.url + id); } + uploadLogoAndSaveOrganization(logoFile, organization) { + return this.uploadLogo(logoFile).pipe( + mergeMap((response: any) => { + organization.logo = response.mediaUrl; + return this.replaceOrCreate(organization); + }), + ) + } + + uploadLogo(logoFile): Observable<string> { + const formData = new FormData(); + formData.append('file', logoFile); + + return this._httpClient.post<string>(`${APP_CONFIG.mediaLibrary.url}media`, formData).pipe( + map((response) => { + return response; + }), + ); + } + replaceOrCreate(data: Organization): Observable<Organization> { if (data.id) { return this._httpClient.put<IOrganization>(APP_CONFIG.organizations.url + data.id, data).pipe( map((response) => { return new Organization(response); - } - )); + }), + ); } - delete data.id; - data.links.forEach((link) => { - delete link.id; - }); + return this._httpClient.post<IOrganization>(APP_CONFIG.organizations.url, data).pipe( map((response) => { return new Organization(response); - } - )); + }), + ); } - /* PAGINATION */ - paginationChanged(limit: number, pageNumber: number) { - this.limit = limit; - this.pageNumber = pageNumber; - this._searchChangeSubject.next(); - } + /* PAGINATION */ + paginationChanged(limit: number, pageNumber: number) { + this.limit = limit; + this.pageNumber = pageNumber; + this._searchChangeSubject.next(); + } - reverseSortOrder(): void { - if (this.sortOptions.order === 'asc') { - this.sortOptions.order = 'desc'; - } else { - this.sortOptions.order = 'asc'; - } + reverseSortOrder(): void { + if (this.sortOptions.order === 'asc') { + this.sortOptions.order = 'desc'; + } else { + this.sortOptions.order = 'asc'; } + } - get searchChange$(): Observable<string> { - return this._searchChangeSubject.asObservable(); - } + get searchChange$(): Observable<string> { + return this._searchChangeSubject.asObservable(); + } } diff --git a/src/app/services/resource.service.ts b/src/app/services/resource.service.ts index d1b0d38d309beb7f3763e593c08a352f987e4737..b598eb31653bfe0f7fd8005c2d93a78bb996abf5 100644 --- a/src/app/services/resource.service.ts +++ b/src/app/services/resource.service.ts @@ -1,13 +1,14 @@ import { Injectable } from '@angular/core'; -import { Observable , Subject } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Observable, Subject, from } from 'rxjs'; +import { map, concatMap } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; -import { Resource, IResource, ResourceRO } from '../models/resource.model'; +import { Resource, IResource, ResourceRO, IResourceFormat } from '../models/resource.model'; import { APP_CONFIG } from './app-config.service'; @Injectable() export class ResourceService { + resourceServiceUrl: string; limit: number; pageNumber: number; sortOptions: { @@ -19,18 +20,19 @@ export class ResourceService { constructor( private _httpClient: HttpClient) { - this._searchChangeSubject = new Subject<any>(); - this.limit = 10; - this.pageNumber = 1; + this.resourceServiceUrl = `${APP_CONFIG.resources.url}resources/`; + this._searchChangeSubject = new Subject<any>(); + this.limit = 10; + this.pageNumber = 1; } - getResources(options?): Observable<ResourceRO> { + getResources(): Observable<ResourceRO> { let query = '?'; - query += 'limit=' + (this.limit ? this.limit : 20); - query += '&offset=' + (this.pageNumber ? (this.pageNumber - 1) * this.limit : 0); - query += '&sort_by=' + this.sortOptions.value + '.' + this.sortOptions.order; + query += `limit=${(this.limit ? this.limit : 20)}`; + query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`; + query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`; - return this._httpClient.get<IResource[]>(APP_CONFIG.resources.url + query, { observe: 'response' }).pipe( + return this._httpClient.get<IResource[]>(this.resourceServiceUrl + query, { observe: 'response' }).pipe( map((response) => { const totalCount = response.headers.get('Content-Range'); const resources = []; @@ -42,48 +44,72 @@ export class ResourceService { } findById(id): Observable<Resource> { - return this._httpClient.get<IResource>(APP_CONFIG.resources.url + id).pipe( + return this._httpClient.get<IResource>(this.resourceServiceUrl + id).pipe( map((response) => { return new Resource(response); - } - )); + }), + ); } delete(id) { - return this._httpClient.delete(APP_CONFIG.resources.url + id); + return this._httpClient.delete(this.resourceServiceUrl + id); } replaceOrCreate(data): Observable<Resource> { if (data.id) { - return this._httpClient.put<IResource>(APP_CONFIG.resources.url + data.id, data).pipe( + return this._httpClient.put<IResource>(this.resourceServiceUrl + data.id, data).pipe( map((response) => { return new Resource(response); - } - )); + }), + ); } - return this._httpClient.post<IResource>(APP_CONFIG.resources.url, data).pipe( + return this._httpClient.post<IResource>(this.resourceServiceUrl, data).pipe( map((response) => { return new Resource(response); - } - )); + }), + ); } - /* PAGINATION */ - paginationChanged(limit: number, pageNumber: number) { - this.limit = limit; - this.pageNumber = pageNumber; - this._searchChangeSubject.next(); - } + createResourceFormats(resourceId: string, resourceFormats: IResourceFormat[]): Observable<IResourceFormat> { + return from(resourceFormats).pipe( + concatMap((rf) => { + return this._httpClient.post<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats`, rf); + }), + ); + } - reverseSortOrder(): void { - if (this.sortOptions.order === 'asc') { - this.sortOptions.order = 'desc'; - } else { - this.sortOptions.order = 'asc'; - } - } + updateResourceFormats(resourceId: string, resourceFormats: IResourceFormat[]) { + return from(resourceFormats).pipe( + concatMap((rf) => { + return this._httpClient.put<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats/${rf.id}`, rf); + }), + ); + } + + deleteResourceFormats(resourceId: string, resourceFormatsId: string[]) { + return from(resourceFormatsId).pipe( + concatMap((rfId) => { + return this._httpClient.delete<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats/${rfId}`); + }), + ); + } - get searchChange$(): Observable<string> { - return this._searchChangeSubject.asObservable(); + /* PAGINATION */ + paginationChanged(limit: number, pageNumber: number) { + this.limit = limit; + this.pageNumber = pageNumber; + this._searchChangeSubject.next(); + } + + reverseSortOrder(): void { + if (this.sortOptions.order === 'asc') { + this.sortOptions.order = 'desc'; + } else { + this.sortOptions.order = 'asc'; } + } + + get searchChange$(): Observable<string> { + return this._searchChangeSubject.asObservable(); + } } diff --git a/src/assets/config/config.json b/src/assets/config/config.json index 3320942c82b8a0ff6ae8e347c87e619f1ab91685..43cecd099741db2447135e4a96bc1d43339ac48c 100644 --- a/src/assets/config/config.json +++ b/src/assets/config/config.json @@ -1,8 +1,11 @@ { "organizations": { - "url": "https://kong-dev.alpha.grandlyon.com/organizations/" + "url": "http://localhost:3000/organizations/" }, "resources": { - "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/" + "url": "http://localhost:3003/" + }, + "mediaLibrary": { + "url": "http://localhost:3006/" } -} \ No newline at end of file +} diff --git a/src/assets/img/notif_error.svg b/src/assets/img/notif_error.svg new file mode 100644 index 0000000000000000000000000000000000000000..7a66bc0ae16910cb1cb4a33a133b6837e2d6651d --- /dev/null +++ b/src/assets/img/notif_error.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> + <style> + .white { + fill: white; + } + </style> + <g id="picto_x5F_warning"> + <path d="M18.8 15.7c.2.3.2.7.2 1-.1.4-.2.6-.5.9-.3.2-.6.4-1 .4H2.6c-.4 0-.7-.1-1-.4-.3-.2-.4-.5-.5-.9-.1-.3 0-.7.2-1L8.8 2.8c.2-.3.4-.5.7-.7.4-.1.7-.1 1 0 .4.1.6.3.8.7l7.5 12.9zm-8.7-3c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.2-.6-.4-1-.4zM8.8 8.1L9 12c0 .1 0 .2.1.2.1.1.2.1.3.1h1.4c.1 0 .2 0 .2-.1.1-.1.1-.2.1-.2l.2-3.9c0-.1 0-.2-.1-.3-.1-.1-.1-.1-.2-.1H9.2c-.1 0-.2 0-.3.1-.1.1-.1.2-.1.3z" class="white"/> + <path d="M18.8 15.7c.2.3.2.7.2 1-.1.4-.2.6-.5.9-.3.2-.6.4-1 .4H2.6c-.4 0-.7-.1-1-.4-.3-.2-.4-.5-.5-.9-.1-.3 0-.7.2-1L8.8 2.8c.2-.3.4-.5.7-.7.4-.1.7-.1 1 0 .4.1.6.3.8.7l7.5 12.9zm-8.7-3c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.2-.6-.4-1-.4zM8.8 8.1L9 12c0 .1 0 .2.1.2.1.1.2.1.3.1h1.4c.1 0 .2 0 .2-.1.1-.1.1-.2.1-.2l.2-3.9c0-.1 0-.2-.1-.3-.1-.1-.1-.1-.2-.1H9.2c-.1 0-.2 0-.3.1-.1.1-.1.2-.1.3z" class="white"/> + </g> +</svg> diff --git a/src/assets/img/notif_ok.svg b/src/assets/img/notif_ok.svg new file mode 100644 index 0000000000000000000000000000000000000000..d7d07dc1eb579c1765df18d638422344bb73e8b9 --- /dev/null +++ b/src/assets/img/notif_ok.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> + <style> + .white { + fill: white; + } + </style> + <g id="picto_x5F_ok"> + <path d="M18.8 10.1c0 1.6-.4 3.1-1.2 4.4-.8 1.4-1.9 2.4-3.2 3.2-1.4.9-2.9 1.3-4.5 1.3s-3.1-.4-4.4-1.2c-1.4-.8-2.4-1.9-3.3-3.2-.8-1.4-1.2-2.8-1.2-4.4 0-1.6.4-3.1 1.2-4.4.8-1.4 1.9-2.4 3.3-3.2 1.4-.8 2.8-1.2 4.4-1.2 1.6 0 3.1.4 4.4 1.2 1.4.8 2.4 1.9 3.2 3.2.9 1.2 1.3 2.7 1.3 4.3zm-9.9 4.7l6.6-6.6c.1-.1.2-.2.2-.4s-.1-.3-.2-.4l-.8-.8c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2L8.5 12 6 9.5c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2l-.8.8c-.1.1-.2.2-.2.4s.1.3.2.4l3.7 3.7c.1.1.2.2.4.2s.3-.1.4-.2z" class="white"/> + <path d="M18.8 10.1c0 1.6-.4 3.1-1.2 4.4-.8 1.4-1.9 2.4-3.2 3.2-1.4.9-2.9 1.3-4.5 1.3s-3.1-.4-4.4-1.2c-1.4-.8-2.4-1.9-3.3-3.2-.8-1.4-1.2-2.8-1.2-4.4 0-1.6.4-3.1 1.2-4.4.8-1.4 1.9-2.4 3.3-3.2 1.4-.8 2.8-1.2 4.4-1.2 1.6 0 3.1.4 4.4 1.2 1.4.8 2.4 1.9 3.2 3.2.9 1.2 1.3 2.7 1.3 4.3zm-9.9 4.7l6.6-6.6c.1-.1.2-.2.2-.4s-.1-.3-.2-.4l-.8-.8c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2L8.5 12 6 9.5c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2l-.8.8c-.1.1-.2.2-.2.4s.1.3.2.4l3.7 3.7c.1.1.2.2.4.2s.3-.1.4-.2z" class="white"/> + </g> +</svg> diff --git a/src/assets/img/notif_warning.svg b/src/assets/img/notif_warning.svg new file mode 100644 index 0000000000000000000000000000000000000000..f544dba648d1f2479de8a24a70cfa85f3ba2f283 --- /dev/null +++ b/src/assets/img/notif_warning.svg @@ -0,0 +1,11 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> + <style> + .white { + fill: white; + } + </style> + <g id="picto_x5F_oops"> + <path d="M17 11.3l1.6.9c.1.1.2.1.2.2s.1.2 0 .3c-.4 1.3-1.1 2.5-2.1 3.5-.1.1-.2.1-.3.2h-.3l-1.6-.9c-.7.6-1.4 1-2.3 1.3v1.8c0 .1 0 .2-.1.3 0 .1-.1.2-.2.2-1.4.3-2.7.3-4.1 0-.1 0-.2-.1-.3-.2 0-.1-.1-.2-.1-.3v-1.8c-.8-.3-1.6-.7-2.3-1.3l-1.6.9c-.1.1-.2.1-.3 0-.1 0-.2-.1-.3-.2-.9-1-1.6-2.2-2.1-3.5v-.3c0-.1.1-.2.2-.2l1.6-.9c-.1-.9-.1-1.8 0-2.6l-1.4-1-.2-.2v-.3c.4-1.3 1.1-2.5 2.1-3.5.1-.1.2-.1.3-.2h.3l1.6.9c.7-.6 1.4-1 2.3-1.3V1.3c0-.1 0-.2.1-.3.1-.1.2-.2.3-.2 1.4-.3 2.7-.3 4.1 0 .1 0 .2.1.2.2.1.1.1.2.1.3v1.8c.8.3 1.6.7 2.3 1.3l1.6-.9c.1-.1.2-.1.3 0 .1 0 .2.1.3.2.9 1 1.6 2.2 2.1 3.5v.3c0 .1-.1.2-.2.2l-1.8.9c.1.9.1 1.8 0 2.7zM13.1 10c0-.9-.3-1.6-1-2.3-.6-.6-1.4-1-2.3-1-.8 0-1.6.3-2.3 1-.6.6-.9 1.4-.9 2.3 0 .9.3 1.7.9 2.3.6.6 1.4.9 2.3.9.9 0 1.6-.3 2.3-.9.7-.7 1-1.5 1-2.3z" class="white"/> + <path d="M17 11.3l1.6.9c.1.1.2.1.2.2s.1.2 0 .3c-.4 1.3-1.1 2.5-2.1 3.5-.1.1-.2.1-.3.2h-.3l-1.6-.9c-.7.6-1.4 1-2.3 1.3v1.8c0 .1 0 .2-.1.3 0 .1-.1.2-.2.2-1.4.3-2.7.3-4.1 0-.1 0-.2-.1-.3-.2 0-.1-.1-.2-.1-.3v-1.8c-.8-.3-1.6-.7-2.3-1.3l-1.6.9c-.1.1-.2.1-.3 0-.1 0-.2-.1-.3-.2-.9-1-1.6-2.2-2.1-3.5v-.3c0-.1.1-.2.2-.2l1.6-.9c-.1-.9-.1-1.8 0-2.6l-1.4-1-.2-.2v-.3c.4-1.3 1.1-2.5 2.1-3.5.1-.1.2-.1.3-.2h.3l1.6.9c.7-.6 1.4-1 2.3-1.3V1.3c0-.1 0-.2.1-.3.1-.1.2-.2.3-.2 1.4-.3 2.7-.3 4.1 0 .1 0 .2.1.2.2.1.1.1.2.1.3v1.8c.8.3 1.6.7 2.3 1.3l1.6-.9c.1-.1.2-.1.3 0 .1 0 .2.1.3.2.9 1 1.6 2.2 2.1 3.5v.3c0 .1-.1.2-.2.2l-1.8.9c.1.9.1 1.8 0 2.7zM13.1 10c0-.9-.3-1.6-1-2.3-.6-.6-1.4-1-2.3-1-.8 0-1.6.3-2.3 1-.6.6-.9 1.4-.9 2.3 0 .9.3 1.7.9 2.3.6.6 1.4.9 2.3.9.9 0 1.6-.3 2.3-.9.7-.7 1-1.5 1-2.3z" class="white"/> + </g> +</svg> diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 0fe39f17326a0e65121c41612229dc0e808aed3d..da5ad8529bfb0812320aa37a2fc27e382d0dc913 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,4 +5,3 @@ export const environment = { production: false, }; - diff --git a/src/index.html b/src/index.html index fffe33505be8307060536ab482fc2758dc8a3794..b0e833c746feb7b256d84120886d6ff18b45cefc 100644 --- a/src/index.html +++ b/src/index.html @@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> - <title>GrandLyon</title> + <title>Plateforme data back office</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="assets/img/favicon.ico"> diff --git a/src/polyfills.ts b/src/polyfills.ts index d310405a6817a4b84bb6117fb709e2c6ea11ef55..749bf310dfa5e49e016fa56a864a22924bbde19d 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -40,12 +40,10 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. **/ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. import 'core-js/es7/reflect'; - /** * Web Animations `@angular/platform-browser/animations` * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. @@ -73,8 +71,6 @@ import 'core-js/es7/reflect'; */ import 'zone.js/dist/zone'; // Included with Angular CLI. - - /*************************************************************************************************** * APPLICATION IMPORTS */ diff --git a/src/styles.scss b/src/styles.scss index b70b8572e5fdc6dd15e4da8edda5c78e7cdb7010..40d762110c62519cfb2acd2506d6c3d54a94ca23 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -2,11 +2,17 @@ $menu-item-color: white; $menu-item-hover-color: white; $menu-item-hover-background-color: #222222; $menu-item-active-background-color: #222222; -// $menu-list-border-left: 1px solid $red; +// $menu-list-border-left: 1px solid $tomato-color; -$red: #d5232a; +$tomato-color: #f72f2f; $dark-blue: #333745; +$pagination-current-background-color: $tomato-color; +$pagination-current-border-color: $tomato-color; +$pagination-focus-border-color: $tomato-color; + +$button-focus-border-color: $tomato-color; + @import "../node_modules/bulma/bulma.sass"; html, @@ -63,10 +69,32 @@ section { .required::after { content: " *"; - color: $red; + color: $tomato-color; } input.ng-invalid:not(form).ng-dirty, input.ng-invalid:not(form).ng-touched, -textarea.ng-invalid:not(form).ng-dirty, textarea.ng-invalid:not(form).ng-touched,{ - border-left: 3px solid $red; /* red */ +textarea.ng-invalid:not(form).ng-dirty, textarea.ng-invalid:not(form).ng-touched { + border-left: 3px solid $tomato-color; /* red */ +} + +.button-gl { + min-width: 7rem; + background: $tomato-color; + border-radius: 2px; + border-width: 0; + font-size: $size-6; + color: white; + line-height: unset; + + &:hover, + &:focus { + color: white; + background: $tomato-color; + opacity: 0.92; + } + + &:disabled { + background: $tomato-color; + opacity: 0.38; + } } \ No newline at end of file diff --git a/src/test.ts b/src/test.ts index 16317897b1c50a3a71bc775a8d6429f2b4c6cf98..b6d614daa243e0869709092a8276dc9d81e7c085 100644 --- a/src/test.ts +++ b/src/test.ts @@ -4,7 +4,7 @@ import 'zone.js/dist/zone-testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, - platformBrowserDynamicTesting + platformBrowserDynamicTesting, } from '@angular/platform-browser-dynamic/testing'; declare const require: any; @@ -12,7 +12,7 @@ declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting() + platformBrowserDynamicTesting(), ); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); diff --git a/src/tslint.json b/src/tslint.json deleted file mode 100644 index 52e2c1a5a74ce268bec92d34cd3bca751624adb3..0000000000000000000000000000000000000000 --- a/src/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ] - } -} diff --git a/tslint.json b/tslint.json index 3ea984c776ef691c92b37ddf933df452cdc3d8bc..e43c8b8df38cc4c2463dd75196bd1715abead658 100644 --- a/tslint.json +++ b/tslint.json @@ -1,130 +1,17 @@ { + "extends": [ + "tslint-config-airbnb" + ], "rulesDirectory": [ - "node_modules/codelyzer" + "node_modules/rxjs-tslint" ], "rules": { - "arrow-return-shorthand": true, - "callable-types": true, - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "curly": true, - "deprecation": { - "severity": "warn" - }, - "eofline": true, - "forin": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "import-spacing": true, - "indent": [ - true, - "spaces" - ], - "interface-over-type-literal": true, - "label-position": true, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-arg": true, - "no-bitwise": true, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-construct": true, - "no-debugger": true, - "no-duplicate-super": true, - "no-empty": false, - "no-empty-interface": true, - "no-eval": true, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-misused-new": true, - "no-non-null-assertion": true, - "no-shadowed-variable": true, - "no-string-literal": false, - "no-string-throw": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unnecessary-initializer": true, - "no-unused-expression": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "prefer-const": true, - "quotemark": [ - true, - "single" - ], - "radix": true, - "semicolon": [ - true, - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "unified-signatures": true, - "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], - "no-output-on-prefix": true, - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true + "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"], + "strict-boolean-expressions": [false], + "max-line-length": [true, 140], + "rxjs-collapse-imports": true, + "rxjs-pipeable-operators-only": true, + "rxjs-no-static-observable-methods": true, + "rxjs-proper-imports": true } -} +} \ No newline at end of file