From 2f0e630047c93e65129a8482cda02bef7a292e87 Mon Sep 17 00:00:00 2001 From: Anarayssa-dev Date: Mon, 15 Jun 2026 04:52:01 -0300 Subject: [PATCH 1/5] 65% --- Brewfile | 7 + Gemfile | 8 +- Gemfile.lock | 68 ++++---- app/assets/stylesheets/admin.css | 147 ++++++++++++++++++ .../stylesheets/admin_gerenciamento.css | 59 +++++++ app/controllers/dashboards_controller.rb | 80 +++++++++- app/javascript/admin_controller.js | 61 ++++++++ app/javascript/controllers/index.js | 12 +- app/models/discente.rb | 3 + app/models/docente.rb | 2 + app/models/turma.rb | 2 + app/views/dashboards/admin.html.erb | 23 +-- app/views/dashboards/admin_avaliacao.html.erb | 9 ++ .../dashboards/admin_gerenciamento.html.erb | 132 ++++++++++++++++ app/views/layouts/admin_layout.html.erb | 58 +++++++ app/views/layouts/application.html.erb | 3 + config/initializers/assets.rb | 2 +- config/routes.rb | 8 + db/migrate/20260615054444_create_turmas.rb | 11 ++ db/migrate/20260615054518_create_discentes.rb | 16 ++ db/migrate/20260615063634_create_docentes.rb | 15 ++ db/schema.rb | 50 +++++- .../cadastrar_usuarios_steps.rb | 123 +++++++++++++++ features/support/chrome.rb | 39 +++++ features/support/env.rb | 25 --- features/support/firefox.rb | 18 +++ features/support/hooks.rb | 14 +- spec/models/discente_spec.rb | 5 + spec/models/docente_spec.rb | 5 + spec/models/turma_spec.rb | 5 + spec/rails_helper.rb | 15 ++ spec/spec_helper.rb | 2 + spec/system/cadastrar_usuarios_spec.rb | 45 ++++++ 33 files changed, 984 insertions(+), 88 deletions(-) create mode 100644 Brewfile create mode 100644 app/assets/stylesheets/admin.css create mode 100644 app/assets/stylesheets/admin_gerenciamento.css create mode 100644 app/javascript/admin_controller.js create mode 100644 app/models/discente.rb create mode 100644 app/models/docente.rb create mode 100644 app/models/turma.rb create mode 100644 app/views/dashboards/admin_avaliacao.html.erb create mode 100644 app/views/dashboards/admin_gerenciamento.html.erb create mode 100644 app/views/layouts/admin_layout.html.erb create mode 100644 db/migrate/20260615054444_create_turmas.rb create mode 100644 db/migrate/20260615054518_create_discentes.rb create mode 100644 db/migrate/20260615063634_create_docentes.rb create mode 100644 features/step_definitions/cadastrar_usuarios_steps.rb create mode 100644 features/support/chrome.rb create mode 100644 features/support/firefox.rb create mode 100644 spec/models/discente_spec.rb create mode 100644 spec/models/docente_spec.rb create mode 100644 spec/models/turma_spec.rb create mode 100644 spec/system/cadastrar_usuarios_spec.rb diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000000..7562594f48 --- /dev/null +++ b/Brewfile @@ -0,0 +1,7 @@ +tap "caskroom/cask" + +cask "google-chrome" +cask "firefox" + +brew "chromedriver" +brew "geckodriver" diff --git a/Gemfile b/Gemfile index 9199cbb410..0ae3eef5b3 100644 --- a/Gemfile +++ b/Gemfile @@ -66,4 +66,10 @@ group :test do gem "database_cleaner" gem "capybara" gem "selenium-webdriver" -end \ No newline at end of file +end + + +# gem para o firefox +# group :test do +# gem "webdrivers", "~> 5.0" +# end diff --git a/Gemfile.lock b/Gemfile.lock index df01ec5e06..3f33fa54a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,9 +83,9 @@ GEM bcrypt_pbkdf (1.1.2) bigdecimal (4.1.2) bindex (0.8.1) - bootsnap (1.24.5) + bootsnap (1.24.6) msgpack (~> 1.2) - brakeman (8.0.4) + brakeman (8.0.5) racc builder (3.3.0) bundler-audit (0.9.3) @@ -180,7 +180,7 @@ GEM jbuilder (2.15.1) actionview (>= 7.0.0) activesupport (>= 7.0.0) - json (2.19.7) + json (2.19.9) kamal (2.11.0) activesupport (>= 7.0) base64 (~> 0.2) @@ -213,9 +213,9 @@ GEM minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) - msgpack (1.8.1) + msgpack (1.8.3) multi_test (1.1.0) - net-imap (0.6.4) + net-imap (0.6.4.1) date net-protocol net-pop (0.1.2) @@ -257,7 +257,7 @@ GEM actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack - psych (5.3.1) + psych (5.4.0) date stringio public_suffix (7.0.5) @@ -348,7 +348,7 @@ GEM lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.47.1, < 2.0) - rubocop-rails (2.35.3) + rubocop-rails (2.35.4) activesupport (>= 4.2.0) lint_roller (~> 1.1) rack (>= 1.1) @@ -362,9 +362,9 @@ GEM ruby-vips (2.3.0) ffi (~> 1.12) logger - rubyzip (3.2.2) + rubyzip (3.3.1) securerandom (0.4.1) - selenium-webdriver (4.43.0) + selenium-webdriver (4.44.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -386,13 +386,13 @@ GEM fugit (~> 1.11) railties (>= 7.1) thor (>= 1.3.1) - sqlite3 (2.9.4-aarch64-linux-gnu) - sqlite3 (2.9.4-aarch64-linux-musl) - sqlite3 (2.9.4-arm-linux-gnu) - sqlite3 (2.9.4-arm-linux-musl) - sqlite3 (2.9.4-arm64-darwin) - sqlite3 (2.9.4-x86_64-linux-gnu) - sqlite3 (2.9.4-x86_64-linux-musl) + sqlite3 (2.9.5-aarch64-linux-gnu) + sqlite3 (2.9.5-aarch64-linux-musl) + sqlite3 (2.9.5-arm-linux-gnu) + sqlite3 (2.9.5-arm-linux-musl) + sqlite3 (2.9.5-arm64-darwin) + sqlite3 (2.9.5-x86_64-linux-gnu) + sqlite3 (2.9.5-x86_64-linux-musl) sshkit (1.25.0) base64 logger @@ -428,7 +428,7 @@ GEM bindex (>= 0.4.0) railties (>= 8.0.0) websocket (1.2.11) - websocket-driver (0.8.0) + websocket-driver (0.8.1) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -496,8 +496,8 @@ CHECKSUMS bcrypt_pbkdf (1.1.2) sha256=c2414c23ce66869b3eb9f643d6a3374d8322dfb5078125c82792304c10b94cf6 bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd bindex (0.8.1) sha256=7b1ecc9dc539ed8bccfc8cb4d2732046227b09d6f37582ff12e50a5047ceb17e - bootsnap (1.24.5) sha256=36b677448524d279b470469aabd5dff4a980e3fa4931a0df68da4a500eb1b6c4 - brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f + bootsnap (1.24.6) sha256=c60bab88c70332290f0a2636a288f675299eb4f804a02a3c085b42eca9da164a + brakeman (8.0.5) sha256=03735f9690d3fd4b32d66aacbf0a6d15a84266bdd06b32c05c8ecc8f6021d2be builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 @@ -541,7 +541,7 @@ CHECKSUMS io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3 jbuilder (2.15.1) sha256=2430bec28fb0cebacb5875b1009cf9d8bc3c303ccb810c4c8b062a4b51457637 - json (2.19.7) sha256=fe432c8639f6efff69f9d73b518a3705d9581ab93156f981ea72806e1e5bcc3e + json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a kamal (2.11.0) sha256=1408864425e0dec7e0a14d712a3b13f614e9f3a425b7661d3f9d287a51d7dd75 language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 @@ -554,9 +554,9 @@ CHECKSUMS mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1 - msgpack (1.8.1) sha256=3fef787cd3965fd119c08a22724a56a93ca25008c3421fc15039f603a8b7c86c + msgpack (1.8.3) sha256=8bda4a6428d3244e50d6bd55854d354edbada88a4e1f4f5731a39a0f86bee6a1 multi_test (1.1.0) sha256=e9e550cdd863fb72becfe344aefdcd4cbd26ebf307847f4a6c039a4082324d10 - net-imap (0.6.4) sha256=9a5598c67a3022c284d98430ef1d4948e7dbdb62596f61081ea8ca933270a02b + net-imap (0.6.4.1) sha256=29f0360d75a7efd3539f16ac1957dea5c0a51ddeceb348db4553c3120914ea0d net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3 net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d @@ -578,7 +578,7 @@ CHECKSUMS prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 propshaft (1.3.2) sha256=1d56a3e56a92c21bfc29caf07406b5386b00d4c47ddf357cf989a5a234b1389e - psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 + psych (5.4.0) sha256=14f72d69a611af663d7d70e4a7b67d9eb1f3ae9f8d916b478961d5a0075ba5b7 public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882 @@ -605,23 +605,23 @@ CHECKSUMS rubocop (1.87.0) sha256=b9d9ddf55116a513f8ef2c7ae660662d8b49301f118d3f0df61865b33a5c188d rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 - rubocop-rails (2.35.3) sha256=6edd45410866912b9b2e90ae3aeafd31d576df2bb2a9c9408f1667a50c32c7de + rubocop-rails (2.35.4) sha256=3aeaa325439c89950e8327565682ea794065d08e2ecbbfe95032bfa295a35df5 rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374 - rubyzip (3.2.2) sha256=c0ed99385f0625415c8f05bcae33fe649ed2952894a95ff8b08f26ca57ea5b3c + rubyzip (3.3.1) sha256=2ed92112c7c43ba2b2527f35e6d99d9c2c99270b640aad5227516436481b1e4e securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - selenium-webdriver (4.43.0) sha256=a634377b964b701c6ac0a009ce3a08fa34ec1e1e7fe9a6d57e3088d14529a65c + selenium-webdriver (4.44.0) sha256=6f1df072529af369589c46f0e01132952aabb250cfd683c274d74dc1eb5d8477 solid_cable (4.0.0) sha256=8379680ef6bf36e195eb876a6306ea290f87d5fa10bc4a757bc2a918f83229b5 solid_cache (1.0.10) sha256=bc05a2fb3ac78a6f43cbb5946679cf9db67dd30d22939ededc385cb93e120d41 solid_queue (1.4.0) sha256=e6a18d196f0b27cb6e3c77c5b31258b05fb634f8ed64fb1866ed164047216c2a - sqlite3 (2.9.4-aarch64-linux-gnu) sha256=ecabed721e6eaad54601d2685f09029d90025efc8d931040dc89cb3f8a2080ec - sqlite3 (2.9.4-aarch64-linux-musl) sha256=ffb4255947fb54c8c3eeca97460c9702b40de91ce390455ef7367ca6a3929a31 - sqlite3 (2.9.4-arm-linux-gnu) sha256=9ee2008b9fbec984c3c165b0d7eedd2bd2a415100b761bfa3a4c6fbec9208bf6 - sqlite3 (2.9.4-arm-linux-musl) sha256=8dc1fe4da6977992cd62decf4a93ccf6cc2e124a5e6a340160d52092f70e837a - sqlite3 (2.9.4-arm64-darwin) sha256=1d5aad413a815d236e96d43f05a1acc600b6cd086800770342a3f9c2877499ff - sqlite3 (2.9.4-x86_64-linux-gnu) sha256=537a3eda71b1df1336d0055cbebe55a7317c34870c192c7b6b9d8d0be6871847 - sqlite3 (2.9.4-x86_64-linux-musl) sha256=3fc5e865b4be9a85d998203ef8d0c0fdcb92f20acf34a254346ff8a19088efec + sqlite3 (2.9.5-aarch64-linux-gnu) sha256=78075b6337d3d182c6d2b4691049ed45cd220826160c9ea18946bf6a1de200dc + sqlite3 (2.9.5-aarch64-linux-musl) sha256=18c801185deb4adc01ddb281e8f672a39e3d1729979ca91e39439cd3eac0402d + sqlite3 (2.9.5-arm-linux-gnu) sha256=1bdfca0c7d63998c60b0f4a8e3c8df2d33800ccc4abd2d612eddbbbc92a4c48b + sqlite3 (2.9.5-arm-linux-musl) sha256=bae1109d12b2e9f588455967729b008e1ff4feb7761749df695019c9079913c6 + sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2 + sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e + sqlite3 (2.9.5-x86_64-linux-musl) sha256=e7d3a7474e8af0f96150c21abc203fbab5437206bfcdf11deab7741c0ca516f2 sshkit (1.25.0) sha256=c8c6543cdb60f91f1d277306d585dd11b6a064cb44eab0972827e4311ff96744 stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06 stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 @@ -641,7 +641,7 @@ CHECKSUMS useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844 web-console (4.3.0) sha256=e13b71301cdfc2093f155b5aa3a622db80b4672d1f2f713119cc7ec7ac6a6da4 websocket (1.2.11) sha256=b7e7a74e2410b5e85c25858b26b3322f29161e300935f70a0e0d3c35e0462737 - websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962 + websocket-driver (0.8.1) sha256=5ab238238ce230e5d4b262d2be39624c867914eab99171dc4952b58b577c2d96 websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241 xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e zeitwerk (2.8.2) sha256=7212a61311083c604184b1ea2574b9aa05cd14f855a0841c06985cabe9181d12 diff --git a/app/assets/stylesheets/admin.css b/app/assets/stylesheets/admin.css new file mode 100644 index 0000000000..e084885488 --- /dev/null +++ b/app/assets/stylesheets/admin.css @@ -0,0 +1,147 @@ +*{ + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Poppins', sans-serif; +} + +/*.header_admin{ + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 10; +} +*/ + +body{ + height: 100vh; +} + +/*menu principal*/ +nav.navbar{ + width: 80px; + height: 100%; + background-color: #7a0553; + + padding: 40px 0 40px 1%; + + position:fixed; + top:0; + left:0; + + overflow: hidden; + transition: 0.7s; + +} + +nav.navbar.expandir{ + width: 300px; +} + +/*conteudo*/ + +.dashboard-content{ + + width: 100%; + height: 100%; + background-color: #fff; + + padding: 40px 10px 0 8%; + +} + +.dashboard-content{ + transition: 0.7s; +} + +.conteudo-admin{ + color: #000; +} + +.dashboard-content.expandir{ + padding-left: 320px; + + transition: .5s; +} + +/*seções*/ +section{ + overflow: hidden; +} + + +/*botão*/ +.btn-expandir{ + width: 100%; + padding-left: 10px; + +} + +.btn-expandir > i{ + color: #fff; + font-size: 20px; + cursor: pointer; +} + +/* navegação*/ +ul.nav-menu{ + height: 100%; + list-style-type: none; +} + +ul li.nav-item{ + transition: 0.2s; + /*margin-left: 10px;*/ +} + +ul li.ativo{ + background-color: #fff; + box-sizing: border-box; + border-end-start-radius: 10px; + border-top-left-radius: 10px; +} + +ul li.nav-item.ativo a{ + color: #7a0553; +} + + +ul li.nav-item:hover{ + background-color: #fff; + box-sizing: border-box; + border-end-start-radius: 10px; + border-top-left-radius: 10px; +} + +ul li.nav-item a:hover{ + color: #7a0553; +} + +ul li.nav-item a{ + color: #fff; + text-decoration: none; + font-size: 20px; + padding: 20px 4%; + + display: flex; + line-height: 40px; + margin-left: 10px; +} + +ul li.nav-item a .nav-text{ + margin-left: 70px; + transition: 0.5s; + opacity:0; + +} + +nav.navbar.expandir .nav-text{ + margin-left: 40px; + opacity: 1; +} + +ul li.nav-item a .nav-icon{ + font-size: 30px; +} + diff --git a/app/assets/stylesheets/admin_gerenciamento.css b/app/assets/stylesheets/admin_gerenciamento.css new file mode 100644 index 0000000000..27e296ea3a --- /dev/null +++ b/app/assets/stylesheets/admin_gerenciamento.css @@ -0,0 +1,59 @@ +.conteudo-admin{ + display: block; + box-sizing: border-box; + justify-content: center; + align-items: center; + min-height: 80vh; + width: 100%; +} + +.btn-group-vertical{ + display: flex; + flex-direction: column; + justify-content: center; + + align-items: center; + + flex-grow: 1; + width: 100%; + padding: 20px; + +} + +.ger-menu{ + padding-top: 10px; + padding-left: 320px; + padding-bottom: 10px; + list-style: none; + width: 100%; +} + + + +ul li.ger-link{ + + width: 100%; + max-width:320px; + padding: 5px; +} + +ul li.ger-link .ger-btn { + display: block; /* Faz o link se comportar como um bloco */ + width: 100%; /* Força o botão a ocupar todos os 320px do pai */ + text-align: center; /* Centraliza o texto dentro do botão */ + color: #000; + text-decoration: none; + font-size: 20px; + padding: 10px; + + background-color: #f8f9fa; + border: 1px solid #ced4da; + border-radius: 8px; + transition: all 0.2s ease-in-out; +} + +ul li.ger-link .ger-btn:hover { + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); +} \ No newline at end of file diff --git a/app/controllers/dashboards_controller.rb b/app/controllers/dashboards_controller.rb index 5252f6b436..5d2d7c5a81 100644 --- a/app/controllers/dashboards_controller.rb +++ b/app/controllers/dashboards_controller.rb @@ -1,9 +1,87 @@ +require "json" + class DashboardsController < ApplicationController before_action :require_login + + layout "admin_layout", only: [ :admin, :admin_gerenciamento, :admin_avaliacao ] + + # tela de admin def admin end + # tela de gerenciamento + def admin_gerenciamento + @turmas = Turma.all + @discentes = Discente.all + @docentes = Docente.all + end + + # importação do json + def importar_json + file = params["json"].tempfile.path + file_path = File.read(file) + + data_hash = JSON.parse(file_path) + + data_hash.each do |turma_dados| + codigo = turma_dados["code"] + semestre = turma_dados["semester"] + turma = turma_dados["classCode"] + + refer_turma = Turma.find_or_create_by!(codigo: codigo, turma: turma, semestre: semestre) + + if turma_dados["dicente"].is_a?(Array) + + turma_dados["dicente"].each do |usuarios| + nome = usuarios["nome"].downcase + curso = usuarios["curso"].downcase + matricula = usuarios["matricula"] + usuario = usuarios["usuario"] + formacao = usuarios["formacao"].downcase + ocupacao = usuarios["ocupacao"].downcase + email = usuarios["email"].downcase + + Discente.find_or_create_by!(nome: nome, curso: curso, matricula: matricula, + usuario: usuario, formacao: formacao, ocupacao: ocupacao, + email: email, turma: refer_turma) + + User.find_or_create_by!(nome: nome, matricula: matricula, perfil: "Discente", password: 123456, password_confirmation: 123456) + end + end + + + if turma_dados["docente"].is_a?(Array) + turma_dados["docente"].each do |usuarios| + nome = usuarios["nome"].downcase + departamento = usuarios["departamento"].downcase + usuario = usuarios["usuario"] + formacao = usuarios["formacao"].downcase + ocupacao = usuarios["ocupacao"].downcase + email = usuarios["email"].downcase + + + + Docente.find_or_create_by!(nome: nome, departamento: departamento, usuario: usuario, formacao: formacao, ocupacao: ocupacao, email: email, turma: refer_turma) + User.find_or_create_by!(nome: nome, matricula: email, perfil: "Docente", password: 123456, password_confirmation: 123456) + end + end + end + + # aviso gerado + flash[:success] = "JSON importado e processado com sucesso!" + # redirect_to admin_gerenciamento_dashboard_path + rescue => e + flash[:error] = "Ocorreu um erro na importação: #{e.message}" + # redirect_to admin_gerenciamento_dashboard_path + end + + + + # tela de avaliações + def admin_avaliacao + end + def discente end -end \ No newline at end of file +end diff --git a/app/javascript/admin_controller.js b/app/javascript/admin_controller.js new file mode 100644 index 0000000000..3dad599706 --- /dev/null +++ b/app/javascript/admin_controller.js @@ -0,0 +1,61 @@ +function ligarMenuAdmin() { + const menuItems = document.querySelectorAll('.nav-item') + const btnExp = document.querySelector('#btn-exp') + const menuSide = document.querySelector('.navbar') + const mainSide = document.querySelector('.dashboard-content') + + + // MANTÉM O ITEM ATIVO + const urlAtual = window.location.pathname + menuItems.forEach((item) => { + const link = item.querySelector('a') + if (link) { + const href = link.getAttribute('href'); + if (urlAtual === href || (href !== '/' && urlAtual.includes(href))) { + item.classList.add('ativo') + } else if (href === '#gerenciamento' && window.location.hash === '#gerenciamento') { + item.classList.add('ativo') + } else { + item.classList.remove('ativo') + } + } + }) + + // --- AJUSTE: LEMBRAR SE O MENU ESTAVA EXPANDIDO --- + if (menuSide && localStorage.getItem('menuExpandido') === 'true') { + menuSide.classList.add('expandir') + mainSide.classList.add('expandir') + } + + // Lógica original do clique nos itens + function selectLink() { + menuItems.forEach((item) => item.classList.remove('ativo')) + this.classList.add('ativo') + } + + menuItems.forEach((item) => { + item.removeEventListener('click', selectLink) + item.addEventListener('click', selectLink) + }) + + // Lógica original do botão de expandir (com o salvamento da memória) + if (btnExp && menuSide && mainSide) { + function toggleMenu(e) { + e.preventDefault() + menuSide.classList.toggle('expandir') + mainSide.classList.toggle('expandir') + + // Salva no navegador se o menu está aberto (true) ou fechado (false) + const estaExpandido = menuSide.classList.contains('expandir') + const mainExpandido = mainSide.classList.contains('expandir') + localStorage.setItem('menuExpandido', estaExpandido) + localStorage.setItem('menuExpandido', mainExpandido) + } + btnExp.removeEventListener('click', toggleMenu) + btnExp.addEventListener('click', toggleMenu) + } +} + +// Garante o funcionamento com as trocas de página do Turbo +document.addEventListener('turbo:load', ligarMenuAdmin) +ligarMenuAdmin() \ No newline at end of file diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 1156bf8362..d0685d3b7b 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -1,4 +1,8 @@ -// Import and register all your controllers from the importmap via controllers/**/*_controller -import { application } from "controllers/application" -import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" -eagerLoadControllersFrom("controllers", application) +// This file is auto-generated by ./bin/rails stimulus:manifest:update +// Run that command whenever you add a new controller or create them with +// ./bin/rails generate stimulus controllerName + +import { application } from "./application" + +import HelloController from "./hello_controller" +application.register("hello", HelloController) diff --git a/app/models/discente.rb b/app/models/discente.rb new file mode 100644 index 0000000000..3fa5993446 --- /dev/null +++ b/app/models/discente.rb @@ -0,0 +1,3 @@ +class Discente < ApplicationRecord + belongs_to :turma +end diff --git a/app/models/docente.rb b/app/models/docente.rb new file mode 100644 index 0000000000..021edff2c7 --- /dev/null +++ b/app/models/docente.rb @@ -0,0 +1,2 @@ +class Docente < ApplicationRecord +end diff --git a/app/models/turma.rb b/app/models/turma.rb new file mode 100644 index 0000000000..41fd637f68 --- /dev/null +++ b/app/models/turma.rb @@ -0,0 +1,2 @@ +class Turma < ApplicationRecord +end diff --git a/app/views/dashboards/admin.html.erb b/app/views/dashboards/admin.html.erb index e587db56c8..019b885d93 100644 --- a/app/views/dashboards/admin.html.erb +++ b/app/views/dashboards/admin.html.erb @@ -1,17 +1,8 @@ -
- - -
-

