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.
This commit is contained in:
2026-03-14 10:36:13 -03:00
parent abdc2fe8ce
commit d7fb768d3b
76 changed files with 19681 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
/**
* Dados agregados do dashboard.
* Construído a partir dos filamentos do usuário.
*/
export interface DashboardData {
totalStockKg: number;
lowStockCount: number;
byMaterial: MaterialSummary[];
lowStockFilaments: LowStockFilament[];
recentFilaments: RecentFilament[];
}
export interface MaterialSummary {
material: string;
count: number;
totalKg: number;
}
export interface LowStockFilament {
id: string;
name: string; // ex: "PETG White"
brand: string;
material: string;
netWeightG: number;
percentage: number;
colorHex: string;
}
export interface RecentFilament {
id: string;
name: string;
brand: string;
material: string;
netWeightG: number;
percentage: number;
colorHex: string;
}
+65
View File
@@ -0,0 +1,65 @@
import type { Material } from '@shared/constants';
/**
* Entidade de domínio: Filamento.
*
* Representa um rolo de filamento no inventário do usuário.
* Esta é uma entidade pura — sem dependências de UI, API ou banco de dados.
*/
export interface Filament {
readonly id: string;
readonly userId: string;
material: Material;
brand: string;
model: string | null;
colorHex: string;
spoolPresetId: string;
totalWeightG: number;
netWeightG: number;
tempHotendC: number | null;
tempBedC: number | null;
flowFactorPct: number | null;
notes: string | null;
readonly updatedAt: string; // ISO 8601 — usado no sync offline
readonly createdAt: string;
}
/**
* DTO para criação de um novo filamento.
* Campos calculados (netWeightG) são omitidos — derivados no use case.
*/
export type CreateFilamentInput = Omit<Filament, 'id' | 'userId' | 'netWeightG' | 'createdAt' | 'updatedAt'>;
/**
* DTO para atualização de um filamento existente.
*/
export type UpdateFilamentInput = Partial<CreateFilamentInput>;
/**
* Filtros disponíveis na listagem de filamentos.
*/
export interface FilamentFilter {
material?: Material;
brand?: string;
search?: string;
stockLevel?: 'low' | 'medium' | 'ok';
sortBy?: 'net_weight_asc' | 'net_weight_desc' | 'created_at_desc';
page?: number;
perPage?: number;
}
/**
* Calcula a porcentagem de filamento disponível em relação ao peso total
* de um rolo cheio (1000g por convenção).
*/
export function calcFilamentPercentage(filament: Filament): number {
const base = 1000;
return Math.min(100, Math.max(0, Math.round((filament.netWeightG / base) * 100)));
}
/**
* Calcula o peso líquido dado o peso total e o peso do carretel vazio.
*/
export function calcNetWeight(totalWeightG: number, spoolWeightG: number): number {
return Math.max(0, totalWeightG - spoolWeightG);
}
+31
View File
@@ -0,0 +1,31 @@
/**
* Entidade de domínio: Preset de Carretel.
*
* Representa o peso do carretel vazio de uma marca/modelo.
* Presets do sistema (isSystem = true) são somente leitura.
*/
export interface SpoolPreset {
readonly id: string;
name: string;
spoolWeightG: number;
readonly isSystem: boolean;
readonly userId: string | null; // null para presets do sistema
readonly createdAt: string;
}
/**
* DTO para criação de um preset customizado.
*/
export type CreateSpoolPresetInput = Pick<SpoolPreset, 'name' | 'spoolWeightG'>;
/**
* DTO para atualização de um preset customizado.
*/
export type UpdateSpoolPresetInput = Partial<CreateSpoolPresetInput>;
/**
* Guard: retorna true se o preset pode ser editado/deletado pelo usuário.
*/
export function isUserOwnedPreset(preset: SpoolPreset): boolean {
return !preset.isSystem && preset.userId !== null;
}
+44
View File
@@ -0,0 +1,44 @@
/**
* Entidade de domínio: Usuário.
*
* Representa o usuário autenticado na sessão.
*/
export interface User {
readonly id: string;
email: string;
name: string | null;
readonly googleId: string | null;
readonly createdAt: string;
}
/**
* Sessão de autenticação persistida localmente.
*/
export interface AuthSession {
readonly accessToken: string;
readonly refreshToken: string;
readonly user: User;
}
/**
* DTO para login com e-mail/senha.
*/
export interface LoginInput {
email: string;
password: string;
}
/**
* DTO para cadastro.
*/
export interface RegisterInput {
email: string;
password: string;
}
/**
* DTO para autenticação via OAuth Google.
*/
export interface GoogleOAuthInput {
idToken: string;
}