Technical Track Record
Case 06: Unit Gestao – Vertical ERP for Bespoke Manufacturing
Briefing
Small manufacturing businesses and ateliers often struggle with fragmented processes and poor financial visibility. I designed and engineered this vertical ERP to centralize the entire operational lifecycle, from order capture and material management to production scheduling and financial close. The system transformed a manual, spreadsheet-driven operation into a structured, scalable digital workflow.
Technical Deep Dive
The architecture adopts a BaaS pattern with Supabase, where I implemented critical business logic directly in the database layer using Postgres Triggers and RPCs (Remote Procedure Calls) to guarantee atomicity in complex order transactions. Security is enforced through an RBAC strategy using RLS (Row Level Security), ensuring full isolation of financial data. For output, I built a server-side PDF rendering engine that generates executive reports and production orders in real time, ensuring document consistency on any device.
Inventory & Order Desk
Documentation
Unit Gestao Beab'atelie Web system for managing orders, scheduling, customers, products, finance, and PDF reports, with Supabase authentication and a Next.js (App Router) interface. Overview Application focused on an atelier's daily flow: customer and product registration, order creation with items, delivery scheduling, monthly financial control, configurable reminders, and PDF report generation. The entire data layer runs on Supabase (Postgres + Auth + Storage), with business rules implemented via triggers, views, and RPCs. Architecture
[Browser]
|-- UI (Next.js App Router)
|-- Server Components (fetchApi)
|-- Client Components (fetchApiClient)
|
/api/* (Route Handlers)
|
Repositories (src/lib/repositories)
|
Supabase (Postgres + Auth + Storage)Tech Stack
| Component | Technology | Version | Notes |
|---|---|---|---|
| Framework | Next.js | 14.2.5 | App Router + Route Handlers |
| UI | React | 18.2.0 | Server/Client Components |
| Language | TypeScript | 5.5.3 | Typing and validation |
| Styles | Tailwind CSS | 3.4.7 | Utility-first |
| Backend as a Service | Supabase JS | 2.45.4 | Auth, Postgres, Storage |
| Validation | Zod | 3.23.8 | Input schemas |
| @react-pdf/renderer | 3.4.4 | Server-side reports | |
| Tests | Vitest | 1.6.0 | Unit tests for utils |
Core Modules
| Module | Responsibility | Key Files |
|---|---|---|
| Orders | Create, edit, list, and attach images to orders, with items and payment | src/components/pedidos, src/app/api/pedidos, src/lib/repositories/pedidos |
| Customers | Customer CRUD | src/components/clientes, src/app/api/clientes, src/lib/repositories/clients |
| Products | Product CRUD and default pricing | src/components/produtos, src/app/api/produtos, src/lib/repositories/produtos |
| Schedule | Aggregate deliveries by date | src/components/agenda, src/app/api/agenda, src/lib/agenda-utils |
| Finance | Incoming (payments) + outgoing (expenses) and monthly summary | src/components/financeiro, src/app/api/financeiro, src/lib/finance-utils |
| Reminders | Per-user configuration and upcoming delivery list | src/components/lembretes, src/app/api/lembretes, src/lib/reminder-utils |
| Reports | PDF for orders, schedule, and finance | src/components/relatorios, src/app/api/relatorios, src/lib/reports/pdf |
| Settings | Theme and reminder preferences | src/app/(app)/configuracoes, src/components/config |
Technical Flows
- Order creation: the form accepts existing or new customers, existing or new items, calculates total and status (Open, Partial, Paid), sends to
POST /api/pedidosand uses RPCcreate_order_with_itemsto persist order and items. - Schedule: lists records from the
agenda_entriesview, aggregates by date viabuildAgendaByDate, and renders by day and time range. - Finance: income entries are read from the
finance_order_entriesview (paid > 0 and within period) and expenses fromexpenses, with summary calculated inbuildFinanceSummary. - Reminders: the backend builds a date window with
parseDaysParam+getRangeForDays, queriesagenda_entries, and returns filtered summary and items. - PDF reports: route handlers generate PDFs via
@react-pdf/rendererand returnapplication/pdfin Node runtime. - Automated message:
GET /api/mensagens/pedido/:idbuilds a message with items, dates, and values for client delivery. - Reference images: upload via
POST /api/pedidos/:id/referencias, with type/size validation, stored in Storage and recorded inorder_reference_images.
Authentication and Authorization
- Login via
POST /api/auth/loginusing Supabase Auth (password), returning HTTP-only cookies:sb-access-token,sb-refresh-token,sb-expires-at. - Middleware protects non-public routes and redirects to
/loginwhen the token is missing or expired. fetchApi(server) injectsAuthorization: Bearer <token>from the cookie.fetchApiClient(browser) uses cookies and attemptsPOST /api/auth/refreshon 401.- Roles in
profiles:sys-admin,admin,user. - RLS applies permissions by role on tables and views; finance endpoints require the admin role.
- In development,
createSupabaseRouteClientcan useSUPABASE_SERVICE_ROLE_KEYwhen there is no token, facilitating local testing.
Database
| Entity | Key fields | Notes |
|---|---|---|
| clients | name, phone, notes | RLS enabled |
| products | name, description, price_default, active, control_code | control_code generated by year |
| orders | client_id, delivery_date, total, paid, status, control_code | status via set_order_status trigger |
| order_items | order_id, product_id, quantity, unit_price | subtotal calculated in DB |
| order_reference_images | order_id, storage_path, file_name, content_type, size | linked to Storage |
| expenses | date, description, category, value | used in finance |
| reminder_settings | user_id, days_before, notify_* | per-user config |
| reminder_logs | order_id, user_id, reminder_date, channel | send logs |
| profiles | id, role | user role |
Rules and automations
set_order_status: defines status based onpaidandtotal.set_paid_date: fillspaid_datewhen payment exists.next_*_control_code: generates yearly sequences for orders and products.agenda_entries: schedule view aggregating customers, products, and dates.finance_order_entries: view for financial income entries.
Storage
- Public bucket
order-referencesfor order-linked images.
API Response format
{ "data": { ... } }Error
{ "error": { "message": "...", "details": ... } }Auth
| Method | Route | Description |
|---|---|---|
| POST | /api/auth/login | Login and set cookies |
| POST | /api/auth/logout | Clear cookies |
| POST | /api/auth/refresh | Refresh session |
| GET | /api/auth/me | Return user and role |
Orders and images
| Method | Route | Description |
|---|---|---|
| GET | /api/pedidos | List orders (filters: delivery_date, client_id) |
| POST | /api/pedidos | Create order with items |
| GET | /api/pedidos/:id | Order detail |
| PATCH | /api/pedidos/:id | Update order and items |
| DELETE | /api/pedidos/:id | Remove order |
| POST | /api/pedidos/:id/referencias | Upload images (multipart) |
| DELETE | /api/pedidos/:id/referencias/:imageId | Remove image |
Customers and products
| Method | Route | Description |
|---|---|---|
| GET | /api/clientes | List customers |
| POST | /api/clientes | Create customer |
| GET | /api/clientes/:id | Customer detail |
| PATCH | /api/clientes/:id | Update customer |
| DELETE | /api/clientes/:id | Remove customer |
| GET | /api/produtos | List products |
| POST | /api/produtos | Create product |
| GET | /api/produtos/:id | Product detail |
| PATCH | /api/produtos/:id | Update product |
| DELETE | /api/produtos/:id | Remove product |
Schedule and reminders
| Method | Route | Description |
|---|---|---|
| GET | /api/agenda | Schedule by period (start_date, end_date, status, client_id) |
| GET | /api/lembretes | Reminders by period (days, base_date, status, client_id) |
| GET | /api/lembretes/config | Read user settings |
| PATCH | /api/lembretes/config | Update user settings |
| POST | /api/lembretes/logs | Register send log |
Finance
| Method | Route | Description |
|---|---|---|
| GET | /api/financeiro | Monthly summary (admin) |
| GET | /api/financeiro/saidas | List expenses (admin) |
| POST | /api/financeiro/saidas | Create expense (admin) |
PDF reports
| Method | Route | Description | |
|---|---|---|---|
| GET | /api/relatorios/pedidos | Order PDF (month, status, client_id) | |
| GET | /api/relatorios/agenda | Schedule PDF (month, `format=calendar | list`) |
| GET | /api/relatorios/financeiro | Finance PDF (month, `detail=summary | full`) |
Messages
| Method | Route | Description |
|---|---|---|
| GET | /api/mensagens/pedido/:id | Automated message for customer |
PDF Reports
- Server-side rendering via
@react-pdf/renderer. src/lib/reports/pdf.tsxcentralizes templates for orders, schedule, and finance.- Runtime configured as
nodejsfor report routes. - Custom fonts in
public/fonts.
Reference Images
- Allowed types:
image/jpeg,image/png,image/webp,image/gif,image/avif. - Max size: 5MB per file.
- Uploads use the admin client for Storage writes and metadata insertion.
Configuration Required environment variables
| Variable | Use |
|---|---|
| NEXT_PUBLIC_SUPABASE_URL | Supabase project URL |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | Anonymous key for clients |
| SUPABASE_SERVICE_ROLE_KEY | Service role for admin operations |
Optional variables (cookies)
| Variable | Use |
|---|---|
| AUTH_COOKIE_DOMAIN | Cookie domain |
| AUTH_COOKIE_SAMESITE | lax, strict, or none |
| AUTH_COOKIE_SECURE | true or false |
Supabase local
npm run supabase:startstarts services;npm run supabase:seedapplies seeds and reset.
Scripts
npm run dev: development environment.npm run build: production build.npm run start: production Next server.npm run lint: lint.npm run test: vitest.npm run supabase:start: start local Supabase.npm run supabase:stop: stop local Supabase.npm run supabase:reset: reset local database.npm run supabase:seed: reset with seed.
Structure
src/
app/ App Router routes and APIs
components/ UI and domain modules
lib/ business rules, repositories, and utils
supabase/
deployed/ applied SQL migrations
seed.sql local seed
public/
fonts/ fonts used in PDFs
tests/ unit tests (vitest)Observability
logErrorwritesconsole.errorwithscope,message, andcontext(ignoring tests); APIs return standardized messages withfail().
Security
- HTTP-only session cookies with
SameSiteandSecureconfiguration; middleware blocks access without a valid session. - RLS enabled on core tables; image upload validates type and size before persisting.
Deploy
- Configure Supabase environment variables.
npm run buildto generate the build.npm run startto serve.- Ensure Node runtime for report routes (
/api/relatorios/*).
Common Issues
- 401 accessing routes: session expired or missing cookies.
- 403 on finance: user without
adminorsys-adminrole. - Empty PDFs: missing fonts in
public/fontsor non-Node runtime. - Upload errors: unsupported type or file > 5MB.