Perfil de Administrador

-

Bem-vindo ao painel do administrador.

-
-
\ No newline at end of file +<% content_for :stylesheet, stylesheet_link_tag("admin", "data-turbo-track": "reload") %> +
+

Perfil de Administrador

+

Bem-vindo ao painel do administrador.

+

Conteúdo

+ Seus testes de criação de downloads entrarão aqui +
diff --git a/app/views/dashboards/admin_avaliacao.html.erb b/app/views/dashboards/admin_avaliacao.html.erb new file mode 100644 index 0000000000..cce4954bd0 --- /dev/null +++ b/app/views/dashboards/admin_avaliacao.html.erb @@ -0,0 +1,9 @@ + +<% content_for :stylesheet, stylesheet_link_tag("admin", "data-turbo-track": "reload") %> + +
+

Avaliações

+

Bem-vindo ao painel do administrador.

+

Conteúdo

+ Seus testes de criação de downloads entrarão aqui +
diff --git a/app/views/dashboards/admin_gerenciamento.html.erb b/app/views/dashboards/admin_gerenciamento.html.erb new file mode 100644 index 0000000000..c8e6aff137 --- /dev/null +++ b/app/views/dashboards/admin_gerenciamento.html.erb @@ -0,0 +1,132 @@ + + +<% content_for :stylesheet, stylesheet_link_tag("admin", "data-turbo-track": "reload") %> +<% content_for :stylesheet, stylesheet_link_tag("admin_gerenciamento", "data-turbo-track": "reload") %> + +
+

