docs: adicionar SPEC, SUPABASE_SCHEMA, API_INTEGRATION, BUILDS_FARM
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# Integração Frontend ↔ Tauri (Rust) ↔ Supabase
|
||||
|
||||
## Visão geral da comunicação
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
||||
│ JavaScript │ invoke │ Tauri/Rust │ HTTP │ Supabase │
|
||||
│ (index.html) │────────▶│ commands │────────▶│ (cloud) │
|
||||
│ │◀────────│ │◀────────│ │
|
||||
└─────────────────┘ response└─────────────────┘ response└──────────────────┘
|
||||
```
|
||||
|
||||
O frontend **nunca faz requisições HTTP direto** para o Supabase. Toda comunicação passa pelos comandos Tauri (Rust).
|
||||
|
||||
---
|
||||
|
||||
## Comandos Tauri disponíveis
|
||||
|
||||
### App
|
||||
| Comando | O que faz |
|
||||
|---------|-----------|
|
||||
| `get_loja` | Retorna a loja configurada |
|
||||
| `get_loja_fixa` | Retorna `"Uniao"`, `"Alianca"` ou `null` (se build fixa) |
|
||||
| `set_loja(loja)` | Define a loja (bloqueado se build fixa) |
|
||||
| `get_config` | Retorna config completo `{ loja, pdv_nome, operador_default, ... }` |
|
||||
| `set_config(cfg)` | Salva config |
|
||||
|
||||
### Storage (SQLite local)
|
||||
| Comando | O que faz |
|
||||
|---------|-----------|
|
||||
| `salvar_fechamento(estado)` | Salva estado completo no SQLite local |
|
||||
| `carregar_rascunho(loja, data)` | Retorna último rascunho para loja+data |
|
||||
| `listar_fechamentos(loja, limite)` | Lista fechamentos de uma loja |
|
||||
| `buscar_fechamento_por_id(id)` | Busca por ID local |
|
||||
| `pendentes_sync` | Retorna fechamentos que ainda não foram syncados |
|
||||
|
||||
### Supabase
|
||||
| Comando | O que faz |
|
||||
|---------|-----------|
|
||||
| `sb_salvar_fechamento(estado)` | Upsert no Supabase via service_role JWT |
|
||||
| `sb_carregar_rascunho(loja, data)` | Busca rascunho mais recente |
|
||||
| `sb_listar_recentes(loja, limite)` | Lista fechamentos recentes |
|
||||
| `sb_buscar_por_id(id)` | Busca por ID |
|
||||
| `sb_buscar_por_uuid(uuid)` | Busca por UUID |
|
||||
| `sb_buscar_por_id_fechamento(id_fechamento)` | Busca por chave natural |
|
||||
| `sb_atualizar_fechamento(id_fechamento, updates)` | PATCH campos específicos |
|
||||
| `sb_salvar_listas(loja, listas)` | Salva operadores/gerentes customizados |
|
||||
| `sb_carregar_listas(loja)` | Carrega listas salvas |
|
||||
| `sb_online` | Retorna `true`/`false` se Supabase está acessível |
|
||||
|
||||
### Printer
|
||||
| Comando | O que faz |
|
||||
|---------|-----------|
|
||||
| `detectar_impressora` | Detecta EPSON TM-T20 USB |
|
||||
| `configurar_impressora(caminho)` | Define caminho da impressora |
|
||||
| `caminho_impressora` | Retorna caminho configurado |
|
||||
| `imprimir_recibo(html)` | Envia HTML puro para impressora |
|
||||
| `teste_impressora` | Imprime página de teste |
|
||||
| `ativar_modo_teste_impressao` | Ativa modo teste (salva PDF em vez de imprimir) |
|
||||
|
||||
---
|
||||
|
||||
## Fluxo de salvar um fechamento
|
||||
|
||||
```javascript
|
||||
// No frontend (index.html), quando operador clica "Salvar":
|
||||
async function salvarFechamento() {
|
||||
const estado = buildEstado(); // coleta todos os campos do formulário
|
||||
|
||||
// 1. Salva localmente (sempre funciona offline)
|
||||
await invoke('salvar_fechamento', { estado });
|
||||
|
||||
// 2. Tenta sync com Supabase
|
||||
try {
|
||||
await invoke('sb_salvar_fechamento', { estado });
|
||||
toast('✅ Salvo e sincronizado');
|
||||
} catch(e) {
|
||||
// Supabase offline — fica pendente no SQLite
|
||||
toast('💾 Salvo localmente (sync pendente)');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Como o Rust conversa com o Supabase
|
||||
|
||||
Em `supabase.rs`, o plugin usa `reqwest` (HTTP client) com:
|
||||
|
||||
```rust
|
||||
fn headers(&self, use_service: bool) -> reqwest::header::HeaderMap {
|
||||
let key = if use_service { &self.service_role_key } else { &self.anon_key };
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::AUTHORIZATION,
|
||||
format!("Bearer {}", key).parse().unwrap(),
|
||||
);
|
||||
headers
|
||||
}
|
||||
```
|
||||
|
||||
- **`use_service = true`:** para INSERT/UPDATE (salvar fechamento)
|
||||
- **`use_service = false`:** para GET (listar, buscar)
|
||||
|
||||
---
|
||||
|
||||
## Upsert — salvar sem duplicar
|
||||
|
||||
O Supabase não tem UPSERT nativo via REST. O plugin usa um truque:
|
||||
|
||||
```http
|
||||
POST /fechamentos_web HTTP/1.1
|
||||
Prefer: resolution=merge-duplicates
|
||||
```
|
||||
|
||||
Se o `id_fechamento` já existe, o PostgREST faz um UPDATE em vez de INSERT. Se não existe, faz INSERT.
|
||||
|
||||
---
|
||||
|
||||
## UUID vs id_fechamento
|
||||
|
||||
| Campo | O que é | Quem gera |
|
||||
|-------|---------|-----------|
|
||||
| `id` | ID sequencial do PostgreSQL | Supabase (auto) |
|
||||
| `uuid` | UUID único do registro local | Frontend JS (`crypto.randomUUID()`) |
|
||||
| `id_fechamento` | Chave natural: `{loja}_{data}_{operador}` | Frontend JS (construído na hora) |
|
||||
|
||||
O `id_fechamento` é a chave de negócio. O `uuid` é para rastrear registros entre local e cloud.
|
||||
|
||||
---
|
||||
|
||||
## Offline first — como funciona
|
||||
|
||||
1. **Operador/edita** → `salvar_fechamento` → SQLite (sempre funciona)
|
||||
2. **Operador/edita** → `sb_salvar_fechamento` → Supabase (só se online)
|
||||
3. **Se offline:** o fechamento fica no SQLite com `sync_status = "local"`
|
||||
4. **Quando conecta:** `pendentes_sync` retorna os pendentes → sync automático
|
||||
|
||||
O sync automático é controlado por `sync_automatico` na config da app.
|
||||
Reference in New Issue
Block a user