feat: versão 1.5 - CRM Beta com leads, funis, campanhas e portal do cliente

This commit is contained in:
Erik Silva
2025-12-24 17:36:52 -03:00
parent 99d828869a
commit dfb91c8ba5
98 changed files with 18255 additions and 1465 deletions

View File

@@ -0,0 +1,180 @@
'use server';
import { writeFile, mkdir } from 'fs/promises';
import { join } from 'path';
import { headers } from 'next/headers';
export async function registerCustomer(formData: FormData) {
try {
// Extrair campos do FormData
const personType = formData.get('person_type') as string;
const email = formData.get('email') as string;
const phone = formData.get('phone') as string;
const password = formData.get('password') as string;
const cpf = formData.get('cpf') as string || '';
const fullName = formData.get('full_name') as string || '';
const cnpj = formData.get('cnpj') as string || '';
const companyName = formData.get('company_name') as string || '';
const tradeName = formData.get('trade_name') as string || '';
const responsibleName = formData.get('responsible_name') as string || '';
const postalCode = formData.get('postal_code') as string || '';
const street = formData.get('street') as string || '';
const number = formData.get('number') as string || '';
const complement = formData.get('complement') as string || '';
const neighborhood = formData.get('neighborhood') as string || '';
const city = formData.get('city') as string || '';
const state = formData.get('state') as string || '';
const message = formData.get('message') as string || '';
const logoFile = formData.get('logo') as File | null;
console.log('[registerCustomer] Recebendo cadastro:', { personType, email, phone });
// Validar campos obrigatórios
if (!email || !phone || !password) {
return { success: false, error: 'E-mail, telefone e senha são obrigatórios' };
}
// Validar campos específicos por tipo
if (personType === 'pf') {
if (!cpf || !fullName) {
return { success: false, error: 'CPF e Nome Completo são obrigatórios para Pessoa Física' };
}
} else if (personType === 'pj') {
if (!cnpj || !companyName) {
return { success: false, error: 'CNPJ e Razão Social são obrigatórios para Pessoa Jurídica' };
}
if (!responsibleName) {
return { success: false, error: 'Nome do responsável é obrigatório para Pessoa Jurídica' };
}
}
// Processar upload de logo
let logoPath = '';
if (logoFile && logoFile.size > 0) {
try {
const bytes = await logoFile.arrayBuffer();
const buffer = Buffer.from(bytes);
// Criar nome único para o arquivo
const timestamp = Date.now();
const fileExt = logoFile.name.split('.').pop();
const fileName = `logo-${timestamp}.${fileExt}`;
const uploadDir = join(process.cwd(), 'public', 'uploads', 'logos');
logoPath = `/uploads/logos/${fileName}`;
// Garantir que o diretório existe
await mkdir(uploadDir, { recursive: true });
// Salvar arquivo
await writeFile(join(uploadDir, fileName), buffer);
console.log('[registerCustomer] Logo salvo:', logoPath);
} catch (uploadError) {
console.error('[registerCustomer] Error uploading logo:', uploadError);
// Continuar sem logo em caso de erro
}
}
// Buscar tenant_id do subdomínio validado pelo middleware
const headersList = await headers();
const hostname = headersList.get('host') || '';
const subdomain = hostname.split('.')[0];
console.log('[registerCustomer] Buscando tenant ID para subdomain:', subdomain);
// Buscar tenant completo do backend (agora com o campo 'id')
const tenantResponse = await fetch(`http://aggios-backend:8080/api/tenant/config?subdomain=${subdomain}`, {
headers: { 'Content-Type': 'application/json' },
});
if (!tenantResponse.ok) {
console.error('[registerCustomer] Erro ao buscar tenant:', tenantResponse.status);
throw new Error('Erro ao identificar a agência. Tente novamente.');
}
const tenantData = await tenantResponse.json();
const tenantId = tenantData.id;
if (!tenantId) {
throw new Error('Tenant não identificado. Acesso negado.');
}
console.log('[registerCustomer] Criando cliente para tenant:', tenantId);
// Preparar nome baseado no tipo
const customerName = personType === 'pf' ? fullName : (tradeName || companyName);
// Preparar endereço completo
const addressParts = [street, number, complement, neighborhood, city, state, postalCode].filter(Boolean);
const fullAddress = addressParts.join(', ');
const response = await fetch('http://aggios-backend:8080/api/public/customers/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tenant_id: tenantId,
name: customerName,
email: email,
phone: phone,
password: password,
company: personType === 'pj' ? companyName : '',
address: fullAddress,
notes: JSON.stringify({
person_type: personType,
cpf, cnpj, full_name: fullName, responsible_name: responsibleName, company_name: companyName, trade_name: tradeName,
postal_code: postalCode, street, number, complement, neighborhood, city, state,
message, logo_path: logoPath, email, phone,
}),
tags: ['cadastro_publico', 'pendente_aprovacao'],
status: 'lead',
source: 'cadastro_publico',
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error('[registerCustomer] Backend error:', errorText);
let errorMessage = 'Erro ao criar cadastro no servidor';
try {
const errorJson = JSON.parse(errorText);
if (errorJson.message) {
// Usar a mensagem clara do backend
errorMessage = errorJson.message;
} else if (errorJson.error) {
// Fallback para mensagens antigas
if (errorJson.error.includes('email') || errorJson.error.includes('duplicate_email')) {
errorMessage = 'Já existe uma conta cadastrada com este e-mail.';
} else if (errorJson.error.includes('duplicate_cpf')) {
errorMessage = 'Já existe uma conta cadastrada com este CPF.';
} else if (errorJson.error.includes('duplicate_cnpj')) {
errorMessage = 'Já existe uma conta cadastrada com este CNPJ.';
} else if (errorJson.error.includes('tenant')) {
errorMessage = 'Agência não identificada. Verifique o link de acesso.';
} else {
errorMessage = errorJson.error;
}
}
} catch (e) {
// Keep default error message
}
throw new Error(errorMessage);
}
const data = await response.json();
console.log('[registerCustomer] Cliente criado:', data.customer?.id);
return {
success: true,
message: 'Cadastro enviado com sucesso! Aguarde a aprovação da agência para receber suas credenciais de acesso.',
customer_id: data.customer?.id,
};
} catch (error: any) {
console.error('[registerCustomer] Error:', error);
return {
success: false,
error: error.message || 'Erro ao processar cadastro',
};
}
}