Files
MeowSpool/mobile/agent.md
T
Felipe d7fb768d3b feat: add domain models and repositories for user, spool presets, and filaments
- Introduced SpoolPreset and User domain models with necessary DTOs and utility functions.
- Created AuthRepository, FilamentRepository, and SpoolPresetRepository interfaces for authentication and data management.
- Implemented UI components for filament display, including ColorSwatch, FilamentCard, and StockBar.
- Developed layout components such as Header and Screen for consistent app structure.
- Added reusable UI components like Badge, Button, Card, and Input for better user interaction.
- Established global constants and theme settings for consistent styling across the application.
- Implemented utility functions for filament calculations and formatting.
- Created Zustand stores for managing authentication, filament, and preset states.
- Configured TypeScript settings for improved development experience.
2026-03-14 10:36:13 -03:00

16 KiB

MeowSpool Mobile — Agent Guide

Visão Geral

Aplicação mobile do MeowSpool construída com React Native + Expo (SDK ~51), usando expo-router para navegação baseada em sistema de arquivos. A arquitetura espelha o backend Rust: Clean Code / Hexagonal, com camadas bem delimitadas do domínio até a apresentação.

O app é offline-first: todos os dados são persistidos localmente em SQLite (expo-sqlite + SQLCipher) e sincronizados com o backend Rust em background usando estratégia last-write-wins via campo updatedAt.


Stack

Componente Biblioteca Observação
Framework expo ~51 Managed Workflow
Navigation expo-router ~3 File-system routing
Linguagem TypeScript 5.x strict mode
Node Version 22 LTS Gerenciado via mise (.mise.toml)
UI React Native + @expo/vector-icons Ionicons
Forms react-hook-form + zod Validação em runtime
Estado global zustand Stores em src/store/
HTTP axios Interceptor Bearer + refresh automático
DB local expo-sqlite + SQLCipher WAL mode, foreign keys
Auth persistência expo-secure-store JWT cifrado no keychain
Safe Area react-native-safe-area-context
Gesture Handler react-native-gesture-handler
Animations react-native-reanimated

Arquitetura de Camadas

src/
├── domain/          ← Entidades puras (sem dependências externas)
├── ports/           ← Interfaces/contratos (traits equivalentes ao Rust)
├── application/     ← Use Cases (orquestram domínio + ports)
├── adapters/
│   ├── remote/      ← Implementações HTTP (Axios → API Rust)
│   └── local/       ← Implementações SQLite (expo-sqlite)
├── store/           ← Estado em memória (Zustand) — cache das queries
├── shared/          ← Design tokens, constantes, utilitários
└── presentation/
    └── components/  ← Componentes UI reutilizáveis

Regra de dependência

presentation → store → application → ports ← adapters
                                  ↑
                               domain

Nenhuma camada interna importa de camadas externas. Os adapters implementam os ports.


Estrutura de Arquivos

