This commit is contained in:
2026-03-14 19:45:08 -03:00
parent 9ac83ad823
commit 74adc35b53
23 changed files with 1082 additions and 150 deletions
+88 -13
View File
@@ -12,8 +12,8 @@ O app é **offline-first**: todos os dados são persistidos localmente em **SQLi
| Componente | Biblioteca | Observação |
| ----------------- | ----------------------------------- | --------------------------------------------------------- |
| Framework | `expo` ~51 | Managed Workflow |
| Navigation | `expo-router` ~3 | File-system routing |
| Framework | `expo` ~54 | Managed Workflow |
| Navigation | `expo-router` ~4 | File-system routing |
| Linguagem | TypeScript 5.x | strict mode |
| Node Version | 22 LTS | Gerenciado via `mise` (`.mise.toml`) |
| Java Version | 17 | Obrigatório para React Native 0.81 (Java 24 quebra CMake) |
@@ -28,6 +28,8 @@ O app é **offline-first**: todos os dados são persistidos localmente em **SQLi
| Animations | `react-native-reanimated` | |
| QR Code render | `react-native-qrcode-svg` | Render de QR Code em tela |
| Câmera / Scanner | `expo-camera` ~17 | ML Kit barcode scan; requer development build |
| File system | `expo-file-system/legacy` | Salvar arquivos no cache; usar import `/legacy` |
| Compartilhamento | `expo-sharing` | Sheet nativo de compartilhamento de arquivos |
---
@@ -105,7 +107,7 @@ mobile/
│ └── filaments/
│ └── [id]/
│ ├── qrcode.tsx ← Ver QR Code (react-native-qrcode-svg)
│ └── label.tsx ← Exportar Etiqueta SVG
│ └── label.tsx ← Exportar Etiqueta (PDF ou SVG)
└── src/
├── domain/
@@ -296,15 +298,16 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
### Filaments
| Método | Rota | Descrição |
| ------ | ----------------------- | -------------------- |
| GET | `/filaments` | Listar (com filtros) |
| POST | `/filaments` | Criar |
| GET | `/filaments/:id` | Detalhe |
| PATCH | `/filaments/:id` | Atualizar |
| DELETE | `/filaments/:id` | Excluir |
| GET | `/filaments/:id/qrcode` | QR Code SVG |
| GET | `/filaments/:id/label` | Etiqueta SVG |
| Método | Rota | Descrição |
| ------ | --------------------------- | -------------------------------------------- |
| GET | `/filaments` | Listar (com filtros) |
| POST | `/filaments` | Criar |
| GET | `/filaments/:id` | Detalhe |
| PATCH | `/filaments/:id` | Atualizar |
| DELETE | `/filaments/:id` | Excluir |
| GET | `/filaments/:id/qrcode` | QR Code PNG |
| GET | `/filaments/:id/label.svg` | Etiqueta SVG (`width_mm`, `height_mm`, `fields`) |
| GET | `/filaments/:id/label.pdf` | Etiqueta PDF (`width_mm`, `height_mm`, `fields`) |
### Spool Presets
@@ -329,7 +332,9 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
- [ ] Implementar sync background com `sync_queue` SQLite → API
- [x] Integrar `react-native-qrcode-svg` para render real do QR Code
- [x] Scanner de QR Code com `expo-camera` v17 (ML Kit) → `scanner.tsx`
- [ ] Gerar SVG de etiqueta (integração com `/filaments/:id/label` do backend)
- [x] Exportar etiqueta SVG via `GET /filaments/:id/label.svg`
- [x] Exportar etiqueta PDF via `GET /filaments/:id/label.pdf`
- [x] Seletor de formato (PDF/SVG) e controle de campos na tela de etiqueta
- [ ] Expo Notifications para alertas de estoque baixo
- [ ] Google OAuth com `expo-auth-session`
- [ ] Testes de integração com Jest + Testing Library
@@ -338,6 +343,15 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
## Mudanças Recentes (14/03/2026)
### ✅ Exportação de Etiqueta PDF e SVG (`label.tsx`)
- **Pacotes instalados**: `expo-file-system`, `expo-sharing`, `react-native-worklets`
- **Atenção**: importar `expo-file-system` como `expo-file-system/legacy` — a API padrão do SDK 54 não exporta `EncodingType` nem `writeAsStringAsync` diretamente
- **Fluxo PDF**: `GET /label.pdf` → resposta como `arraybuffer``btoa` para Base64 → salvar com `FileSystem.writeAsStringAsync` (encoding Base64) → `Sharing.shareAsync`
- **Fluxo SVG**: `GET /label.svg` → resposta como texto → salvar com encoding `'utf8'``Sharing.shareAsync`
- **Seletor de formato**: chips PDF/SVG no footer; padrão é PDF
- **Campos selecionáveis**: `enabledContent` (Set) é convertido para string CSV e enviado como `fields` query param — o backend respeita a seleção
### ✅ Implementações Completadas
#### 1. **Delete Filament — Integração com API**
@@ -510,6 +524,67 @@ Base: `EXPO_PUBLIC_API_URL` (padrão: `http://localhost:3000/api/v1`)
- **Resultado**: Ícone agora exibe com fundo turquesa arredondado, alinhado com design do Paper
- **Impacto**: Visual correto da tela de login (G3-0) sincronizado com mockups
#### 7. **Color Picker — Modal Customizada (Substituindo Alert.prompt)**
- **Arquivos**: `app/(app)/inventory/new.tsx`, `app/(app)/inventory/[id]/edit.tsx`
- **Problema**: `Alert.prompt()` apresentava bugs e comportamentos inconsistentes em React Native/Expo
- **Solução**: Implementada Modal customizada com componentes nativos
- **Mudanças**:
- Adicionadas importações: `Modal`, `KeyboardAvoidingView`, `Platform` do React Native
- Novos estados: `isColorPickerOpen` (boolean), `colorInputValue` (string)
- Funções de controle:
- `handleOpenColorPicker()`: abre modal focando no input
- `handleConfirmColor()`: valida hex, atualiza cor, fecha modal
- `handleCancelColor()`: descarta input, fecha modal
- JSX: Modal envolta em Fragment com overlay semi-transparente
- Estilos novos: `modalOverlay`, `modalContent`, `modalCard`, `modalInput`, `modalButtons`, etc.
- **Código de exemplo**:
```typescript
const [isColorPickerOpen, setIsColorPickerOpen] = useState(false);
const [colorInputValue, setColorInputValue] = useState('');
const handleConfirmColor = (): void => {
if (!colorInputValue) {
Alert.alert('Erro', 'Digite um código hexadecimal');
return;
}
const normalized = normalizeHex(colorInputValue.trim());
if (isValidHex(normalized)) {
setSelectedColor(normalized);
setHexInput(normalized);
setIsColorPickerOpen(false);
setColorInputValue('');
} else {
Alert.alert('Erro', 'Código hexadecimal inválido. Use o formato #RRGGBB ou #RGB.');
}
};
// No JSX:
<Modal visible={isColorPickerOpen} transparent animationType="fade">
<View style={styles.modalOverlay}>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<View style={styles.modalCard}>
<TextInput
placeholder="#FF5733"
value={colorInputValue}
onChangeText={setColorInputValue}
autoFocus
/>
{/* Botões confirmar/cancelar */}
</View>
</KeyboardAvoidingView>
</View>
</Modal>
```
- **Impacto**:
- ✅ Color picker agora funciona consistentemente em todas plataformas
- ✅ UX melhorada com feedback visual imediato
- ✅ Suporte completo a teclado (autoFocus, KeyboardAvoidingView)
- ✅ Validação de hex pré-digitação com `isValidHex()` e `normalizeHex()`
### Arquitetura Mantida
- Todos use cases injetados via `container.ts` (Dependency Injection)