140 lines
5.1 KiB
Markdown
140 lines
5.1 KiB
Markdown
# 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.
|