Files
techxcar/docs/superpowers/specs/2026-06-16-techxcar-design.md
T
Luciano Milani 5de37bb512 Inicial
2026-07-02 12:47:55 +01:00

12 KiB
Raw Blame History

TechXCar — Design Spec

Data: 2026-06-16 Versão: 1.0


1. Visão Geral

TechXCar é um SaaS multi-tenant de gestão de oficina automóvel. Cada oficina tem o seu espaço isolado com clientes, veículos, ordens de trabalho, faturação, técnicos e relatórios. A plataforma é gerida por um super-admin que cria e convida oficinas manualmente.

Inspiração: LubeLogger, mas focado na gestão operacional da oficina (não no diário do proprietário do veículo).


2. Stack Técnica

Camada Tecnologia
Frontend React 19 + TypeScript + Vite
Backend Go + Fiber (framework HTTP)
Base de dados PostgreSQL 16
Cache / Filas Redis 7
Auth JWT (access 15min + refresh 30d, httpOnly cookie)
Passwords bcrypt cost 12
PDF Geração server-side em Go (html/template → PDF)
Notificações Telegram Bot API + SMTP
Frontend UI Tailwind CSS 4 + shadcn/ui
Estado cliente TanStack Query + Zustand
Formulários React Hook Form + Zod
Roteamento React Router v7
Migrations golang-migrate
Deploy Docker Compose → Coolify

3. Arquitectura de Deploy

Serviços Docker

techxcar/
├── frontend/          # React 19 + TypeScript + Vite (servido via Nginx)
├── backend/           # Go + Fiber
├── docker-compose.yml         # Desenvolvimento
└── docker-compose.prod.yml    # Produção (Coolify)

Quatro serviços:

  • frontend — Nginx a servir o build Vite na porta 80
  • backend — Binário Go na porta 8080
  • postgres — PostgreSQL 16
  • redis — Redis 7

Configuração em dois níveis

  1. .env — valores de arranque imutáveis em runtime: DATABASE_URL, REDIS_URL, JWT_SECRET, PORT, APP_ENV. Requerem reinício do container para alterar.
  2. platform_settings / tenant_settings (DB) — configuração dinâmica gerida pela Web UI sem redeploy: SMTP, Telegram Bot Token, templates de notificação, limites, dados de faturação. Cached em Redis por 5 minutos.