Painel de Gerenciamento

+

Aqui você pode gerenciar as configurações do painel administrativo.

+ +
+ + + + +
+ +
+ <%= hidden_field_tag :authenticity_token, form_authenticity_token %> +
+ +
+
+ + + +
+ +
+ +
+

Turmas Cadastradas (<%= @turmas.count %>)

+ <% if @turmas.any? %> + + + + + + + + + + <% @turmas.each do |turma| %> + + + + + + <% end %> + +
Código da DisciplinaTurmaSemestre
<%= turma.codigo.upcase %><%= turma.turma %><%= turma.semestre %>
+ <% else %> +

Nenhuma turma cadastrada.

+ <% end %> +
+ +
+

Discentes Cadastrados (<%= @discentes.count %>)

+ <% if @discentes.any? %> + + + + + + + + + + + <% @discentes.each do |aluno| %> + + + + + + + <% end %> + +
NomeMatrículaCursoE-mail
<%= aluno.nome.titleize %><%= aluno.matricula %><%= aluno.curso.upcase %><%= aluno.email %>
+ <% else %> +

Nenhum discente cadastrado.

+ <% end %> +
+ +
+

Docentes Cadastrados (<%= @docentes.count %>)

+ <% if @docentes.any? %> + + + + + + + + + + + <% @docentes.each do |professor| %> + + + + + + + <% end %> + +
NomeDepartamento / OcupaçãoUsuário de LoginE-mail
<%= professor.nome.titleize %><%= professor.ocupacao&.upcase %><%= professor.usuario %><%= professor.email %>
+ <% else %> +

