fix: correcao de login e configuracoes de ambiente local
Alteracoes: - Configura .env do backend-old com DATABASE_URL e porta 8214 - Atualiza server.go com correcoes de servidor - Adiciona package-lock.json e atualiza package.json do backend Medusa - Adiciona docker-compose.yml para servicos locais - Corrige authUtils.ts e CadastroProdutoWizard.tsx no frontend
This commit is contained in:
parent
9635455db2
commit
915a49967e
7 changed files with 19217 additions and 127 deletions
|
|
@ -30,7 +30,7 @@ MERCADOPAGO_PUBLIC_KEY=TEST-9c50d236-b8a9-4042-bdbf-93e4e0286f21
|
|||
# CORS_ORIGINS=*
|
||||
# CORS_ORIGINS=https://example.com
|
||||
# CORS_ORIGINS=https://app.saveinmed.com,https://admin.saveinmed.com,http://localhost:3000
|
||||
CORS_ORIGINS=http://localhost:3000
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
|
||||
|
||||
# Swagger Configuration
|
||||
# Host without scheme (ex: localhost:8214 or api.saveinmed.com)
|
||||
|
|
@ -41,5 +41,5 @@ SWAGGER_SCHEMES=http,https
|
|||
# Testing (Optional)
|
||||
# SKIP_DB_TEST=1
|
||||
|
||||
# API Configuration
|
||||
BACKEND_HOST=https://api.saveinmed.com.br
|
||||
# API Configuration (produção - comentado para dev)
|
||||
# BACKEND_HOST=https://api.saveinmed.com.br
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ func New(cfg config.Config) (*Server, error) {
|
|||
|
||||
auth := middleware.RequireAuth([]byte(cfg.JWTSecret))
|
||||
adminOnly := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin") // Keep for strict admin routes if any
|
||||
// Allow Admin, Superadmin, Dono, Gerente to manage products
|
||||
productManagers := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin", "superadmin", "Dono", "Gerente")
|
||||
// Allow Admin, Superadmin, Dono, Gerente, Seller to manage products
|
||||
productManagers := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin", "superadmin", "Dono", "Gerente", "Seller")
|
||||
|
||||
// Companies (Empresas)
|
||||
mux.Handle("POST /api/v1/companies", chain(http.HandlerFunc(h.CreateCompany), middleware.Logger, middleware.Gzip))
|
||||
|
|
@ -109,6 +109,7 @@ func New(cfg config.Config) (*Server, error) {
|
|||
mux.Handle("GET /api/v1/laboratorios", chain(http.HandlerFunc(h.ListManufacturers), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/v1/categorias", chain(http.HandlerFunc(h.ListCategories), middleware.Logger, middleware.Gzip))
|
||||
mux.Handle("GET /api/v1/produtos-catalogo", chain(http.HandlerFunc(h.ListProducts), middleware.Logger, middleware.Gzip)) // Alias
|
||||
mux.Handle("POST /api/v1/produtos-catalogo", chain(http.HandlerFunc(h.CreateProduct), middleware.Logger, middleware.Gzip, productManagers)) // Alias for frontend
|
||||
mux.Handle("GET /api/v1/produtos-catalogo/codigo-ean/{ean}", chain(http.HandlerFunc(h.GetProductByEAN), middleware.Logger, middleware.Gzip))
|
||||
|
||||
mux.Handle("GET /api/v1/inventory", chain(http.HandlerFunc(h.ListInventory), middleware.Logger, middleware.Gzip, auth))
|
||||
|
|
|
|||
19106
backend/package-lock.json
generated
Normal file
19106
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -25,7 +25,8 @@
|
|||
"@medusajs/admin-sdk": "2.12.5",
|
||||
"@medusajs/cli": "2.12.5",
|
||||
"@medusajs/framework": "2.12.5",
|
||||
"@medusajs/medusa": "2.12.5"
|
||||
"@medusajs/medusa": "2.12.5",
|
||||
"date-fns": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/test-utils": "2.12.5",
|
||||
|
|
|
|||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
version: "3.8"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: saveinmed-postgres
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: medusa-v2
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: saveinmed-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
@ -704,16 +704,17 @@ const CadastroProdutoWizard: React.FC = () => {
|
|||
const data = await response.json();
|
||||
|
||||
|
||||
// A API pode retornar { items: [...] } ou direto um array
|
||||
let produtos = data.items || data.data || data || [];
|
||||
// A API retorna { products: [...], total, page, page_size }
|
||||
let produtos = data.products || data.items || data.data || data || [];
|
||||
|
||||
|
||||
// Se for array, filtrar localmente por nome
|
||||
if (Array.isArray(produtos)) {
|
||||
const nomeMinusculo = nome.toLowerCase();
|
||||
const produtosFiltrados = produtos.filter(produto =>
|
||||
produto.nome && produto.nome.toLowerCase().includes(nomeMinusculo)
|
||||
);
|
||||
const produtosFiltrados = produtos.filter((produto: any) => {
|
||||
const prodNome = produto.nome || produto.name || "";
|
||||
return prodNome.toLowerCase().includes(nomeMinusculo);
|
||||
});
|
||||
|
||||
|
||||
if (produtosFiltrados.length > 0) {
|
||||
|
|
@ -850,55 +851,22 @@ const CadastroProdutoWizard: React.FC = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
setCriandoLaboratorio(true);
|
||||
// Laboratórios no backend são apenas strings na coluna manufacturer
|
||||
// Não é necessário criar via POST - basta usar o texto digitado
|
||||
const novoLab = {
|
||||
id: laboratorioNome.trim(),
|
||||
nome: laboratorioNome.trim(),
|
||||
};
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (!token) {
|
||||
toast.error("Token de acesso não encontrado");
|
||||
setCriandoLaboratorio(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const response = await fetch(`${process.env.NEXT_PUBLIC_BFF_API_URL}/laboratorios`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nome: laboratorioNome.trim()
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
toast.error(errorData.message || "Erro ao criar laboratório");
|
||||
setCriandoLaboratorio(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const novoLab = await response.json();
|
||||
|
||||
// Adicionar à lista de laboratórios
|
||||
// Adicionar à lista local de laboratórios para exibição
|
||||
setLaboratorios((prev) => [...prev, novoLab]);
|
||||
|
||||
// Selecionar o novo laboratório
|
||||
const labId = novoLab.$id || novoLab.id;
|
||||
setStepOne((prev) => ({ ...prev, laboratorio: labId }));
|
||||
setStepOne((prev) => ({ ...prev, laboratorio: novoLab.id }));
|
||||
setShowLaboratorioSuggestions(false);
|
||||
setLaboratoriosSugeridos([]);
|
||||
|
||||
toast.success("Laboratório criado com sucesso!");
|
||||
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar laboratório:", error);
|
||||
toast.error("Erro ao criar laboratório");
|
||||
} finally {
|
||||
setCriandoLaboratorio(false);
|
||||
}
|
||||
toast.success("Laboratório adicionado!");
|
||||
};
|
||||
|
||||
const handleSubmitStepOne = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
|
|
@ -925,79 +893,66 @@ const CadastroProdutoWizard: React.FC = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Primeiro, verificar se precisamos criar um novo laboratório
|
||||
let laboratorioId = stepOne.laboratorio;
|
||||
// Resolver nome do laboratório (manufacturer) - labs são strings, não entidades separadas
|
||||
let labNome = "";
|
||||
if (laboratorioNome.trim()) {
|
||||
// Usuário digitou um nome de laboratório (novo ou existente)
|
||||
labNome = laboratorioNome.trim();
|
||||
} else if (stepOne.laboratorio) {
|
||||
// Laboratório selecionado da lista
|
||||
const labSelecionado = laboratorios.find(lab => (lab.$id || lab.id || lab.nome) === stepOne.laboratorio);
|
||||
labNome = labSelecionado?.nome || labSelecionado?.name || stepOne.laboratorio;
|
||||
}
|
||||
|
||||
if (!laboratorioId && laboratorioNome.trim()) {
|
||||
// Criar novo laboratório
|
||||
// Resolver nome da categoria
|
||||
let catNome = "";
|
||||
if (stepOne.categoria) {
|
||||
const catSelecionada = categorias.find(cat => (cat.$id || cat.id || cat.nome) === stepOne.categoria);
|
||||
catNome = catSelecionada?.nome || catSelecionada?.name || stepOne.categoria;
|
||||
}
|
||||
|
||||
try {
|
||||
const labResponse = await fetch(`${process.env.NEXT_PUBLIC_BFF_API_URL}/laboratorios`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nome: laboratorioNome.trim()
|
||||
}),
|
||||
});
|
||||
// Buscar empresa_id do usuário logado
|
||||
let sellerIdStr = await getCurrentUserEmpresaId();
|
||||
if (!sellerIdStr) {
|
||||
sellerIdStr = empresaId || localStorage.getItem('empresaId');
|
||||
if (!sellerIdStr) {
|
||||
const userStr = localStorage.getItem('user');
|
||||
if (userStr) {
|
||||
const user = JSON.parse(userStr);
|
||||
sellerIdStr = user.company_id || user.empresa_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!labResponse.ok) {
|
||||
const errorData = await labResponse.json().catch(() => ({}));
|
||||
toast.error(errorData.message || "Erro ao criar laboratório");
|
||||
if (!sellerIdStr) {
|
||||
toast.error("Erro ao obter dados da empresa. Faça login novamente.");
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const novoLab = await labResponse.json();
|
||||
|
||||
// Adicionar à lista de laboratórios
|
||||
setLaboratorios((prev) => [...prev, novoLab]);
|
||||
|
||||
laboratorioId = novoLab.$id || novoLab.id;
|
||||
labNome = novoLab.nome || novoLab.name || laboratorioNome.trim();
|
||||
|
||||
// Atualizar estado do stepOne
|
||||
setStepOne((prev) => ({ ...prev, laboratorio: laboratorioId }));
|
||||
|
||||
} catch (error) {
|
||||
console.error("Erro ao criar laboratório:", error);
|
||||
toast.error("Erro ao criar laboratório");
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Buscar nome do laboratório existente
|
||||
const labSelecionado = laboratorios.find(lab => (lab.$id || lab.id) === laboratorioId);
|
||||
labNome = labSelecionado?.nome || labSelecionado?.name || '';
|
||||
}
|
||||
|
||||
// Buscar os nomes de categoria
|
||||
const catSelecionada = categorias.find(cat => (cat.$id || cat.id) === stepOne.categoria);
|
||||
const catNome = catSelecionada?.nome || catSelecionada?.name || '';
|
||||
|
||||
const payload = {
|
||||
documentId: "unique()",
|
||||
data: {
|
||||
codigo_ean: stepOne.codigo_ean.trim(),
|
||||
codigo_interno: stepOne.codigo_interno.trim() || stepOne.codigo_ean.trim(),
|
||||
nome: stepOne.nome.trim(),
|
||||
descricao: stepOne.descricao.trim() || "Descrição do produto",
|
||||
// Usar valores padrão para campos ocultos
|
||||
preco_base: Number(stepOne.preco_base) || 0,
|
||||
preco_fabrica: Number(stepOne.preco_fabrica) || 0,
|
||||
pmc: Number(stepOne.pmc) || 0,
|
||||
desconto_comercial: Number(stepOne.desconto_comercial) || 10,
|
||||
valor_substituicao_tributaria: Number(stepOne.valor_substituicao_tributaria) || 5.00,
|
||||
preco_nf: Number(stepOne.preco_nf) || 0,
|
||||
lab_nome: labNome,
|
||||
cat_nome: catNome,
|
||||
}
|
||||
// Converter valores monetários para centavos (Backend Go espera int64 cents)
|
||||
const toCents = (val: string | number): number => {
|
||||
const num = typeof val === 'string' ? parseFloat(val) : val;
|
||||
return Number.isFinite(num) ? Math.round(num * 100) : 0;
|
||||
};
|
||||
|
||||
// Payload no formato registerProductRequest do Backend Go
|
||||
const payload = {
|
||||
seller_id: sellerIdStr,
|
||||
ean_code: stepOne.codigo_ean.trim(),
|
||||
name: stepOne.nome.trim(),
|
||||
description: stepOne.descricao.trim() || "Descrição do produto",
|
||||
manufacturer: labNome,
|
||||
category: catNome,
|
||||
subcategory: stepOne.subcategoria.trim() || "",
|
||||
price_cents: toCents(stepOne.preco_base),
|
||||
internal_code: stepOne.codigo_interno.trim() || stepOne.codigo_ean.trim(),
|
||||
factory_price_cents: toCents(stepOne.preco_fabrica),
|
||||
pmc_cents: toCents(stepOne.pmc),
|
||||
commercial_discount_cents: toCents(stepOne.desconto_comercial),
|
||||
tax_substitution_cents: toCents(stepOne.valor_substituicao_tributaria),
|
||||
invoice_price_cents: toCents(stepOne.preco_nf),
|
||||
};
|
||||
|
||||
const response = await fetch(`${process.env.NEXT_PUBLIC_BFF_API_URL}/produtos-catalogo`, {
|
||||
method: 'POST',
|
||||
|
|
@ -1013,13 +968,13 @@ const CadastroProdutoWizard: React.FC = () => {
|
|||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
toast.error(errorData.message || "Erro ao criar produto no catálogo");
|
||||
toast.error(errorData.error || errorData.message || "Erro ao criar produto no catálogo");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const catalogoId = data.documentId || data.$id || data.id;
|
||||
const catalogoId = data.id || data.documentId || data.$id;
|
||||
|
||||
if (catalogoId) {
|
||||
setReferenciaCatalogoId(catalogoId);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,11 @@ export function extractEmpresaId(userData: UserMeResponse | null): string | null
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Fallback: verificar company_id direto (Backend Go retorna isso)
|
||||
const anyData = userData as any;
|
||||
if (anyData.company_id && typeof anyData.company_id === 'string') {
|
||||
return anyData.company_id;
|
||||
}
|
||||
|
||||
// Verifica se há empresas associadas
|
||||
if (!userData.empresasDados) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue