feat: integrate barcode scanning functionality in 'Almoxarifado' for improved product search and registration, along with image upload support for enhanced inventory management
This commit is contained in:
118
packages/backend/convex/actions/buscarInfoProduto.ts
Normal file
118
packages/backend/convex/actions/buscarInfoProduto.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { action } from '../_generated/server';
|
||||
import { v } from 'convex/values';
|
||||
|
||||
interface OpenFoodFactsProduct {
|
||||
product?: {
|
||||
product_name?: string;
|
||||
product_name_pt?: string;
|
||||
generic_name?: string;
|
||||
generic_name_pt?: string;
|
||||
categories?: string;
|
||||
categories_tags?: string[];
|
||||
image_url?: string;
|
||||
image_front_url?: string;
|
||||
image_front_small_url?: string;
|
||||
brands?: string;
|
||||
quantity?: string;
|
||||
packaging?: string;
|
||||
};
|
||||
status?: number;
|
||||
status_verbose?: string;
|
||||
}
|
||||
|
||||
interface ProductInfo {
|
||||
nome?: string;
|
||||
descricao?: string;
|
||||
categoria?: string;
|
||||
imagemUrl?: string;
|
||||
marca?: string;
|
||||
quantidade?: string;
|
||||
embalagem?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca informações de produto via API externa (Open Food Facts)
|
||||
* Esta é uma funcionalidade opcional que pode ser usada para preencher
|
||||
* automaticamente informações de produtos quando disponível.
|
||||
*/
|
||||
export const buscarInfoProdutoPorCodigoBarras = action({
|
||||
args: {
|
||||
codigoBarras: v.string()
|
||||
},
|
||||
handler: async (ctx, args): Promise<ProductInfo | null> => {
|
||||
const { codigoBarras } = args;
|
||||
|
||||
// Validar formato básico de código de barras (EAN-13, UPC, etc.)
|
||||
if (!codigoBarras || codigoBarras.length < 8 || codigoBarras.length > 14) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Tentar buscar na API Open Food Facts (gratuita, sem autenticação)
|
||||
const response = await fetch(
|
||||
`https://world.openfoodfacts.org/api/v0/product/${codigoBarras}.json`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': 'SGSE-App/1.0 (Almoxarifado)'
|
||||
},
|
||||
signal: AbortSignal.timeout(5000) // Timeout de 5 segundos
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = (await response.json()) as OpenFoodFactsProduct;
|
||||
|
||||
if (data.status !== 1 || !data.product) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const product = data.product;
|
||||
|
||||
// Extrair categoria (primeira categoria disponível)
|
||||
let categoria: string | undefined;
|
||||
if (product.categories_tags && product.categories_tags.length > 0) {
|
||||
// Pegar a primeira categoria e limpar tags
|
||||
const primeiraCategoria = product.categories_tags[0];
|
||||
categoria = primeiraCategoria
|
||||
.replace(/^pt:/, '')
|
||||
.replace(/^en:/, '')
|
||||
.replace(/-/g, ' ')
|
||||
.split(' ')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
} else if (product.categories) {
|
||||
categoria = product.categories.split(',')[0].trim();
|
||||
}
|
||||
|
||||
const info: ProductInfo = {
|
||||
nome: product.product_name_pt || product.product_name || undefined,
|
||||
descricao: product.generic_name_pt || product.generic_name || undefined,
|
||||
categoria,
|
||||
imagemUrl:
|
||||
product.image_front_url ||
|
||||
product.image_url ||
|
||||
product.image_front_small_url ||
|
||||
undefined,
|
||||
marca: product.brands || undefined,
|
||||
quantidade: product.quantity || undefined,
|
||||
embalagem: product.packaging || undefined
|
||||
};
|
||||
|
||||
// Retornar apenas se tiver pelo menos nome ou descrição
|
||||
if (info.nome || info.descricao) {
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
// Log do erro mas não falhar a operação
|
||||
console.error('Erro ao buscar informações do produto:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user