From 27ac79bb753718b1ed3b20fcb63eb1f0758e10a8 Mon Sep 17 00:00:00 2001 From: Raphael Date: Wed, 6 Nov 2024 21:18:57 -0300 Subject: [PATCH 1/4] adicionando component de login --- angular.json | 3 + package-lock.json | 302 +++++++++++++++- src/app/app.component.html | 335 ------------------ src/app/app.routes.ts | 8 +- .../auth/pages/login/login.component.html | 62 ++++ .../auth/pages/login/login.component.scss | 107 ++++++ .../auth/pages/login/login.component.spec.ts | 23 ++ .../core/auth/pages/login/login.component.ts | 30 ++ src/assets/images/Mask group.svg | 19 + src/styles.scss | 8 + 10 files changed, 555 insertions(+), 342 deletions(-) create mode 100644 src/app/core/auth/pages/login/login.component.html create mode 100644 src/app/core/auth/pages/login/login.component.scss create mode 100644 src/app/core/auth/pages/login/login.component.spec.ts create mode 100644 src/app/core/auth/pages/login/login.component.ts create mode 100644 src/assets/images/Mask group.svg diff --git a/angular.json b/angular.json index 0726251..0b3dde4 100644 --- a/angular.json +++ b/angular.json @@ -97,5 +97,8 @@ } } } + }, + "cli": { + "analytics": "7a4874a4-790d-4cca-8dfc-c7a3dc0b9963" } } diff --git a/package-lock.json b/package-lock.json index db5da3b..2cf9098 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,14 @@ "@angular/platform-browser": "^17.3.0", "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", + "@apollo/client": "^3.0.0", + "@types/lodash": "^4.17.10", + "bootstrap": "^5.3.3", + "graphql": "^16", + "lodash": "^4.17.21", + "ngx-webstorage": "^13.0.0", + "primeicons": "^7.0.0", + "primeng": "^17.18.11", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -503,6 +511,48 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@apollo/client": { + "version": "3.11.8", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.11.8.tgz", + "integrity": "sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@wry/caches": "^1.0.0", + "@wry/equality": "^0.5.6", + "@wry/trie": "^0.5.0", + "graphql-tag": "^2.12.6", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.18.0", + "prop-types": "^15.7.2", + "rehackt": "^0.1.0", + "response-iterator": "^0.2.6", + "symbol-observable": "^4.0.0", + "ts-invariant": "^0.10.3", + "tslib": "^2.3.0", + "zen-observable-ts": "^1.2.5" + }, + "peerDependencies": { + "graphql": "^15.0.0 || ^16.0.0", + "graphql-ws": "^5.5.5", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0", + "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" + }, + "peerDependenciesMeta": { + "graphql-ws": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "subscriptions-transport-ws": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", @@ -2670,6 +2720,14 @@ "node": ">=12" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3211,6 +3269,16 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", @@ -3692,6 +3760,11 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -3940,6 +4013,50 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wry/caches": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", + "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/context": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", + "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/equality": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", + "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/trie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", + "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -4473,6 +4590,24 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6543,6 +6678,28 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -6606,6 +6763,14 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hosted-git-info": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", @@ -7443,8 +7608,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -7916,8 +8080,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -8027,6 +8190,17 @@ "node": ">=8.0" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8562,6 +8736,18 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/ngx-webstorage": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-13.0.1.tgz", + "integrity": "sha512-CyKAqhIAmJQbdeIK1lqQZ7uSoVLPFV0StyjkANn96I36HN/RKtNw9+l7fQTfmavnZpmZ9ctffcpsXRHPzSZvlw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -8890,7 +9076,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8975,6 +9160,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optimism": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz", + "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==", + "dependencies": { + "@wry/caches": "^1.0.0", + "@wry/context": "^0.7.0", + "@wry/trie": "^0.4.3", + "tslib": "^2.3.0" + } + }, + "node_modules/optimism/node_modules/@wry/trie": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", + "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -9615,6 +9822,26 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==" + }, + "node_modules/primeng": { + "version": "17.18.11", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.11.tgz", + "integrity": "sha512-LzV0fFZmb3GdnaRqi1+GP+RPtW0a+jztL5pH1zRWY7+7pyQ0n1YNyTXzmqVcdks/CmoyjNhutWEmexwi6vFVeA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "rxjs": "^6.0.0 || ^7.8.1", + "zone.js": "~0.14.0" + } + }, "node_modules/proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", @@ -9649,6 +9876,16 @@ "node": ">=10" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9761,6 +9998,11 @@ "node": ">= 0.8" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/read-package-json": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.1.tgz", @@ -9964,6 +10206,23 @@ "node": ">=6" } }, + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10053,6 +10312,14 @@ "node": ">=0.10.0" } }, + "node_modules/response-iterator": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", + "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -10985,7 +11252,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, "engines": { "node": ">=0.10" } @@ -11239,6 +11505,17 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-invariant": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", + "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tslib": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", @@ -12514,6 +12791,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "node_modules/zen-observable-ts": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", + "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", + "dependencies": { + "zen-observable": "0.8.15" + } + }, "node_modules/zone.js": { "version": "0.14.10", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", diff --git a/src/app/app.component.html b/src/app/app.component.html index 36093e1..67e7bd4 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,336 +1 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - - diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index dc39edb..f335cf4 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,3 +1,9 @@ import { Routes } from '@angular/router'; -export const routes: Routes = []; +export const routes: Routes = [ + { + path: 'login', + loadComponent: () => import('./core/auth/pages/login/login.component').then(m => m.LoginComponent) + }, + { path: '', redirectTo: '/login', pathMatch: 'full' } +] diff --git a/src/app/core/auth/pages/login/login.component.html b/src/app/core/auth/pages/login/login.component.html new file mode 100644 index 0000000..77f929c --- /dev/null +++ b/src/app/core/auth/pages/login/login.component.html @@ -0,0 +1,62 @@ +
+ + +
+
+

Vaga Front-end AchieveMore

+ +
+ +
+

Bem vindo(a)

+

Use esse formulário para fazer login

+
+
+

Área restrita

+

Digite seu e-mail e senha para entrar

+ +
+ + + +
+ + + +
+ A senha é obrigatória. + A senha deve ter pelo menos 6 + caracteres. +
+
+ +
+

esqueceu a senha

