From 115397932c775afc571fe0fd7bbd977cef9dbe97 Mon Sep 17 00:00:00 2001 From: Rafael Batista Date: Thu, 16 Apr 2026 12:14:44 -0300 Subject: [PATCH] boas vindas no front-end --- app/controllers/app/dashboard_controller.rb | 35 +++++ .../onboarding_welcome_controller.js | 123 ++++++++++++++++++ .../_onboarding_welcome_modal.html.erb | 43 ++++++ app/views/app/dashboard/index.html.erb | 1 + 4 files changed, 202 insertions(+) create mode 100644 app/javascript/controllers/onboarding_welcome_controller.js create mode 100644 app/views/app/dashboard/_onboarding_welcome_modal.html.erb diff --git a/app/controllers/app/dashboard_controller.rb b/app/controllers/app/dashboard_controller.rb index 7e67e400..824f4c07 100644 --- a/app/controllers/app/dashboard_controller.rb +++ b/app/controllers/app/dashboard_controller.rb @@ -1,8 +1,17 @@ class App::DashboardController < ApplicationController + ONBOARDING_STEP_PATHS = { + "created_technician" => :app_technicians_path, + "created_customer" => :app_clients_path, + "created_first_work_order" => :app_order_services_path, + "moved_work_order_status" => :app_order_services_path, + "viewed_reports" => :app_reports_path + }.freeze + def index authorize! :read, :dashboard initialize_default_dashboard_variables + set_onboarding_welcome_modal case current_user.role when "admin" @@ -142,4 +151,30 @@ def initialize_default_dashboard_variables @overdue_count = 0 @budgets_total_value = 0 end + + def set_onboarding_welcome_modal + @onboarding_progress = current_user.user_onboarding_progress + @show_onboarding_welcome_modal = onboarding_welcome_eligible? + @onboarding_start_path = next_onboarding_step_path + end + + def onboarding_welcome_eligible? + return false if current_user.tecnico? + return true if @onboarding_progress.nil? + return false if @onboarding_progress.dismissed_at.present? + return false if @onboarding_progress.finished_at.present? + + !@onboarding_progress.finished_all_steps? + end + + def next_onboarding_step_path + completed_steps = @onboarding_progress&.completed_steps || {} + + next_step = UserOnboardingProgress::STEP_KEYS.find do |step_key| + completed_steps.fetch(step_key, false) != true + end + + route_name = ONBOARDING_STEP_PATHS[next_step] || :app_dashboard_path + send(route_name) + end end diff --git a/app/javascript/controllers/onboarding_welcome_controller.js b/app/javascript/controllers/onboarding_welcome_controller.js new file mode 100644 index 00000000..3658e4db --- /dev/null +++ b/app/javascript/controllers/onboarding_welcome_controller.js @@ -0,0 +1,123 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["modal", "startButton", "dismissButton"] + static values = { + autoShow: Boolean, + startPath: String + } + + connect() { + this.modalInstance = null + this.fallbackBackdrop = null + + if (!this.hasModalTarget) return + + if (typeof window.bootstrap !== "undefined") { + this.modalInstance = new window.bootstrap.Modal(this.modalTarget) + + if (this.autoShowValue) { + this.modalInstance.show() + } + return + } + + if (this.autoShowValue) { + this.showFallback() + } + } + + disconnect() { + if (this.modalInstance) { + this.modalInstance.hide() + this.modalInstance.dispose() + this.modalInstance = null + return + } + + this.hideFallback() + } + + async start() { + this.setButtonsDisabled(true) + + await this.sendOperation("resume") + this.hideModal() + + this.dispatch("started") + + if (this.hasStartPathValue && this.startPathValue) { + window.location.href = this.startPathValue + return + } + + this.setButtonsDisabled(false) + } + + async dismiss() { + this.setButtonsDisabled(true) + + await this.sendOperation("dismiss") + this.hideModal() + this.setButtonsDisabled(false) + } + + hideModal() { + if (this.modalInstance) { + this.modalInstance.hide() + return + } + + this.hideFallback() + } + + async sendOperation(operation) { + const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") + + try { + await fetch("/onboarding_progress", { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + "X-CSRF-Token": csrfToken || "" + }, + credentials: "same-origin", + body: JSON.stringify({ operation }) + }) + } catch (error) { + console.warn("Falha ao atualizar onboarding:", error) + } + } + + setButtonsDisabled(disabled) { + if (this.hasStartButtonTarget) this.startButtonTarget.disabled = disabled + if (this.hasDismissButtonTarget) this.dismissButtonTarget.disabled = disabled + } + + showFallback() { + this.modalTarget.classList.add("show") + this.modalTarget.style.display = "block" + this.modalTarget.removeAttribute("aria-hidden") + this.modalTarget.setAttribute("aria-modal", "true") + this.modalTarget.setAttribute("role", "dialog") + document.body.classList.add("modal-open") + + this.fallbackBackdrop = document.createElement("div") + this.fallbackBackdrop.className = "modal-backdrop fade show" + document.body.appendChild(this.fallbackBackdrop) + } + + hideFallback() { + this.modalTarget.classList.remove("show") + this.modalTarget.style.display = "none" + this.modalTarget.setAttribute("aria-hidden", "true") + this.modalTarget.removeAttribute("aria-modal") + document.body.classList.remove("modal-open") + + if (this.fallbackBackdrop) { + this.fallbackBackdrop.remove() + this.fallbackBackdrop = null + } + } +} diff --git a/app/views/app/dashboard/_onboarding_welcome_modal.html.erb b/app/views/app/dashboard/_onboarding_welcome_modal.html.erb new file mode 100644 index 00000000..2f796f7c --- /dev/null +++ b/app/views/app/dashboard/_onboarding_welcome_modal.html.erb @@ -0,0 +1,43 @@ +
+ +
diff --git a/app/views/app/dashboard/index.html.erb b/app/views/app/dashboard/index.html.erb index 7d84ba56..dea8b0f4 100644 --- a/app/views/app/dashboard/index.html.erb +++ b/app/views/app/dashboard/index.html.erb @@ -1,5 +1,6 @@
+ <%= render "onboarding_welcome_modal" %> <% flash.each do |type, message| %> <% bootstrap_class = { notice: "alert-success", alert: "alert-danger", error: "alert-danger" }[type.to_sym] || "alert-info" %>