testes/Modulos Angular/scripts/create-domain-v2.js

1263 lines
39 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
const readline = require('readline');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const https = require('https');
const http = require('http');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 🎨 Cores para console
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m'
};
const log = {
info: (msg) => console.log(`${colors.blue} ${msg}${colors.reset}`),
success: (msg) => console.log(`${colors.green}${msg}${colors.reset}`),
warning: (msg) => console.log(`${colors.yellow}⚠️ ${msg}${colors.reset}`),
error: (msg) => console.log(`${colors.red}${msg}${colors.reset}`),
title: (msg) => console.log(`${colors.cyan}${colors.bright}🚀 ${msg}${colors.reset}\n`),
step: (msg) => console.log(`${colors.bright}🔧 ${msg}${colors.reset}`)
};
// 🔐 Configuração da API PraFrota
let apiCredentials = {
token: null,
tenantId: null,
baseUrl: 'https://prafrota-be-bff-tenant-api.grupopra.tech'
};
// 📝 Configuração do domínio V2.0 - EXPANDIDA
let domainConfig = {
// Básicos
name: '',
displayName: '',
menuPosition: '',
// Funcionalidades V1
hasPhotos: false,
hasSideCard: false,
hasKilometer: false,
hasColor: false,
hasStatus: false,
remoteSelects: [],
// ✨ NOVOS V2.0 - Core Patterns
hasFooter: false,
footerConfig: {
columns: [] // { field, type, format, label, precision }
},
hasCheckboxGrouped: false,
checkboxGroupedConfig: {
fieldName: 'options',
groups: [] // { id, label, icon, items }
},
hasBulkActions: false,
bulkActionsConfig: {
type: 'basic', // 'basic' | 'advanced'
actions: []
},
// ✨ NOVOS V2.0 - Enhanced Patterns
hasDateRangeUtils: false,
hasAdvancedSideCard: false,
sideCardConfig: {
imageField: '',
statusConfig: {},
formatFunctions: []
},
hasExtendedSearchOptions: false,
searchOptionsConfig: {
useStates: false,
useVehicleTypes: false,
useStatusComplex: false,
customOptions: []
},
// ✨ NOVOS V2.0 - Advanced Patterns
hasAdvancedSubTabs: false,
subTabsConfig: {
enableComponents: false,
dynamicComponents: []
}
};
// 🎯 TEMPLATES LIBRARY V2.0
// ===== FOOTER CONFIG TEMPLATES =====
const FooterTemplates = {
count: {
type: 'count',
format: 'default',
label: 'Total:',
precision: 0
},
sum_currency: {
type: 'sum',
format: 'currency',
label: 'Total:',
precision: 2
},
sum_number: {
type: 'sum',
format: 'number',
label: 'Total:',
precision: 2
},
avg_number: {
type: 'avg',
format: 'number',
label: 'Média:',
precision: 1
},
avg_default: {
type: 'avg',
format: 'default',
label: 'Média:',
precision: 0
},
min_number: {
type: 'min',
format: 'number',
label: 'Mínimo:',
precision: 2
},
max_number: {
type: 'max',
format: 'number',
label: 'Máximo:',
precision: 2
}
};
// ===== CHECKBOX GROUPED TEMPLATES =====
const CheckboxGroupedTemplates = {
security: {
id: 'security',
label: 'Segurança',
icon: 'fa-shield-alt',
items: [
{ id: 1, name: 'Airbag', value: false },
{ id: 2, name: 'Freios ABS', value: false },
{ id: 3, name: 'Alarme antifurto', value: false },
{ id: 4, name: 'Cinto de 3 pontos', value: false },
{ id: 5, name: 'Controle de tração', value: false }
]
},
comfort: {
id: 'comfort',
label: 'Conforto',
icon: 'fa-couch',
expanded: false,
items: [
{ id: 51, name: 'Ar-condicionado', value: false },
{ id: 52, name: 'Direção elétrica', value: false },
{ id: 53, name: 'Vidros elétricos', value: false },
{ id: 54, name: 'Piloto automático', value: false }
]
},
multimedia: {
id: 'multimedia',
label: 'Multimídia',
icon: 'fa-tv',
expanded: false,
items: [
{ id: 101, name: 'Central multimídia', value: false },
{ id: 102, name: 'GPS integrado', value: false },
{ id: 103, name: 'Bluetooth', value: false },
{ id: 104, name: 'USB', value: false }
]
}
};
// ===== BULK ACTIONS TEMPLATES =====
const BulkActionsTemplates = {
basic: [
{
id: 'delete-selected',
label: 'Excluir Selecionados',
icon: 'fas fa-trash',
action: `(selectedItems) => this.bulkDelete(selectedItems as ${capitalize(domainConfig.name)}[])`
},
{
id: 'export-selected',
label: 'Exportar Selecionados',
icon: 'fas fa-download',
action: `(selectedItems) => this.bulkExport(selectedItems as ${capitalize(domainConfig.name)}[])`
}
],
advanced: [
{
id: 'update-data',
label: 'Atualizar Dados',
icon: 'fas fa-sync-alt',
subActions: [
{
id: 'update-api-external',
label: 'Via API Externa',
icon: 'fas fa-cloud',
action: `(selectedItems) => this.updateViaExternalAPI(selectedItems as ${capitalize(domainConfig.name)}[])`
},
{
id: 'update-bulk-edit',
label: 'Edição em Lote',
icon: 'fas fa-edit',
action: `(selectedItems) => this.bulkEdit(selectedItems as ${capitalize(domainConfig.name)}[])`
}
]
},
{
id: 'status-change',
label: 'Alterar Status',
icon: 'fas fa-toggle-on',
action: `(selectedItems) => this.bulkStatusChange(selectedItems as ${capitalize(domainConfig.name)}[])`
}
]
};
// ===== SEARCH OPTIONS LIBRARY =====
const SearchOptionsLibrary = {
estados: [
{ value: "AC", label: "Acre" },
{ value: "AL", label: "Alagoas" },
{ value: "AP", label: "Amapá" },
{ value: "AM", label: "Amazonas" },
{ value: "BA", label: "Bahia" },
{ value: "CE", label: "Ceará" },
{ value: "DF", label: "Distrito Federal" },
{ value: "ES", label: "Espírito Santo" },
{ value: "GO", label: "Goiás" },
{ value: "MA", label: "Maranhão" },
{ value: "MT", label: "Mato Grosso" },
{ value: "MS", label: "Mato Grosso do Sul" },
{ value: "MG", label: "Minas Gerais" },
{ value: "PA", label: "Pará" },
{ value: "PB", label: "Paraíba" },
{ value: "PR", label: "Paraná" },
{ value: "PE", label: "Pernambuco" },
{ value: "PI", label: "Piauí" },
{ value: "RJ", label: "Rio de Janeiro" },
{ value: "RN", label: "Rio Grande do Norte" },
{ value: "RS", label: "Rio Grande do Sul" },
{ value: "RO", label: "Rondônia" },
{ value: "RR", label: "Roraima" },
{ value: "SC", label: "Santa Catarina" },
{ value: "SP", label: "São Paulo" },
{ value: "SE", label: "Sergipe" },
{ value: "TO", label: "Tocantins" }
],
vehicleTypes: [
{ value: 'CAR', label: 'Carro' },
{ value: 'PICKUP_TRUCK', label: 'Caminhonete' },
{ value: 'TRUCK', label: 'Caminhão' },
{ value: 'TRUCK_TRAILER', label: 'Caminhão com Carreta' },
{ value: 'MOTORCYCLE', label: 'Motocicleta' },
{ value: 'VAN', label: 'Van' },
{ value: 'BUS', label: 'Ônibus' },
{ value: 'TRAILER', label: 'Carreta' },
{ value: 'SEMI_TRUCK', label: 'Semi-Reboque' },
{ value: 'MINIBUS', label: 'Micro Ônibus' },
{ value: 'MOTOR_SCOOTER', label: 'Motoneta' },
{ value: 'MOPED', label: 'Ciclomotor' },
{ value: 'TRICYCLE', label: 'Triciclo' },
{ value: 'UTILITY', label: 'Utilitário' },
{ value: 'OTHER', label: 'Outro' }
],
statusComplex: [
{ value: 'active', label: 'Ativo' },
{ value: 'inactive', label: 'Inativo' },
{ value: 'pending', label: 'Pendente' },
{ value: 'processing', label: 'Processando' },
{ value: 'completed', label: 'Concluído' },
{ value: 'cancelled', label: 'Cancelado' },
{ value: 'suspended', label: 'Suspenso' },
{ value: 'archived', label: 'Arquivado' }
]
};
// 🔐 FUNÇÕES DE CONSULTA À API REAL
async function requestAuthCredentials() {
log.title('🔐 CONFIGURAÇÃO DA API');
log.info('Para gerar interfaces baseadas em dados reais, precisamos acessar a API PraFrota.');
log.info('');
log.info('📋 Como obter as credenciais:');
log.info('1. Abra a aplicação no navegador (localhost:4200)');
log.info('2. Faça login normalmente');
log.info('3. Abra DevTools (F12) → Console');
log.info('4. Execute: localStorage.getItem("prafrota_auth_token")');
log.info('5. Execute: localStorage.getItem("tenant_id")');
log.info('');
return new Promise((resolve) => {
rl.question(`${colors.bright}🔑 Token de autenticação (ou deixe vazio para pular):${colors.reset} `, (token) => {
if (!token.trim()) {
log.warning('Token não fornecido. Usando geração de interface padrão.');
apiCredentials.token = null;
apiCredentials.tenantId = null;
resolve();
return;
}
apiCredentials.token = token.trim();
rl.question(`${colors.bright}🏢 Tenant ID:${colors.reset} `, (tenantId) => {
apiCredentials.tenantId = tenantId.trim();
log.success('Credenciais configuradas! Tentaremos acessar dados reais da API.');
resolve();
});
});
});
}
async function analyzeRealAPI(domainName) {
if (!apiCredentials.token || !apiCredentials.tenantId) {
log.info('Credenciais não fornecidas. Usando interface padrão.');
return { success: false, reason: 'no_credentials' };
}
log.step(`🔍 Analisando API real para domínio: ${domainName}`);
// Endpoints para testar (singular e plural)
const endpoints = [
`${domainName}?page=1&limit=1`,
`${domainName}s?page=1&limit=1`,
`api/${domainName}?page=1&limit=1`,
`api/${domainName}s?page=1&limit=1`
];
for (const endpoint of endpoints) {
try {
log.info(`🔍 Testando endpoint: ${endpoint}`);
const response = await makeAPIRequest(endpoint);
if (response && response.data && Array.isArray(response.data) && response.data.length > 0) {
const sampleObject = response.data[0];
log.success(`✅ Dados reais encontrados! ${Object.keys(sampleObject).length} campos detectados`);
log.info(`📊 Total de registros: ${response.totalCount || 'N/A'}`);
log.info(`🔗 Endpoint usado: ${endpoint}`);
// Mostrar preview dos campos
log.info('📝 Campos detectados:');
Object.entries(sampleObject).slice(0, 5).forEach(([key, value]) => {
const type = typeof value;
const preview = type === 'string' && value.length > 30
? value.substring(0, 30) + '...'
: value;
log.info(`${key} (${type}): ${preview}`);
});
if (Object.keys(sampleObject).length > 5) {
log.info(` ... e mais ${Object.keys(sampleObject).length - 5} campos`);
}
return {
success: true,
strategy: 'real_api_data',
interface: generateInterfaceFromRealData(domainName, sampleObject),
fields: extractFieldsFromRealData(sampleObject),
metadata: {
source: 'authenticated_api',
endpoint: endpoint,
sampleSize: response.data.length,
totalCount: response.totalCount || 'unknown',
timestamp: new Date().toISOString()
}
};
} else {
log.warning(`⚠️ Endpoint ${endpoint} sem dados válidos`);
}
} catch (error) {
log.warning(`⚠️ Erro no endpoint ${endpoint}: ${error.message}`);
}
}
log.error('❌ Nenhum endpoint retornou dados válidos');
return {
success: false,
reason: 'no_data_found',
message: 'Endpoints existem mas não retornaram dados válidos. Verifique se o domínio existe na API.'
};
}
async function makeAPIRequest(endpoint) {
const fullUrl = `${apiCredentials.baseUrl}/${endpoint}`;
return new Promise((resolve, reject) => {
const urlParts = new URL(fullUrl);
const options = {
hostname: urlParts.hostname,
port: urlParts.port || 443,
path: urlParts.pathname + urlParts.search,
method: 'GET',
headers: {
'Accept': 'application/json',
'x-tenant-user-auth': apiCredentials.token,
'x-tenant-uuid': apiCredentials.tenantId,
'User-Agent': 'Create-Domain-V2.0'
}
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (error) {
reject(new Error('Invalid JSON response'));
}
});
});
req.on('error', (error) => {
reject(error);
});
req.setTimeout(10000, () => {
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
}
function generateInterfaceFromRealData(domainName, sampleObject) {
const className = domainName.charAt(0).toUpperCase() + domainName.slice(1);
let interfaceCode = `/**
* 🎯 ${className} Interface - DADOS REAIS DA API PraFrota
*
* ✨ Auto-gerado a partir de dados reais da API autenticada
* 📅 Gerado em: ${new Date().toLocaleString()}
* 🔗 Fonte: API Response Analysis com autenticação
*/
export interface ${className} {\n`;
Object.entries(sampleObject).forEach(([key, value]) => {
const type = detectTypeScriptFromRealData(value);
const isOptional = value === null || value === undefined;
const optional = isOptional ? '?' : '';
const description = inferFieldDescription(key, type);
interfaceCode += ` ${key}${optional}: ${type}; // ${description}\n`;
});
interfaceCode += '}';
return interfaceCode;
}
function detectTypeScriptFromRealData(value) {
if (value === null || value === undefined) return 'any';
const type = typeof value;
if (type === 'string') {
// Detectar datas ISO
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) return 'string';
// Detectar emails
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'string';
return 'string';
}
if (type === 'number') return 'number';
if (type === 'boolean') return 'boolean';
if (Array.isArray(value)) {
if (value.length === 0) return 'any[]';
return `${detectTypeScriptFromRealData(value[0])}[]`;
}
if (type === 'object') return 'any';
return 'any';
}
function extractFieldsFromRealData(sampleObject) {
return Object.entries(sampleObject).map(([name, value]) => ({
name,
type: detectTypeScriptFromRealData(value),
required: value !== null && value !== undefined,
description: inferFieldDescription(name, detectTypeScriptFromRealData(value)),
realValue: typeof value === 'string' ? value.substring(0, 50) + '...' : value
}));
}
function inferFieldDescription(fieldName, type) {
const descriptions = {
id: 'Identificador único',
name: 'Nome do registro',
email: 'Endereço de email',
phone: 'Número de telefone',
address: 'Endereço',
status: 'Status do registro',
active: 'Se está ativo',
created_at: 'Data de criação',
updated_at: 'Data de atualização',
deleted_at: 'Data de exclusão',
description: 'Descrição',
title: 'Título',
price: 'Preço',
amount: 'Valor/Quantia',
quantity: 'Quantidade',
user_id: 'ID do usuário',
company_id: 'ID da empresa',
tenant_id: 'ID do tenant',
brand: 'Marca',
model: 'Modelo',
year: 'Ano',
plate: 'Placa'
};
return descriptions[fieldName] || `Campo ${fieldName} (${type})`;
}
// 🎯 Função principal
async function main() {
try {
log.title('CRIADOR DE DOMÍNIOS V2.0 - SISTEMA PRAFROTA');
log.info('🆕 Versão 2.0 com FooterConfig, CheckboxGrouped, BulkActions e mais!');
// Verificar pré-requisitos
await checkPrerequisites();
// 🔐 Solicitar credenciais da API (novo V2.0)
await requestAuthCredentials();
// Coletar informações V2.0
await gatherDomainInfoV2();
// Confirmar configuração
await confirmConfigurationV2();
// Criar branch para o desenvolvimento
await createBranch();
// Gerar domínio V2.0
await generateDomainV2();
// Compilar e testar
await compileAndTest();
// Commit automático
await autoCommit();
log.success('🎉 DOMÍNIO V2.0 CRIADO COM SUCESSO!');
log.success('🚀 Sistema totalmente integrado com todos os padrões mais recentes!');
showNewFeaturesV2();
} catch (error) {
log.error(`Erro: ${error.message}`);
process.exit(1);
} finally {
rl.close();
}
}
// ===== V2.0 SPECIFIC FUNCTIONS =====
// 📝 Coletar informações V2.0
async function gatherDomainInfoV2() {
log.title('CONFIGURAÇÃO DO DOMÍNIO V2.0');
// Básicos
await gatherBasicInfo();
// ✨ NOVO: Footer Configuration
await gatherFooterConfig();
// ✨ NOVO: Checkbox Grouped Configuration
await gatherCheckboxGroupedConfig();
// ✨ NOVO: Bulk Actions Configuration
await gatherBulkActionsConfig();
// ✨ NOVO: Enhanced Features
await gatherEnhancedFeaturesConfig();
// Funcionalidades V1 (legacy)
await gatherLegacyFeatures();
}
// Helper para perguntas yes/no
function askYesNo(question) {
return new Promise((resolve) => {
rl.question(`${question} (s/N): `, (answer) => {
resolve(answer.toLowerCase() === 's' || answer.toLowerCase() === 'sim');
});
});
}
// Helper para perguntas com opções
function askOptions(question, options) {
return new Promise((resolve) => {
const optionsText = options.map((opt, i) => `${i + 1}. ${opt}`).join('\n');
rl.question(`${question}\n${optionsText}\nEscolha (1-${options.length}): `, (answer) => {
const index = parseInt(answer) - 1;
if (index >= 0 && index < options.length) {
resolve(options[index]);
} else {
log.warning('Opção inválida, usando primeira opção');
resolve(options[0]);
}
});
});
}
// Configuração básica
async function gatherBasicInfo() {
domainConfig.name = await ask('Nome do domínio (ex: products): ');
domainConfig.displayName = await ask('Nome para exibição (ex: Produtos): ');
domainConfig.menuPosition = await ask('Posição no menu (após qual item): ');
}
// ✨ NOVO: Footer Configuration
async function gatherFooterConfig() {
log.info('\n📊 CONFIGURAÇÃO DE FOOTER NAS COLUNAS');
domainConfig.hasFooter = await askYesNo('Deseja footer com totalização nas colunas?');
if (domainConfig.hasFooter) {
log.info('Tipos disponíveis: count, sum_currency, sum_number, avg_number, avg_default, min_number, max_number');
const numColumns = await askNumber('Quantas colunas terão footer? (1-5): ', 1, 5);
for (let i = 0; i < numColumns; i++) {
log.info(`\n--- Coluna ${i + 1} ---`);
const field = await ask(`Campo da coluna ${i + 1} (ex: price, quantity): `);
const type = await askOptions(`Tipo de footer para ${field}:`, [
'count', 'sum_currency', 'sum_number', 'avg_number', 'avg_default'
]);
domainConfig.footerConfig.columns.push({
field,
...FooterTemplates[type]
});
}
}
}
// ✨ NOVO: Checkbox Grouped Configuration
async function gatherCheckboxGroupedConfig() {
log.info('\n☑ CONFIGURAÇÃO DE CHECKBOX AGRUPADO');
domainConfig.hasCheckboxGrouped = await askYesNo('Deseja checkbox agrupado (ex: opcionais, características)?');
if (domainConfig.hasCheckboxGrouped) {
domainConfig.checkboxGroupedConfig.fieldName = await ask('Nome do campo (ex: options, features): ') || 'options';
const groupTypes = await askOptions('Quais grupos incluir:', [
'Segurança + Conforto',
'Segurança + Conforto + Multimídia',
'Todos os grupos',
'Customizado'
]);
switch (groupTypes) {
case 'Segurança + Conforto':
domainConfig.checkboxGroupedConfig.groups = [
CheckboxGroupedTemplates.security,
CheckboxGroupedTemplates.comfort
];
break;
case 'Segurança + Conforto + Multimídia':
domainConfig.checkboxGroupedConfig.groups = [
CheckboxGroupedTemplates.security,
CheckboxGroupedTemplates.comfort,
CheckboxGroupedTemplates.multimedia
];
break;
case 'Todos os grupos':
domainConfig.checkboxGroupedConfig.groups = Object.values(CheckboxGroupedTemplates);
break;
default:
log.info('Modo customizado não implementado, usando Segurança + Conforto');
domainConfig.checkboxGroupedConfig.groups = [
CheckboxGroupedTemplates.security,
CheckboxGroupedTemplates.comfort
];
}
}
}
// ✨ NOVO: Bulk Actions Configuration
async function gatherBulkActionsConfig() {
log.info('\n⚡ CONFIGURAÇÃO DE AÇÕES EM LOTE');
domainConfig.hasBulkActions = await askYesNo('Deseja ações em lote (bulk actions)?');
if (domainConfig.hasBulkActions) {
domainConfig.bulkActionsConfig.type = await askOptions('Tipo de ações:', [
'basic', 'advanced'
]);
domainConfig.bulkActionsConfig.actions = BulkActionsTemplates[domainConfig.bulkActionsConfig.type];
}
}
// ✨ NOVO: Enhanced Features Configuration
async function gatherEnhancedFeaturesConfig() {
log.info('\n🚀 FUNCIONALIDADES AVANÇADAS');
// DateRangeUtils
domainConfig.hasDateRangeUtils = await askYesNo('Integrar DateRangeUtils automaticamente?');
// Advanced SideCard
if (await askYesNo('Deseja SideCard avançado (com statusConfig e imageField)?')) {
domainConfig.hasAdvancedSideCard = true;
domainConfig.sideCardConfig.imageField = await ask('Campo para imagem (ex: photos, images): ') || 'photos';
}
// Extended Search Options
domainConfig.hasExtendedSearchOptions = await askYesNo('Usar searchOptions pré-definidos (Estados, Status, etc.)?');
if (domainConfig.hasExtendedSearchOptions) {
domainConfig.searchOptionsConfig.useStates = await askYesNo(' • Incluir Estados (UF)?');
domainConfig.searchOptionsConfig.useVehicleTypes = await askYesNo(' • Incluir Tipos de Veículo?');
domainConfig.searchOptionsConfig.useStatusComplex = await askYesNo(' • Incluir Status Complexos?');
}
// Advanced SubTabs
domainConfig.hasAdvancedSubTabs = await askYesNo('Usar SubTabs avançadas (componentes dinâmicos)?');
}
// Legacy features (V1)
async function gatherLegacyFeatures() {
log.info('\n📋 FUNCIONALIDADES BÁSICAS');
domainConfig.hasPhotos = await askYesNo('Incluir sub-aba de fotos?');
domainConfig.hasSideCard = await askYesNo('Incluir side card básico?') && !domainConfig.hasAdvancedSideCard;
domainConfig.hasStatus = await askYesNo('Incluir campo de status?');
domainConfig.hasKilometer = await askYesNo('Incluir campo de quilometragem?');
domainConfig.hasColor = await askYesNo('Incluir campo de cor?');
}
// Helper functions
function ask(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer.trim());
});
});
}
function askNumber(question, min = 1, max = 10) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
const num = parseInt(answer);
if (num >= min && num <= max) {
resolve(num);
} else {
log.warning(`Número inválido, usando ${min}`);
resolve(min);
}
});
});
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// Mostrar novas funcionalidades V2.0
function showNewFeaturesV2() {
log.info('\n🎯 FUNCIONALIDADES V2.0 IMPLEMENTADAS:');
if (domainConfig.hasFooter) {
log.success(` ✅ FooterConfig: ${domainConfig.footerConfig.columns.length} colunas com totalização`);
}
if (domainConfig.hasCheckboxGrouped) {
log.success(` ✅ CheckboxGrouped: ${domainConfig.checkboxGroupedConfig.groups.length} grupos configurados`);
}
if (domainConfig.hasBulkActions) {
log.success(` ✅ BulkActions: Modo ${domainConfig.bulkActionsConfig.type}`);
}
if (domainConfig.hasDateRangeUtils) {
log.success(` ✅ DateRangeUtils: Integração automática`);
}
if (domainConfig.hasAdvancedSideCard) {
log.success(` ✅ SideCard Avançado: Com statusConfig e imageField`);
}
if (domainConfig.hasExtendedSearchOptions) {
log.success(` ✅ SearchOptions Estendidos: Estados, Tipos, Status`);
}
log.info('\n📝 Próximos passos:');
log.info(` 1. Testar: http://localhost:4200/app/${domainConfig.name}`);
log.info(` 2. Push: git push origin feature/domain-${domainConfig.name}`);
log.info(` 3. Documentar: Adicionar no .mcp/config.json`);
}
// Confirmar configuração V2.0
async function confirmConfigurationV2() {
log.title('CONFIRMAÇÃO DA CONFIGURAÇÃO V2.0');
console.log('📋 Resumo:');
console.log(` • Domínio: ${domainConfig.name} (${domainConfig.displayName})`);
console.log(` • Menu: Após "${domainConfig.menuPosition}"`);
// V2.0 Features
console.log('\n🆕 Funcionalidades V2.0:');
console.log(` • Footer: ${domainConfig.hasFooter ? '✅ ' + domainConfig.footerConfig.columns.length + ' colunas' : '❌'}`);
console.log(` • CheckboxGrouped: ${domainConfig.hasCheckboxGrouped ? '✅ ' + domainConfig.checkboxGroupedConfig.groups.length + ' grupos' : '❌'}`);
console.log(` • BulkActions: ${domainConfig.hasBulkActions ? '✅ ' + domainConfig.bulkActionsConfig.type : '❌'}`);
console.log(` • DateRangeUtils: ${domainConfig.hasDateRangeUtils ? '✅' : '❌'}`);
console.log(` • SideCard Avançado: ${domainConfig.hasAdvancedSideCard ? '✅' : '❌'}`);
console.log(` • SearchOptions Estendidos: ${domainConfig.hasExtendedSearchOptions ? '✅' : '❌'}`);
// V1 Features
console.log('\n📋 Funcionalidades Básicas:');
console.log(` • Fotos: ${domainConfig.hasPhotos ? '✅' : '❌'}`);
console.log(` • SideCard: ${domainConfig.hasSideCard ? '✅' : '❌'}`);
console.log(` • Status: ${domainConfig.hasStatus ? '✅' : '❌'}`);
console.log(` • Quilometragem: ${domainConfig.hasKilometer ? '✅' : '❌'}`);
console.log(` • Cor: ${domainConfig.hasColor ? '✅' : '❌'}`);
const confirm = await askYesNo('\nConfirma a criação do domínio com estas configurações?');
if (!confirm) {
throw new Error('Operação cancelada pelo usuário');
}
}
// ===== GERAÇÃO V2.0 =====
// Gerar domínio V2.0
async function generateDomainV2() {
log.title('GERANDO DOMÍNIO V2.0');
// Criar estrutura de pastas
await createDirectoryStructure();
// Gerar arquivos V2.0
await generateComponentV2();
await generateServiceV2();
await generateInterfaceV2();
await generateTemplatesV2();
// Integrações V2.0
await updateRoutingV2();
await updateSidebarV2();
await updateMCPConfigV2();
log.success('Domínio V2.0 gerado com sucesso!');
}
// ===== FUNÇÕES AUXILIARES V2.0 =====
// Verificar pré-requisitos
async function checkPrerequisites() {
log.info('Verificando pré-requisitos...');
// Verificar se está na branch main
try {
const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
if (currentBranch !== 'main') {
throw new Error(`Você deve estar na branch main. Branch atual: ${currentBranch}`);
}
log.success('Branch main ativa');
} catch (error) {
throw new Error('Erro ao verificar branch Git');
}
// Verificar se Git está configurado
try {
const gitUser = execSync('git config user.name', { encoding: 'utf8' }).trim();
const gitEmail = execSync('git config user.email', { encoding: 'utf8' }).trim();
log.success(`Git configurado: ${gitUser} <${gitEmail}>`);
} catch (error) {
throw new Error('Git não configurado. Execute: git config --global user.name "Seu Nome" && git config --global user.email "seu@email.com"');
}
}
// Criar branch para desenvolvimento
async function createBranch() {
log.title('CRIANDO BRANCH DE DESENVOLVIMENTO');
const branchName = `feature/domain-${domainConfig.name}`;
try {
log.info(`Criando branch: ${branchName}`);
execSync(`git checkout -b ${branchName}`, { stdio: 'inherit' });
log.success(`Branch ${branchName} criada e ativada`);
} catch (error) {
throw new Error(`Erro ao criar branch: ${error.message}`);
}
}
// Criar estrutura de diretórios
async function createDirectoryStructure() {
const domainPath = `projects/idt_app/src/app/domain/${domainConfig.name}`;
log.info('Criando estrutura de diretórios...');
if (!fs.existsSync(domainPath)) {
fs.mkdirSync(domainPath, { recursive: true });
log.success(`Diretório criado: ${domainPath}`);
}
}
// Gerar arquivos V2.0
async function generateComponentV2() {
try {
const generators = require('./create-domain-v2-generators.js');
const componentContent = generators.generateComponentV2(domainConfig);
const componentPath = `projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.component.ts`;
fs.writeFileSync(componentPath, componentContent);
log.success(`Component V2.0 gerado: ${componentPath}`);
} catch (error) {
log.error(`Erro ao gerar component: ${error.message}`);
throw error;
}
}
async function generateServiceV2() {
try {
const generators = require('./create-domain-v2-generators.js');
const serviceContent = generators.generateServiceV2(domainConfig);
const servicePath = `projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.service.ts`;
fs.writeFileSync(servicePath, serviceContent);
log.success(`Service V2.0 gerado: ${servicePath}`);
} catch (error) {
log.error(`Erro ao gerar service: ${error.message}`);
throw error;
}
}
async function generateInterfaceV2() {
try {
let interfaceContent;
// 🚀 NOVA FUNCIONALIDADE V2.0: Tentar API real primeiro
log.step('🔍 Tentando gerar interface a partir de dados reais da API...');
const apiResult = await analyzeRealAPI(domainConfig.name);
if (apiResult.success) {
log.success('✅ Interface gerada a partir de dados REAIS da API!');
log.info(`📊 Estratégia: ${apiResult.strategy}`);
log.info(`🔗 Endpoint: ${apiResult.metadata.endpoint}`);
log.info(`📋 Campos detectados: ${apiResult.fields.length}`);
interfaceContent = apiResult.interface;
} else {
// Fallback para gerador padrão
log.warning('⚠️ Não foi possível acessar dados reais da API');
log.info(`📋 Motivo: ${apiResult.message || 'Credenciais não fornecidas'}`);
log.info('🔄 Usando geração padrão de interface...');
const generators = require('./create-domain-v2-generators.js');
interfaceContent = await generators.generateInterfaceV2(domainConfig);
}
const interfacePath = `projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.interface.ts`;
fs.writeFileSync(interfacePath, interfaceContent);
log.success(`Interface V2.0 gerada: ${interfacePath}`);
if (apiResult.success) {
log.success('🎉 INTERFACE BASEADA EM DADOS REAIS DA API!');
}
} catch (error) {
log.error(`Erro ao gerar interface: ${error.message}`);
throw error;
}
}
async function generateTemplatesV2() {
// HTML Template
const htmlContent = `<div class="domain-container">
<div class="main-content">
<app-tab-system
#tabSystem
[config]="tabConfig"
[events]="tabEvents"
[showDebugInfo]="false"
(tabSelected)="onTabSelected($event)"
(tabClosed)="onTabClosed($event)"
(tabAdded)="onTabAdded($event)"
(tableEvent)="onTableEvent($event)">
</app-tab-system>
</div>
</div>`;
// SCSS Template
const scssContent = `// 🎨 ${capitalize(domainConfig.name)} Component Styles - V2.0
// Estilos específicos para o domínio ${domainConfig.displayName}
.domain-container {
height: 100%;
display: flex;
flex-direction: column;
background: var(--background);
.main-content {
flex: 1;
overflow: hidden;
padding: 0;
}
}
// 🆕 V2.0: Estilos específicos para funcionalidades
${domainConfig.hasBulkActions ? `
// Bulk Actions específicos
.bulk-actions {
margin-bottom: 1rem;
.bulk-action-button {
margin-right: 0.5rem;
&.advanced {
background: var(--idt-primary-color);
color: white;
}
}
}` : ''}
${domainConfig.hasFooter ? `
// Footer customizations
.footer-enhanced {
font-weight: 600;
.footer-currency {
color: var(--success-color);
}
.footer-count {
color: var(--info-color);
}
}` : ''}
// 📱 Responsividade
@media (max-width: 768px) {
.domain-container {
.main-content {
padding: 0.5rem;
}
}
}`;
const htmlPath = `projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.component.html`;
const scssPath = `projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.component.scss`;
fs.writeFileSync(htmlPath, htmlContent);
fs.writeFileSync(scssPath, scssContent);
log.success(`Templates V2.0 gerados: HTML e SCSS`);
}
// Atualizar roteamento V2.0
async function updateRoutingV2() {
log.info('Atualizando roteamento...');
const routesPath = 'projects/idt_app/src/app/app.routes.ts';
let routesContent = fs.readFileSync(routesPath, 'utf8');
// Adicionar import
const importLine = `import { ${capitalize(domainConfig.name)}Component } from './domain/${domainConfig.name}/${domainConfig.name}.component';`;
if (!routesContent.includes(importLine)) {
routesContent = routesContent.replace(
/import.*from.*;\n(?=\n)/,
`$&${importLine}\n`
);
}
// Adicionar rota
const routeLine = ` { path: '${domainConfig.name}', component: ${capitalize(domainConfig.name)}Component },`;
if (!routesContent.includes(routeLine)) {
routesContent = routesContent.replace(
/export const routes: Routes = \[\n/,
`$&${routeLine}\n`
);
}
fs.writeFileSync(routesPath, routesContent);
log.success('Roteamento atualizado');
}
// Atualizar sidebar V2.0
async function updateSidebarV2() {
log.info('Atualizando menu lateral...');
const sidebarPath = 'projects/idt_app/src/app/shared/components/sidebar/sidebar.component.ts';
let sidebarContent = fs.readFileSync(sidebarPath, 'utf8');
// Encontrar posição do menu
const menuPositionRegex = new RegExp(`title: '${domainConfig.menuPosition}'[\\s\\S]*?},`);
const match = sidebarContent.match(menuPositionRegex);
if (match) {
const newMenuItem = `{
title: '${domainConfig.displayName}',
icon: 'fas fa-cube',
route: '${domainConfig.name}',
permissions: ['${domainConfig.name}:read']
},`;
const insertPosition = match.index + match[0].length;
sidebarContent = sidebarContent.slice(0, insertPosition) + '\n ' + newMenuItem + sidebarContent.slice(insertPosition);
}
fs.writeFileSync(sidebarPath, sidebarContent);
log.success('Menu lateral atualizado');
}
// Atualizar configuração MCP V2.0
async function updateMCPConfigV2() {
log.info('Atualizando configuração MCP...');
try {
const mcpPath = '.mcp/config.json';
const mcpContent = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
// Adicionar domínio à lista
if (!mcpContent.projects.idt_app.domains.includes(domainConfig.name)) {
mcpContent.projects.idt_app.domains.push(domainConfig.name);
}
// Adicionar contexto específico V2.0
const domainContext = {
description: `Domínio de ${domainConfig.displayName} - V2.0`,
files: [
`projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.component.ts`,
`projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.service.ts`,
`projects/idt_app/src/app/domain/${domainConfig.name}/${domainConfig.name}.interface.ts`
],
features: {
baseDomainComponent: true,
registryPattern: true,
apiClientService: true,
// V2.0 Features
footerConfig: domainConfig.hasFooter,
checkboxGrouped: domainConfig.hasCheckboxGrouped,
bulkActions: domainConfig.hasBulkActions,
dateRangeUtils: domainConfig.hasDateRangeUtils,
advancedSideCard: domainConfig.hasAdvancedSideCard,
extendedSearchOptions: domainConfig.hasExtendedSearchOptions
}
};
mcpContent.contexts[`domain-${domainConfig.name}`] = domainContext;
fs.writeFileSync(mcpPath, JSON.stringify(mcpContent, null, 2));
log.success('Configuração MCP V2.0 atualizada');
} catch (error) {
log.warning(`Erro ao atualizar MCP: ${error.message} - será atualizado manualmente`);
}
}
// Compilar e testar automaticamente
async function compileAndTest() {
log.title('COMPILAÇÃO E TESTES V2.0');
try {
log.info('Compilando aplicação com funcionalidades V2.0...');
execSync('ng build idt_app --configuration development', {
stdio: 'inherit',
timeout: 120000 // 2 minutos timeout
});
log.success('Compilação V2.0 realizada com sucesso! ✨');
} catch (error) {
log.error(`Erro na compilação: ${error.message}`);
throw new Error('Falha na compilação - verifique os erros acima');
}
}
// Commit automático V2.0
async function autoCommit() {
log.title('COMMIT AUTOMÁTICO V2.0');
try {
const branchName = `feature/domain-${domainConfig.name}`;
// Adicionar todos os arquivos
execSync('git add .', { stdio: 'inherit' });
// Criar mensagem de commit V2.0
const v2Features = [];
if (domainConfig.hasFooter) v2Features.push(`FooterConfig: ${domainConfig.footerConfig.columns.length} colunas`);
if (domainConfig.hasCheckboxGrouped) v2Features.push(`CheckboxGrouped: ${domainConfig.checkboxGroupedConfig.groups.length} grupos`);
if (domainConfig.hasBulkActions) v2Features.push(`BulkActions: ${domainConfig.bulkActionsConfig.type}`);
if (domainConfig.hasDateRangeUtils) v2Features.push('DateRangeUtils integration');
if (domainConfig.hasAdvancedSideCard) v2Features.push('Advanced SideCard');
if (domainConfig.hasExtendedSearchOptions) v2Features.push('Extended SearchOptions');
const commitMessage = `feat: add ${domainConfig.displayName} domain (V2.0)
✨ Features V2.0 implementadas:
${v2Features.map(f => `- ${f}`).join('\n')}
🔧 Arquivos gerados:
- Component: ${capitalize(domainConfig.name)}Component (BaseDomainComponent + Registry Pattern)
- Service: ${capitalize(domainConfig.name)}Service (DomainService + ApiClientService)
- Interface: ${capitalize(domainConfig.name)} TypeScript
- Templates: HTML e SCSS com V2.0 features
🔗 Integrações:
- Roteamento: app.routes.ts
- Menu: sidebar.component.ts (após ${domainConfig.menuPosition})
- MCP: .mcp/config.json com features V2.0
🎯 Gerado automaticamente via create-domain-v2.js`;
// Fazer commit
execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
log.success(`Commit V2.0 realizado na branch ${branchName}! 📝`);
log.info(`Para fazer push: git push origin ${branchName}`);
} catch (error) {
log.warning(`Erro no commit automático: ${error.message}`);
log.info('Você pode fazer o commit manualmente depois');
}
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// ===== EXECUÇÃO =====
if (require.main === module) {
main();
}
module.exports = {
domainConfig,
FooterTemplates,
CheckboxGroupedTemplates,
BulkActionsTemplates,
SearchOptionsLibrary,
generateComponentV2,
generateServiceV2,
generateInterfaceV2
};