Correções para funcionamento do login adm
This commit is contained in:
@@ -38,4 +38,4 @@ SMTP_DOMAIN=gade.com.br
|
||||
|
||||
# ── App ───────────────────────────────────────────────────────
|
||||
APP_HOST=localhost:3000
|
||||
APP_NAME=Gade Logística
|
||||
APP_NAME=Reem Logística
|
||||
|
||||
125
CLAUDE.md
Normal file
125
CLAUDE.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project overview
|
||||
|
||||
**Reem Logística** is a Rails 7 web application for controlling delivery costs for **Gade Hospitalar** (account_id: 95907). It manages consolidations of driver payments across delivery routes (UBS SUL, UBS LESTE, EMAD, STS, SAD, etc.).
|
||||
|
||||
## Commands
|
||||
|
||||
All commands run inside Docker. The PostgreSQL database lives externally (not in docker-compose).
|
||||
|
||||
```bash
|
||||
# Start
|
||||
docker-compose up -d
|
||||
|
||||
# Rails console
|
||||
docker-compose exec app bundle exec rails console
|
||||
|
||||
# Run migrations
|
||||
docker-compose exec app bundle exec rails db:migrate
|
||||
|
||||
# Seed (creates admin + test drivers)
|
||||
docker-compose exec app bundle exec rails db:seed
|
||||
|
||||
# Logs
|
||||
docker-compose logs -f app
|
||||
|
||||
# Tests (RSpec)
|
||||
docker-compose exec app bundle exec rspec
|
||||
docker-compose exec app bundle exec rspec spec/models/consolidacao_spec.rb # single file
|
||||
|
||||
# Rake task (runs manually what the cron job runs hourly)
|
||||
docker-compose exec app bundle exec rake historico:atualizar
|
||||
|
||||
# Activate the hourly cron job on the server
|
||||
docker-compose exec app bundle exec whenever --update-crontab
|
||||
```
|
||||
|
||||
## Setup from scratch
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env: set DB_HOST, DB_PASSWORD, and generate SECRET_KEY_BASE with:
|
||||
# openssl rand -hex 64
|
||||
docker-compose build --no-cache
|
||||
docker-compose up -d
|
||||
docker-compose exec app bundle exec rails db:create db:migrate db:seed
|
||||
```
|
||||
|
||||
Default credentials (change in production): `admin@gade.com` / `Gade@2026!`
|
||||
|
||||
## Critical constraint: read-only external table
|
||||
|
||||
The table `public.db_reem_simplerout_2026` is owned by an external system (SimpleRoute) that updates it every hour. **NEVER** create migrations for it, and never run INSERT/UPDATE/DELETE/DROP/TRUNCATE on it.
|
||||
|
||||
`Entrega` (app/models/entrega.rb) maps to this table and enforces `readonly? = true`. All queries go through its scopes — the most important being `.pagas` (`status='completed' AND checkin IS NOT NULL`), which defines payment eligibility.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data flow
|
||||
|
||||
1. External system writes to `db_reem_simplerout_2026` hourly
|
||||
2. `Atualizar HistoricoEstimadoJob` (triggered by Whenever cron every hour) counts paid deliveries per driver and writes to `historico_estimados`
|
||||
3. Admins/managers create **Consolidações** (payment batches) covering a date range + list of drivers
|
||||
4. A 3-step wizard classifies each delivery per driver (Normal/Retirada/Bônus/Desconto toggles)
|
||||
5. Finalizing a consolidation sends WhatsApp (Twilio) and email notifications to drivers
|
||||
6. Drivers view their own panel at `/motorista` using a 4-digit PIN or QR code from their printed payslip
|
||||
|
||||
### Key models
|
||||
|
||||
| Model | Table | Notes |
|
||||
|---|---|---|
|
||||
| `Entrega` | `db_reem_simplerout_2026` | Read-only. External data. |
|
||||
| `Consolidacao` | `consolidacoes` | Payment batch. Enum status: `rascunho/finalizada/arquivada`. Soft-delete via `deleted_at`. |
|
||||
| `ConsolidacaoMotorista` | `consolidacao_motoristas` | Join: which drivers are in a consolidation. |
|
||||
| `ConsolidacaoEntrega` | `consolidacao_entregas` | Each classified delivery. Enum tipo: `normal/retirada/bonus/desconto`. |
|
||||
| `HistoricoEstimado` | `historico_estimados` | Snapshot of hourly estimated values per driver. |
|
||||
| `Configuracao` | `configuracoes` | Key-value store for prices and feature flags. Access via `Configuracao.valor('chave')`. |
|
||||
| `AuditoriaLog` | `auditoria_logs` | Audit trail. Controllers include `Auditavel` concern and call `auditar!(:acao, registro)`. |
|
||||
| `User` | `users` | Devise auth. Roles: `admin/gerente/operador/motorista`. Drivers may have no email — they log in via `pin_code` (4 digits, unique per driver) or `login_token` (QR). |
|
||||
|
||||
### Authorization
|
||||
|
||||
Pundit policies in `app/policies/`. `ApplicationController` includes `Pundit::Authorization` and calls `authenticate_user!` on every action. The motorista panel (`/motorista/**`) uses a separate session mechanism (`motorista/sessoes_controller.rb`) — drivers are NOT authenticated via Devise.
|
||||
|
||||
### Two login flows for drivers
|
||||
|
||||
- `/auth/login` (Devise, Fase 2) — PIN tab on the standard login page
|
||||
- `/motorista/login` (Fase 8) — dedicated numeric keypad, used by QR codes printed on payslips. Prefer this for mobile. Both use `pin_code`.
|
||||
|
||||
### PDF generation
|
||||
|
||||
`app/services/pdf/` contains three classes:
|
||||
- `BasePdf` — shared black/orange header and footer layout
|
||||
- `RelatorioMotoristaPdf` — detailed delivery table per driver
|
||||
- `HoleritePdf` — payslip style + embedded QR code (via `rqrcode`/`chunky_png`) linking to `/motorista/acesso/:token`
|
||||
|
||||
### The consolidation wizard
|
||||
|
||||
`/consolidacoes/:id/wizard` → `/consolidacao_entregas/validar` → `/consolidacao_entregas/revisar`
|
||||
|
||||
- **Passo 1** (wizard): pick a driver. Shows progress per driver.
|
||||
- **Passo 2** (validar): color-coded toggle buttons per delivery (orange=Normal, dark orange=Retirada, white=Bônus, black=Desconto). Toggle individual or mass-classify. Stimulus controller (`validacao_controller.js`) handles real-time totals and progress.
|
||||
- **Passo 3** (revisar): summary for the selected driver, then move to the next driver or save as draft.
|
||||
|
||||
Finalizing is blocked (server-side + UI) until all drivers have all eligible deliveries classified.
|
||||
|
||||
### Notifications (NotificacaoService)
|
||||
|
||||
Controlled by `Configuracao` flags `notificacao_whatsapp` and `notificacao_email`. Failures are rescued and logged — they never abort the finalization. WhatsApp requires Twilio ENV vars + driver `telefone` field in `+5511999998888` format.
|
||||
|
||||
## Design system
|
||||
|
||||
Dark theme (default). Colors: background `#0a0a0a`, cards `#1a1a1a`, accent/buttons `#f97316` (orange), text `#ffffff`, success `#22c55e`, error `#ef4444`. Minimum font 16px desktop / 18px mobile. Touch targets minimum 48px height.
|
||||
|
||||
## Environment variables
|
||||
|
||||
All config comes from `.env` (never hardcoded). Key variables:
|
||||
- `DB_HOST` / `DB_NAME` / `DB_USER` / `DB_PASSWORD` — PostgreSQL connection
|
||||
- `DB_EXISTING_ACCOUNT_ID` — filters `db_reem_simplerout_2026` to Gade's account (default: 95907)
|
||||
- `SECRET_KEY_BASE` — generate with `openssl rand -hex 64`
|
||||
- `APP_HOST` — used in QR code URLs and notification links
|
||||
- `TWILIO_*` — WhatsApp notifications (optional)
|
||||
- `SMTP_*` — Email notifications (optional, only activates if `SMTP_USERNAME` is set)
|
||||
@@ -1,4 +1,4 @@
|
||||
# 🚛 Gade Logística — Sistema de Controle de Custos
|
||||
# 🚛 Reem Logística — Sistema de Controle de Custos
|
||||
|
||||
Sistema web para controle de custos de entregas hospitalares da **Gade Hospitalar**.
|
||||
|
||||
|
||||
2
Rakefile
Normal file
2
Rakefile
Normal file
@@ -0,0 +1,2 @@
|
||||
require_relative "config/application"
|
||||
Rails.application.load_tasks
|
||||
@@ -4,7 +4,7 @@ class Admin::UsuariosController < ApplicationController
|
||||
|
||||
def index
|
||||
authorize User
|
||||
@usuarios = policy_scope(User).order(:nome).page(params[:page]).per(20)
|
||||
@usuarios = policy_scope(User).order(:nome).limit(200)
|
||||
@roles = User.roles.keys
|
||||
end
|
||||
|
||||
|
||||
@@ -47,14 +47,15 @@ class DashboardController < ApplicationController
|
||||
.count
|
||||
.sort_by { |_, v| -v }
|
||||
.first(6)
|
||||
.to_h
|
||||
|
||||
# Evolução diária do mês (para Chart.js)
|
||||
@grafico_diario = build_grafico_diario(entregas_mes, config[:entrega])
|
||||
|
||||
# Consolidações do mês
|
||||
@consolidacoes_mes = Consolidacao.do_mes(@data_referencia)
|
||||
@consolidacoes_abertas = @consolidacoes_mes.where(status: :aberta).count
|
||||
@consolidacoes_fechadas = @consolidacoes_mes.where(status: :fechada).count
|
||||
@consolidacoes_mes = Consolidacao.ativas.where(created_at: @mes_inicio.beginning_of_day..@mes_fim.end_of_day)
|
||||
@consolidacoes_abertas = @consolidacoes_mes.where(status: :rascunho).count
|
||||
@consolidacoes_fechadas = @consolidacoes_mes.where(status: :finalizada).count
|
||||
|
||||
# Histórico estimado mais recente
|
||||
@historico_recente = HistoricoEstimado.order(created_at: :desc).limit(5)
|
||||
|
||||
@@ -23,7 +23,7 @@ class Users::SessionsController < Devise::SessionsController
|
||||
def after_sign_in_path_for(resource)
|
||||
case resource.role
|
||||
when 'motorista'
|
||||
motorista_path
|
||||
motorista_dashboard_path
|
||||
else
|
||||
dashboard_path
|
||||
end
|
||||
@@ -40,13 +40,13 @@ class Users::SessionsController < Devise::SessionsController
|
||||
private
|
||||
|
||||
def handle_pin_login
|
||||
pin = params[:user][:pin].to_s.strip
|
||||
pin = params[:pin].to_s.strip
|
||||
user = User.find_by(pin_code: pin, role: 'motorista')
|
||||
|
||||
if user&.active?
|
||||
if user&.ativo?
|
||||
sign_in(user)
|
||||
AuditoriaLog.registrar(user: user, acao: 'login_pin', entidade: 'User', dados_novos: { detalhes: "" }, request: request)
|
||||
redirect_to motorista_path, notice: "Bem-vindo, #{user.nome_display}!"
|
||||
redirect_to motorista_dashboard_path, notice: "Bem-vindo, #{user.nome_display}!"
|
||||
else
|
||||
flash.now[:alert] = 'PIN inválido ou motorista inativo.'
|
||||
@pin_login = true
|
||||
|
||||
3
app/models/application_record.rb
Normal file
3
app/models/application_record.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
primary_abstract_class
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# app/models/entrega.rb
|
||||
#
|
||||
# Model de LEITURA para a tabela existente da Gade Hospitalar.
|
||||
# Model de LEITURA para a tabela existente da Reem Transporte.
|
||||
# NUNCA criar migration para esta tabela.
|
||||
# NUNCA executar INSERT, UPDATE, DELETE ou DROP nesta tabela.
|
||||
#
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# app/models/user.rb
|
||||
class User < ApplicationRecord
|
||||
devise :database_authenticatable,
|
||||
:registerable,
|
||||
|
||||
:recoverable,
|
||||
:rememberable,
|
||||
:validatable
|
||||
|
||||
@@ -30,7 +30,7 @@ module Pdf
|
||||
@pdf.fill_rectangle [-40 + 0, @pdf.cursor + 40], 8, 70 # faixa laranja lateral
|
||||
|
||||
@pdf.fill_color 'FFFFFF'
|
||||
@pdf.text_box 'GADE HOSPITALAR',
|
||||
@pdf.text_box 'REEM TRANSPORTE',
|
||||
at: [10, @pdf.cursor + 20], size: 20, style: :bold
|
||||
@pdf.fill_color LARANJA
|
||||
@pdf.text_box 'Sistema de Controle de Custos Logísticos',
|
||||
@@ -44,7 +44,7 @@ module Pdf
|
||||
@pdf.repeat(:all) do
|
||||
@pdf.bounding_box [0, 20], width: @pdf.bounds.width do
|
||||
@pdf.fill_color CINZA
|
||||
@pdf.text "Gerado em #{Time.current.strftime('%d/%m/%Y %H:%M')} · Gade Hospitalar — documento interno",
|
||||
@pdf.text "Gerado em #{Time.current.strftime('%d/%m/%Y %H:%M')} · Reem Transporte — documento interno",
|
||||
size: 8, align: :center
|
||||
@pdf.fill_color '000000'
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
<div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5">
|
||||
<p class="text-gray-400 text-xs mb-1">Account ID</p>
|
||||
<p class="text-white font-mono text-sm">95907 (Gade Hospitalar)</p>
|
||||
<p class="text-white font-mono text-sm">95907 (Reem Transporte)</p>
|
||||
</div>
|
||||
<div class="p-4 bg-[#0a0a0a] rounded-xl border border-white/5">
|
||||
<p class="text-gray-400 text-xs mb-1">Atualização</p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div style="font-family: Arial, sans-serif; max-width: 520px; margin: 0 auto; background: #0a0a0a; border-radius: 12px; overflow: hidden;">
|
||||
|
||||
<div style="background: #0a0a0a; padding: 24px; border-left: 6px solid #f97316;">
|
||||
<h1 style="color: #ffffff; margin: 0; font-size: 20px;">GADE HOSPITALAR</h1>
|
||||
<h1 style="color: #ffffff; margin: 0; font-size: 20px;">REEM TRANSPORTE</h1>
|
||||
<p style="color: #f97316; margin: 4px 0 0; font-size: 13px;">Sistema de Logística</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="bg-[#0a0a0a] rounded-lg p-4 mb-5 flex items-center gap-3">
|
||||
<div class="w-2 h-12 bg-orange-500 rounded"></div>
|
||||
<div>
|
||||
<p class="text-white font-black text-lg leading-tight">GADE HOSPITALAR</p>
|
||||
<p class="text-white font-black text-lg leading-tight">REEM TRANSPORTE</p>
|
||||
<p class="text-orange-500 text-xs">Holerite de Pagamento — Entregas</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
44
app/views/devise/passwords/edit.html.erb
Normal file
44
app/views/devise/passwords/edit.html.erb
Normal file
@@ -0,0 +1,44 @@
|
||||
<%# app/views/devise/passwords/edit.html.erb — Redefinir senha (link do e-mail) %>
|
||||
<% content_for :title, 'Nova senha' %>
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
<div class="w-full max-w-sm">
|
||||
|
||||
<div class="text-center mb-8">
|
||||
<div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3">
|
||||
<span class="text-3xl">🔒</span>
|
||||
</div>
|
||||
<h1 class="text-white font-bold text-2xl">Definir nova senha</h1>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#111111] border border-white/10 rounded-2xl p-8">
|
||||
<%= form_with(url: user_password_path, scope: :user, method: :put, data: { turbo: false }) do |f| %>
|
||||
<%= f.hidden_field :reset_password_token, value: params[:reset_password_token] %>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<%= f.label :password, 'Nova senha', class: 'block text-sm font-medium text-gray-300 mb-1.5' %>
|
||||
<%= f.password_field :password, autofocus: true, autocomplete: 'new-password',
|
||||
class: 'w-full px-4 py-3 bg-[#0a0a0a] border border-white/10 rounded-xl text-white
|
||||
focus:outline-none focus:border-[#f97316] focus:ring-1 focus:ring-[#f97316] text-base' %>
|
||||
<p class="text-gray-600 text-xs mt-1">Mínimo de 6 caracteres</p>
|
||||
</div>
|
||||
<div>
|
||||
<%= f.label :password_confirmation, 'Confirmar nova senha', class: 'block text-sm font-medium text-gray-300 mb-1.5' %>
|
||||
<%= f.password_field :password_confirmation, autocomplete: 'new-password',
|
||||
class: 'w-full px-4 py-3 bg-[#0a0a0a] border border-white/10 rounded-xl text-white
|
||||
focus:outline-none focus:border-[#f97316] focus:ring-1 focus:ring-[#f97316] text-base' %>
|
||||
</div>
|
||||
|
||||
<%= f.submit 'Salvar nova senha',
|
||||
class: 'w-full py-3.5 bg-[#f97316] hover:bg-orange-500 text-white font-semibold
|
||||
rounded-xl transition-colors cursor-pointer text-base min-h-[48px]' %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p class="text-center mt-6">
|
||||
<%= link_to '← Voltar para o login', new_user_session_path,
|
||||
class: 'text-sm text-[#f97316] hover:text-orange-400 transition-colors' %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
47
app/views/devise/passwords/new.html.erb
Normal file
47
app/views/devise/passwords/new.html.erb
Normal file
@@ -0,0 +1,47 @@
|
||||
<%# app/views/devise/passwords/new.html.erb — Esqueci a senha (estilizada) %>
|
||||
<% content_for :title, 'Recuperar senha' %>
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
<div class="w-full max-w-sm">
|
||||
|
||||
<div class="text-center mb-8">
|
||||
<div class="w-16 h-16 bg-[#f97316] rounded-2xl flex items-center justify-center mx-auto mb-3">
|
||||
<span class="text-3xl">🔑</span>
|
||||
</div>
|
||||
<h1 class="text-white font-bold text-2xl">Recuperar senha</h1>
|
||||
<p class="text-gray-400 text-sm mt-1">Informe seu e-mail para receber as instruções</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-[#111111] border border-white/10 rounded-2xl p-8">
|
||||
<%= form_with(url: user_password_path, scope: :user, method: :post, data: { turbo: false }) do |f| %>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<%= f.label :email, 'E-mail', class: 'block text-sm font-medium text-gray-300 mb-1.5' %>
|
||||
<%= f.email_field :email,
|
||||
autofocus: true, autocomplete: 'email',
|
||||
class: 'w-full px-4 py-3 bg-[#0a0a0a] border border-white/10 rounded-xl text-white
|
||||
placeholder-gray-600 focus:outline-none focus:border-[#f97316] focus:ring-1
|
||||
focus:ring-[#f97316] transition-colors text-base',
|
||||
placeholder: 'seu@email.com' %>
|
||||
</div>
|
||||
|
||||
<%= f.submit 'Enviar instruções',
|
||||
class: 'w-full py-3.5 bg-[#f97316] hover:bg-orange-500 text-white font-semibold
|
||||
rounded-xl transition-colors cursor-pointer text-base min-h-[48px]' %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p class="text-center mt-6">
|
||||
<%= link_to '← Voltar para o login', new_user_session_path,
|
||||
class: 'text-sm text-[#f97316] hover:text-orange-400 transition-colors' %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-600 text-xs text-center mt-6">
|
||||
⚠️ O envio de e-mail depende da configuração SMTP no servidor.<br>
|
||||
Sem SMTP, peça ao administrador para redefinir sua senha.
|
||||
</p>
|
||||
|
||||
<p class="text-gray-500 text-xs text-center mt-4">Reem Transporte © <%= Date.today.year %></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -10,7 +10,7 @@
|
||||
d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-white">Gade Logística</h1>
|
||||
<h1 class="text-3xl font-bold text-white">Reem Logística</h1>
|
||||
<p class="text-gray-400 mt-1 text-sm">Sistema de Controle de Custos</p>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<%# Login por e-mail %>
|
||||
<div id="form-email" class="p-8">
|
||||
<%= form_with(url: user_session_path, method: :post) do |f| %>
|
||||
<%= form_with(url: user_session_path, scope: :user, method: :post, data: { turbo: false }) do |f| %>
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<%= f.label :email, 'E-mail', class: 'block text-sm font-medium text-gray-300 mb-1.5' %>
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<%# Login por PIN %>
|
||||
<div id="form-pin" class="p-8 hidden">
|
||||
<%= form_with(url: user_session_path, method: :post) do |f| %>
|
||||
<%= form_with(url: user_session_path, method: :post, data: { turbo: false }) do |f| %>
|
||||
<%= f.hidden_field :pin_login, value: '1' %>
|
||||
|
||||
<div class="space-y-6">
|
||||
@@ -148,7 +148,7 @@
|
||||
</div>
|
||||
|
||||
<p class="text-center text-gray-600 text-xs mt-6">
|
||||
Gade Hospitalar © <%= Date.today.year %>
|
||||
Reem Transporte © <%= Date.today.year %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="flex items-center gap-3 px-6 py-5 border-b border-[#2a2a2a]">
|
||||
<div class="w-9 h-9 bg-orange-500 rounded-lg flex items-center justify-center font-black text-black text-lg">G</div>
|
||||
<div>
|
||||
<p class="font-bold text-white text-sm leading-tight">Gade Hospitalar</p>
|
||||
<p class="font-bold text-white text-sm leading-tight">Reem Transporte</p>
|
||||
<p class="text-orange-500 text-xs">Logística</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-7 h-7 bg-orange-500 rounded flex items-center justify-center font-black text-black text-sm">G</div>
|
||||
<span class="font-bold text-white text-sm">Gade Logística</span>
|
||||
<span class="font-bold text-white text-sm">Reem Logística</span>
|
||||
</div>
|
||||
<div class="w-12"></div>
|
||||
</header>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-param" content="<%= request_forgery_protection_token %>">
|
||||
<meta name="csrf-token" content="<%= form_authenticity_token %>">
|
||||
<title><%= content_for?(:title) ? "#{yield(:title)} | Gade Logística" : "Gade Logística" %></title>
|
||||
<title><%= content_for?(:title) ? "#{yield(:title)} | Reem Logística" : "Reem Logística" %></title>
|
||||
|
||||
<%# Tailwind via CDN em desenvolvimento — compilar em produção %>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<%# Logo %>
|
||||
<div class="text-center mb-8">
|
||||
<div class="w-16 h-16 bg-orange-500 rounded-2xl flex items-center justify-center font-black text-black text-3xl mx-auto mb-3">G</div>
|
||||
<h1 class="text-white font-bold text-2xl">Gade Logística</h1>
|
||||
<h1 class="text-white font-bold text-2xl">Reem Logística</h1>
|
||||
<p class="text-gray-400 text-base mt-1">Digite seu PIN de 4 números</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ require "rails/all"
|
||||
Bundler.require(*Rails.groups)
|
||||
require "dotenv/load" if defined?(Dotenv)
|
||||
|
||||
module GadeLogistica
|
||||
module ReemLogistica
|
||||
class Application < Rails::Application
|
||||
config.load_defaults 7.1
|
||||
|
||||
|
||||
@@ -11,6 +11,4 @@ Devise.setup do |config|
|
||||
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
|
||||
config.reset_password_within = 6.hours
|
||||
config.sign_out_via = :delete
|
||||
config.responder.error_status = :unprocessable_entity
|
||||
config.responder.redirect_status = :see_other
|
||||
end
|
||||
|
||||
6
config/initializers/inflections.rb
Normal file
6
config/initializers/inflections.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# Ensina ao Rails os plurais corretos em português.
|
||||
# Sem isso: consolidacao → "consolidacaos" e configuracao → "configuracaos"
|
||||
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||
inflect.irregular 'consolidacao', 'consolidacoes'
|
||||
inflect.irregular 'configuracao', 'configuracoes'
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
class CreateConsolidacaoMotoristas < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :consolidacao_motoristas do |t|
|
||||
t.references :consolidacao, null: false, foreign_key: true
|
||||
t.references :consolidacao, null: false, foreign_key: { to_table: :consolidacoes }
|
||||
t.string :motorista_nome, null: false # driver da tabela existente
|
||||
t.decimal :valor_total, precision: 10, scale: 2, default: 0
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class CreateConsolidacaoEntregas < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :consolidacao_entregas do |t|
|
||||
t.references :consolidacao, null: false, foreign_key: true
|
||||
t.references :consolidacao, null: false, foreign_key: { to_table: :consolidacoes }
|
||||
t.string :tracking_id, null: false # FK lógico para db_reem_simplerout_2026
|
||||
t.string :motorista_nome, null: false
|
||||
t.integer :tipo, null: false, default: 0
|
||||
|
||||
12
db/seeds.rb
12
db/seeds.rb
@@ -1,5 +1,5 @@
|
||||
# db/seeds.rb
|
||||
# Dados iniciais do sistema Gade Hospitalar
|
||||
# Dados iniciais do sistema Reem Transporte
|
||||
# Execute com: bundle exec rails db:seed
|
||||
|
||||
puts "🌱 Criando dados iniciais..."
|
||||
@@ -30,7 +30,7 @@ configs = [
|
||||
{ chave: 'preco_desconto', valor: '15.00', descricao: 'Valor descontado por problema (R$)' },
|
||||
{ chave: 'notificacao_whatsapp', valor: 'false', descricao: 'Notificações via WhatsApp (true/false)' },
|
||||
{ chave: 'notificacao_email', valor: 'false', descricao: 'Notificações via e-mail (true/false)' },
|
||||
{ chave: 'empresa_nome', valor: 'Gade Hospitalar', descricao: 'Nome da empresa exibido no sistema' },
|
||||
{ chave: 'empresa_nome', valor: 'Reem Transporte', descricao: 'Nome da empresa exibido no sistema' },
|
||||
]
|
||||
|
||||
configs.each do |cfg|
|
||||
@@ -52,9 +52,9 @@ puts " ⚠️ ALTERE A SENHA DO ADMIN NO PRIMEIRO ACESSO!"
|
||||
{ nome: 'Maria Santos', pin: '5678' },
|
||||
{ nome: 'Carlos Souza', pin: '9012' },
|
||||
].each do |m|
|
||||
User.find_or_create_by!(nome: m[:nome], role: :motorista) do |u|
|
||||
u.pin_code = m[:pin]
|
||||
u.ativo = true
|
||||
end
|
||||
u = User.find_or_initialize_by(nome: m[:nome], role: :motorista)
|
||||
u.pin_code = m[:pin]
|
||||
u.ativo = true
|
||||
u.save!(validate: false)
|
||||
puts " ✅ Motorista: #{m[:nome]} / PIN: #{m[:pin]}"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user