Files
nutriscan/lib/types.ts
T
Evandro Fonseca Junior bfb58ef16e feat(search): implement paginated API fetching for large page sizes
- Removed upper limit on page_size validation to support requests >100
- Added automatic pagination logic to fetch from Open Food Facts API in chunks of 1000
- Accumulates products across multiple API pages until desired count is reached
- Updated error messages and product data fields for consistency
2025-10-09 07:16:26 -03:00

432 lines
9.2 KiB
TypeScript

/**
* Tipos para sistema de filtros de produtos
* Baseado na API do Open Food Facts
*/
export interface ProductFilters {
// Filtros de classificação nutricional
nutritionGrades?: ("a" | "b" | "c" | "d" | "e")[];
novaGroups?: (1 | 2 | 3 | 4)[];
// Filtros de categoria e marca
categories?: string[];
brands?: string[];
// Filtros de ingredientes e aditivos
allergens?: string[];
excludeAllergens?: string[];
additives?: string[];
excludeAdditives?: string[];
noAdditives?: boolean;
// Filtros nutricionais (valores por 100g)
nutritionRanges?: {
energy?: { min?: number; max?: number };
fat?: { min?: number; max?: number };
carbohydrates?: { min?: number; max?: number };
proteins?: { min?: number; max?: number };
sugars?: { min?: number; max?: number };
fiber?: { min?: number; max?: number };
sodium?: { min?: number; max?: number };
};
// Filtros de país
countries?: string[];
// Ordenação
sortBy?:
| "relevance"
| "nutrition_grade"
| "name"
| "brand"
| "energy"
| "fat"
| "sugars"
| "completeness"
| "with_images"
| "with_nutrition"
| "eco_score"
| "nova_group";
sortOrder?: "asc" | "desc";
// Assinatura de índice para compatibilidade
[key: string]: unknown;
}
export interface FilterOption {
value: string;
label: string;
count?: number;
color?: string;
description?: string;
}
export interface NutritionGrade {
grade: "a" | "b" | "c" | "d" | "e";
label: string;
color: string;
description: string;
}
export interface NovaGroup {
group: 1 | 2 | 3 | 4;
label: string;
color: string;
description: string;
}
export interface SortOption {
value: string;
label: string;
description?: string;
icon?: string;
}
export interface FilterState {
filters: ProductFilters;
isLoading: boolean;
hasActiveFilters: boolean;
totalResults: number;
}
export interface FilterSidebarProps {
filters: ProductFilters;
onFiltersChange: (filters: ProductFilters) => void;
onClearFilters: () => void;
isLoading?: boolean;
totalResults?: number;
}
// Constantes para configuração dos filtros
export const NUTRITION_GRADES: NutritionGrade[] = [
{
grade: "a",
label: "A",
color: "bg-green-500",
description: "Muito bom",
},
{
grade: "b",
label: "B",
color: "bg-green-400",
description: "Bom",
},
{
grade: "c",
label: "C",
color: "bg-yellow-500",
description: "Regular",
},
{
grade: "d",
label: "D",
color: "bg-orange-500",
description: "Ruim",
},
{
grade: "e",
label: "E",
color: "bg-red-500",
description: "Muito ruim",
},
];
export const NOVA_GROUPS: NovaGroup[] = [
{
group: 1,
label: "1",
color: "bg-green-500",
description: "Alimentos não processados ou minimamente processados",
},
{
group: 2,
label: "2",
color: "bg-yellow-500",
description: "Ingredientes culinários processados",
},
{
group: 3,
label: "3",
color: "bg-orange-500",
description: "Alimentos processados",
},
{
group: 4,
label: "4",
color: "bg-red-500",
description: "Alimentos ultraprocessados",
},
];
export const SORT_OPTIONS: SortOption[] = [
{
value: "relevance",
label: "Relevância",
description: "Resultados mais relevantes primeiro",
icon: "TrendingUp",
},
{
value: "nutrition_grade",
label: "Nutri-Score",
description: "Melhor classificação nutricional primeiro",
icon: "Zap",
},
{
value: "name",
label: "Nome (A-Z)",
description: "Ordenar por nome do produto",
icon: "AlphabeticalOrder",
},
{
value: "brand",
label: "Marca (A-Z)",
description: "Ordenar por marca",
icon: "Building2",
},
{
value: "energy",
label: "Calorias",
description: "Menor valor energético primeiro",
icon: "Flame",
},
{
value: "fat",
label: "Gorduras",
description: "Menor teor de gordura primeiro",
icon: "Droplet",
},
{
value: "sugars",
label: "Açúcares",
description: "Menor teor de açúcar primeiro",
icon: "Candy",
},
{
value: "completeness",
label: "Completude",
description: "Produtos com mais informações primeiro",
icon: "FileCheck",
},
{
value: "with_images",
label: "Com Fotos",
description: "Produtos com imagens primeiro",
icon: "Image",
},
{
value: "with_nutrition",
label: "Com Info Nutricional",
description: "Produtos com dados nutricionais completos",
icon: "Activity",
},
{
value: "eco_score",
label: "Eco-Score",
description: "Melhor impacto ambiental primeiro",
icon: "Leaf",
},
{
value: "nova_group",
label: "Nível de Processamento",
description: "Menos processados primeiro",
icon: "Layers",
},
];
// Categorias populares para filtros
export const POPULAR_CATEGORIES = [
"Beverages",
"Dairy",
"Snacks",
"Bakery",
"Meat",
"Fruits",
"Vegetables",
"Cereals",
"Sweets",
"Condiments",
"Frozen foods",
"Canned foods",
];
// Categorias com ícones para interface
export const POPULAR_CATEGORIES_WITH_ICONS = [
{ value: "Beverages", label: "Bebidas", icon: "Coffee" },
{ value: "Dairy", label: "Laticínios", icon: "Milk" },
{ value: "Snacks", label: "Snacks", icon: "Cookie" },
{ value: "Bakery", label: "Padaria", icon: "Croissant" },
{ value: "Meat", label: "Carnes", icon: "Beef" },
{ value: "Fruits", label: "Frutas", icon: "Apple" },
{ value: "Vegetables", label: "Vegetais", icon: "Carrot" },
{ value: "Cereals", label: "Cereais", icon: "Wheat" },
{ value: "Sweets", label: "Doces", icon: "Candy" },
{ value: "Condiments", label: "Condimentos", icon: "Soup" },
{ value: "Frozen foods", label: "Congelados", icon: "Snowflake" },
{ value: "Canned foods", label: "Enlatados", icon: "Package" },
];
// Alérgenos comuns
export const COMMON_ALLERGENS = [
"gluten",
"milk",
"eggs",
"nuts",
"peanuts",
"soy",
"fish",
"shellfish",
"sesame",
];
// Aditivos comuns
export const COMMON_ADDITIVES = [
"preservatives",
"artificial colors",
"artificial flavors",
"sweeteners",
"emulsifiers",
"stabilizers",
"thickeners",
];
// Tipos para sistema de cache e paginação
export interface PaginationState {
currentPage: number;
totalPages: number;
itemsPerPage: number;
totalItems: number;
startIndex: number;
endIndex: number;
}
export interface PaginationConfig {
itemsPerPage: number;
maxVisiblePages: number;
showFirstLast: boolean;
showPrevNext: boolean;
}
export interface CacheProgress {
currentPage: number;
totalPages: number;
loadedProducts: number;
totalProducts: number;
isLoading: boolean;
error?: string;
}
export interface SearchCacheConfig {
ttl: number; // Time to live em milissegundos
maxSize: number; // Tamanho máximo em bytes
maxEntries: number; // Número máximo de entradas
enableCompression: boolean; // Se deve comprimir dados
}
export interface PaginationProps {
currentPage: number;
totalPages: number;
totalItems: number;
itemsPerPage: number;
onPageChange: (page: number) => void;
onItemsPerPageChange?: (itemsPerPage: number) => void;
config?: Partial<PaginationConfig>;
className?: string;
}
// Tipos para Cache de Produtos
export interface CachedProduct {
code: string;
product_name: string;
brands?: string;
categories?: string;
ingredients_text?: string;
nutriments?: {
energy_100g?: number;
fat_100g?: number;
carbohydrates_100g?: number;
proteins_100g?: number;
sugars_100g?: number;
fiber_100g?: number;
sodium_100g?: number;
};
nutrition_grades?: string;
nova_group?: number;
image_url?: string;
image_small_url?: string;
image_front_url?: string;
image_front_small_url?: string;
countries?: string;
countries_tags?: string[];
allergens_tags?: string[];
additives_tags?: string[];
_metadata: {
firstSeen: number;
lastUpdated: number;
updateCount: number;
};
}
export interface StorageStats {
totalProducts: number;
storageSizeMB: number;
lastUpdated: number;
averageProductSize: number;
}
export interface CacheSearchResult {
products: CachedProduct[];
totalCount: number;
fromCache: boolean;
fromAPI: boolean;
}
export interface CacheFile {
products: Record<string, CachedProduct>;
metadata: {
totalProducts: number;
lastSync: number;
};
}
// Tipos para otimização de carregamento de imagens
export interface OptimizedImageProps {
src: string;
alt: string;
priority?: number;
onPageChange?: number;
className?: string;
fill?: boolean;
width?: number;
height?: number;
sizes?: string;
placeholder?: "blur" | "empty";
blurDataURL?: string;
quality?: number;
loading?: "lazy" | "eager";
onLoad?: () => void;
onError?: (error: Error) => void;
}
export interface ImageLoadTask {
src: string;
priority: number;
load: () => void;
onComplete?: () => void;
onError?: (error: Error) => void;
}
export interface ImageLoadingState {
isVisible: boolean;
shouldLoad: boolean;
isLoading: boolean;
hasError: boolean;
error: Error | null;
}
export interface UseOptimizedImageLoadingOptions {
currentPage: number;
itemsPerPage: number;
rootMargin?: string;
threshold?: number;
}