+
+ + +
+
+ + + +
+ +
\ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.scss b/src/app/core/auth/pages/login/login.component.scss new file mode 100644 index 0000000..52356c9 --- /dev/null +++ b/src/app/core/auth/pages/login/login.component.scss @@ -0,0 +1,107 @@ +.flex{ + display: flex; + align-items: center; + flex-direction: column; +} + +.container{ + background-color: #F5F7F8; + height: 100vh; + + .position-container{ + z-index: 120; + top: 24px; + position: absolute; + width: 100%; + } + + .header{ + margin-bottom: 60px; + flex-direction: row; + justify-content: space-between; + width: 60%; + .menu{ + display: flex; + p{ + margin-right: 8px; + } + } + } + + .title-container{ + margin-bottom: 120px; + } + + + + .banner-img{ + margin-top: 16px; + } + + .card{ + width: 446px; + height: 480px; + padding: 48px; + gap: 8px; + border-radius: 15px; + background-color: #FFFFFF; + align-items: start !important; + form{ + input{ + width: 350px; + height: 50px; + border: 1px solid #E2E8F0; + border-radius: 8px + }; + button{ + width: 350px; + height: 41px; + background-color: #5E72E4; + color: #FFFFFF; + border-radius: 8px; + font-family: Open Sans; + font-size: 14px; + font-weight: 700; + line-height: 21px; + box-shadow: 0px 0px 6px 0px #000000; + margin-top: 24px; + + } + + .email-container{ + margin-bottom: 24px; + margin-top: 24px; + align-items: start; + label{ + margin-bottom: 8px; + } + } + + .password-container{ + margin-bottom: 24px; + align-items: start; + label{ + margin-bottom: 8px; + } + } + + .forget-container{ + align-items: start !important; + width: 100%; + } + } + } + + .footer{ + text-align: center; + padding: 10px 0; + position: absolute; + + bottom: 0; + margin-top: 100px; + .politics{ + display: flex; + justify-content: space-between; + } + } +} \ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.spec.ts b/src/app/core/auth/pages/login/login.component.spec.ts new file mode 100644 index 0000000..1e19e5d --- /dev/null +++ b/src/app/core/auth/pages/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/auth/pages/login/login.component.ts b/src/app/core/auth/pages/login/login.component.ts new file mode 100644 index 0000000..798a5f6 --- /dev/null +++ b/src/app/core/auth/pages/login/login.component.ts @@ -0,0 +1,30 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-login', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], + templateUrl: './login.component.html', + styleUrl: './login.component.scss' +}) +export class LoginComponent { + loginForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(6)]] + }); + } + + onSubmit() { + if (this.loginForm.valid) { + console.log('Formulário Enviado:', this.loginForm.value); + } else { + console.log('Formulário Inválido'); + } + } + +} diff --git a/src/assets/images/Mask group.svg b/src/assets/images/Mask group.svg new file mode 100644 index 0000000..7ee0625 --- /dev/null +++ b/src/assets/images/Mask group.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/styles.scss b/src/styles.scss index 90d4ee0..409aae3 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1 +1,9 @@ /* You can add global styles to this file, and also import other style files */ +/* Reset básico */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} \ No newline at end of file From dc1b825a58584b509df4c4bc37107de67ff925f9 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 9 Nov 2024 12:17:55 -0300 Subject: [PATCH 2/4] adicionando component auth e consumo de api de login --- src/app/app.config.ts | 3 +- src/app/app.routes.ts | 5 ++ .../auth/pages/login/login.component.html | 4 +- .../auth/pages/login/login.component.scss | 90 +++++++++++++++++-- .../core/auth/pages/login/login.component.ts | 12 ++- src/app/shared/reqres.service.spec.ts | 16 ++++ src/app/shared/reqres.service.ts | 16 ++++ src/index.html | 3 + src/main.ts | 2 + src/styles.scss | 1 + 10 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/app/shared/reqres.service.spec.ts create mode 100644 src/app/shared/reqres.service.ts diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 6c6ef60..b0fe989 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -2,7 +2,8 @@ import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import { provideHttpClient } from '@angular/common/http'; export const appConfig: ApplicationConfig = { - providers: [provideRouter(routes)] + providers: [provideRouter(routes), provideHttpClient()] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index f335cf4..ac7ab30 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,3 +1,4 @@ +import { provideHttpClient } from '@angular/common/http'; import { Routes } from '@angular/router'; export const routes: Routes = [ @@ -7,3 +8,7 @@ export const routes: Routes = [ }, { path: '', redirectTo: '/login', pathMatch: 'full' } ] + +export const APP_PROVIDERS = [ + provideHttpClient() + ]; \ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.html b/src/app/core/auth/pages/login/login.component.html index 77f929c..1356571 100644 --- a/src/app/core/auth/pages/login/login.component.html +++ b/src/app/core/auth/pages/login/login.component.html @@ -16,7 +16,7 @@

Bem vindo(a)

Use esse formulário para fazer login

-

Área restrita

+

Área restrita

Digite seu e-mail e senha para entrar

@@ -57,6 +57,6 @@

Área restrita

Política de Privacidade

Política de Segurança

-

© 2024 AchieveMore - Todos os direitos reservados.

+

© 2024 AchieveMore - Todos os direitos reservados.

\ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.scss b/src/app/core/auth/pages/login/login.component.scss index 52356c9..1550e91 100644 --- a/src/app/core/auth/pages/login/login.component.scss +++ b/src/app/core/auth/pages/login/login.component.scss @@ -20,16 +20,40 @@ flex-direction: row; justify-content: space-between; width: 60%; + color: #FFFFFF; + font-size: 14px; + font-weight: 700; + line-height: 21px; + text-align: left; + .menu{ display: flex; p{ margin-right: 8px; + font-weight: 400; } } } .title-container{ margin-bottom: 120px; + color: #FFFFFF; + text-align: center; + h1{ + font-size: 48px; + font-weight: 700; + line-height: 60px; + letter-spacing: -0.8px; + } + + p{ + font-size: 16px; + font-weight: 400; + line-height: 24px; + + } + + } @@ -46,20 +70,45 @@ border-radius: 15px; background-color: #FFFFFF; align-items: start !important; + h3{ + font-size: 30px; + font-weight: 700; + line-height: 41.1px; + letter-spacing: -0.8px; + text-align: left; + color: #172B4D; + } + p{ + font-size: 14px; + font-weight: 400; + line-height: 19.88px; + text-align: left; + color: #8392AB; + } form{ input{ width: 350px; height: 50px; border: 1px solid #E2E8F0; - border-radius: 8px + border-radius: 8px; + padding: 4px + }; + + input::placeholder { + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: left; + color:#A0AEC0; + padding-left: 20px; + } button{ width: 350px; height: 41px; background-color: #5E72E4; color: #FFFFFF; border-radius: 8px; - font-family: Open Sans; font-size: 14px; font-weight: 700; line-height: 21px; @@ -72,22 +121,34 @@ margin-bottom: 24px; margin-top: 24px; align-items: start; - label{ - margin-bottom: 8px; - } + + } + label{ + margin-bottom: 8px; + font-size: 14px; + font-weight: 400; + line-height: 19.07px; + text-align: left; + color: #172B4D; } .password-container{ margin-bottom: 24px; align-items: start; - label{ - margin-bottom: 8px; - } } .forget-container{ align-items: start !important; width: 100%; + p{ + + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: left; + color: #172B4D; + + } } } } @@ -102,6 +163,19 @@ .politics{ display: flex; justify-content: space-between; + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: left; + color: #8392AB; + } + + .rights{ + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + color: #8392AB; } } } \ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.ts b/src/app/core/auth/pages/login/login.component.ts index 798a5f6..c4d0055 100644 --- a/src/app/core/auth/pages/login/login.component.ts +++ b/src/app/core/auth/pages/login/login.component.ts @@ -1,18 +1,21 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ReqresService } from '../../../../shared/reqres.service'; +import { HttpClientModule } from '@angular/common/http'; + @Component({ selector: 'app-login', standalone: true, - imports: [CommonModule, ReactiveFormsModule], + imports: [CommonModule, ReactiveFormsModule, HttpClientModule], templateUrl: './login.component.html', styleUrl: './login.component.scss' }) export class LoginComponent { loginForm: FormGroup; - constructor(private fb: FormBuilder) { + constructor(private fb: FormBuilder, private reqResService: ReqresService) { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(6)]] @@ -22,6 +25,11 @@ export class LoginComponent { onSubmit() { if (this.loginForm.valid) { console.log('Formulário Enviado:', this.loginForm.value); + const body = { + "email": this.loginForm?.get('email')?.value, + "password": this.loginForm?.get('password')?.value + } + this.reqResService.postLogin(body).subscribe((res) => console.log('sucesso', res)); } else { console.log('Formulário Inválido'); } diff --git a/src/app/shared/reqres.service.spec.ts b/src/app/shared/reqres.service.spec.ts new file mode 100644 index 0000000..0215aa4 --- /dev/null +++ b/src/app/shared/reqres.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ReqresService } from './reqres.service'; + +describe('ReqresService', () => { + let service: ReqresService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ReqresService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/reqres.service.ts b/src/app/shared/reqres.service.ts new file mode 100644 index 0000000..f4383a8 --- /dev/null +++ b/src/app/shared/reqres.service.ts @@ -0,0 +1,16 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ReqresService { + urlApi = 'https://reqres.in/api/login'; + constructor(private http: HttpClient) { } + + postLogin(body: any): Observable { + return this.http.post(`${this.urlApi}`, body); + } + +} diff --git a/src/index.html b/src/index.html index 4a8084c..f46d9c5 100644 --- a/src/index.html +++ b/src/index.html @@ -6,6 +6,9 @@ + + + diff --git a/src/main.ts b/src/main.ts index 35b00f3..aa7c8be 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,3 +4,5 @@ import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent, appConfig) .catch((err) => console.error(err)); + + diff --git a/src/styles.scss b/src/styles.scss index 409aae3..82c5ff7 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -6,4 +6,5 @@ margin: 0; padding: 0; box-sizing: border-box; + font-family: Open Sans; } \ No newline at end of file From 0669be198c98e48ac3f75cd8d19478c6ae87f492 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 10 Nov 2024 23:54:38 -0300 Subject: [PATCH 3/4] consumindo api de usuarios, adicionando um auth guard, adicionando icones --- package-lock.json | 21 +- package.json | 1 + src/app/app.component.html | 3 +- src/app/app.component.ts | 3 +- src/app/app.config.ts | 3 +- src/app/app.routes.ts | 12 +- .../auth/pages/login/login.component.html | 28 +- .../auth/pages/login/login.component.scss | 343 +++++++++++------- .../core/auth/pages/login/login.component.ts | 12 +- .../pages/dashboard/dashboard.component.html | 75 ++++ .../pages/dashboard/dashboard.component.scss | 281 ++++++++++++++ .../dashboard/dashboard.component.spec.ts | 23 ++ .../pages/dashboard/dashboard.component.ts | 63 ++++ .../table-users/table-users.component.html | 68 ++++ .../table-users/table-users.component.scss | 207 +++++++++++ .../table-users/table-users.component.spec.ts | 23 ++ .../table-users/table-users.component.ts | 130 +++++++ src/app/core/guard/auth.guard.spec.ts | 17 + src/app/core/guard/auth.guard.ts | 16 + .../snackbar/snackbar.component.html | 4 + .../snackbar/snackbar.component.scss | 20 + .../snackbar/snackbar.component.spec.ts | 23 ++ .../components/snackbar/snackbar.component.ts | 32 ++ src/app/shared/reqres.service.ts | 16 - .../auth.service.spec.ts} | 8 +- src/app/shared/services/auth.service.ts | 55 +++ .../shared/services/snackbar.service.spec.ts | 16 + src/app/shared/services/snackbar.service.ts | 15 + src/app/shared/services/users.service.spec.ts | 16 + src/app/shared/services/users.service.ts | 25 ++ src/assets/images/Icon-user-menu.svg | 4 + src/assets/images/arrow symbol.svg | 3 + src/assets/images/arrow-symbol-login.svg | 3 + src/assets/images/config.svg | 3 + src/assets/images/icon-dashboard.svg | 11 + src/assets/images/icon-manager.svg | 5 + src/assets/images/order.svg | 3 + src/assets/images/search-icon.svg | 3 + src/assets/images/sino.svg | 11 + src/assets/images/user.svg | 4 + src/index.html | 4 +- src/styles.scss | 4 +- 42 files changed, 1441 insertions(+), 176 deletions(-) create mode 100644 src/app/core/dashboard/pages/dashboard/dashboard.component.html create mode 100644 src/app/core/dashboard/pages/dashboard/dashboard.component.scss create mode 100644 src/app/core/dashboard/pages/dashboard/dashboard.component.spec.ts create mode 100644 src/app/core/dashboard/pages/dashboard/dashboard.component.ts create mode 100644 src/app/core/dashboard/pages/dashboard/table-users/table-users.component.html create mode 100644 src/app/core/dashboard/pages/dashboard/table-users/table-users.component.scss create mode 100644 src/app/core/dashboard/pages/dashboard/table-users/table-users.component.spec.ts create mode 100644 src/app/core/dashboard/pages/dashboard/table-users/table-users.component.ts create mode 100644 src/app/core/guard/auth.guard.spec.ts create mode 100644 src/app/core/guard/auth.guard.ts create mode 100644 src/app/shared/components/snackbar/snackbar.component.html create mode 100644 src/app/shared/components/snackbar/snackbar.component.scss create mode 100644 src/app/shared/components/snackbar/snackbar.component.spec.ts create mode 100644 src/app/shared/components/snackbar/snackbar.component.ts delete mode 100644 src/app/shared/reqres.service.ts rename src/app/shared/{reqres.service.spec.ts => services/auth.service.spec.ts} (55%) create mode 100644 src/app/shared/services/auth.service.ts create mode 100644 src/app/shared/services/snackbar.service.spec.ts create mode 100644 src/app/shared/services/snackbar.service.ts create mode 100644 src/app/shared/services/users.service.spec.ts create mode 100644 src/app/shared/services/users.service.ts create mode 100644 src/assets/images/Icon-user-menu.svg create mode 100644 src/assets/images/arrow symbol.svg create mode 100644 src/assets/images/arrow-symbol-login.svg create mode 100644 src/assets/images/config.svg create mode 100644 src/assets/images/icon-dashboard.svg create mode 100644 src/assets/images/icon-manager.svg create mode 100644 src/assets/images/order.svg create mode 100644 src/assets/images/search-icon.svg create mode 100644 src/assets/images/sino.svg create mode 100644 src/assets/images/user.svg diff --git a/package-lock.json b/package-lock.json index 2cf9098..a463d73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^17.3.0", + "@angular/cdk": "^17.3.10", "@angular/common": "^17.3.0", "@angular/compiler": "^17.3.0", "@angular/core": "^17.3.0", @@ -283,6 +284,22 @@ "@angular/core": "17.3.12" } }, + "node_modules/@angular/cdk": { + "version": "17.3.10", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.10.tgz", + "integrity": "sha512-b1qktT2c1TTTe5nTji/kFAVW92fULK0YhYAvJ+BjZTPKu2FniZNe8o4qqQ0pUuvtMu+ZQxp/QqFYoidIVCjScg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "17.3.10", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.10.tgz", @@ -5843,7 +5860,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12" }, @@ -9444,7 +9461,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", - "dev": true, + "devOptional": true, "dependencies": { "entities": "^4.5.0" }, diff --git a/package.json b/package.json index f686829..4d23706 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "private": true, "dependencies": { "@angular/animations": "^17.3.0", + "@angular/cdk": "^17.3.10", "@angular/common": "^17.3.0", "@angular/compiler": "^17.3.0", "@angular/core": "^17.3.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 67e7bd4..15589ca 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,2 @@ - + + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 80d3e2b..24e48d8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { SnackbarComponent } from './shared/components/snackbar/snackbar.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet], + imports: [RouterOutlet,SnackbarComponent], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) diff --git a/src/app/app.config.ts b/src/app/app.config.ts index b0fe989..e48bb56 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -3,7 +3,8 @@ import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { provideHttpClient } from '@angular/common/http'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; export const appConfig: ApplicationConfig = { - providers: [provideRouter(routes), provideHttpClient()] + providers: [provideRouter(routes), provideHttpClient(), provideAnimationsAsync()] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index ac7ab30..772652b 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,12 +1,20 @@ import { provideHttpClient } from '@angular/common/http'; import { Routes } from '@angular/router'; +import { authGuard } from './core/guard/auth.guard'; export const routes: Routes = [ + { path: '', redirectTo: '/login', pathMatch: 'full' }, { path: 'login', loadComponent: () => import('./core/auth/pages/login/login.component').then(m => m.LoginComponent) - }, - { path: '', redirectTo: '/login', pathMatch: 'full' } + }, + { + path: 'dashboard', + loadComponent: () => import('./core/dashboard/pages/dashboard/dashboard.component').then(m => m.DashboardComponent), + canActivate: [authGuard] + }, + + ] export const APP_PROVIDERS = [ diff --git a/src/app/core/auth/pages/login/login.component.html b/src/app/core/auth/pages/login/login.component.html index 1356571..71a87a0 100644 --- a/src/app/core/auth/pages/login/login.component.html +++ b/src/app/core/auth/pages/login/login.component.html @@ -5,8 +5,8 @@

Vaga Front-end AchieveMore

@@ -42,21 +42,29 @@

Área restrita

-

esqueceu a senha

+ +

esqueceu a senha

+ + + + - + \ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.scss b/src/app/core/auth/pages/login/login.component.scss index 1550e91..7ced3a8 100644 --- a/src/app/core/auth/pages/login/login.component.scss +++ b/src/app/core/auth/pages/login/login.component.scss @@ -1,181 +1,258 @@ -.flex{ +.flex { display: flex; align-items: center; flex-direction: column; } -.container{ +.container { background-color: #F5F7F8; - height: 100vh; + height: 1080px; - .position-container{ + .position-container { z-index: 120; - top: 24px; + padding-top: 24px; position: absolute; width: 100%; - } - - .header{ - margin-bottom: 60px; - flex-direction: row; - justify-content: space-between; - width: 60%; - color: #FFFFFF; - font-size: 14px; - font-weight: 700; - line-height: 21px; - text-align: left; - - .menu{ - display: flex; - p{ - margin-right: 8px; - font-weight: 400; - } - } - } + flex-direction: column; + height: 1080px; - .title-container{ - margin-bottom: 120px; - color: #FFFFFF; - text-align: center; - h1{ - font-size: 48px; + .header { + margin-bottom: 60px; + flex-direction: row; + justify-content: space-between; + width: 60%; + color: #FFFFFF; + font-size: 14px; font-weight: 700; - line-height: 60px; - letter-spacing: -0.8px; - } + line-height: 21px; + text-align: left; - p{ - font-size: 16px; - font-weight: 400; - line-height: 24px; - + .menu { + display: flex; + + p { + margin-right: 29px; + font-weight: 400; + } + } } - } - - .banner-img{ - margin-top: 16px; - } + .title-container { + margin-bottom: 120px; + color: #FFFFFF; + text-align: center; - .card{ - width: 446px; - height: 480px; - padding: 48px; - gap: 8px; - border-radius: 15px; - background-color: #FFFFFF; - align-items: start !important; - h3{ - font-size: 30px; - font-weight: 700; - line-height: 41.1px; - letter-spacing: -0.8px; - text-align: left; - color: #172B4D; - } - p{ - font-size: 14px; - font-weight: 400; - line-height: 19.88px; - text-align: left; - color: #8392AB; - } - form{ - input{ - width: 350px; - height: 50px; - border: 1px solid #E2E8F0; - border-radius: 8px; - padding: 4px - - }; + h1 { + font-size: 48px; + font-weight: 700; + line-height: 60px; + letter-spacing: -0.8px; + } - input::placeholder { + p { font-size: 16px; font-weight: 400; line-height: 24px; - text-align: left; - color:#A0AEC0; - padding-left: 20px; - } - button{ - width: 350px; - height: 41px; - background-color: #5E72E4; - color: #FFFFFF; - border-radius: 8px; - font-size: 14px; - font-weight: 700; - line-height: 21px; - box-shadow: 0px 0px 6px 0px #000000; - margin-top: 24px; } - .email-container{ - margin-bottom: 24px; - margin-top: 24px; - align-items: start; - + + } + + + + .banner-img { + margin-top: 16px; + } + + .card { + width: 446px; + height: 480px; + padding: 48px; + gap: 8px; + border-radius: 15px; + background-color: #FFFFFF; + align-items: start !important; + + h3 { + font-size: 30px; + font-weight: 700; + line-height: 41.1px; + letter-spacing: -0.8px; + text-align: left; + color: #172B4D; } - label{ - margin-bottom: 8px; + + p { font-size: 14px; font-weight: 400; - line-height: 19.07px; + line-height: 19.88px; text-align: left; - color: #172B4D; + color: #8392AB; } - .password-container{ - margin-bottom: 24px; - align-items: start; - } + form { + input { + width: 350px; + height: 50px; + border: 1px solid #E2E8F0; + border-radius: 8px; + padding: 10px 10px 10px 20px; + + } - .forget-container{ - align-items: start !important; - width: 100%; - p{ + ; + input::placeholder { font-size: 16px; font-weight: 400; line-height: 24px; text-align: left; + color: #A0AEC0; + } + + button { + width: 350px; + height: 41px; + background-color: #5E72E4; + color: #FFFFFF; + border-radius: 8px; + font-size: 14px; + font-weight: 700; + line-height: 21px; + box-shadow: 0px 0px 6px 0px #000000; + margin-top: 24px; + + } + + .email-container { + margin-bottom: 24px; + margin-top: 24px; + align-items: start; + + } + + label { + margin-bottom: 8px; + font-size: 14px; + font-weight: 400; + line-height: 19.07px; + text-align: left; color: #172B4D; + } + .password-container { + margin-bottom: 24px; + align-items: start; } - } - } - } - .footer{ - text-align: center; - padding: 10px 0; - position: absolute; + .forget-container { + align-items: center !important; + width: 100%; + display: flex; + flex-direction: row; + p { - bottom: 0; - margin-top: 100px; - .politics{ - display: flex; - justify-content: space-between; - font-size: 16px; - font-weight: 400; - line-height: 24px; - text-align: left; - color: #8392AB; + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: left; + color: #172B4D; + + } + + .switch { + position: relative; + display: inline-block; + width: 36px; + height: 18.5px; + margin-bottom: 0; + margin-right: 8px; + } + + .switch input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + } + + .slider:before { + position: absolute; + content: ""; + height: 13px; + width: 10px; + left: 0px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + } + + input:checked + .slider { + background-color: #2196F3; + } + + input:focus + .slider { + box-shadow: 0 0 1px #2196F3; + } + + input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); + } + + .slider.round { + border-radius: 34px; + } + + .slider.round:before { + border-radius: 50%; + } + } + } } - .rights{ - font-size: 14px; - font-weight: 400; - line-height: 21px; - text-align: left; - color: #8392AB; + .footer { + text-align: center; + padding: 10px 0; + position: absolute; + bottom: 0; + width: 377px; + .politics { + display: flex; + justify-content: space-between; + font-size: 16px; + font-weight: 400; + line-height: 24px; + text-align: left; + color: #8392AB; + margin-bottom: 38px; + } + + .rights { + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + color: #8392AB; + text-align: center; + } } } } \ No newline at end of file diff --git a/src/app/core/auth/pages/login/login.component.ts b/src/app/core/auth/pages/login/login.component.ts index c4d0055..d61172d 100644 --- a/src/app/core/auth/pages/login/login.component.ts +++ b/src/app/core/auth/pages/login/login.component.ts @@ -1,8 +1,9 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; -import { ReqresService } from '../../../../shared/reqres.service'; import { HttpClientModule } from '@angular/common/http'; +import { AuthService } from '../../../../shared/services/auth.service'; + @Component({ @@ -15,7 +16,9 @@ import { HttpClientModule } from '@angular/common/http'; export class LoginComponent { loginForm: FormGroup; - constructor(private fb: FormBuilder, private reqResService: ReqresService) { + constructor(private fb: FormBuilder, + private authService: AuthService, + ) { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(6)]] @@ -24,12 +27,13 @@ export class LoginComponent { onSubmit() { if (this.loginForm.valid) { - console.log('Formulário Enviado:', this.loginForm.value); const body = { "email": this.loginForm?.get('email')?.value, "password": this.loginForm?.get('password')?.value } - this.reqResService.postLogin(body).subscribe((res) => console.log('sucesso', res)); + localStorage.setItem('email', this.loginForm?.get('email')?.value); + this.authService.login(body); + } else { console.log('Formulário Inválido'); } diff --git a/src/app/core/dashboard/pages/dashboard/dashboard.component.html b/src/app/core/dashboard/pages/dashboard/dashboard.component.html new file mode 100644 index 0000000..2763981 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/dashboard.component.html @@ -0,0 +1,75 @@ +
+ +
+ + + +
+
+
+
+

+ gerenciar / +

+ Usuarios +
+ + +
+
+ +
+ + +
+ +
+ +

{{userLoggedIn.first_name}} {{userLoggedIn.last_name}}

+
+
+ logout + Logout +
+ + +
+ +
+ + +
+ +
+ + +
\ No newline at end of file diff --git a/src/app/core/dashboard/pages/dashboard/dashboard.component.scss b/src/app/core/dashboard/pages/dashboard/dashboard.component.scss new file mode 100644 index 0000000..da51717 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/dashboard.component.scss @@ -0,0 +1,281 @@ +.container { + background: linear-gradient(to bottom, #5E72E4 236.95px, #F5F7F8 0); + height: 1080px; + width: 100%; + padding: 16px 16px 26px 16px; + + .container-layout { + display: flex; + flex-direction: row; + height: 100%; + max-height: 100%; + + .menu { + width: 296px; + height: 750px; + background-color: #FFFFFF; + border-radius: 12px; + padding: 24px 27.5px 24px 27.5px; + display: flex; + flex-direction: column; + align-items: center; + + h1 { + font-size: 14px; + font-weight: 700; + line-height: 19.6px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #172B4D; + } + + hr { + margin-top: 24px; + width: 100%; + margin-bottom: 32px; + border: 0; + height: 1px; + background: linear-gradient(90deg, rgba(224, 225, 226, 0) 0%, #E0E1E2 49.52%, rgba(224, 225, 226, 0.15625) 99.04%); + + + } + + .menu-container { + width: 100%; + } + + .menu-item { + display: flex; + align-items: center; + cursor: pointer; + + div { + display: flex; + align-items: center; + } + + span { + font-size: 14px; + font-weight: 400; + line-height: 19.6px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #172B4D; + } + + .menu-arrow { + margin-left: auto; + transition: transform 0.3s; + transform: rotate(90deg); + } + + .menu-arrow.active { + transform: rotate(0deg); + } + } + + + + + .submenu { + margin-top: 4px; + padding-left: 24px; + + .Gerenciar { + font-size: 12px; + font-weight: 700; + line-height: 16.8px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #8591A4; + } + + font-size: 14px; + font-weight: 400; + line-height: 19.6px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #6C757D; + cursor: pointer; + + .selected { + color: #5E72E4; + } + } + } + + .list-container { + display: flex; + flex-direction: column; + width: 100%; + margin-left: 31px; + height: 100%; + max-height: 100%; + + .config-container { + display: flex; + align-items: center; + width: 100%; + justify-content: space-between; + margin-bottom: 60px; + + div { + display: flex; + align-items: center; + } + + .manager-container { + div { + display: flex; + flex-direction: column; + margin-right: 64px; + + p { + font-size: 14px; + font-weight: 400; + line-height: 19.6px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #FFFFFF80; + } + + span { + font-size: 16px; + font-weight: 700; + line-height: 22.4px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #FFFFFF; + } + } + + } + + .user-container { + + .input-container { + position: relative; + + .search-input { + font-size: 14px; + height: 42px; + width: 240px; + color: #8392AB; + border: 1px solid #E9ECEF; + border-radius: 8px; + margin-right: 24px; + padding: 10px 10px 10px 40px; + } + + input::placeholder { + color: #8392AB; + + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + + } + + .input-icon { + position: absolute; + left: 10px; + } + } + + div { + margin-right: 24px; + font-size: 14px; + font-weight: 400; + line-height: 19.6px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #FFFFFF; + + img { + margin-right: 8px; + } + } + + .tooltip { + position: relative; + display: inline-block; + + .logout { + cursor: pointer; + } + + .tooltip-text { + visibility: hidden; + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + text-align: center; + border-radius: 5px; + padding: 5px; + position: absolute; + z-index: 1; + top: 100%; + margin-left: -40px; + opacity: 0; + transition: opacity 0.3s; + font-size: 14px; + font-weight: 400; + line-height: 19.6px; + } + } + + .tooltip:hover .tooltip-text { + visibility: visible; + opacity: 1; + } + + img { + margin-right: 24px; + } + } + } + } + + .footer { + text-align: center; + bottom: 0; + width: 100%; + display: flex; + height: 100%; + max-height: 100%; + align-items: end; + justify-content: space-between; + + .politics { + display: flex; + justify-content: space-between; + + p:first-of-type { + margin-right: 28px; + } + } + + .rights, .politics { + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + + color: #8392AB; + + } + + } + } +} \ No newline at end of file diff --git a/src/app/core/dashboard/pages/dashboard/dashboard.component.spec.ts b/src/app/core/dashboard/pages/dashboard/dashboard.component.spec.ts new file mode 100644 index 0000000..815d1b8 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/dashboard.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/dashboard/pages/dashboard/dashboard.component.ts b/src/app/core/dashboard/pages/dashboard/dashboard.component.ts new file mode 100644 index 0000000..e6e4aaa --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/dashboard.component.ts @@ -0,0 +1,63 @@ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { Component } from '@angular/core'; +import { TableUsersComponent } from './table-users/table-users.component'; +import { SnackbarService } from '../../../../shared/services/snackbar.service'; +import { AuthService } from '../../../../shared/services/auth.service'; +import { users, UsersService } from '../../../../shared/services/users.service'; + +interface MenuItem { + label: string; + icon: string; + submenu?: string[]; + isOpen?: boolean; +} + + +@Component({ + selector: 'app-dashboard', + standalone: true, + imports: [CommonModule, HttpClientModule, TableUsersComponent], + templateUrl: './dashboard.component.html', + styleUrl: './dashboard.component.scss' +}) +export class DashboardComponent { + listUsers: users[] = []; + userLoggedIn: users = {}; + constructor(private authService: AuthService, private usersService: UsersService, private snackbarService: SnackbarService){} + menuItems: MenuItem[] = [ + { label: 'Dashboard', icon: '../../../../../assets/images/icon-dashboard.svg', submenu: ['Gerenciar'], isOpen: false }, + { label: 'Usuários', icon: '../../../../../assets/images/Icon-user-menu.svg', submenu: ['Listagem', 'Cadastrar'], isOpen: true } + ]; + selectedSubItem: string | null = 'Listagem'; + ngOnInit(){ + this.getUsers(); + } + + toggleSubmenu(item: MenuItem) { + item.isOpen = !item.isOpen; + } + + selectSubItem(subItem: string) { + this.selectedSubItem = subItem; + } + + logout(){ + this.authService.logout(); + } + + getUsers(){ + this.usersService.getUsers() + .subscribe({ + next: ((users: any) => { + const emailLocalStorage = localStorage.getItem('email'); + this.listUsers = [...users.data]; + this.userLoggedIn = {...users.data?.find((user: users) => user.email === emailLocalStorage)}; + }), + error: ((error: any) => { + console.log('', error) + this.snackbarService.show({type: 'Error', text: 'Error ao carregar a lista de usuarios', color: 'red'}); + }) + }) + } +} diff --git a/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.html b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.html new file mode 100644 index 0000000..b2e7f74 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.html @@ -0,0 +1,68 @@ +
+
+
Listagem de usuários
+

Visualização dos usuários cadastrados.

+
+ +
+
+
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
Nome
+
+
Cargo
+
+
Escritório
+
+
Idade
+
+
Data de Início
+
+
Salário
+
{{ user.name ? (user.name) : '-' }}{{ user.position ? user.position : '-' }}{{ user.office ? user.office : '-' }}{{ user.age ? user.age : '-' }}{{ user.startDate ? (user.startDate | date: 'dd/MM/yyyy') : '-' }}{{ user.salary ? (user.salary | currency: 'BRL') : '-' }}
+ +
+

Mostrando 1 a {{ entriesPerPage }} de {{ paginatedUsers.length }} entradas

+ +
+
\ No newline at end of file diff --git a/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.scss b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.scss new file mode 100644 index 0000000..15ce736 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.scss @@ -0,0 +1,207 @@ +.table-container { + width: 100%; + margin: auto; + background-color: #FFFFFF; + border-radius: 15px; + + + hr { + border: 0.8px solid #DEE2E6; + margin-bottom: 8px; + } + + .title { + padding: 24px; + + h5 { + margin-bottom: 5px; + font-family: Open Sans; + font-size: 20px; + font-weight: 600; + line-height: 27.4px; + letter-spacing: -0.8px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #172B4D; + } + + p { + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #172B4D; + } + } + + + + + .filter-container { + display: flex; + justify-content: space-between; + margin-bottom: 15px; + padding-left: 24px; + padding-right: 24px; + + .range-container { + display: flex; + flex-direction: row; + align-items: center; + + select { + height: 33px; + width: 55px; + border: 1px solid #E9EAED; + border-radius: 4px; + color: #8392AB; + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-underline-position: from-font; + text-decoration-skip-ink: none; + padding: 4px; + + } + + label { + margin-left: 4px; + font-size: 12px; + font-weight: 400; + line-height: 15px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + + } + + color: #8392AB; + } + } + + + .search-input { + font-size: 14px; + height: 42px; + color: #8392AB; + border: 1px solid #E9ECEF; + border-radius: 8px; + width: 200px; + padding: 10px 10px 10px 20px; + } + + input::placeholder { + color: #8392AB; + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + + } + + + table { + width: 100%; + border-collapse: collapse; + background-color: #FFFFFF; + border-top: 0.8px solid #DEE2E6; + + thead { + background-color: #FFFFFF; + } + + th, + td { + padding: 17px 24px; + text-align: left; + border-bottom: 0.8px solid #DEE2E6; + } + + .th-align { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 10px; + font-weight: 700; + line-height: 15px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #8392AB; + } + + td { + + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #8392AB; + } + + + tbody tr:hover { + background-color: #f5f5f5; + } + } + + .container-pagination { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 15.5px 24px 15.5px 24px; + + p { + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #8392AB; + } + + .pagination { + display: flex; + justify-content: center; + gap: 8px; + + button { + width: 34px; + height: 34px; + border-radius: 100%; + cursor: pointer; + background-color: #FFFFFF; + } + + .button-page { + border: 1px solid #E9ECEF; + color: #8392AB; + } + + .arrow-button { + border: 1px solid #8392AB; + color: #8392AB; + } + + button:disabled { + color: #aaa; + cursor: not-allowed; + border: 1px solid #aaa; + } + + .active { + background: linear-gradient(124deg, #5E72E4 -1.8%, #825EE4 105.23%); + color: #FFFFFF; + } + } + + } +} \ No newline at end of file diff --git a/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.spec.ts b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.spec.ts new file mode 100644 index 0000000..80a6aad --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TableUsersComponent } from './table-users.component'; + +describe('TableUsersComponent', () => { + let component: TableUsersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TableUsersComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TableUsersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.ts b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.ts new file mode 100644 index 0000000..a547c27 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/table-users/table-users.component.ts @@ -0,0 +1,130 @@ +import { CommonModule, registerLocaleData } from '@angular/common'; +import { Component, DEFAULT_CURRENCY_CODE, Input, LOCALE_ID } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import ptBr from '@angular/common/locales/pt'; +import { users } from '../../../../../shared/services/users.service'; + + +interface User { + name: string; + position: string; + office: string; + age: number; + startDate: Date; + salary: number; +} +registerLocaleData(ptBr); +@Component({ + selector: 'app-table-users', + standalone: true, + imports: [ + CommonModule, + FormsModule + ], + providers: [ + { provide: LOCALE_ID, useValue: 'pt' }, + ], + templateUrl: './table-users.component.html', + styleUrls: ['./table-users.component.scss'] +}) +export class TableUsersComponent { + randomList = [ + { position: 'Diretor', office: 'Rio de Janeiro', age: 61, startDate: new Date('1952-02-12'), salary: 100000 }, + { position: 'Coordenador', office: 'Belo Horizonte', age: 42, startDate: new Date('2020-10-20'), salary: 15000 }, + { position: 'Coordenador', office: 'São Paulo', age: 36, startDate: new Date('2022-06-13'), salary: 15000 }, + { position: 'Supervisor', office: 'São Paulo', age: 25, startDate: new Date('2019-01-18'), salary: 8000 }, + { position: 'Assistente', office: 'São Paulo', age: 37, startDate: new Date('2018-03-30'), salary: 3500 }, + { position: 'TI', office: 'Osasco', age: 22, startDate: new Date('2019-03-30'), salary: 5800 }, + { position: 'TI', office: 'Paulista', age: 19, startDate: new Date('2023-03-30'), salary: 5800 }, + ]; + @Input() users: users[] = []; + listUsers: User[] = []; + filteredUser: User[] = []; + paginatedUsers: User[] = []; + currentPage: number = 1; + entriesPerPage: number = 10; + totalPages: number = 1; + searchTerm: string = ''; + sortField: keyof User | '' = ''; + sortDirection: 'asc' | 'desc' = 'asc'; + + constructor(){ } + + ngOnInit() { + const filtred = this.users?.map((users: any) => + { + const randomData = this.randomList[Math.floor(Math.random() * this.randomList.length)]; + return {name: `${users.first_name} ${users.last_name}`, ...randomData} + }); + this.listUsers = [...filtred]; + this.filteredUser = [...filtred]; + this.updatePagination(); + + } + + updatePagination() { + // Ordenar a lista antes de paginar + this.listUsers.sort((a, b) => { + if (!this.sortField) return 0; + const valueA = a[this.sortField]; + const valueB = b[this.sortField]; + const order = this.sortDirection === 'asc' ? 1 : -1; + return valueA > valueB ? order : valueA < valueB ? -order : 0; + }); + + this.totalPages = Math.ceil(this.listUsers.length / this.entriesPerPage); + this.paginatedUsers = this.listUsers.slice( + (this.currentPage - 1) * this.entriesPerPage, + this.currentPage * this.entriesPerPage + ); + } + + onEntriesChange(event: Event) { + const value = (event.target as HTMLSelectElement).value; + this.entriesPerPage = parseInt(value, 10); + this.currentPage = 1; + this.updatePagination(); + } + + goToPage(page: number) { + this.currentPage = page; + this.updatePagination(); + } + + previousPage() { + if (this.currentPage > 1) { + this.currentPage--; + this.updatePagination(); + } + } + + nextPage() { + if (this.currentPage < this.totalPages) { + this.currentPage++; + this.updatePagination(); + } + } + + filterTable() { + this.listUsers = this.filteredUser.filter(user => + user.name.toLowerCase().includes(this.searchTerm.toLowerCase()) + ); + this.currentPage = 1; + this.updatePagination(); + } + + sortTable(field: keyof User) { + if (this.sortField === field) { + this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + this.sortField = field; + this.sortDirection = 'asc'; + } + this.updatePagination(); + } + + get pages(): number[] { + return Array.from({ length: this.totalPages }, (x, i) => i + 1); + } +} diff --git a/src/app/core/guard/auth.guard.spec.ts b/src/app/core/guard/auth.guard.spec.ts new file mode 100644 index 0000000..4ae275e --- /dev/null +++ b/src/app/core/guard/auth.guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanActivateFn } from '@angular/router'; + +import { authGuard } from './auth.guard'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/src/app/core/guard/auth.guard.ts b/src/app/core/guard/auth.guard.ts new file mode 100644 index 0000000..ab1eab0 --- /dev/null +++ b/src/app/core/guard/auth.guard.ts @@ -0,0 +1,16 @@ +// auth.guard.ts +import { inject } from '@angular/core'; +import { CanActivateFn } from '@angular/router'; +import { AuthService } from '../../shared/services/auth.service'; + + +export const authGuard: CanActivateFn = (route, state) => { + const authService = inject(AuthService); + + if (authService.isAuthenticated()) { + return true; + } else { + authService.redirectToLogin(); + return false; + } +}; \ No newline at end of file diff --git a/src/app/shared/components/snackbar/snackbar.component.html b/src/app/shared/components/snackbar/snackbar.component.html new file mode 100644 index 0000000..e469bab --- /dev/null +++ b/src/app/shared/components/snackbar/snackbar.component.html @@ -0,0 +1,4 @@ +
+

{{ messageType }}

+

{{ messageText }}

+
\ No newline at end of file diff --git a/src/app/shared/components/snackbar/snackbar.component.scss b/src/app/shared/components/snackbar/snackbar.component.scss new file mode 100644 index 0000000..5a70061 --- /dev/null +++ b/src/app/shared/components/snackbar/snackbar.component.scss @@ -0,0 +1,20 @@ +.snackbar { + position: fixed; + top: 20px; + right: 20px; + color: white; + padding: 16px; + border-radius: 4px; + transition: opacity 0.3s ease; + opacity: 0.9; + z-index: 1000; + min-width: 200px; + text-align: center; + } + .red{ + background-color: red; + } + + .green{ + background-color: #4BB543; + } \ No newline at end of file diff --git a/src/app/shared/components/snackbar/snackbar.component.spec.ts b/src/app/shared/components/snackbar/snackbar.component.spec.ts new file mode 100644 index 0000000..ab1ae14 --- /dev/null +++ b/src/app/shared/components/snackbar/snackbar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SnackbarComponent } from './snackbar.component'; + +describe('SnackbarComponent', () => { + let component: SnackbarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SnackbarComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SnackbarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/snackbar/snackbar.component.ts b/src/app/shared/components/snackbar/snackbar.component.ts new file mode 100644 index 0000000..aed0b1c --- /dev/null +++ b/src/app/shared/components/snackbar/snackbar.component.ts @@ -0,0 +1,32 @@ +import { Component } from '@angular/core'; +import { SnackbarService } from '../../services/snackbar.service'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-snackbar', + standalone: true, + imports: [CommonModule], + templateUrl: './snackbar.component.html', + styleUrl: './snackbar.component.scss' +}) +export class SnackbarComponent { + messageType: string = ''; + messageText: string = ''; + messageColor: string = ''; + isVisible: boolean = false; + + constructor(private snackbarService: SnackbarService) {} + + ngOnInit() { + this.snackbarService.snackbarState$.subscribe((message: any) => { + this.messageType = message?.type; + this.messageText = message?.text; + this.messageColor = message?.color; + this.isVisible = true; + + setTimeout(() => { + this.isVisible = false; + }, 3000); + }); + } +} diff --git a/src/app/shared/reqres.service.ts b/src/app/shared/reqres.service.ts deleted file mode 100644 index f4383a8..0000000 --- a/src/app/shared/reqres.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; - -@Injectable({ - providedIn: 'root' -}) -export class ReqresService { - urlApi = 'https://reqres.in/api/login'; - constructor(private http: HttpClient) { } - - postLogin(body: any): Observable { - return this.http.post(`${this.urlApi}`, body); - } - -} diff --git a/src/app/shared/reqres.service.spec.ts b/src/app/shared/services/auth.service.spec.ts similarity index 55% rename from src/app/shared/reqres.service.spec.ts rename to src/app/shared/services/auth.service.spec.ts index 0215aa4..f1251ca 100644 --- a/src/app/shared/reqres.service.spec.ts +++ b/src/app/shared/services/auth.service.spec.ts @@ -1,13 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { ReqresService } from './reqres.service'; +import { AuthService } from './auth.service'; -describe('ReqresService', () => { - let service: ReqresService; +describe('AuthService', () => { + let service: AuthService; beforeEach(() => { TestBed.configureTestingModule({}); - service = TestBed.inject(ReqresService); + service = TestBed.inject(AuthService); }); it('should be created', () => { diff --git a/src/app/shared/services/auth.service.ts b/src/app/shared/services/auth.service.ts new file mode 100644 index 0000000..04709f8 --- /dev/null +++ b/src/app/shared/services/auth.service.ts @@ -0,0 +1,55 @@ +// auth.service.ts +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { BehaviorSubject } from 'rxjs'; +import { SnackbarService } from './snackbar.service'; + + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + private isAuthenticatedSubject = new BehaviorSubject(false); + public isAuthenticated$ = this.isAuthenticatedSubject.asObservable(); + urlApi = 'https://reqres.in/api/login'; + constructor(private http: HttpClient, private router: Router, private snackbarService: SnackbarService) { + this.isAuthenticatedSubject.next(!!localStorage.getItem('token')); + } + + login(body: any) { + return this.http.post(this.urlApi, body).subscribe( { + next: (response) => { + if (response.token) { + localStorage.setItem('token', response.token); + this.isAuthenticatedSubject.next(true); + this.router.navigate(['/dashboard']); + this.snackbarService.show({type: 'Sucesso', text: 'Login realizado com sucesso', color: 'green'}); + } + }, + error: (error) => { + console.log(error) + if(error.status === 400){ + this.snackbarService.show({type: 'Error', text: 'Erro ao fazer login. Usuário não encontrado.', color: 'red'}); + }else{ + this.snackbarService.show({type: 'Error', text: 'Erro ao fazer login. Tentar novamente.', color: 'red'}); + } + + } + }); + } + + logout() { + localStorage.removeItem('token'); + this.isAuthenticatedSubject.next(false); + this.router.navigate(['/login']); + } + + isAuthenticated(): boolean { + return !!localStorage.getItem('token'); + } + + redirectToLogin() { + this.router.navigate(['/login']); + } +} \ No newline at end of file diff --git a/src/app/shared/services/snackbar.service.spec.ts b/src/app/shared/services/snackbar.service.spec.ts new file mode 100644 index 0000000..3872524 --- /dev/null +++ b/src/app/shared/services/snackbar.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SnackbarService } from './snackbar.service'; + +describe('SnackbarService', () => { + let service: SnackbarService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SnackbarService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/services/snackbar.service.ts b/src/app/shared/services/snackbar.service.ts new file mode 100644 index 0000000..d851804 --- /dev/null +++ b/src/app/shared/services/snackbar.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class SnackbarService { + + private snackbarSubject = new Subject(); + snackbarState$ = this.snackbarSubject.asObservable(); + + show(message: any ) { + this.snackbarSubject.next(message); + } +} diff --git a/src/app/shared/services/users.service.spec.ts b/src/app/shared/services/users.service.spec.ts new file mode 100644 index 0000000..f81244a --- /dev/null +++ b/src/app/shared/services/users.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UsersService } from './users.service'; + +describe('UsersService', () => { + let service: UsersService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UsersService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/services/users.service.ts b/src/app/shared/services/users.service.ts new file mode 100644 index 0000000..11b3173 --- /dev/null +++ b/src/app/shared/services/users.service.ts @@ -0,0 +1,25 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +export interface users { + avatar?: string, + email?: string, + first_name?: string, + id?: number, + last_name?: string, +} + +@Injectable({ + providedIn: 'root' +}) +export class UsersService { + + urlApi = 'https://reqres.in/api/users'; + constructor(private http: HttpClient) { } + + getUsers(): Observable { + return this.http.get(this.urlApi); + } + +} diff --git a/src/assets/images/Icon-user-menu.svg b/src/assets/images/Icon-user-menu.svg new file mode 100644 index 0000000..2d2eb8f --- /dev/null +++ b/src/assets/images/Icon-user-menu.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/arrow symbol.svg b/src/assets/images/arrow symbol.svg new file mode 100644 index 0000000..4e33fa6 --- /dev/null +++ b/src/assets/images/arrow symbol.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/arrow-symbol-login.svg b/src/assets/images/arrow-symbol-login.svg new file mode 100644 index 0000000..0818fa3 --- /dev/null +++ b/src/assets/images/arrow-symbol-login.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/config.svg b/src/assets/images/config.svg new file mode 100644 index 0000000..f4bad0f --- /dev/null +++ b/src/assets/images/config.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon-dashboard.svg b/src/assets/images/icon-dashboard.svg new file mode 100644 index 0000000..c3c8a8f --- /dev/null +++ b/src/assets/images/icon-dashboard.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/icon-manager.svg b/src/assets/images/icon-manager.svg new file mode 100644 index 0000000..7a7138d --- /dev/null +++ b/src/assets/images/icon-manager.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/order.svg b/src/assets/images/order.svg new file mode 100644 index 0000000..fa3f451 --- /dev/null +++ b/src/assets/images/order.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/search-icon.svg b/src/assets/images/search-icon.svg new file mode 100644 index 0000000..883ccfb --- /dev/null +++ b/src/assets/images/search-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/sino.svg b/src/assets/images/sino.svg new file mode 100644 index 0000000..62793d9 --- /dev/null +++ b/src/assets/images/sino.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/user.svg b/src/assets/images/user.svg new file mode 100644 index 0000000..4666b96 --- /dev/null +++ b/src/assets/images/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/index.html b/src/index.html index f46d9c5..f9b1f5d 100644 --- a/src/index.html +++ b/src/index.html @@ -9,8 +9,10 @@ + + - + diff --git a/src/styles.scss b/src/styles.scss index 82c5ff7..66de765 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -7,4 +7,6 @@ padding: 0; box-sizing: border-box; font-family: Open Sans; -} \ No newline at end of file +} +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } \ No newline at end of file From a64a9c8fa18bcc7b4ed3abdd187f57e3f8642249 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 11 Nov 2024 09:30:33 -0300 Subject: [PATCH 4/4] componentizando side menu, top menu, adicionando blank dashboard --- .../auth/pages/login/login.component.scss | 2 +- .../core/auth/pages/login/login.component.ts | 4 +- .../blank-dashboard.component.html | 4 + .../blank-dashboard.component.scss | 37 +++ .../blank-dashboard.component.spec.ts | 23 ++ .../blank-dashboard.component.ts | 18 ++ .../pages/dashboard/dashboard.component.html | 61 +---- .../pages/dashboard/dashboard.component.scss | 225 +----------------- .../pages/dashboard/dashboard.component.ts | 40 +--- .../side-menu/side-menu.component.html | 23 ++ .../side-menu/side-menu.component.scss | 97 ++++++++ .../side-menu/side-menu.component.spec.ts | 23 ++ .../side-menu/side-menu.component.ts | 35 +++ .../top-menu/top-menu.component.html | 31 +++ .../top-menu/top-menu.component.scss | 127 ++++++++++ .../top-menu/top-menu.component.spec.ts | 23 ++ .../dashboard/top-menu/top-menu.component.ts | 20 ++ src/app/shared/services/auth.service.ts | 1 - 18 files changed, 486 insertions(+), 308 deletions(-) create mode 100644 src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.html create mode 100644 src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.scss create mode 100644 src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.spec.ts create mode 100644 src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.ts create mode 100644 src/app/core/dashboard/pages/dashboard/side-menu/side-menu.component.html create mode 100644 src/app/core/dashboard/pages/dashboard/side-menu/side-menu.component.scss create mode 100644 src/app/core/dashboard/pages/dashboard/side-menu/side-menu.component.spec.ts create mode 100644 src/app/core/dashboard/pages/dashboard/side-menu/side-menu.component.ts create mode 100644 src/app/core/dashboard/pages/dashboard/top-menu/top-menu.component.html create mode 100644 src/app/core/dashboard/pages/dashboard/top-menu/top-menu.component.scss create mode 100644 src/app/core/dashboard/pages/dashboard/top-menu/top-menu.component.spec.ts create mode 100644 src/app/core/dashboard/pages/dashboard/top-menu/top-menu.component.ts diff --git a/src/app/core/auth/pages/login/login.component.scss b/src/app/core/auth/pages/login/login.component.scss index 7ced3a8..c6955e5 100644 --- a/src/app/core/auth/pages/login/login.component.scss +++ b/src/app/core/auth/pages/login/login.component.scss @@ -125,7 +125,7 @@ line-height: 21px; box-shadow: 0px 0px 6px 0px #000000; margin-top: 24px; - + cursor: pointer; } .email-container { diff --git a/src/app/core/auth/pages/login/login.component.ts b/src/app/core/auth/pages/login/login.component.ts index d61172d..71d9249 100644 --- a/src/app/core/auth/pages/login/login.component.ts +++ b/src/app/core/auth/pages/login/login.component.ts @@ -3,6 +3,7 @@ import { Component } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AuthService } from '../../../../shared/services/auth.service'; +import { SnackbarService } from '../../../../shared/services/snackbar.service'; @@ -18,6 +19,7 @@ export class LoginComponent { constructor(private fb: FormBuilder, private authService: AuthService, + private snackbarService: SnackbarService ) { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], @@ -35,7 +37,7 @@ export class LoginComponent { this.authService.login(body); } else { - console.log('Formulário Inválido'); + this.snackbarService.show({type: 'Error', text: 'Formulário Inválido.', color: 'red'}); } } diff --git a/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.html b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.html new file mode 100644 index 0000000..fc296af --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.html @@ -0,0 +1,4 @@ +
+
Esse Dashboard está em construção.
+ +
diff --git a/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.scss b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.scss new file mode 100644 index 0000000..44eae0e --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.scss @@ -0,0 +1,37 @@ +.card-container{ + width: 100%; + margin: auto; + background-color: #FFFFFF; + border-radius: 15px; + height: 585px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + h5 { + margin-bottom: 5px; + font-family: Open Sans; + font-size: 20px; + font-weight: 600; + line-height: 27.4px; + letter-spacing: -0.8px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: #172B4D; + margin-bottom: 20px; + } + button { + width: 350px; + height: 41px; + background-color: #5E72E4; + color: #FFFFFF; + border-radius: 8px; + font-size: 14px; + font-weight: 700; + line-height: 21px; + box-shadow: 0px 0px 6px 0px #000000; + margin-top: 24px; + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.spec.ts b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.spec.ts new file mode 100644 index 0000000..f02ce0e --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BlankDashboardComponent } from './blank-dashboard.component'; + +describe('BlankDashboardComponent', () => { + let component: BlankDashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BlankDashboardComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BlankDashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.ts b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.ts new file mode 100644 index 0000000..de46d02 --- /dev/null +++ b/src/app/core/dashboard/pages/dashboard/blank-dashboard/blank-dashboard.component.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Output } from '@angular/core'; + +@Component({ + selector: 'app-blank-dashboard', + standalone: true, + imports: [CommonModule], + templateUrl: './blank-dashboard.component.html', + styleUrl: './blank-dashboard.component.scss' +}) +export class BlankDashboardComponent { + @Output() goToListPage = new EventEmitter(); + + + goToList(){ + this.goToListPage.emit('Listagem'); + } +} diff --git a/src/app/core/dashboard/pages/dashboard/dashboard.component.html b/src/app/core/dashboard/pages/dashboard/dashboard.component.html index 2763981..20b043c 100644 --- a/src/app/core/dashboard/pages/dashboard/dashboard.component.html +++ b/src/app/core/dashboard/pages/dashboard/dashboard.component.html @@ -2,63 +2,16 @@
- +
-
-
-
-

- gerenciar / -

- Usuarios -
- - -
-
- -
- - -
- -
- -

{{userLoggedIn.first_name}} {{userLoggedIn.last_name}}

-
-
- logout - Logout -
- - -
+ + -
- + + + +