Coolify

  • Secrets geridos pelo Coolify (injectados como variáveis de ambiente)
  • HTTPS automático via Coolify (Let's Encrypt)
  • Health checks em /api/v1/health

4. Multi-tenancy

Estratégia: Schema-per-tenant no PostgreSQL

  • Schema public: tabelas globais (tenants, invites, super_admins, platform_settings)
  • Schema tenant_<uuid>: dados isolados de cada oficina
  • Middleware no backend define SET search_path = tenant_<uuid> por request, com base no JWT

Onboarding de oficinas

Via convite: Super-admin gera token assinado com expiração configurável. Oficina clica no link, preenche nome, NIF, email, password → schema PostgreSQL provisionado automaticamente via migrations.

Via criação manual: Super-admin cria a oficina no painel /admin, define credenciais iniciais, envia email de boas-vindas.


5. Autenticação & Autorização

Níveis de acesso

super_admin           — gestão da plataforma, todos os tenants
tenant_admin          — dono/gestor principal da oficina
manager               — gestor operacional, sem acesso a settings financeiras
technician            — técnico interno, acesso a OTs atribuídas
external_technician   — sem login; apenas para atribuição e registo de custos

Fluxo JWT

  • POST /api/v1/auth/login → devolve access_token (body) + refresh_token (httpOnly cookie)
  • POST /api/v1/auth/refresh → renova access token usando refresh token do cookie
  • POST /api/v1/auth/logout → invalida refresh token
  • Rate limiting: 10 tentativas/min por IP nas rotas de auth (Redis)

6. Módulos de Negócio

6.1 Clientes & Veículos

  • Cliente: nome, NIF, telefone, email, morada, notas
  • Veículo: pertence a um cliente (opcional), matrícula, marca, modelo, ano, VIN, km actual
  • Um cliente pode ter N veículos
  • OT pode ser criada sem cliente nem veículo (cliente temporário)

6.2 Ordens de Trabalho (OT)

Estados e transições:

Aberta → Em Progresso → Concluída → Faturada
                    ↓
                Cancelada (de qualquer estado excepto Faturada)

Conteúdo de uma OT:

  • Cliente + Veículo (ambos opcionais)
  • Items: serviços/peças do catálogo (qty, preço unitário, desconto por item)
  • Técnico(s) atribuído(s) + registo de horas (horas × custo/hora)
  • Notas internas (não visíveis ao cliente)
  • Notas para o cliente (aparecem no PDF)
  • Log de estados: timestamp + utilizador responsável por cada transição
  • Total calculado: soma de items + mão de obra

6.3 Catálogo de Serviços & Peças

  • Item: código, nome, categoria, unidade (un/hora/litro/kg), preço base, activo/inactivo
  • Categorias livres (criadas pela oficina)
  • Ao adicionar a OT: preço copiado do catálogo mas editável por OT
  • Pesquisa por código ou nome no momento de adição à OT

6.4 Faturação & Orçamentos

  • Orçamento: gerado a partir de OT em estado "Aberta", PDF com logo e dados da oficina
  • Fatura: gerada quando OT passa a "Faturada", numeração sequencial por oficina
  • PDFs gerados server-side em Go com chromedp (render HTML → PDF via Chrome headless); armazenados localmente (volume Docker) ou S3-compatible (configurável nas Settings)
  • Dados da oficina para PDF: logo, nome, NIF, morada, IBAN — configuráveis nas Settings da Web UI

6.5 Técnicos (Staff)

  • Interno: tem conta no sistema (role technician), aparece na atribuição de OTs
  • Externo: registo simplificado (nome, contacto, custo/hora), sem login
  • Registo de horas por OT: horas trabalhadas × custo/hora = custo de mão de obra
  • Listagem com estado activo/inactivo

6.6 Combustíveis & Despesas da Oficina

  • Registo: data, tipo (combustível / peças / ferramentas / outros), valor, descrição, veículo (opcional)
  • Filtros por período e tipo
  • Totais agregados no dashboard e relatórios

6.7 Notificações ao Cliente

  • Telegram: token do bot configurável nas Settings; envia mensagem quando OT muda para "Concluída" ou "Faturada"
  • Email: SMTP configurável nas Settings; template HTML editável na Web UI
  • Configurável por oficina: quais eventos disparam notificação e qual canal
  • Envio assíncrono: o handler HTTP publica um job numa lista Redis; uma goroutine dedicada consome a lista e envia as notificações (não bloqueia o request HTTP)

6.8 Dashboard & Relatórios

Dashboard (tempo real):

  • Receita do mês actual vs mês anterior
  • Nº de OTs por estado (cards)
  • OTs abertas recentes (tabela)
  • Serviços mais realizados (top 5)

Relatórios (com filtro de período):

  • Receita por período (dia/semana/mês)
  • OTs por estado e por técnico
  • Despesas vs Receita
  • Exportação em PDF e CSV

7. Estrutura do Backend Go

backend/
├── cmd/server/main.go
├── internal/
│   ├── auth/           # JWT, middleware, bcrypt
│   ├── tenant/         # Gestão tenants, schema provisioning
│   ├── workorder/      # Ordens de trabalho
│   ├── client/         # Clientes & veículos
│   ├── catalog/        # Serviços & peças
│   ├── invoice/        # Faturação, geração PDF
│   ├── staff/          # Técnicos
│   ├── expense/        # Combustíveis & despesas
│   ├── notification/   # Telegram + Email (worker assíncrono)
│   ├── report/         # Relatórios & exportações
│   └── settings/       # Settings dinâmicas (DB + cache Redis)
├── pkg/
│   ├── database/       # Pool PostgreSQL, migrations
│   ├── redis/          # Cliente Redis
│   └── pdf/            # Geração PDF
└── migrations/
    ├── public/         # Schema público
    └── tenant/         # Schema tenant (aplicado no provisionamento)

API

  • Base: /api/v1/
  • Autenticação: Bearer token no header Authorization
  • Tenant resolvido via claim tenant_id no JWT
  • Respostas: { "data": ..., "error": null, "meta": { "page": ..., "total": ... } }
  • Paginação cursor-based

8. Estrutura do Frontend React

frontend/
├── src/
│   ├── pages/
│   │   ├── auth/           # Login
│   │   ├── admin/          # Super-admin (dashboard, tenants, settings)
│   │   └── app/            # Área da oficina
│   │       ├── dashboard/
│   │       ├── work-orders/
│   │       ├── clients/
│   │       ├── vehicles/
│   │       ├── catalog/
│   │       ├── staff/
│   │       ├── expenses/
│   │       ├── reports/
│   │       └── settings/
│   ├── components/
│   │   ├── ui/             # shadcn/ui base components
│   │   └── shared/         # Componentes reutilizáveis da app
│   ├── hooks/              # Custom hooks (useAuth, useTenant, etc.)
│   ├── lib/
│   │   ├── api.ts          # Cliente HTTP (fetch + interceptors)
│   │   └── queryClient.ts  # TanStack Query config
│   ├── store/              # Zustand stores
│   └── i18n/               # Estrutura i18n (PT por defeito, EN preparado)

Rotas protegidas

  • /login — público
  • /admin/* — requer role super_admin
  • /app/* — requer role tenant_admin, manager ou technician
  • Guards no React Router v7 redireccionam para /login se JWT expirado

9. Base de Dados — Schemas

Schema public

tenants           (id uuid PK, slug text UNIQUE, name text, status, plan, created_at)
invites           (id uuid PK, tenant_id uuid FK, token text UNIQUE, expires_at, used_at nullable)
super_admins      (id uuid PK, email text UNIQUE, password_hash text, created_at)
platform_settings (key text PK, value text, updated_at)

Schema tenant_<uuid> (por oficina)

users             (id uuid PK, email, password_hash, role, name, active)
clients           (id uuid PK, name, nif, phone, email, address, notes, created_at)
vehicles          (id uuid PK, client_id uuid nullable FK, plate, brand, model, year, vin, mileage, notes)
work_orders       (id uuid PK, number serial, client_id nullable, vehicle_id nullable, status, internal_notes, client_notes, created_by, created_at, updated_at)
wo_items          (id uuid PK, work_order_id FK, catalog_item_id nullable FK, description, qty, unit_price, discount_pct, total)
wo_staff_hours    (id uuid PK, work_order_id FK, staff_id FK, hours, cost_per_hour, total)
wo_status_log     (id uuid PK, work_order_id FK, from_status, to_status, changed_by FK, changed_at)
catalog_items     (id uuid PK, code, name, category, unit, base_price, active)
invoices          (id uuid PK, work_order_id FK, type: quote|invoice, number serial, pdf_path, issued_at)
staff             (id uuid PK, user_id nullable FK, name, email, phone, type: internal|external, hourly_rate, active)
expenses          (id uuid PK, vehicle_id nullable FK, type, amount, description, date)
tenant_settings   (key text PK, value text, updated_at)

10. Internacionalização (i18n)

  • Estrutura i18n no frontend desde o início (ficheiros JSON por locale em src/i18n/)
  • Idioma padrão e único no MVP: Português de Portugal (pt-PT)
  • Inglês (en) preparado na estrutura mas não traduzido no MVP
  • Datas, moeda e números formatados com Intl API (locale pt-PT, moeda EUR)

11. Segurança

  • HTTPS obrigatório em produção (Coolify / Let's Encrypt)
  • JWT com expiração curta + refresh token rotativo em httpOnly cookie
  • Passwords com bcrypt cost 12
  • Rate limiting em todas as rotas de auth (Redis)
  • Row-level isolation via PostgreSQL search_path por tenant
  • Headers de segurança: CSP, HSTS, X-Frame-Options (middleware Fiber)
  • Inputs validados com Zod (frontend) e struct validation em Go (backend)
  • Ficheiros PDF armazenados fora do webroot, servidos via endpoint autenticado

12. Fora de Âmbito (MVP)

  • Billing automático / Stripe
  • App mobile nativa
  • Integração com sistemas de diagnóstico OBD
  • Portal self-service para clientes da oficina
  • Multi-idioma completo (EN traduzido)
  • Backups automáticos (responsabilidade do Coolify/infra)