Nenhum docente cadastrado.

+ <% end %> +
+ +
+ + +
\ No newline at end of file diff --git a/app/views/layouts/admin_layout.html.erb b/app/views/layouts/admin_layout.html.erb new file mode 100644 index 0000000000..10704dbf9d --- /dev/null +++ b/app/views/layouts/admin_layout.html.erb @@ -0,0 +1,58 @@ + + + + Painel Administrativo + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "admin", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "admin_gerenciamento", "data-turbo-track": "reload" %> + + <%= javascript_importmap_tags %> + + + + + + +
+ <%= yield %> +
+ + <%= javascript_include_tag "admin_controller", "data-turbo-track": "reload" %> + + \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9e51e3817f..d6a9de0242 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -10,6 +10,7 @@ <%= csp_meta_tag %> <%= yield :head %> + <%= yield :stylesheet %> <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %> <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %> @@ -17,6 +18,8 @@ + + <%# Includes all stylesheet files in app/assets/stylesheets %> <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 487324424f..68eced570b 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -4,4 +4,4 @@ Rails.application.config.assets.version = "1.0" # Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path +# Rails.application.config.assets.paths << Emoji.images_path \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 2e0c0f5b48..12cfaf3791 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,4 +20,12 @@ get "/admin", to: "dashboards#admin", as: :admin_dashboard get "/discente", to: "dashboards#discente", as: :discente_dashboard + get "/avaliacao", to: "dashboards#admin_avaliacao", as: :admin_avaliacao_dashboard + get "/gerenciamento", to: "dashboards#admin_gerenciamento", as: :admin_gerenciamento_dashboard + + #usando o controller existente + #get '/json/importar', to: 'json#importar' + #get '/json/resultados', to: 'json#resultados' + post 'admin/importar_json', to: 'dashboards#importar_json', as: :importar_json + end diff --git a/db/migrate/20260615054444_create_turmas.rb b/db/migrate/20260615054444_create_turmas.rb new file mode 100644 index 0000000000..3a46afd6f0 --- /dev/null +++ b/db/migrate/20260615054444_create_turmas.rb @@ -0,0 +1,11 @@ +class CreateTurmas < ActiveRecord::Migration[8.1] + def change + create_table :turmas do |t| + t.string :codigo + t.string :turma + t.string :semestre + + t.timestamps + end + end +end diff --git a/db/migrate/20260615054518_create_discentes.rb b/db/migrate/20260615054518_create_discentes.rb new file mode 100644 index 0000000000..ab22a43f00 --- /dev/null +++ b/db/migrate/20260615054518_create_discentes.rb @@ -0,0 +1,16 @@ +class CreateDiscentes < ActiveRecord::Migration[8.1] + def change + create_table :discentes do |t| + t.string :nome + t.string :curso + t.string :matricula + t.string :usuario + t.string :formacao + t.string :ocupacao + t.string :email + t.references :turma, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20260615063634_create_docentes.rb b/db/migrate/20260615063634_create_docentes.rb new file mode 100644 index 0000000000..a5524b54a3 --- /dev/null +++ b/db/migrate/20260615063634_create_docentes.rb @@ -0,0 +1,15 @@ +class CreateDocentes < ActiveRecord::Migration[8.1] + def change + create_table :docentes do |t| + t.string :nome + t.string :departamento + t.string :usuario + t.string :formacao + t.string :ocupacao + t.string :email + t.string :turma + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index cb5f04ad4b..f2857524dd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,53 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_06_04_024535) do +ActiveRecord::Schema[8.1].define(version: 2026_06_15_063634) do + create_table "dado_users", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "curso" + t.string "email" + t.string "formacao" + t.string "matricula" + t.string "nome" + t.string "ocupacao" + t.datetime "updated_at", null: false + t.string "usuario" + end + + create_table "discentes", force: :cascade do |t| + t.datetime "created_at", null: false + t.string "curso" + t.string "email" + t.string "formacao" + t.string "matricula" + t.string "nome" + t.string "ocupacao" + t.integer "turma_id", null: false + t.datetime "updated_at", null: false + t.string "usuario" + t.index ["turma_id"], name: "index_discentes_on_turma_id" + end + + create_table "docentes", force: :cascade do |t| + t.string "ccupacao" + t.datetime "created_at", null: false + t.string "departamento" + t.string "email" + t.string "formacao" + t.string "nome" + t.string "turma" + t.datetime "updated_at", null: false + t.string "usuario" + end + + create_table "turmas", force: :cascade do |t| + t.string "codigo" + t.datetime "created_at", null: false + t.string "semestre" + t.string "turma" + t.datetime "updated_at", null: false + end + create_table "users", force: :cascade do |t| t.datetime "created_at", null: false t.string "email", null: false @@ -22,4 +68,6 @@ t.index ["email"], name: "index_users_on_email", unique: true t.index ["matricula"], name: "index_users_on_matricula", unique: true end + + add_foreign_key "discentes", "turmas" end diff --git a/features/step_definitions/cadastrar_usuarios_steps.rb b/features/step_definitions/cadastrar_usuarios_steps.rb new file mode 100644 index 0000000000..9086828475 --- /dev/null +++ b/features/step_definitions/cadastrar_usuarios_steps.rb @@ -0,0 +1,123 @@ + +# Cenário: [Feliz] Cadastrar usuário com dados válidos +Dado('que eu estou logado no sistema com o perfil de {string}') do |string| + @user_perfil = string.capitalize + + User.find_or_create_by!(email: "admin@camaar.com") do |user| + user.nome = @user_perfil + user.matricula = "000000001" + user.perfil = @user_perfil + user.password = "123456" + user.password_confirmation = "123456" + end + + visit '/' + + fill_in 'E-mail ou matrícula', with: 'admin@camaar.com' + fill_in 'Senha', with: '123456' + + click_button 'Entrar' + + expect(page).to have_current_path('/admin') + # expect(page).to have_content('Bem-vindo, #{string}') +end + + +Dado('que eu acesso a opção de {string} no menu lateral') do |string| + @opcao_menu = string + expect(page).to have_link(@opcao_menu) # mudar para have_link ou have_button dependendo do tipo de elemento + click_link @opcao_menu +end + + +Quando('eu clico em {string}') do |string| + @import_usuarios = 'Importar Dados' + expect(page).to have_content(@import_usuarios) # mudar para have_link ou have_button dependendo do tipo de elemento + +end + +Quando('seleciono um arquivo JSON contendo os dados de novos usuários do SIGAA') do + pending # Write code here that turns the phrase above into concrete actions +end + +=begin +Quando('os dados estão válidos no arquivo importado') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema deve processar o arquivo e criar solicitações de definição de senha para cada usuário novo') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('os usuários devem receber um e-mail com um link para definir suas senhas') do + pending # Write code here that turns the phrase above into concrete actions +end + + +# Cenário: [Feliz] Usuário permanece pendente enquanto não define a senha +Dado('que um usuário foi cadastrado com sucesso no sistema e recebeu um e-mail para definir a senha') do + pending # Write code here that turns the phrase above into concrete actions +end + +Dado('o usuário ainda não definiu sua senha') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema deve manter o status do usuário como {string}') do |string| + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o usuário não deve ter acesso ao sistema até que a senha seja definida') do + pending # Write code here that turns the phrase above into concrete actions +end + +# Cenário: [Feliz] Usuário define a senha e é ativado +Dado('o usuário definiu sua senha') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema deve atualizar o status do usuário para {string}') do |string| + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o usuário deve conseguir acessar o sistema com suas credenciais') do + pending # Write code here that turns the phrase above into concrete actions +end + + +# Cenário: [Triste] Tentar cadastrar usuário já existente +Dado('que estou logado no sistema com o perfil de {string}') do |string| + pending # Write code here that turns the phrase above into concrete actions +end + +Quando('seleciono um arquivo JSON contendo os dados de novos usuários') do + pending # Write code here that turns the phrase above into concrete actions +end + +Quando('os dados estão inválidos no arquivo importado') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema deve exibir uma mensagem de erro {string}') do |string| + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema não deve criar solicitações de definição de senha para os usuários') do + pending # Write code here that turns the phrase above into concrete actions +end + + +# Cenário: [Triste] Tentar cadastrar usuário já existente +Quando('os dados estão válidos mas correspondem a usuários já existentes no sistema') do + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema deve exibir uma mensagem de aviso {string}') do |string| + pending # Write code here that turns the phrase above into concrete actions +end + +Então('o sistema não deve criar solicitações de definição de senha para os usuários duplicados') do + pending # Write code here that turns the phrase above into concrete actions +end + +=end diff --git a/features/support/chrome.rb b/features/support/chrome.rb new file mode 100644 index 0000000000..7c2800c5f4 --- /dev/null +++ b/features/support/chrome.rb @@ -0,0 +1,39 @@ +require 'capybara/rspec' +require 'selenium/webdriver' +require 'cucumber/rails' + +options = Selenium::WebDriver::Chrome::Options.new +options.add_preference(:download, prompt_for_download: false, + default_directory: '/tmp/downloads') + +options.add_preference(:browser, set_download_behavior: { behavior: 'allow' }) + +Capybara.register_driver :chrome do |app| + Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) +end + +Capybara.register_driver :headless_chrome do |app| + options.add_argument('--headless') + options.add_argument('--disable-gpu') + options.add_argument('--window-size=1280,800') + + driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) + + ### Allow file downloads in Google Chrome when headless!!! + ### https://bugs.chromium.org/p/chromium/issues/detail?id=696481#c89 + bridge = driver.browser.send(:bridge) + + path = '/session/:session_id/chromium/send_command' + path[':session_id'] = bridge.session_id + + bridge.http.call(:post, path, cmd: 'Page.setDownloadBehavior', + params: { + behavior: 'allow', + downloadPath: '/tmp/downloads' + }) + ### + + driver +end + +Capybara.javascript_driver = ENV['GUI'] ? :chrome : :headless_chrome diff --git a/features/support/env.rb b/features/support/env.rb index f2ab1f926b..3b97d14087 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -51,28 +51,3 @@ # The :transaction strategy is faster, but might give you threading problems. # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature Cucumber::Rails::Database.javascript_strategy = :truncation - - -require 'capybara/cucumber' -require 'capybara/dsl' -require 'rspec/expectations' -require 'selenium-webdriver' - -Capybara.configure do |config| - config.default_driver = :selenium_chrome_headless - config.default_max_wait_time = 5 - config.ignore_hidden_elements = true -end - -Capybara.register_driver :selenium_chrome_headless do |app| - options = Selenium::WebDriver::Chrome::Options.new - options.add_argument('--headless=new') - options.add_argument('--no-sandbox') - options.add_argument('--disable-dev-shm-usage') - options.add_argument('--window-size=1280,900') - - Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) -end - -# World(Capybara::DSL) -- já incluso por capybara/cucumber -World(RSpec::Matchers) diff --git a/features/support/firefox.rb b/features/support/firefox.rb new file mode 100644 index 0000000000..201e85a7ac --- /dev/null +++ b/features/support/firefox.rb @@ -0,0 +1,18 @@ +require 'capybara/rspec' +require 'selenium/webdriver' +require 'cucumber/rails' + +Capybara.register_driver :firefox do |app| + Capybara::Selenium::Driver.new(app, browser: :firefox) +end + +Capybara.register_driver :headless_firefox do |app| + options = Selenium::WebDriver::Firefox::Options.new + options.headless! # added on https://github.com/SeleniumHQ/selenium/pull/4762 + + Capybara::Selenium::Driver.new app, + browser: :firefox, + options: options +end + +Capybara.javascript_driver = ENV['GUI'] ? :firefox : :headless_firefox diff --git a/features/support/hooks.rb b/features/support/hooks.rb index c2ef98a81a..6c07443f96 100644 --- a/features/support/hooks.rb +++ b/features/support/hooks.rb @@ -4,10 +4,14 @@ After do |cenario| if cenario.failed? - nome = cenario.name.gsub(/[^a-z0-9]/i, '_').downcase - caminho = "tmp/screenshots/#{nome}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.png" - FileUtils.mkdir_p('tmp/screenshots') - page.save_screenshot(caminho) - attach(File.read(caminho), 'image/png') + begin + nome = cenario.name.gsub(/[^a-z0-9]/i, '_').downcase + caminho = "tmp/screenshots/#{nome}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.png" + FileUtils.mkdir_p('tmp/screenshots') + page.save_screenshot(caminho) + attach(File.read(caminho), 'image/png') + rescue => e + puts "Erro ao capturar screenshot: #{e.message}" + end end end diff --git a/spec/models/discente_spec.rb b/spec/models/discente_spec.rb new file mode 100644 index 0000000000..648ff8105f --- /dev/null +++ b/spec/models/discente_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Discente, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/docente_spec.rb b/spec/models/docente_spec.rb new file mode 100644 index 0000000000..0cd405d9aa --- /dev/null +++ b/spec/models/docente_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Docente, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/turma_spec.rb b/spec/models/turma_spec.rb new file mode 100644 index 0000000000..b7629b1e16 --- /dev/null +++ b/spec/models/turma_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Turma, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index ef75d46770..0d076bbccf 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -47,6 +47,21 @@ # You can uncomment this line to turn off ActiveRecord support entirely. # config.use_active_record = false + # RSpec.configure do |config| + # ... suas outras configurações ... + + config.before(:each, type: :system) do + # Verifica se a máquina tem Chrome + has_chrome = !`which google-chrome 2>/dev/null || which chrome 2>/dev/null`.empty? + + if has_chrome + # Código para o seu colega (Usa o Chrome normal com interface) + driven_by :selenium, using: :headless_chrome + else + # Código baseado na sua sugestão (Usa o Firefox normal com interface) + driven_by :selenium, using: :headless_firefox + end + end # RSpec Rails uses metadata to mix in different behaviours to your tests, # for example enabling you to call `get` and `post` in request specs. e.g.: diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 327b58ea1f..e662a23997 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +require 'capybara/rspec' + # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause diff --git a/spec/system/cadastrar_usuarios_spec.rb b/spec/system/cadastrar_usuarios_spec.rb new file mode 100644 index 0000000000..d90d2abe73 --- /dev/null +++ b/spec/system/cadastrar_usuarios_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + + +RSpec.describe "Cadastrar usuário com dados válidos", type: :system do + before do + # has_firefox = system('which firefox > /dev/null 2>&1 || where firefox > /dev/null 2>&1') + driven_by(:rack_test) + # driven_by(:selenium, using: has_firefox ? :headless_firefox : :headless_chrome) + + @admin = User.create!( + nome: "Administrador", + email: "admin@camaar.com", + matricula: "000000001", + perfil: "Administrador", + password: "123456" + ) + end + + it "logar no perfil de Administrador" do + visit '/login' + + fill_in 'E-mail ou matrícula', with: @admin.email + fill_in 'Senha', with: @admin.password + + click_button 'Entrar' + + visit '/admin' + + expect(page).to have_current_path('/admin') + end + + it "Acessar a opção de Gerenciamento" do + visit '/login' + + fill_in 'E-mail ou matrícula', with: @admin.email + fill_in 'Senha', with: @admin.password + + click_button 'Entrar' + visit '/admin' + + expect(page).to have_link("Gerenciamento") + click_link 'Gerenciamento' + expect(page).to have_content("Painel de Gerenciamento") + end +end From 9b572bd7357b63ea4f83d53d20a9af912d08888c Mon Sep 17 00:00:00 2001 From: Anarayssa-dev Date: Tue, 16 Jun 2026 22:05:40 -0300 Subject: [PATCH 2/5] feat: adiciona testes de sistema RSpec e passos do Cucumber --- Gemfile | 2 + Gemfile.lock | 20 +-- app/assets/stylesheets/admin.css | 23 ++-- app/controllers/dashboards_controller.rb | 69 ++++------ app/mailers/application_mailer.rb | 2 +- app/mailers/user_mailer.rb | 13 ++ .../importer/academic_data_importer.rb | 119 ++++++++++++++++++ .../dashboards/admin_gerenciamento.html.erb | 96 +------------- app/views/layouts/admin_layout.html.erb | 2 + app/views/layouts/mailer.html.erb | 9 +- .../user_mailer/primeiro_acesso.html.erb | 5 + .../user_mailer/primeiro_acesso.text.erb | 3 + config/environments/development.rb | 6 + config/routes.rb | 11 +- db/migrate/20260604024535_create_users.rb | 2 + db/migrate/20260615063634_create_docentes.rb | 2 +- db/schema.rb | 3 +- db/seeds.rb | 1 + .../cadastrar_usuarios_steps.rb | 103 +++++++++++---- spec/fixtures/user_mailer/primeiro_acesso | 3 + spec/mailers/previews/user_mailer_preview.rb | 9 ++ spec/mailers/user_mailer_spec.rb | 18 +++ spec/system/cadastrar_usuarios_spec.rb | 76 +++++++++++ 23 files changed, 407 insertions(+), 190 deletions(-) create mode 100644 app/mailers/user_mailer.rb create mode 100644 app/services/importer/academic_data_importer.rb create mode 100644 app/views/user_mailer/primeiro_acesso.html.erb create mode 100644 app/views/user_mailer/primeiro_acesso.text.erb create mode 100644 spec/fixtures/user_mailer/primeiro_acesso create mode 100644 spec/mailers/previews/user_mailer_preview.rb create mode 100644 spec/mailers/user_mailer_spec.rb diff --git a/Gemfile b/Gemfile index 0ae3eef5b3..0c97d9569d 100644 --- a/Gemfile +++ b/Gemfile @@ -68,8 +68,10 @@ group :test do gem "selenium-webdriver" end +gem "activerecord-import" # gem para o firefox # group :test do # gem "webdrivers", "~> 5.0" # end + diff --git a/Gemfile.lock b/Gemfile.lock index 3f33fa54a9..8345483a93 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,6 +56,8 @@ GEM activemodel (= 8.1.3) activesupport (= 8.1.3) timeout (>= 0.4.0) + activerecord-import (2.2.0) + activerecord (>= 4.2) activestorage (8.1.3) actionpack (= 8.1.3) activejob (= 8.1.3) @@ -100,7 +102,7 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - concurrent-ruby (1.3.6) + concurrent-ruby (1.3.7) connection_pool (3.0.2) crass (1.0.6) cucumber (10.2.0) @@ -330,7 +332,7 @@ GEM rspec-mocks (>= 3.13.0, < 5.0.0) rspec-support (>= 3.13.0, < 5.0.0) rspec-support (3.13.7) - rubocop (1.87.0) + rubocop (1.88.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -362,9 +364,9 @@ GEM ruby-vips (2.3.0) ffi (~> 1.12) logger - rubyzip (3.3.1) + rubyzip (3.4.0) securerandom (0.4.1) - selenium-webdriver (4.44.0) + selenium-webdriver (4.45.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -448,6 +450,7 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + activerecord-import bcrypt (~> 3.1.7) bootsnap brakeman @@ -487,6 +490,7 @@ CHECKSUMS activejob (8.1.3) sha256=a149b1766aa8204c3c3da7309e4becd40fcd5529c348cffbf6c9b16b565fe8d3 activemodel (8.1.3) sha256=90c05cbe4cef3649b8f79f13016191ea94c4525ce4a5c0fb7ef909c4b91c8219 activerecord (8.1.3) sha256=8003be7b2466ba0a2a670e603eeb0a61dd66058fccecfc49901e775260ac70ab + activerecord-import (2.2.0) sha256=f8ca99b196e50775723d1f1d192c379f656378dc9f5628240992a0d78807fa4b activestorage (8.1.3) sha256=0564ce9309143951a67615e1bb4e090ee54b8befed417133cae614479b46384d activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af @@ -502,7 +506,7 @@ CHECKSUMS bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9 capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef - concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab + concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0 connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d cucumber (10.2.0) sha256=fdedbd31ecf40858b60f04853f2aa15c44f5c30bbac29c6a227fa1e7005a8158 @@ -602,16 +606,16 @@ CHECKSUMS rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47 rspec-rails (8.0.4) sha256=06235692fc0892683d3d34977e081db867434b3a24ae0dd0c6f3516bad4e22df rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c - rubocop (1.87.0) sha256=b9d9ddf55116a513f8ef2c7ae660662d8b49301f118d3f0df61865b33a5c188d + rubocop (1.88.0) sha256=e420ddf1662d0ef34bc8a2910ac4b396a7ddda0b51a708264405241734b08e0b rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 rubocop-rails (2.35.4) sha256=3aeaa325439c89950e8327565682ea794065d08e2ecbbfe95032bfa295a35df5 rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374 - rubyzip (3.3.1) sha256=2ed92112c7c43ba2b2527f35e6d99d9c2c99270b640aad5227516436481b1e4e + rubyzip (3.4.0) sha256=6de39bc9eba302b635a476d16c9e16b0872ad24517c2f98f2b3a7ea23caff57b securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - selenium-webdriver (4.44.0) sha256=6f1df072529af369589c46f0e01132952aabb250cfd683c274d74dc1eb5d8477 + selenium-webdriver (4.45.0) sha256=ecac65a4df86ac6f7d707e6dcbacaa9c08b6cf2b966babecfb9653c5aa13e2d1 solid_cable (4.0.0) sha256=8379680ef6bf36e195eb876a6306ea290f87d5fa10bc4a757bc2a918f83229b5 solid_cache (1.0.10) sha256=bc05a2fb3ac78a6f43cbb5946679cf9db67dd30d22939ededc385cb93e120d41 solid_queue (1.4.0) sha256=e6a18d196f0b27cb6e3c77c5b31258b05fb634f8ed64fb1866ed164047216c2a diff --git a/app/assets/stylesheets/admin.css b/app/assets/stylesheets/admin.css index e084885488..bdb7c3ad32 100644 --- a/app/assets/stylesheets/admin.css +++ b/app/assets/stylesheets/admin.css @@ -4,13 +4,16 @@ box-sizing: border-box; font-family: 'Poppins', sans-serif; } - -/*.header_admin{ - position: fixed; - top: 0; - left: 0; +/* +.header_admin{ + color: yellow; width: 100%; - z-index: 10; + height: 0; + background-color: yellow; + top:0; + + + padding: 40px 0 10px 0; } */ @@ -22,7 +25,7 @@ body{ nav.navbar{ width: 80px; height: 100%; - background-color: #7a0553; + background-color: #6f2477; padding: 40px 0 40px 1%; @@ -88,6 +91,8 @@ section{ ul.nav-menu{ height: 100%; list-style-type: none; + + padding-top: 10px; } ul li.nav-item{ @@ -103,7 +108,7 @@ ul li.ativo{ } ul li.nav-item.ativo a{ - color: #7a0553; + color: #6f2477; } @@ -115,7 +120,7 @@ ul li.nav-item:hover{ } ul li.nav-item a:hover{ - color: #7a0553; + color: #6f2477; } ul li.nav-item a{ diff --git a/app/controllers/dashboards_controller.rb b/app/controllers/dashboards_controller.rb index 5d2d7c5a81..5a155aa56f 100644 --- a/app/controllers/dashboards_controller.rb +++ b/app/controllers/dashboards_controller.rb @@ -19,63 +19,36 @@ def admin_gerenciamento # importação do json def importar_json - file = params["json"].tempfile.path - file_path = File.read(file) - - data_hash = JSON.parse(file_path) - - data_hash.each do |turma_dados| - codigo = turma_dados["code"] - semestre = turma_dados["semester"] - turma = turma_dados["classCode"] - - refer_turma = Turma.find_or_create_by!(codigo: codigo, turma: turma, semestre: semestre) - - if turma_dados["dicente"].is_a?(Array) - - turma_dados["dicente"].each do |usuarios| - nome = usuarios["nome"].downcase - curso = usuarios["curso"].downcase - matricula = usuarios["matricula"] - usuario = usuarios["usuario"] - formacao = usuarios["formacao"].downcase - ocupacao = usuarios["ocupacao"].downcase - email = usuarios["email"].downcase - - Discente.find_or_create_by!(nome: nome, curso: curso, matricula: matricula, - usuario: usuario, formacao: formacao, ocupacao: ocupacao, - email: email, turma: refer_turma) - - User.find_or_create_by!(nome: nome, matricula: matricula, perfil: "Discente", password: 123456, password_confirmation: 123456) - end + # codigo de auxilio + if params["json"].blank? + puts "ERRO: O parâmetro 'json' veio vazio!" + flash[:error] = "Por favor, selecione um arquivo JSON." + redirect_to admin_gerenciamento_dashboard_path and return end + puts "Arquivo recebido com sucesso: #{params["json"].original_filename}" - if turma_dados["docente"].is_a?(Array) - turma_dados["docente"].each do |usuarios| - nome = usuarios["nome"].downcase - departamento = usuarios["departamento"].downcase - usuario = usuarios["usuario"] - formacao = usuarios["formacao"].downcase - ocupacao = usuarios["ocupacao"].downcase - email = usuarios["email"].downcase - - + # Chama o service + resultado = Importer::AcademicDataImporter.import!(params["json"]) - Docente.find_or_create_by!(nome: nome, departamento: departamento, usuario: usuario, formacao: formacao, ocupacao: ocupacao, email: email, turma: refer_turma) - User.find_or_create_by!(nome: nome, matricula: email, perfil: "Docente", password: 123456, password_confirmation: 123456) - end - end + if resultado + puts "SUCESSO: O Service Object terminou sem erros." + flash[:success] = "JSON importado com sucesso!" + else + puts "AVISO: O Service retornou falso." end - # aviso gerado - flash[:success] = "JSON importado e processado com sucesso!" - # redirect_to admin_gerenciamento_dashboard_path + redirect_to admin_gerenciamento_dashboard_path + rescue => e + puts "EXCEÇÃO CAPTURADA NO CONTROLLER: #{e.message}" + puts e.backtrace.first(2) flash[:error] = "Ocorreu um erro na importação: #{e.message}" - # redirect_to admin_gerenciamento_dashboard_path + redirect_to admin_gerenciamento_dashboard_path end + + # tela de avaliações @@ -84,4 +57,4 @@ def admin_avaliacao def discente end -end +end \ No newline at end of file diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 3c34c8148f..5f345efe04 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" + default from: "ES2026_Grupo01@camaar.com" layout "mailer" end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 0000000000..91b44e54e3 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,13 @@ +class UserMailer < ApplicationMailer + + def primeiro_acesso(usuario) + @usuario = usuario + + @url_definir_senha = definir_senha_url(email: @usuario.email) + + mail( + to: @usuario.email, + subject: 'Bem-vindo ao CAMAAR! Defina sua senha de acesso' + ) + end +end diff --git a/app/services/importer/academic_data_importer.rb b/app/services/importer/academic_data_importer.rb new file mode 100644 index 0000000000..7b2472120e --- /dev/null +++ b/app/services/importer/academic_data_importer.rb @@ -0,0 +1,119 @@ +module Importer + class AcademicDataImporter + def self.import!(upload_file) + start_time = Time.current + + file_path = File.read(upload_file.tempfile.path) + data_hash = JSON.parse(file_path) + + ActiveRecord::Base.transaction do + unique_turmas = data_hash.uniq { |t| [ t["code"], t["classCode"], t["semester"] ] } + + turma_objects = unique_turmas.map do |t| + Turma.new(codigo: t["code"], turma: t["classCode"], semestre: t["semester"]) + end + # duplicação de objetos + Turma.import turma_objects, on_duplicate_key_ignore: true, validate: false + + turmas_hash = Turma.all.pluck(:codigo, :turma, :semestre, :id).each_with_object({}) do |(codigo, turma, semestre, id), hash| + hash["#{codigo}-#{turma}-#{semestre}"] = id + end + + # armazenar os discentes e docentes + raw_discentes = [] + raw_docentes = [] + + data_hash.each do |turma_dados| + turma_id = turmas_hash["#{turma_dados['code']}-#{turma_dados['classCode']}-#{turma_dados['semester']}"] + next unless turma_id + + if turma_dados["dicente"].is_a?(Array) + turma_dados["dicente"].each do |d| + raw_discentes << d.merge("turma_id" => turma_id) + end + end + + if turma_dados["docente"].is_a?(Hash) + d_user = turma_dados["docente"] + raw_docentes << d_user.merge("turma_id" => turma_id) + end + end + + + unique_discentes = raw_discentes.uniq { |d| d["matricula"] } + unique_docentes = raw_docentes.uniq { |d| d["email"] } + + discente_objects = unique_discentes.map do |members| + Discente.new( + nome: members["nome"]&.downcase, + curso: members["curso"]&.downcase, + matricula: members["matricula"], + usuario: members["usuario"], + formacao: members["formacao"]&.downcase, + ocupacao: members["ocupacao"]&.downcase, + email: members["email"]&.downcase, + turma_id: members["turma_id"] + ) + end + Discente.import discente_objects, on_duplicate_key_ignore: true, validate: false + + users_discente_objects = unique_discentes.map do |members| + User.new( + nome: members["nome"]&.downcase, + matricula: members["matricula"], + email: members["email"], + perfil: "Discente", + password: "123456", + password_confirmation: "123456", + primeiro_acesso: false + ) + + end + User.import users_discente_objects, on_duplicate_key_ignore: true, validate: false + + emails_importados = users_discente_objects.map(&:email) + usuarios_salvos = User.where(email: emails_importados) + + usuarios_salvos.each do |usuario| + UsuarioMailer.email_primeiro_acesso(usuario).deliver_later + end + + docente_objects = unique_docentes.map do |members| + Docente.new( + nome: members["nome"]&.downcase, + departamento: members["departamento"]&.downcase, + usuario: members["usuario"], + formacao: members["formacao"]&.downcase, + ocupacao: members["ocupacao"]&.downcase, + email: members["email"]&.downcase, + turma: members["turma_id"] + ) + end + Docente.import docente_objects, on_duplicate_key_ignore: true, validate: false + + users_docente_objects = unique_docentes.map do |members| + User.new( + nome: members["nome"]&.downcase, + matricula: members["email"], + email: members["email"], + perfil: "Docente", + password: "123456", + password_confirmation: "123456", + primeiro_acesso: false + ) + end + + User.import users_docente_objects, on_duplicate_key_ignore: true, validate: false + end + # registro de erros + rescue StandardError => e + error = "Erro na transação de importação: #{e.message}" + Rails.logger.error error + raise + + elapsed_time = Time.current - start_time + Rails.logger.info "Tempo total de importação: #{elapsed_time.round(2)} segundos" + true + end + end +end \ No newline at end of file diff --git a/app/views/dashboards/admin_gerenciamento.html.erb b/app/views/dashboards/admin_gerenciamento.html.erb index c8e6aff137..80ed7b7b65 100644 --- a/app/views/dashboards/admin_gerenciamento.html.erb +++ b/app/views/dashboards/admin_gerenciamento.html.erb @@ -34,99 +34,11 @@
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
- + + + +
- - - -
- -
- -
-