mobile/
├── .mise.toml                            ← node 22 LTS
├── app.json                              ← Expo config, scheme "meowspool"
├── babel.config.js                       ← module-resolver + reanimated
├── tsconfig.json                         ← aliases @domain, @ports, @application,
│                                           @adapters, @presentation, @store, @shared
├── index.js                              ← expo-router entry point
├── package.json
│
├── app/                                  ← expo-router file-system routes
│   ├── _layout.tsx                       ← Root layout (carrega sessão)
│   ├── (auth)/
│   │   ├── _layout.tsx                   ← Redireciona se já autenticado
│   │   ├── login.tsx                     ← G3-0
│   │   ├── register.tsx                  ← IX-0
│   │   ├── forgot-password.tsx           ← L2-0
│   │   ├── verify-email.tsx              ← RU-0
│   │   ├── reset-password.tsx            ← 117-0
│   │   └── password-reset-done.tsx       ← 135-0
│   └── (app)/
│       ├── _layout.tsx                   ← Stack autenticado
│       ├── (tabs)/
│       │   ├── _layout.tsx               ← Bottom tabs: Início|Estoque|[+]|Config|Perfil
│       │   ├── home.tsx                  ← 1-0   Dashboard
│       │   ├── inventory.tsx             ← CW-0  Inventário
│       │   ├── add.tsx                   ← FAB → redireciona para /inventory/new
│       │   ├── config.tsx                ← M4-0  Presets de Carretéis
│       │   └── profile.tsx               ← 1LQ-0 Perfil
│       ├── inventory/
│       │   ├── new.tsx                   ← 2X-0  Cadastro de Filamento
│       │   ├── filters.tsx               ← 17H-0 Filtros (bottom sheet)
│       │   ├── [id].tsx                  ← 6L-0  Detalhe do Filamento
│       │   └── [id]/
│       │       └── edit.tsx              ← 13O-0 Editar Filamento
│       ├── config/
│       │   └── presets/
│       │       ├── new.tsx               ← QM-0  Novo Preset
│       │       └── [id]/
│       │           └── edit.tsx          ← 1KD-0 Editar Preset
│       └── filaments/
│           └── [id]/
│               ├── qrcode.tsx            ← 1CF-0 Ver QR Code
│               └── label.tsx             ← 1FZ-0 Exportar Etiqueta SVG
│
└── src/
    ├── domain/
    │   ├── Filament.ts                   ← interface Filament, calcNetWeight
    │   ├── SpoolPreset.ts                ← interface SpoolPreset, isUserOwnedPreset
    │   ├── User.ts                       ← interface User, AuthSession, DTOs
    │   └── Dashboard.ts                  ← DashboardData, MaterialSummary, etc.
    ├── ports/
    │   ├── FilamentRepository.ts         ← interface IFilamentRepository
    │   ├── SpoolPresetRepository.ts      ← interface ISpoolPresetRepository
    │   └── AuthRepository.ts             ← interface IAuthRepository
    ├── application/
    │   ├── filament/
    │   │   ├── CreateFilamentUseCase.ts
    │   │   ├── UpdateFilamentUseCase.ts
    │   │   ├── ListFilamentsUseCase.ts
    │   │   └── DeleteFilamentUseCase.ts
    │   ├── preset/
    │   │   └── PresetUseCases.ts         ← List, Create, Update, Delete
    │   └── auth/
    │       └── AuthUseCases.ts           ← Login, Register, Google, Logout, etc.
    ├── adapters/
    │   ├── remote/
    │   │   ├── httpClient.ts             ← Axios + interceptors JWT + refresh
    │   │   ├── ApiAuthRepository.ts      ← /api/v1/auth/*
    │   │   ├── ApiFilamentRepository.ts  ← /api/v1/filaments/*
    │   │   └── ApiSpoolPresetRepository.ts ← /api/v1/spool-presets/*
    │   └── local/
    │       ├── database.ts               ← init SQLite, WAL, foreign keys, tabelas
    │       ├── LocalFilamentRepository.ts
    │       └── LocalSpoolPresetRepository.ts
    ├── store/
    │   ├── authStore.ts                  ← Zustand: sessão, SecureStore
    │   ├── filamentStore.ts              ← Zustand: lista + filtros em memória
    │   └── presetStore.ts                ← Zustand: systemPresets + userPresets
    ├── shared/
    │   ├── theme.ts                      ← Design tokens (cores, tipografia, espaçamento)
    │   ├── constants.ts                  ← API_BASE_URL, SecureStore keys, MATERIALS
    │   └── utils/
    │       └── filament.ts               ← calcFilamentPercentage, formatWeight, etc.
    └── presentation/
        └── components/
            ├── ui/
            │   ├── Button.tsx            ← primary, secondary, ghost, danger
            │   ├── Input.tsx             ← label, leftIcon, rightLabel, isPassword, error
            │   ├── Card.tsx              ← container surface com borda
            │   └── Badge.tsx             ← badge dinâmico de estoque
            ├── layout/
            │   ├── Screen.tsx            ← SafeArea + scroll + keyboardAvoiding
            │   └── Header.tsx            ← título centralizado, back, slot direito
            └── filament/
                ├── StockBar.tsx          ← barra de progresso + badge numérico
                ├── ColorSwatch.tsx       ← quadrado/círculo com cor hex
                └── FilamentCard.tsx      ← card de lista

Design Tokens

Arquivo: src/shared/theme.ts

Paleta (siamês)

Token Valor Uso
bgBase #1E1B18 Fundo geral
bgSurface #2A2622 Cards, containers
bgHover #332F2B Hover/pressed em cards
textPrimary #F5EEDC Títulos, peso líquido
textSecondary #C9C1B0 Descrições, labels
accent #38BCC2 Botões de ação, elementos ativos
accentMuted #38BCC226 Background de badges accent (10%)
stockLow #FF6B6B ≤ 15% — vermelho
stockMedium #FF9F43 ≤ 35% — laranja
stockOk #38BCC2 > 35% — accent
border #3D3830 Bordas sutis
error #FF6B6B Mensagens de erro

Tipografia

  • UI: Inter
  • Mono (slugs, hex, URLs): JetBrains Mono
  • App é dark-mode exclusivo — nenhum suporte a light mode

Navegação (expo-router)

Estrutura de grupos

(auth)  ← sem autenticação; redireciona para (app) se sessão válida
(app)   ← com autenticação; redireciona para (auth) se sem sessão
  (tabs) ← bottom tabs fixos
  • Scheme: meowspool://
  • Filamento: meowspool://filament/<id>/(app)/inventory/<id>
  • QR público: meowspool.app/f/<slug> (web)

Estratégia Offline-First

  1. Escrita: toda mutação persiste primeiro no SQLite (synced = 0) e enfileira em sync_queue.
  2. Leitura: lê sempre do SQLite; a API é usada apenas para sync.
  3. Sync: background job consome sync_queue e envia para o backend Rust.
  4. Conflito: last-write-wins via updatedAt (ISO 8601). O backend é a fonte de verdade em conflitos.
  5. Auth offline: token armazenado no expo-secure-store; refresh automático via interceptor Axios.

Schema SQLite

-- filaments
id TEXT PRIMARY KEY, user_id TEXT, material TEXT, brand TEXT, model TEXT,
color_hex TEXT, spool_preset_id TEXT, total_weight_g REAL, net_weight_g REAL,
temp_hotend_c REAL, temp_bed_c REAL, flow_factor_pct REAL, notes TEXT,
synced INTEGER DEFAULT 0, updated_at TEXT, created_at TEXT

-- spool_presets
id TEXT PRIMARY KEY, user_id TEXT, name TEXT, spool_weight_g REAL,
is_system INTEGER DEFAULT 0, synced INTEGER DEFAULT 0, created_at TEXT

-- sync_queue
id INTEGER PRIMARY KEY AUTOINCREMENT, entity_type TEXT, entity_id TEXT,
operation TEXT, payload TEXT, created_at TEXT

Convenções de Código

Nomenclatura

  • Componentes React: PascalCase (FilamentCard.tsx)
  • Hooks: camelCase com prefixo use (useFilamentStore)
  • Use Cases: PascalCase com sufixo UseCase (CreateFilamentUseCase)
  • Repositórios: PascalCase com prefixo de implementação (ApiFilamentRepository, LocalFilamentRepository)
  • Stores: camelCase (filamentStore.ts), exportados como useXxxStore

Imports

Sempre usar aliases em vez de caminhos relativos:

import { Filament } from '@domain/Filament';
import { useFilamentStore } from '@store/filamentStore';
import { colors } from '@shared/theme';
import { Button } from '@presentation/components/ui/Button';

Componentes de tela

  • Toda tela usa SafeAreaView com backgroundColor: colors.bgBase
  • Header manual (não usa o header do expo-router): headerShown: false
  • CTA fixo: View com padding: spacing[5] + borderTopWidth: 1 + borderTopColor: colors.border
  • ScrollViews com showsVerticalScrollIndicator={false} e keyboardShouldPersistTaps="handled"

Formulários

  • react-hook-form + zodResolver para todos os formulários
  • Validações no schema Zod (nunca inline)
  • isLoading local no componente durante submit
  • Erros de API exibidos via Alert.alert

API do Backend (rotas relevantes)

Base: EXPO_PUBLIC_API_URL (padrão: http://localhost:3000/api/v1)

Auth

Método Rota Descrição
POST /auth/login Login email/senha
POST /auth/register Cadastro
POST /auth/google OAuth Google
POST /auth/refresh Refresh token
POST /auth/logout Logout
POST /auth/forgot-password Solicitar reset de senha
POST /auth/reset-password Confirmar reset com token
POST /auth/verify-email Verificar e-mail com código

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

Spool Presets

Método Rota Descrição
GET /spool-presets Listar (sistema + usuário)
POST /spool-presets Criar preset do usuário
PATCH /spool-presets/:id Atualizar preset do usuário
DELETE /spool-presets/:id Excluir preset do usuário

Dashboard

Método Rota Descrição
GET /dashboard Dados agregados do dashboard

Próximos Passos (integrações pendentes)

  • Instanciar e injetar repositórios concretos nos Use Cases (DI container simples ou Context)
  • Implementar sync background com sync_queue SQLite → API
  • Integrar react-native-qrcode-svg para render real do QR Code
  • Gerar SVG de etiqueta (integração com /filaments/:id/label do backend)
  • Expo Notifications para alertas de estoque baixo
  • Google OAuth com expo-auth-session
  • Testes de integração com Jest + Testing Library

Executando o Projeto

# Instalar dependências (na pasta mobile/)
mise install   # garante Node 22
npm install

# Iniciar servidor de desenvolvimento
npx expo start

# iOS
npx expo run:ios

# Android
npx expo run:android

Variável de ambiente: crie mobile/.env com:

EXPO_PUBLIC_API_URL=http://localhost:3000/api/v1