weaver-frontend-cli
Version:
🕷️ Weaver CLI - Generador completo de arquitectura Clean Architecture con parser OpenAPI avanzado para entidades CRUD y flujos de negocio complejos
1,081 lines (1,006 loc) • 66.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createReduxFlow = createReduxFlow;
exports.toPascalCase = toPascalCase;
exports.toCamelCase = toCamelCase;
exports.toKebabCase = toKebabCase;
exports.toSnakeCase = toSnakeCase;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
/**
* Genera todo el flujo Redux basándose en el response de una operación
* @param targetBasePath - Path base donde generar archivos
* @param responseSchema - Schema del response parseado del Swagger
* @param apiName - Nombre de la API (platform, appointment, etc.)
* @param options - Opciones del flujo Redux (incluye nombre, tipo de storage, ID field)
*/
async function createReduxFlow(targetBasePath, responseSchema, apiName, options) {
// Crear info simplificada
const flowNameKebab = toKebabCase(options.flowName);
const flowNamePascal = toPascalCase(options.flowName);
const flowNameCamel = toCamelCase(options.flowName);
const info = {
flowName: flowNameKebab,
flowNamePascal: flowNamePascal,
flowNameCamel: flowNameCamel,
apiName: apiName,
apiNamePascal: toPascalCase(apiName),
apiNameCamel: toCamelCase(apiName),
isArray: options.isArray,
idField: options.idField,
};
console.log(chalk_1.default.blue(`\n📦 Generando flujo Redux: ${flowNamePascal}`));
console.log(chalk_1.default.gray(` API: ${apiName}`));
console.log(chalk_1.default.gray(` Storage: ${options.isArray ? "Lista (Array)" : "Objeto Ăşnico"}`));
if (options.idField)
console.log(chalk_1.default.gray(` Campo ID: ${options.idField}`));
console.log(chalk_1.default.gray(` Target: ${targetBasePath}`));
try {
// 1. Generar Core Files
await generateReduxCoreFiles(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Core files"));
// 2. Generar DTOs Redux
await generateReduxDTOs(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Redux DTOs"));
// 3. Generar/Actualizar Repository Interface
await generateReduxRepositoryInterface(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Repository Interface"));
// 4. Generar Use Cases
await generateReduxUseCases(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Use Cases"));
// 5. Generar/Actualizar Facade
await generateReduxFacade(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Facade"));
// 6. Generar/Actualizar Redux Slice
await generateReduxSlice(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Redux Slice"));
// 7. Generar Reducers
await generateReduxReducers(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Reducers"));
// 8. Generar Repository Implementation
await generateReduxRepository(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Repository Implementation"));
// 9. Generar Mappers (opcional)
await generateReduxMappers(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Mappers"));
// 10. Generar Archivos de Injection
await generateReduxInjectionFiles(targetBasePath, info, responseSchema);
console.log(chalk_1.default.green(" âś“ Injection files"));
// 11. Registrar en redux-core.ts (solo primera vez)
await registerInReduxCore(targetBasePath, info);
console.log(chalk_1.default.green(" âś“ Redux Core registration"));
console.log(chalk_1.default.green(`\nâś… Flujo Redux generado: ~${options.isArray ? "20" : "21"} archivos`));
console.log(chalk_1.default.cyan(`\nđź’ˇ Uso:`));
console.log(chalk_1.default.gray(` import { Injection${info.apiNamePascal}ReduxFacade } from '@${apiName}/facade/redux/${apiName}/injection';`));
console.log(chalk_1.default.gray(` const facade = Injection${info.apiNamePascal}ReduxFacade.${info.apiNamePascal}ReduxFacade();`));
if (info.isArray) {
console.log(chalk_1.default.gray(` facade.create${info.flowNamePascal}(data, { dispatch });`));
}
else {
console.log(chalk_1.default.gray(` facade.save${info.flowNamePascal}(data, { dispatch });`));
}
}
catch (error) {
console.error(chalk_1.default.red("❌ Error generando flujo Redux:"), error);
throw error;
}
}
/**
* Convierte un string a PascalCase
*/
function toPascalCase(str) {
return str
.split(/[-_\s]/)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join("");
}
/**
* Convierte un string a camelCase
*/
function toCamelCase(str) {
const pascal = toPascalCase(str);
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
}
/**
* Construye el path relativo para un flow Redux
* Ejemplo: "user-preferences" o "services-by-location"
*/
function buildFlowPath(info) {
return `custom/${info.flowName}`;
}
/**
* Convierte un string a kebab-case
*/
function toKebabCase(str) {
return str
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, "-")
.toLowerCase();
}
/**
* Convierte de camelCase a snake_case
*/
function toSnakeCase(str) {
return str
.replace(/([a-z])([A-Z])/g, "$1_$2")
.replace(/[\s-]+/g, "_")
.toLowerCase();
}
// ========================================
// FUNCIONES DE GENERACIÓN ESPECÍFICAS
// ========================================
/**
* Genera archivos core (interfaces, types)
*/
async function generateReduxCoreFiles(basePath, info, schema) {
const corePath = path.join(basePath, "core");
// 1. Generar i-config-repository-dto.ts (si no existe)
const configPath = path.join(corePath, "interfaces", "i-config-repository-dto.ts");
if (!(await fs.pathExists(configPath))) {
const configContent = `import { TypedUseSelectorHook } from 'react-redux';
import { Selector${info.apiNamePascal}Redux } from '../types/selector-${info.apiNameCamel}-redux';
export interface IConfigDTO {
loadService?: boolean;
dispatch?: any; // AppDispatch
selector?: Selector${info.apiNamePascal}Redux;
key?: string;
}
`;
await fs.ensureDir(path.dirname(configPath));
await fs.writeFile(configPath, configContent);
}
// 2. Generar selector-{api-name}-redux.ts (si no existe)
const selectorPath = path.join(corePath, "types", `selector-${info.apiNameCamel}-redux.ts`);
if (!(await fs.pathExists(selectorPath))) {
const selectorContent = `import { TypedUseSelectorHook } from 'react-redux';
import { I${info.apiNamePascal}InitialStateReduxDTO } from '@${info.apiName}/infrastructure/repositories/redux/${info.apiName}/${info.apiName}.slice';
type RootState = {
${info.apiNameCamel}: I${info.apiNamePascal}InitialStateReduxDTO;
};
export type Selector${info.apiNamePascal}Redux = TypedUseSelectorHook<RootState>;
`;
await fs.ensureDir(path.dirname(selectorPath));
await fs.writeFile(selectorPath, selectorContent);
}
}
/**
* Genera los DTOs de Redux
*/
async function generateReduxDTOs(basePath, info, schema) {
const flowPath = buildFlowPath(info);
const dtoPath = path.join(basePath, "domain/models/redux", info.apiName, flowPath);
await fs.ensureDir(dtoPath);
// Generar interface principal
const dtoContent = generateDTOInterface(info, schema);
await fs.writeFile(path.join(dtoPath, `i-${info.flowName}-redux-dto.ts`), dtoContent);
// Generar index.ts
const indexContent = `export * from './i-${info.flowName}-redux-dto';
`;
await fs.writeFile(path.join(dtoPath, "index.ts"), indexContent);
}
/**
* Genera las interfaces del DTO de forma recursiva
*/
function generateDTOInterface(info, schema) {
const nestedInterfaces = [];
const processedTypes = new Set();
// Generar la interface principal
const mainFields = schema.fields
.map((field) => {
const fieldType = mapFieldTypeToTypescriptRecursive(field, info.flowNamePascal, nestedInterfaces, processedTypes);
const fieldNameCamel = toCamelCase(field.name);
return ` ${fieldNameCamel}?: ${fieldType};`;
})
.join("\n");
const mainInterface = `/**
* Redux DTO para ${info.flowNamePascal}
* Path: domain/models/redux/${info.apiName}/custom/${info.flowName}/
*/
export interface I${info.flowNamePascal}ReduxDTO {
${mainFields}
}`;
// Retornar la interface principal seguida de las interfaces anidadas
if (nestedInterfaces.length > 0) {
return `${mainInterface}\n\n${nestedInterfaces.join("\n\n")}`;
}
return mainInterface;
}
/**
* Mapea un campo a su tipo TypeScript de forma recursiva, generando interfaces anidadas
*/
function mapFieldTypeToTypescriptRecursive(field, parentName, nestedInterfaces, processedTypes) {
let baseType = field.type;
// Si tiene campos anidados (es un objeto con propiedades)
if (field.nestedFields && field.nestedFields.length > 0) {
// Generar nombre de la interface anidada con sufijo ReduxDTO
const fieldNamePascal = toPascalCase(field.name);
const nestedInterfaceName = `I${fieldNamePascal}ReduxDTO`;
// Evitar duplicados
if (!processedTypes.has(nestedInterfaceName)) {
processedTypes.add(nestedInterfaceName);
// Generar campos de la interface anidada recursivamente
const nestedFields = field.nestedFields
.map((nestedField) => {
const nestedFieldType = mapFieldTypeToTypescriptRecursive(nestedField, nestedInterfaceName, nestedInterfaces, processedTypes);
const nestedFieldNameCamel = toCamelCase(nestedField.name);
return ` ${nestedFieldNameCamel}?: ${nestedFieldType};`;
})
.join("\n");
// Agregar la interface anidada
nestedInterfaces.push(`export interface ${nestedInterfaceName} {\n${nestedFields}\n}`);
}
baseType = nestedInterfaceName;
}
else {
// Mapear tipos básicos
if (baseType === "string")
baseType = "string";
else if (baseType === "number" || baseType === "integer")
baseType = "number";
else if (baseType === "boolean")
baseType = "boolean";
else if (baseType === "object")
baseType = "any";
else
baseType = "any";
}
// Manejar arrays
if (field.isArray) {
return `${baseType}[]`;
}
return baseType;
}
/**
* Mapea un campo a su tipo TypeScript
*/
function mapFieldTypeToTypescript(field) {
let baseType = field.type;
// Mapear tipos básicos
if (baseType === "string")
baseType = "string";
else if (baseType === "number" || baseType === "integer")
baseType = "number";
else if (baseType === "boolean")
baseType = "boolean";
else if (baseType === "any" || baseType === "object")
baseType = "any";
// Si es array
if (field.isArray) {
return `${baseType}[]`;
}
return baseType;
}
/**
* Genera o actualiza la interface del repositorio Redux
*/
async function generateReduxRepositoryInterface(basePath, info, schema) {
const repositoryName = info.flowName;
const repoPath = path.join(basePath, "domain/services/repositories/redux", info.apiName, "custom", `i-${repositoryName}-redux-repository.ts`);
const methodsContent = info.isArray
? generateArrayRepositoryMethods(info)
: generateObjectRepositoryMethods(info);
if (await fs.pathExists(repoPath)) {
// Actualizar archivo existente agregando métodos
let content = await fs.readFile(repoPath, "utf-8");
// Buscar el cierre de la clase para agregar antes
const classEndPattern = /}\s*$/;
if (classEndPattern.test(content)) {
content = content.replace(classEndPattern, `\n${methodsContent}\n}`);
await fs.writeFile(repoPath, content);
}
}
else {
// Crear archivo nuevo
const flowPath = buildFlowPath(info);
const repositoryClassName = info.flowNamePascal;
const repoContent = `import { IConfigDTO } from '@${info.apiName}/core/interfaces';
import { I${info.flowNamePascal}ReduxDTO } from '@${info.apiName}/domain/models/redux/${info.apiName}/${flowPath}';
/**
* Interface del repositorio Redux para ${repositoryClassName}
* Path: domain/services/repositories/redux/${info.apiName}/custom/i-${repositoryName}-redux-repository.ts
*/
export abstract class I${repositoryClassName}ReduxRepository {
${methodsContent}
}
`;
await fs.ensureDir(path.dirname(repoPath));
await fs.writeFile(repoPath, repoContent);
}
}
function generateArrayRepositoryMethods(info) {
return ` abstract create${info.flowNamePascal}(params: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void;
abstract read${info.flowNamePascal}ById(id: string, config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | null;
abstract readAll${info.flowNamePascal}(config: IConfigDTO): I${info.flowNamePascal}ReduxDTO[] | null;
abstract update${info.flowNamePascal}(id: string, data: Partial<I${info.flowNamePascal}ReduxDTO>, config: IConfigDTO): void;
abstract delete${info.flowNamePascal}(id: string, config: IConfigDTO): void;
abstract clearAll${info.flowNamePascal}(config: IConfigDTO): void;
`;
}
function generateObjectRepositoryMethods(info) {
return ` abstract save${info.flowNamePascal}(params: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void;
abstract read${info.flowNamePascal}(config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | null;
abstract read${info.flowNamePascal}Property<K extends keyof I${info.flowNamePascal}ReduxDTO>(
propertyName: K,
config: IConfigDTO
): I${info.flowNamePascal}ReduxDTO[K] | null;
abstract update${info.flowNamePascal}(data: Partial<I${info.flowNamePascal}ReduxDTO>, config: IConfigDTO): void;
abstract clear${info.flowNamePascal}(config: IConfigDTO): void;
`;
}
// Las siguientes funciones serán implementadas en las siguientes iteraciones...
// Por ahora, creo stubs básicos para que compile
async function generateReduxUseCases(basePath, info, schema) {
const flowPath = buildFlowPath(info);
const useCasePath = path.join(basePath, "domain/services/use_cases/redux", info.apiName, flowPath);
await fs.ensureDir(useCasePath);
if (info.isArray) {
// Generar use cases para arrays
await generateArrayUseCases(useCasePath, info);
}
else {
// Generar use cases para objetos
await generateObjectUseCases(useCasePath, info);
}
// Generar injection de use cases
await generateUseCaseInjection(useCasePath, info);
}
async function generateArrayUseCases(useCasePath, info) {
// 1. Create Use Case
const createContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Create${info.flowNamePascal}UseCase implements UseCase<I${info.flowNamePascal}ReduxDTO, void> {
private static instance: Create${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Create${info.flowNamePascal}UseCase {
if (!Create${info.flowNamePascal}UseCase.instance)
Create${info.flowNamePascal}UseCase.instance = new Create${info.flowNamePascal}UseCase();
return Create${info.flowNamePascal}UseCase.instance;
}
public execute(param: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.create${info.flowNamePascal}(param, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `create-${info.flowName}-use-case.ts`), createContent);
// 2. Read Use Case
const readContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Read${info.flowNamePascal}UseCase implements UseCase<string | null, I${info.flowNamePascal}ReduxDTO | I${info.flowNamePascal}ReduxDTO[] | null> {
private static instance: Read${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Read${info.flowNamePascal}UseCase {
if (!Read${info.flowNamePascal}UseCase.instance)
Read${info.flowNamePascal}UseCase.instance = new Read${info.flowNamePascal}UseCase();
return Read${info.flowNamePascal}UseCase.instance;
}
public execute(param: string | null, config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | I${info.flowNamePascal}ReduxDTO[] | null {
if (param) {
return this.${info.flowNameCamel}ReduxRepository.read${info.flowNamePascal}ById(param, config);
} else {
return this.${info.flowNameCamel}ReduxRepository.readAll${info.flowNamePascal}(config);
}
}
}
`;
await fs.writeFile(path.join(useCasePath, `read-${info.flowName}-use-case.ts`), readContent);
// 3. Update Use Case
const updateContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Update${info.flowNamePascal}UseCase implements UseCase<{ id: string; data: Partial<I${info.flowNamePascal}ReduxDTO> }, void> {
private static instance: Update${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Update${info.flowNamePascal}UseCase {
if (!Update${info.flowNamePascal}UseCase.instance)
Update${info.flowNamePascal}UseCase.instance = new Update${info.flowNamePascal}UseCase();
return Update${info.flowNamePascal}UseCase.instance;
}
public execute(param: { id: string; data: Partial<I${info.flowNamePascal}ReduxDTO> }, config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.update${info.flowNamePascal}(param.id, param.data, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `update-${info.flowName}-use-case.ts`), updateContent);
// 4. Delete Use Case
const deleteContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/${info.apiName}/injection";
export class Delete${info.flowNamePascal}UseCase implements UseCase<string, void> {
private static instance: Delete${info.flowNamePascal}UseCase;
private ${info.apiNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.apiNamePascal}ReduxRepository();
public static getInstance(): Delete${info.flowNamePascal}UseCase {
if (!Delete${info.flowNamePascal}UseCase.instance)
Delete${info.flowNamePascal}UseCase.instance = new Delete${info.flowNamePascal}UseCase();
return Delete${info.flowNamePascal}UseCase.instance;
}
public execute(param: string, config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.delete${info.flowNamePascal}(param, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `delete-${info.flowName}-use-case.ts`), deleteContent);
// 5. Clear Use Case
const clearContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/${info.apiName}/injection";
export class Clear${info.flowNamePascal}UseCase implements UseCase<any, void> {
private static instance: Clear${info.flowNamePascal}UseCase;
private ${info.apiNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.apiNamePascal}ReduxRepository();
public static getInstance(): Clear${info.flowNamePascal}UseCase {
if (!Clear${info.flowNamePascal}UseCase.instance)
Clear${info.flowNamePascal}UseCase.instance = new Clear${info.flowNamePascal}UseCase();
return Clear${info.flowNamePascal}UseCase.instance;
}
public execute(config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.clearAll${info.flowNamePascal}(config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `clear-${info.flowName}-use-case.ts`), clearContent);
}
async function generateObjectUseCases(useCasePath, info) {
// 1. Save Use Case
const saveContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Save${info.flowNamePascal}UseCase implements UseCase<I${info.flowNamePascal}ReduxDTO, void> {
private static instance: Save${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Save${info.flowNamePascal}UseCase {
if (!Save${info.flowNamePascal}UseCase.instance)
Save${info.flowNamePascal}UseCase.instance = new Save${info.flowNamePascal}UseCase();
return Save${info.flowNamePascal}UseCase.instance;
}
public execute(param: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.save${info.flowNamePascal}(param, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `save-${info.flowName}-use-case.ts`), saveContent);
// 2. Read Use Case
const readContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Read${info.flowNamePascal}UseCase implements UseCase<void, I${info.flowNamePascal}ReduxDTO | null> {
private static instance: Read${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Read${info.flowNamePascal}UseCase {
if (!Read${info.flowNamePascal}UseCase.instance)
Read${info.flowNamePascal}UseCase.instance = new Read${info.flowNamePascal}UseCase();
return Read${info.flowNamePascal}UseCase.instance;
}
public execute(config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | null {
return this.${info.flowNameCamel}ReduxRepository.read${info.flowNamePascal}(config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `read-${info.flowName}-use-case.ts`), readContent);
// 3. Read Property Use Case (genérico)
const readPropertyContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Read${info.flowNamePascal}PropertyUseCase implements UseCase<string, any> {
private static instance: Read${info.flowNamePascal}PropertyUseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Read${info.flowNamePascal}PropertyUseCase {
if (!Read${info.flowNamePascal}PropertyUseCase.instance)
Read${info.flowNamePascal}PropertyUseCase.instance = new Read${info.flowNamePascal}PropertyUseCase();
return Read${info.flowNamePascal}PropertyUseCase.instance;
}
public execute<K extends keyof I${info.flowNamePascal}ReduxDTO>(
propertyName: K,
config: IConfigDTO
): I${info.flowNamePascal}ReduxDTO[K] | null {
return this.${info.flowNameCamel}ReduxRepository.read${info.flowNamePascal}Property(propertyName, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `read-${info.flowName}-property-use-case.ts`), readPropertyContent);
// 4. Update Use Case
const updateContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/injection";
export class Update${info.flowNamePascal}UseCase implements UseCase<Partial<I${info.flowNamePascal}ReduxDTO>, void> {
private static instance: Update${info.flowNamePascal}UseCase;
private ${info.flowNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.flowNamePascal}ReduxRepository();
public static getInstance(): Update${info.flowNamePascal}UseCase {
if (!Update${info.flowNamePascal}UseCase.instance)
Update${info.flowNamePascal}UseCase.instance = new Update${info.flowNamePascal}UseCase();
return Update${info.flowNamePascal}UseCase.instance;
}
public execute(param: Partial<I${info.flowNamePascal}ReduxDTO>, config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.update${info.flowNamePascal}(param, config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `update-${info.flowName}-use-case.ts`), updateContent);
// 5. Clear Use Case
const clearContent = `import { UseCase } from "@core/interfaces/use-case";
import { IConfigDTO } from "@${info.apiName}/core/interfaces";
import { InjectionRepositoriesRedux } from "@${info.apiName}/infrastructure/repositories/redux/${info.apiName}/injection";
export class Clear${info.flowNamePascal}UseCase implements UseCase<any, void> {
private static instance: Clear${info.flowNamePascal}UseCase;
private ${info.apiNameCamel}ReduxRepository = InjectionRepositoriesRedux.${info.apiNamePascal}ReduxRepository();
public static getInstance(): Clear${info.flowNamePascal}UseCase {
if (!Clear${info.flowNamePascal}UseCase.instance)
Clear${info.flowNamePascal}UseCase.instance = new Clear${info.flowNamePascal}UseCase();
return Clear${info.flowNamePascal}UseCase.instance;
}
public execute(config: IConfigDTO): void {
this.${info.flowNameCamel}ReduxRepository.clear${info.flowNamePascal}(config);
}
}
`;
await fs.writeFile(path.join(useCasePath, `clear-${info.flowName}-use-case.ts`), clearContent);
}
async function generateUseCaseInjection(useCasePath, info) {
const injectionPath = path.join(useCasePath, "injection");
await fs.ensureDir(injectionPath);
let imports = "";
let methods = "";
if (info.isArray) {
imports = `import { Create${info.flowNamePascal}UseCase } from "../create-${info.flowName}-use-case";
import { Read${info.flowNamePascal}UseCase } from "../read-${info.flowName}-use-case";
import { Update${info.flowNamePascal}UseCase } from "../update-${info.flowName}-use-case";
import { Delete${info.flowNamePascal}UseCase } from "../delete-${info.flowName}-use-case";
import { Clear${info.flowNamePascal}UseCase } from "../clear-${info.flowName}-use-case";
`;
methods = ` public static Create${info.flowNamePascal}UseCase() {
return Create${info.flowNamePascal}UseCase.getInstance();
}
public static Read${info.flowNamePascal}UseCase() {
return Read${info.flowNamePascal}UseCase.getInstance();
}
public static Update${info.flowNamePascal}UseCase() {
return Update${info.flowNamePascal}UseCase.getInstance();
}
public static Delete${info.flowNamePascal}UseCase() {
return Delete${info.flowNamePascal}UseCase.getInstance();
}
public static Clear${info.flowNamePascal}UseCase() {
return Clear${info.flowNamePascal}UseCase.getInstance();
}
`;
}
else {
imports = `import { Save${info.flowNamePascal}UseCase } from "../save-${info.flowName}-use-case";
import { Read${info.flowNamePascal}UseCase } from "../read-${info.flowName}-use-case";
import { Read${info.flowNamePascal}PropertyUseCase } from "../read-${info.flowName}-property-use-case";
import { Update${info.flowNamePascal}UseCase } from "../update-${info.flowName}-use-case";
import { Clear${info.flowNamePascal}UseCase } from "../clear-${info.flowName}-use-case";
`;
methods = ` public static Save${info.flowNamePascal}UseCase() {
return Save${info.flowNamePascal}UseCase.getInstance();
}
public static Read${info.flowNamePascal}UseCase() {
return Read${info.flowNamePascal}UseCase.getInstance();
}
public static Read${info.flowNamePascal}PropertyUseCase() {
return Read${info.flowNamePascal}PropertyUseCase.getInstance();
}
public static Update${info.flowNamePascal}UseCase() {
return Update${info.flowNamePascal}UseCase.getInstance();
}
public static Clear${info.flowNamePascal}UseCase() {
return Clear${info.flowNamePascal}UseCase.getInstance();
}
`;
}
const injectionContent = `${imports}
/**
* InyecciĂłn de dependencias para Use Cases de ${info.flowNamePascal} Redux
*/
export class Injection${info.flowNamePascal}ReduxUseCase {
${methods}}
`;
await fs.writeFile(path.join(injectionPath, `injection-${info.flowName}-redux-use-case.ts`), injectionContent);
}
async function generateReduxFacade(basePath, info, schema) {
const facadePath = path.join(basePath, "facade/redux", info.apiName);
const facadeFile = path.join(facadePath, `${info.apiName}-redux-facade.ts`);
// Verificar si el facade ya existe
if (await fs.pathExists(facadeFile)) {
// Actualizar facade existente agregando métodos
await updateExistingFacade(facadeFile, info);
}
else {
// Crear facade nuevo
await createNewFacade(facadePath, info);
}
// Generar/actualizar injection de facade
await generateFacadeInjection(facadePath, info);
}
async function createNewFacade(facadePath, info) {
await fs.ensureDir(facadePath);
const facadeContent = generateFacadeContent(info, false);
await fs.writeFile(path.join(facadePath, `${info.apiName}-redux-facade.ts`), facadeContent);
}
async function updateExistingFacade(facadeFile, info) {
let content = await fs.readFile(facadeFile, "utf-8");
// Agregar imports necesarios
const newImport = `import { I${info.flowNamePascal}ReduxDTO } from '@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}';`;
if (!content.includes(newImport)) {
// Buscar el último import y agregar después
const importPattern = /import.*from.*;\n/g;
const imports = content.match(importPattern);
if (imports && imports.length > 0) {
const lastImport = imports[imports.length - 1];
content = content.replace(lastImport, `${lastImport}${newImport}\n`);
}
}
// Agregar import del use case injection
const useCaseInjectionImport = `import { Injection${info.flowNamePascal}ReduxUseCase } from '@${info.apiName}/domain/services/use_cases/redux/${info.apiName}/${info.flowName}/injection';`;
if (!content.includes(useCaseInjectionImport)) {
const importPattern = /import.*from.*;\n/g;
const imports = content.match(importPattern);
if (imports && imports.length > 0) {
const lastImport = imports[imports.length - 1];
content = content.replace(lastImport, `${lastImport}${useCaseInjectionImport}\n`);
}
}
// Agregar propiedades de use cases en la clase
const useCaseProperties = generateUseCaseProperties(info);
const constructorPattern = /public static getInstance\(\)/;
content = content.replace(constructorPattern, `${useCaseProperties}\n\n public static getInstance()`);
// Agregar métodos públicos antes del cierre de la clase
const methods = generateFacadeMethods(info);
const classEndPattern = /}\s*$/;
content = content.replace(classEndPattern, `${methods}\n}`);
await fs.writeFile(facadeFile, content);
}
function generateFacadeContent(info, isUpdate) {
const imports = `import { IConfigDTO } from '@${info.apiName}/core/interfaces';
import { I${info.flowNamePascal}ReduxDTO } from '@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}';
import { Injection${info.flowNamePascal}ReduxUseCase } from '@${info.apiName}/domain/services/use_cases/redux/${info.apiName}/custom/${info.flowName}/injection';
`;
const useCaseProperties = generateUseCaseProperties(info);
const methods = generateFacadeMethods(info);
return `${imports}
/**
* Facade para manejar el estado Redux de ${info.apiNamePascal} API
* Path: facade/redux/${info.apiName}/${info.apiName}-redux-facade.ts
*/
export class ${info.apiNamePascal}ReduxFacade {
private static instance: ${info.apiNamePascal}ReduxFacade;
${useCaseProperties}
public static getInstance(): ${info.apiNamePascal}ReduxFacade {
if (!${info.apiNamePascal}ReduxFacade.instance)
${info.apiNamePascal}ReduxFacade.instance = new ${info.apiNamePascal}ReduxFacade();
return ${info.apiNamePascal}ReduxFacade.instance;
}
${methods}
}
`;
}
function generateUseCaseProperties(info) {
if (info.isArray) {
return ` private create${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Create${info.flowNamePascal}UseCase();
private read${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Read${info.flowNamePascal}UseCase();
private update${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Update${info.flowNamePascal}UseCase();
private delete${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Delete${info.flowNamePascal}UseCase();
private clear${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Clear${info.flowNamePascal}UseCase();
`;
}
else {
return ` private save${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Save${info.flowNamePascal}UseCase();
private read${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Read${info.flowNamePascal}UseCase();
private read${info.flowNamePascal}PropertyUseCase = Injection${info.flowNamePascal}ReduxUseCase.Read${info.flowNamePascal}PropertyUseCase();
private update${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Update${info.flowNamePascal}UseCase();
private clear${info.flowNamePascal}UseCase = Injection${info.flowNamePascal}ReduxUseCase.Clear${info.flowNamePascal}UseCase();
`;
}
}
function generateFacadeMethods(info) {
if (info.isArray) {
return ` public create${info.flowNamePascal}(params: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void {
this.create${info.flowNamePascal}UseCase.execute(params, config);
}
public read${info.flowNamePascal}(id: string, config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | null {
return this.read${info.flowNamePascal}UseCase.execute(id, config) as I${info.flowNamePascal}ReduxDTO | null;
}
public readAll${info.flowNamePascal}(config: IConfigDTO): I${info.flowNamePascal}ReduxDTO[] | null {
return this.read${info.flowNamePascal}UseCase.execute(null, config) as I${info.flowNamePascal}ReduxDTO[] | null;
}
public update${info.flowNamePascal}(id: string, data: Partial<I${info.flowNamePascal}ReduxDTO>, config: IConfigDTO): void {
this.update${info.flowNamePascal}UseCase.execute({ id, data }, config);
}
public delete${info.flowNamePascal}(id: string, config: IConfigDTO): void {
this.delete${info.flowNamePascal}UseCase.execute(id, config);
}
public clearAll${info.flowNamePascal}(config: IConfigDTO): void {
this.clear${info.flowNamePascal}UseCase.execute(config);
}
`;
}
else {
return ` public save${info.flowNamePascal}(params: I${info.flowNamePascal}ReduxDTO, config: IConfigDTO): void {
this.save${info.flowNamePascal}UseCase.execute(params, config);
}
public read${info.flowNamePascal}(config: IConfigDTO): I${info.flowNamePascal}ReduxDTO | null {
return this.read${info.flowNamePascal}UseCase.execute(config);
}
public read${info.flowNamePascal}Property<K extends keyof I${info.flowNamePascal}ReduxDTO>(
propertyName: K,
config: IConfigDTO
): I${info.flowNamePascal}ReduxDTO[K] | null {
return this.read${info.flowNamePascal}PropertyUseCase.execute(propertyName, config);
}
public update${info.flowNamePascal}(data: Partial<I${info.flowNamePascal}ReduxDTO>, config: IConfigDTO): void {
this.update${info.flowNamePascal}UseCase.execute(data, config);
}
public clear${info.flowNamePascal}(config: IConfigDTO): void {
this.clear${info.flowNamePascal}UseCase.execute(config);
}
`;
}
}
async function generateFacadeInjection(facadePath, info) {
const injectionPath = path.join(facadePath, "injection");
const injectionFile = path.join(injectionPath, `injection-${info.apiName}-redux-facade.ts`);
if (await fs.pathExists(injectionFile)) {
// Ya existe, no tocar (se crea solo una vez)
return;
}
await fs.ensureDir(injectionPath);
const injectionContent = `import { ${info.apiNamePascal}ReduxFacade } from "../${info.apiName}-redux-facade";
export class Injection${info.apiNamePascal}ReduxFacade {
public static ${info.apiNamePascal}ReduxFacade() {
return ${info.apiNamePascal}ReduxFacade.getInstance();
}
}
`;
await fs.writeFile(injectionFile, injectionContent);
}
async function generateReduxSlice(basePath, info, schema) {
const slicePath = path.join(basePath, "infrastructure/repositories/redux", info.apiName);
const sliceFile = path.join(slicePath, `${info.apiName}.slice.ts`);
// Verificar si el slice ya existe
if (await fs.pathExists(sliceFile)) {
// Actualizar slice existente
await updateExistingSlice(sliceFile, info);
}
else {
// Crear slice nuevo
await createNewSlice(slicePath, info);
}
}
async function createNewSlice(slicePath, info) {
await fs.ensureDir(slicePath);
const sliceContent = generateSliceContent(info, false);
await fs.writeFile(path.join(slicePath, `${info.apiName}.slice.ts`), sliceContent);
}
async function updateExistingSlice(sliceFile, info) {
let content = await fs.readFile(sliceFile, "utf-8");
// 1. Agregar import del reducer
const flowPath = buildFlowPath(info);
const reducerImport = generateSliceReducerImport(info, flowPath);
// Verificar si ya existe el import por el nombre del primer reducer
const firstReducerName = info.isArray
? `create${info.flowNamePascal}Reducer`
: `save${info.flowNamePascal}Reducer`;
if (!content.includes(firstReducerName)) {
// Buscar TODOS los bloques de imports de reducers (multilĂnea)
const reducerImportPattern = /import\s*\{[^}]+\}\s*from\s*["']\.\/.*\.reducer["'];?/gs;
const imports = content.match(reducerImportPattern);
if (imports && imports.length > 0) {
const lastImport = imports[imports.length - 1];
content = content.replace(lastImport, `${lastImport}\n${reducerImport}`);
}
else {
// Si no hay imports de reducers, agregar después del comentario
const reducerCommentPattern = /\/\/ Imports de reducers\s*/;
if (reducerCommentPattern.test(content)) {
content = content.replace(reducerCommentPattern, `// Imports de reducers\n${reducerImport}`);
}
}
}
// 2. Agregar import del DTO
const dtoImport = `import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/${flowPath}";`;
// Verificar si ya existe el import del DTO por su nombre
if (!content.includes(`I${info.flowNamePascal}ReduxDTO`)) {
// Buscar la secciĂłn de imports de DTOs y agregar
const dtoImportPattern = /import.*ReduxDTO.*from.*domain\/models\/redux[^\n]*/g;
const dtoImports = content.match(dtoImportPattern);
if (dtoImports && dtoImports.length > 0) {
const lastDtoImport = dtoImports[dtoImports.length - 1];
content = content.replace(lastDtoImport, `${lastDtoImport}\n${dtoImport}`);
}
}
// 3. Agregar property al interface del state
const stateProperty = ` ${info.flowNameCamel}: I${info.flowNamePascal}ReduxDTO${info.isArray ? "[]" : ""} | null;`;
const interfacePattern = new RegExp(`export interface I${info.apiNamePascal}InitialStateReduxDTO \\{[^}]*\\}`, "s");
const interfaceMatch = content.match(interfacePattern);
if (interfaceMatch &&
!content.includes(`${info.flowNameCamel}:`)) {
const interfaceContent = interfaceMatch[0];
const newInterfaceContent = interfaceContent.replace("}", `${stateProperty}\n}`);
content = content.replace(interfaceContent, newInterfaceContent);
}
// 4. Agregar property al initialState
const initialStateProperty = ` ${info.flowNameCamel}: null,`;
const initialStatePattern = /const initialState: .*\{[\s\S]*?\};/;
const initialStateMatch = content.match(initialStatePattern);
if (initialStateMatch &&
!content.includes(`${info.flowNameCamel}: null`)) {
const initialStateContent = initialStateMatch[0];
const newInitialStateContent = initialStateContent.replace("};", `${initialStateProperty}\n};`);
content = content.replace(initialStateContent, newInitialStateContent);
}
// 5. Agregar reducers al slice
const reducerActions = generateSliceReducerActions(info);
const reducersPattern = /reducers: \{[\s\S]*?\}/;
const reducersMatch = content.match(reducersPattern);
if (reducersMatch) {
const reducersContent = reducersMatch[0];
const newReducersContent = reducersContent.replace("}", `${reducerActions}\n }`);
content = content.replace(reducersContent, newReducersContent);
}
await fs.writeFile(sliceFile, content);
}
function generateSliceContent(info, isUpdate) {
const flowPath = buildFlowPath(info);
const reducerImport = generateSliceReducerImport(info, flowPath);
const dtoImport = `import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/${flowPath}";`;
const reducerActions = generateSliceReducerActions(info);
return `import { createSlice } from "@reduxjs/toolkit";
// Imports de reducers
${reducerImport}
// Imports de DTOs
${dtoImport}
/**
* Key para identificar el slice en el store global
*/
export const key${info.apiNamePascal} = "${info.apiName}";
/**
* Interface del estado inicial del slice
*/
export interface I${info.apiNamePascal}InitialStateReduxDTO {
${info.flowNameCamel}: I${info.flowNamePascal}ReduxDTO${info.isArray ? "[]" : ""} | null;
}
/**
* Estado inicial del slice
*/
const initialState: I${info.apiNamePascal}InitialStateReduxDTO = {
${info.flowNameCamel}: null,
};
/**
* Slice de Redux Toolkit para ${info.apiNamePascal} API
*/
export const ${info.apiNameCamel}Slice = createSlice({
name: key${info.apiNamePascal},
initialState,
reducers: {
${reducerActions}
},
});
`;
}
function generateSliceReducerImport(info, flowPath) {
if (info.isArray) {
return `import {
create${info.flowNamePascal}Reducer,
update${info.flowNamePascal}Reducer,
delete${info.flowNamePascal}Reducer,
setAll${info.flowNamePascal}Reducer,
clearAll${info.flowNamePascal}Reducer
} from "./${flowPath}/${info.flowName}.reducer";
`;
}
else {
return `import {
save${info.flowNamePascal}Reducer,
update${info.flowNamePascal}Reducer,
clear${info.flowNamePascal}Reducer
} from "./${flowPath}/${info.flowName}.reducer";
`;
}
}
function generateSliceReducerActions(info) {
if (info.isArray) {
return ` // ${info.flowNamePascal} (Array)
create${info.flowNamePascal}Action: create${info.flowNamePascal}Reducer,
update${info.flowNamePascal}Action: update${info.flowNamePascal}Reducer,
delete${info.flowNamePascal}Action: delete${info.flowNamePascal}Reducer,
setAll${info.flowNamePascal}Action: setAll${info.flowNamePascal}Reducer,
clearAll${info.flowNamePascal}Action: clearAll${info.flowNamePascal}Reducer,`;
}
else {
return ` // ${info.flowNamePascal} (Object)
save${info.flowNamePascal}Action: save${info.flowNamePascal}Reducer,
update${info.flowNamePascal}Action: update${info.flowNamePascal}Reducer,
clear${info.flowNamePascal}Action: clear${info.flowNamePascal}Reducer,`;
}
}
async function generateReduxReducers(basePath, info, schema) {
const flowPath = buildFlowPath(info);
const reducerPath = path.join(basePath, "infrastructure/repositories/redux", info.apiName, flowPath);
await fs.ensureDir(reducerPath);
const reducerContent = info.isArray
? generateArrayReducers(info)
: generateObjectReducers(info);
await fs.writeFile(path.join(reducerPath, `${info.flowName}.reducer.ts`), reducerContent);
}
function generateArrayReducers(info) {
const idField = info.idField || "id";
return `import { PayloadAction } from "@reduxjs/toolkit";
import { I${info.apiNamePascal}InitialStateReduxDTO } from "../../${info.apiName}.slice";
import { I${info.flowNamePascal}ReduxDTO } from "@${info.apiName}/domain/models/redux/${info.apiName}/custom/${info.flowName}";
/**
* Reducers para ${info.flowNamePascal} (Array)
* Path: infrastructure/repositories/redux/${info.apiName}/custom/${info.flowName}/${info.flowName}.reducer.ts
*/
/**
* Reducer: Crear/Agregar un ${info.flowNamePascal} al array
*/
export const create${info.flowNamePascal}Reducer = (
state: I${info.apiNamePascal}InitialStateReduxDTO,
action: PayloadAction<I${info.flowNamePascal}ReduxDTO>
) => {
if (!state.${info.flowNameCamel}) {
state.${info.flowNameCamel} = [action.payload];
} else {
state.${info.flowNameCamel}.push(action.payload);
}
};
/**
* Reducer: Actualizar un ${info.flowNamePascal} especĂfico en el array
*/
export const update${info.flowNamePascal}Reducer = (
state: I${info.apiNamePascal}InitialStateReduxDTO,
action: PayloadAction<{ id: string; data: Partial<I${info.flowNamePascal}ReduxDTO> }>
) => {
if (state.${info.flowNameCamel}) {
const index = state.${info.flowNameCamel}.findIndex(item => item.${idField} === action.payload.id);
if (index !== -1) {
state.${info.flowNameCamel}[index] = { ...state.${info.flowNameCamel}[index], ...action.payload.data };
}
}
};
/**
* Reducer: Eliminar un ${info.flowNam