Turmas Cadastradas (<%= @turmas.count %>)

- <% if @turmas.any? %> - - - - - - - - - - <% @turmas.each do |turma| %> - - - - - - <% end %> - -
Código da DisciplinaTurmaSemestre
<%= turma.codigo.upcase %><%= turma.turma %><%= turma.semestre %>
- <% else %> -

Nenhuma turma cadastrada.

- <% end %> -
- -
-

Discentes Cadastrados (<%= @discentes.count %>)

- <% if @discentes.any? %> - - - - - - - - - - - <% @discentes.each do |aluno| %> - - - - - - - <% end %> - -
NomeMatrículaCursoE-mail
<%= aluno.nome.titleize %><%= aluno.matricula %><%= aluno.curso.upcase %><%= aluno.email %>
- <% else %> -

Nenhum discente cadastrado.

- <% end %> -
- -
-

Docentes Cadastrados (<%= @docentes.count %>)

- <% if @docentes.any? %> - - - - - - - - - - - <% @docentes.each do |professor| %> - - - - - - - <% end %> - -
NomeDepartamento / OcupaçãoUsuário de LoginE-mail
<%= professor.nome.titleize %><%= professor.ocupacao&.upcase %><%= professor.usuario %><%= professor.email %>
- <% else %> -

Nenhum docente cadastrado.

- <% end %> -
- -
- \ No newline at end of file diff --git a/app/views/layouts/admin_layout.html.erb b/app/views/layouts/admin_layout.html.erb index 10704dbf9d..ca2c5d4ae4 100644 --- a/app/views/layouts/admin_layout.html.erb +++ b/app/views/layouts/admin_layout.html.erb @@ -17,6 +17,8 @@ + +