From 76491dcb238c6e403a0e1d3bfa5387f80cb63be8 Mon Sep 17 00:00:00 2001 From: Rafael Batista Date: Thu, 16 Apr 2026 15:05:13 -0300 Subject: [PATCH 1/3] ajuste do onboarding --- app/assets/stylesheets/css/app.css | 504 +++++++++++++----- app/controllers/app/budgets_controller.rb | 2 + app/controllers/app/dashboard_controller.rb | 6 +- .../app/order_services_controller.rb | 1 - .../controllers/onboarding_tour_controller.js | 31 ++ app/models/user_onboarding_progress.rb | 1 + .../dashboard/_onboarding_checklist.html.erb | 2 +- .../app/dashboard/_onboarding_tour.html.erb | 12 +- app/views/layouts/application.html.erb | 2 +- app/views/layouts/partials/_sidebar.html.erb | 2 +- 10 files changed, 418 insertions(+), 145 deletions(-) diff --git a/app/assets/stylesheets/css/app.css b/app/assets/stylesheets/css/app.css index c9420a2..8ae41e2 100644 --- a/app/assets/stylesheets/css/app.css +++ b/app/assets/stylesheets/css/app.css @@ -15,7 +15,7 @@ File: app.css -body { +body { font-size: 14px; @@ -27,15 +27,15 @@ body { overflow-x: hidden; - font-family: Roboto, sans-serif - -} - -html.page-pending .wrapper, -html.page-pending .page-footer { - opacity: 0; - visibility: hidden; -} + font-family: Roboto, sans-serif + +} + +html.page-pending .wrapper, +html.page-pending .page-footer { + opacity: 0; + visibility: hidden; +} @@ -519,7 +519,7 @@ input[type="file"].form-control.bg-dark::file-selector-button { -.metismenu .has-arrow:after { +.metismenu .has-arrow:after { position: absolute; @@ -5624,132 +5624,364 @@ payment-methods { align-items: stretch; } #knowledgeBaseModalBody li { margin-bottom: 0.4em; } -#knowledgeBaseModalBody p { - margin-bottom: 1em; -} - -/* Calendar page - technician filter */ -.calendar-filter-card { - background: linear-gradient(180deg, #f8fafc 0%, #eef3f8 100%); - border: 1px solid #d9e2ec; -} - -.calendar-tech-filter-group { - flex-wrap: nowrap; -} - -.calendar-tech-filter-icon { - border-right: 0; - min-height: 38px; - display: flex; - align-items: center; - justify-content: center; -} - -.calendar-tech-filter-group > .select2-container { - flex: 1 1 auto; - width: 1% !important; -} - -.calendar-tech-filter-group > .select2-container .select2-selection { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.calendar-filter-card .select2-container--bootstrap-5 .select2-selection { - min-height: 38px; -} - -.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple { - min-height: 38px; - max-height: 112px; - overflow-y: auto; - padding: 4px 6px; - border-color: #b6c2cf; -} - -.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered { +#knowledgeBaseModalBody p { + margin-bottom: 1em; +} + +/* Calendar page - technician filter */ +.calendar-filter-card { + background: linear-gradient(180deg, #f8fafc 0%, #eef3f8 100%); + border: 1px solid #d9e2ec; +} + +.calendar-tech-filter-group { + flex-wrap: nowrap; +} + +.calendar-tech-filter-icon { + border-right: 0; + min-height: 38px; + display: flex; + align-items: center; + justify-content: center; +} + +.calendar-tech-filter-group > .select2-container { + flex: 1 1 auto; + width: 1% !important; +} + +.calendar-tech-filter-group > .select2-container .select2-selection { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.calendar-filter-card .select2-container--bootstrap-5 .select2-selection { + min-height: 38px; +} + +.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple { + min-height: 38px; + max-height: 112px; + overflow-y: auto; + padding: 4px 6px; + border-color: #b6c2cf; +} + +.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; + margin: 0; + padding: 0; +} + +.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice { + margin: 0; + padding: 2px 8px; + border: 1px solid #bfd7ff; + background: #eaf2ff; + color: #24416b; + border-radius: 999px; + font-size: 12px; + line-height: 1.4; +} + +.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove { + margin-right: 6px; + color: #24416b; +} + +.calendar-filter-card .select2-container--bootstrap-5.select2-container--focus .select2-selection { + border-color: #86b7fe; + box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.15); +} + +.audit-date-group .form-control { + background-color: #0f1a26; + color: #e8edf3; + border-color: #4a5562; +} + +.audit-date-group .form-control::placeholder { + color: #a8b3bf; + opacity: 1; +} + +.audit-date-group .input-group-text { + background-color: #132235; + border-color: #4a5562; + color: #e8edf3; + cursor: pointer; +} + +.logs-filters-bar { + background: rgba(12, 21, 32, 0.85); + border: 1px solid rgba(124, 136, 149, 0.25); + border-radius: 12px; + padding: 12px; +} + +.logs-filter-input { + min-height: 40px; +} + +.logs-filter-input.form-select, +.logs-filter-input.form-control { + background-color: #0f1a26; + color: #e8edf3; + border-color: #4a5562; +} + +.logs-filter-input.form-select:focus, +.logs-filter-input.form-control:focus { + border-color: #6ea8fe; + box-shadow: 0 0 0 0.2rem rgba(110, 168, 254, 0.2); +} + +.logs-filter-addon { + background-color: #132235; + border-color: #4a5562; + color: #9fb2c5; +} + +.logs-apply-btn { + min-width: 150px; +} + +.logs-clear-btn { + min-width: 90px; +} + +/* Onboarding tour (Intro.js) */ +.introjs-tooltip { + position: relative; +} + +.introjs-tooltip .introjs-tooltip-header { + position: relative; + padding-right: 72px; +} + +.introjs-tooltip .introjs-skipbutton { + position: absolute !important; + top: 8px !important; + right: 10px !important; + left: auto !important; + display: inline-block !important; + width: auto !important; + height: auto !important; + line-height: 1.2 !important; + padding: 4px 9px !important; + border-radius: 999px !important; + text-align: center !important; + text-decoration: none !important; + z-index: 10 !important; +} + +.introjs-tooltip.introjs-tooltip-onboarding { + min-width: 320px; + max-width: 372px; + border: 1px solid #d8e1ed; + border-radius: 16px; + background: #f8faff; + box-shadow: 0 18px 42px rgba(15, 23, 42, 0.18); + padding: 14px 16px 14px; + position: relative; + overflow: hidden; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-tooltip-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + position: static !important; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-tooltip-title { + margin: 0; + color: #1f2937; + font-size: 0.9rem; + font-weight: 800; + line-height: 1.35; + letter-spacing: 0.01em; + padding-right: 72px; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-tooltiptext { + color: #4b5563; + font-size: 0.9rem; + line-height: 1.5; + font-weight: 500; + padding: 0 0 6px; + margin: 0; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-progress { + background: #e3e9f2; + border-radius: 999px; + height: 8px; + margin: 10px 0 14px; + overflow: hidden; + box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.06); +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-progressbar { + background: linear-gradient(90deg, #3f8bd2 0%, #2d79bc 100%); + border-radius: 999px; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-tooltipbuttons { + border-top: 1px solid #dfe6f0; + margin-top: 6px; + padding-top: 12px; display: flex; - flex-wrap: wrap; - gap: 6px; align-items: center; - margin: 0; - padding: 0; -} - -.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice { - margin: 0; - padding: 2px 8px; - border: 1px solid #bfd7ff; - background: #eaf2ff; - color: #24416b; - border-radius: 999px; - font-size: 12px; - line-height: 1.4; -} - -.calendar-filter-card .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove { - margin-right: 6px; - color: #24416b; -} - -.calendar-filter-card .select2-container--bootstrap-5.select2-container--focus .select2-selection { - border-color: #86b7fe; - box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.15); -} - -.audit-date-group .form-control { - background-color: #0f1a26; - color: #e8edf3; - border-color: #4a5562; -} - -.audit-date-group .form-control::placeholder { - color: #a8b3bf; - opacity: 1; -} - -.audit-date-group .input-group-text { - background-color: #132235; - border-color: #4a5562; - color: #e8edf3; - cursor: pointer; -} - -.logs-filters-bar { - background: rgba(12, 21, 32, 0.85); - border: 1px solid rgba(124, 136, 149, 0.25); - border-radius: 12px; - padding: 12px; -} - -.logs-filter-input { - min-height: 40px; -} - -.logs-filter-input.form-select, -.logs-filter-input.form-control { - background-color: #0f1a26; - color: #e8edf3; - border-color: #4a5562; -} - -.logs-filter-input.form-select:focus, -.logs-filter-input.form-control:focus { - border-color: #6ea8fe; - box-shadow: 0 0 0 0.2rem rgba(110, 168, 254, 0.2); + justify-content: flex-start; + gap: 8px; } - -.logs-filter-addon { - background-color: #132235; - border-color: #4a5562; - color: #9fb2c5; -} - -.logs-apply-btn { - min-width: 150px; -} - -.logs-clear-btn { - min-width: 90px; + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-button { + float: none !important; + flex: 0 0 auto; + margin: 0 !important; + min-width: auto; + border-radius: 10px; + border: 1px solid #cbd7e8; + background: #f3f7ff; + color: #22374f; + font-size: 0.82rem; + font-weight: 700; + text-shadow: none; + box-shadow: none; + padding: 8px 14px; + line-height: 1.2; + transition: all 0.16s ease; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-button:focus { + outline: none; + box-shadow: 0 0 0 0.2rem rgba(63, 139, 210, 0.2); +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-button:hover { + background: #eaf2fe; + border-color: #b8cbe5; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-nextbutton, +.introjs-tooltip.introjs-tooltip-onboarding .introjs-donebutton { + order: 2; + margin-left: auto !important; + margin-right: 0 !important; + min-width: auto; + background: #2f7ec1; + border-color: #2f7ec1; + color: #fff; + box-shadow: 0 3px 10px rgba(47, 126, 193, 0.2); +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-nextbutton:hover, +.introjs-tooltip.introjs-tooltip-onboarding .introjs-donebutton:hover { + background: #276da8; + border-color: #276da8; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-prevbutton { + order: 1; + margin-right: auto !important; + background: #edf2fa; + border-color: #c8d4e5; + color: #6e7f97; } + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-skipbutton { + position: absolute !important; + top: 10px !important; + right: 12px !important; + left: auto !important; + float: none !important; + margin: 0 !important; + display: inline-block !important; + align-items: center !important; + color: #6b7280 !important; + font-weight: 700 !important; + font-size: 0.76rem !important; + letter-spacing: 0.015em !important; + text-transform: none !important; + white-space: nowrap !important; + padding: 4px 9px !important; + border: 1px solid #d5dfed !important; + border-radius: 999px !important; + background: #f3f7ff !important; + line-height: 1.2 !important; + text-decoration: none !important; + box-shadow: none !important; + opacity: 1 !important; + transition: all 0.16s ease !important; + z-index: 5 !important; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-skipbutton:hover { + color: #2f7ec1 !important; + border-color: #b8cae3 !important; + background: #eaf2ff !important; + text-decoration: none !important; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-skipbutton:focus { + outline: none !important; + box-shadow: 0 0 0 0.2rem rgba(63, 139, 210, 0.18) !important; + color: #2f7ec1 !important; +} + +.introjs-tooltip.introjs-tooltip-onboarding .introjs-disabled, +.introjs-tooltip.introjs-tooltip-onboarding .introjs-disabled:hover { + opacity: 0.45; + cursor: not-allowed; +} + +@media (max-width: 576px) { + .introjs-tooltip .introjs-tooltip-header { + padding-right: 64px; + } + + .introjs-tooltip .introjs-skipbutton { + top: 8px !important; + right: 8px !important; + font-size: 0.72rem !important; + padding: 4px 8px !important; + } + + .introjs-tooltip.introjs-tooltip-onboarding { + min-width: 280px; + max-width: 320px; + padding: 14px; + } + + .introjs-tooltip.introjs-tooltip-onboarding .introjs-tooltip-title { + font-size: 0.84rem; + padding-right: 64px; + } + + .introjs-tooltip.introjs-tooltip-onboarding .introjs-button { + font-size: 0.8rem; + padding: 8px 12px; + min-width: auto; + } + + .introjs-tooltip.introjs-tooltip-onboarding .introjs-nextbutton, + .introjs-tooltip.introjs-tooltip-onboarding .introjs-donebutton { + min-width: auto; + } + + .introjs-tooltip.introjs-tooltip-onboarding .introjs-skipbutton { + top: 9px !important; + right: 10px !important; + font-size: 0.72rem !important; + padding: 4px 8px !important; + } +} diff --git a/app/controllers/app/budgets_controller.rb b/app/controllers/app/budgets_controller.rb index 615977d..f1b4104 100644 --- a/app/controllers/app/budgets_controller.rb +++ b/app/controllers/app/budgets_controller.rb @@ -27,6 +27,7 @@ def create @budget = base_scope.new(budget_params) if @budget.save + mark_onboarding_step("created_budget") redirect_to app_budgets_path, notice: "Orçamento criado com sucesso." else @budget.service_items.build if @budget.service_items.empty? @@ -73,6 +74,7 @@ def send_for_approval def approve already_linked = @budget.order_service.present? @budget.approve_and_create_order_service!(approver_role: :gestor) + mark_onboarding_step("created_first_work_order") notice = already_linked ? "Orçamento já aprovado. OS vinculada mantida como pendente." : "Orçamento aprovado pelo gestor e OS criada como pendente." redirect_to app_budget_path(@budget), notice: notice rescue ActiveRecord::RecordInvalid => e diff --git a/app/controllers/app/dashboard_controller.rb b/app/controllers/app/dashboard_controller.rb index 1d6e136..1b8ec14 100644 --- a/app/controllers/app/dashboard_controller.rb +++ b/app/controllers/app/dashboard_controller.rb @@ -2,7 +2,8 @@ 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, + "created_budget" => :app_budgets_path, + "created_first_work_order" => :app_budgets_path, "moved_work_order_status" => :app_order_services_path, "viewed_reports" => :app_reports_path }.freeze @@ -184,7 +185,8 @@ def onboarding_checklist_steps [ { key: "created_technician", label: "Cadastrar técnico", path: app_technicians_path }, { key: "created_customer", label: "Cadastrar cliente", path: app_clients_path }, - { key: "created_first_work_order", label: "Criar primeira ordem de serviço", path: app_order_services_path }, + { key: "created_budget", label: "Criar primeiro orçamento", path: app_budgets_path }, + { key: "created_first_work_order", label: "Aprovar orçamento para gerar a primeira OS", path: app_budgets_path }, { key: "moved_work_order_status", label: "Atualizar status da ordem de serviço", path: app_order_services_path }, { key: "viewed_reports", label: "Visualizar relatórios", path: app_reports_path } ] diff --git a/app/controllers/app/order_services_controller.rb b/app/controllers/app/order_services_controller.rb index 847dc00..a0af163 100644 --- a/app/controllers/app/order_services_controller.rb +++ b/app/controllers/app/order_services_controller.rb @@ -62,7 +62,6 @@ def create @order_service.created_without_budget = true if @order_service.update(order_service_params_with_auto_schedule_status) - mark_onboarding_step("created_first_work_order") redirect_to app_order_service_url(@order_service), notice: "Ordem de serviço criada com sucesso." else set_other_resources diff --git a/app/javascript/controllers/onboarding_tour_controller.js b/app/javascript/controllers/onboarding_tour_controller.js index 5c25904..585e653 100644 --- a/app/javascript/controllers/onboarding_tour_controller.js +++ b/app/javascript/controllers/onboarding_tour_controller.js @@ -50,16 +50,20 @@ export default class extends Controller { if (!stepKey) return this.persistLastSeen(stepKey) + this.enforceSkipButtonLayout() }) + this.intro.onafterchange(() => this.enforceSkipButtonLayout()) this.intro.onexit(() => this.clearAutoStartParam()) this.intro.oncomplete(() => this.clearAutoStartParam()) const resumeIndex = this.findResumeIndex(progress?.last_seen_step, builtSteps) this.intro.start() + this.enforceSkipButtonLayout() if (resumeIndex > 0) { this.intro.goToStep(resumeIndex + 1) + this.enforceSkipButtonLayout() } } @@ -151,4 +155,31 @@ export default class extends Controller { url.searchParams.delete("resume_onboarding") window.history.replaceState({}, "", url.toString()) } + + enforceSkipButtonLayout() { + window.requestAnimationFrame(() => { + const tooltip = document.querySelector(".introjs-tooltip") + const skipButton = document.querySelector(".introjs-skipbutton") + const title = document.querySelector(".introjs-tooltip .introjs-tooltip-title") + + if (!tooltip || !skipButton) return + + tooltip.style.position = "relative" + skipButton.style.position = "absolute" + skipButton.style.top = "10px" + skipButton.style.right = "12px" + skipButton.style.left = "auto" + skipButton.style.width = "auto" + skipButton.style.height = "auto" + skipButton.style.lineHeight = "1.2" + skipButton.style.padding = "4px 9px" + skipButton.style.margin = "0" + skipButton.style.display = "inline-block" + skipButton.style.borderRadius = "999px" + skipButton.style.textDecoration = "none" + skipButton.style.zIndex = "20" + + if (title) title.style.paddingRight = "72px" + }) + } } diff --git a/app/models/user_onboarding_progress.rb b/app/models/user_onboarding_progress.rb index 3597cc5..06c19b8 100644 --- a/app/models/user_onboarding_progress.rb +++ b/app/models/user_onboarding_progress.rb @@ -2,6 +2,7 @@ class UserOnboardingProgress < ApplicationRecord STEP_KEYS = %w[ created_technician created_customer + created_budget created_first_work_order moved_work_order_status viewed_reports diff --git a/app/views/app/dashboard/_onboarding_checklist.html.erb b/app/views/app/dashboard/_onboarding_checklist.html.erb index 483d190..8d2f827 100644 --- a/app/views/app/dashboard/_onboarding_checklist.html.erb +++ b/app/views/app/dashboard/_onboarding_checklist.html.erb @@ -10,7 +10,7 @@
Primeiros passos
- 0/5 + 0/6