@cbm-common/module-repository
v0.0.1
Published
Biblioteca Angular para la gestión avanzada de módulos funcionales y sus detalles. Permite administrar módulos, opciones, permisos y relaciones país-módulo con una API robusta y flexible.
Readme
Module Repository
Biblioteca Angular para la gestión avanzada de módulos funcionales y sus detalles. Permite administrar módulos, opciones, permisos y relaciones país-módulo con una API robusta y flexible.
📋 Características Principales
- ✅ Gestión de Módulos: Listado, paginación, consulta individual y CRUD completo
- ✅ Opciones y Permisos: Control granular de opciones (crear, actualizar, eliminar, listar, ver, reversar)
- ✅ Relación País-Módulo: Soporte para módulos generales y específicos por país
- ✅ Control de Estados: Habilitar/deshabilitar módulos y opciones con auditoría
- ✅ Búsqueda avanzada: Filtrado por nombre, código, país y estado
- ✅ Arquitectura Moderna: Angular 20.1.5, inyección de dependencias, patrón Repository
- ✅ TypeScript Tipado: Interfaces y modelos robustos
🚀 Instalación
npm install @cbm-common/module-repository⚙️ Configuración
1. Importar el Módulo
import { CbmModuleModule } from '@cbm-common/module-repository';
@NgModule({
imports: [
CbmModuleModule.forRoot({
baseUrl: 'https://api.cbm.com/modules'
})
]
})
export class AppModule { }2. Configuración Standalone (Angular 20+)
import { CbmModuleModule } from '@cbm-common/module-repository';
@Component({
standalone: true,
imports: [CbmModuleModule.forRoot({
baseUrl: 'https://api.cbm.com/modules'
})]
})
export class AppComponent { }📚 API Reference
Interfaz del Repositorio
interface ICbmModuleRepository {
list(params: CbmModuleModel.ListParams): Observable<CbmModuleModel.ListResponse>;
listAll(): Observable<CbmModuleModel.ListAllResponse>;
listPaginated(params: CbmModuleModel.ListPaginatedParams): Observable<CbmModuleModel.ListPaginatedResponse>;
getOne(id: string): Observable<CbmModuleModel.GetOneResponse>;
save(data: CbmModuleModel.SaveBody): Observable<CbmModuleModel.ConfirmResponse>;
update(id: string, data: CbmModuleModel.UpdateBody): Observable<CbmModuleModel.ConfirmResponse>;
changeStatus(id: string, data: CbmModuleModel.ChangeStatusBody): Observable<CbmModuleModel.ConfirmResponse>;
delete(id: string): Observable<CbmModuleModel.ConfirmResponse>;
}💡 Uso Básico
Inyección del Servicio
import { CbmModuleRepository } from '@cbm-common/module-repository';
@Component({
selector: 'app-module-manager'
})
export class ModuleManagerComponent {
modules: CbmModuleModel.ListPaginatedResponse.Items[] = [];
constructor(private moduleRepository: CbmModuleRepository) {
this.loadModules();
}
loadModules() {
const params: CbmModuleModel.ListPaginatedParams = {
page: 1,
size: 20,
enabled: true,
country_id: 'ECU'
};
this.moduleRepository.listPaginated(params).subscribe({
next: (response) => {
if (response.success) {
this.modules = response.items;
}
},
error: (error) => {
console.error('Error al cargar módulos:', error);
}
});
}
}Crear Nuevo Módulo
createNewModule() {
const moduleData: CbmModuleModel.SaveBody = {
name: 'Inventario',
code: 'INV',
description: 'Gestión de inventarios',
host: 'inventario.cbm.com',
country_id: 'ECU',
type: 'country'
};
this.moduleRepository.save(moduleData).subscribe({
next: (response) => {
if (response.success) {
console.log('Módulo creado exitosamente');
this.loadModules();
}
},
error: (error) => console.error('Error al crear módulo:', error)
});
}Actualizar Módulo
updateModule(moduleId: string) {
const updateData: CbmModuleModel.UpdateBody = {
name: 'Inventario Avanzado',
description: 'Gestión avanzada de inventarios',
host: 'inventario-avanzado.cbm.com'
};
this.moduleRepository.update(moduleId, updateData).subscribe({
next: (response) => {
if (response.success) {
console.log('Módulo actualizado exitosamente');
this.loadModules();
}
},
error: (error) => console.error('Error al actualizar módulo:', error)
});
}Cambiar Estado de Módulo
toggleModuleStatus(moduleId: string, enable: boolean) {
const statusData: CbmModuleModel.ChangeStatusBody = {
enabled: enable,
disabled_reason: enable ? undefined : 'Módulo deshabilitado por mantenimiento'
};
this.moduleRepository.changeStatus(moduleId, statusData).subscribe({
next: (response) => {
if (response.success) {
console.log(`Módulo ${enable ? 'habilitado' : 'deshabilitado'} exitosamente`);
this.loadModules();
}
},
error: (error) => console.error('Error al cambiar estado:', error)
});
}📊 Modelos de Datos
Parámetros de Búsqueda y Paginación
interface ListPaginatedParams {
page: number;
size: number;
country_id?: string;
enabled?: boolean;
code?: string;
name?: string;
}Módulo y Detalle
interface Module {
_id: string;
name: string;
code: string;
description: string;
host: string;
enabled: boolean;
country_id?: string;
type?: 'general' | 'country';
module_detail: ModuleDetail[];
}
interface ModuleDetail {
_id: string;
name: string;
description?: string;
option_create?: boolean;
option_update?: boolean;
option_delete?: boolean;
option_list?: boolean;
option_view?: boolean;
option_reverse?: boolean;
router?: string;
icon?: string;
enabled?: boolean;
}Ejemplo de Datos
{
"_id": "64f1a2b3c4d5e6f7g8h9i0j1",
"name": "Inventario",
"code": "INV",
"description": "Gestión de inventarios",
"host": "inventario.cbm.com",
"enabled": true,
"country_id": "ECU",
"type": "country",
"module_detail": [
{
"_id": "detail-001",
"name": "Crear Producto",
"option_create": true,
"option_update": false,
"option_delete": false,
"option_list": true,
"option_view": true,
"router": "/productos/crear",
"icon": "add_box",
"enabled": true
}
]
}🔍 Casos de Uso Comunes
1. Selector de Módulos en Formularios
@Component({
selector: 'app-module-selector'
})
export class ModuleSelectorComponent {
modules: CbmModuleModel.ListResponse.Data[] = [];
selectedModule: string = '';
constructor(private moduleRepository: CbmModuleRepository) {
this.loadActiveModules();
}
loadActiveModules() {
const params: CbmModuleModel.ListParams = {
enabled: true
};
this.moduleRepository.list(params).subscribe({
next: (response) => {
if (response.success) {
this.modules = response.data;
}
},
error: (error) => {
console.error('Error al cargar módulos:', error);
}
});
}
onModuleChange(moduleId: string) {
const selected = this.modules.find(mod => mod._id === moduleId);
if (selected) {
console.log('Módulo seleccionado:', selected.name);
}
}
}2. Dashboard de Módulos
@Component({
selector: 'app-module-dashboard'
})
export class ModuleDashboardComponent {
stats = {
totalModules: 0,
activeModules: 0,
inactiveModules: 0,
modulesByCountry: new Map<string, number>()
};
constructor(private moduleRepository: CbmModuleRepository) {
this.loadDashboardData();
}
loadDashboardData() {
this.moduleRepository.listAll().subscribe({
next: (response) => {
if (response.success) {
this.calculateStats(response.data);
}
}
});
}
private calculateStats(modules: CbmModuleModel.ListAllResponse.Data[]) {
this.stats.totalModules = modules.length;
this.stats.activeModules = modules.filter(m => m.enabled).length;
this.stats.inactiveModules = modules.filter(m => !m.enabled).length;
modules.forEach(module => {
const country = module.country_id || 'general';
const current = this.stats.modulesByCountry.get(country) || 0;
this.stats.modulesByCountry.set(country, current + 1);
});
}
}🏗️ Arquitectura y Patrones
Patrón Repository
La biblioteca implementa el patrón Repository para mantener la separación entre la lógica de negocio y el acceso a datos:
// Interfaz del repositorio
export interface ICbmModuleRepository {
list(params: CbmModuleModel.ListParams): Observable<CbmModuleModel.ListResponse>;
// ...otros métodos
}
// Implementación del servicio
@Injectable({ providedIn: 'root' })
export class CbmModuleService implements ICbmModuleRepository {
constructor(
private readonly http: HttpClient,
@Inject(MODULE_MODULE_CONFIG)
private readonly config: ICbmModuleModuleConfig
) {}
list(params: CbmModuleModel.ListParams): Observable<CbmModuleModel.ListResponse> {
return this.http.get<CbmModuleModel.ListResponse>(this.config.baseUrl, { params: { ...params } });
}
// ...implementación de métodos
}
// Wrapper del repositorio
@Injectable({ providedIn: 'root' })
export class CbmModuleRepository implements ICbmModuleRepository {
constructor(private service: CbmModuleService) {}
list(params: CbmModuleModel.ListParams): Observable<CbmModuleModel.ListResponse> {
return this.service.list(params);
}
// ...delegación a service
}Configuración Centralizada
@NgModule({
imports: [
CbmModuleModule.forRoot({
baseUrl: environment.apiUrl + '/modules'
})
]
})
export class AppModule {}🔧 Mejores Prácticas
Manejo de Errores
@Component({
selector: 'app-safe-module-handler'
})
export class SafeModuleHandlerComponent {
modules: CbmModuleModel.ListResponse.Data[] = [];
loading = false;
error: string | null = null;
constructor(private moduleRepository: CbmModuleRepository) {}
safeLoadModules() {
this.loading = true;
this.error = null;
const params: CbmModuleModel.ListParams = {
enabled: true
};
this.moduleRepository.list(params).subscribe({
next: (response) => {
this.loading = false;
if (response.success && response.data) {
this.modules = response.data;
} else {
this.error = 'Respuesta inválida del servidor';
}
},
error: (error) => {
this.loading = false;
if (error.status === 404) {
this.error = 'Servicio de módulos no disponible';
} else if (error.status === 500) {
this.error = 'Error interno del servidor';
} else {
this.error = 'Error de conexión';
}
}
});
}
}Validación de Datos
@Injectable({ providedIn: 'root' })
export class ModuleValidationService {
constructor(private moduleRepository: CbmModuleRepository) {}
validateModuleData(data: CbmModuleModel.SaveBody): string[] {
const errors: string[] = [];
if (!data.code?.trim()) {
errors.push('El código es obligatorio');
}
if (!data.name?.trim()) {
errors.push('El nombre es obligatorio');
}
if (!data.description?.trim()) {
errors.push('La descripción es obligatoria');
}
if (!data.host?.trim()) {
errors.push('El host es obligatorio');
}
return errors;
}
validateAndSave(data: CbmModuleModel.SaveBody): Observable<boolean> {
const errors = this.validateModuleData(data);
if (errors.length > 0) {
console.error('Errores de validación:', errors);
return of(false);
}
return this.moduleRepository.save(data).pipe(
map(response => response.success),
catchError(() => of(false))
);
}
}📋 Lista de Verificación para Implementación
- [ ] ✅ Configurar el módulo
CbmModuleModule.forRoot()en el AppModule - [ ] ✅ Inyectar
CbmModuleRepositoryen los componentes que lo necesiten - [ ] ✅ Implementar manejo de errores para todas las operaciones CRUD
- [ ] ✅ Validar datos antes de enviar al servidor
- [ ] ✅ Implementar indicadores de carga durante las operaciones
- [ ] ✅ Manejar estados de error y respuestas no exitosas
- [ ] ✅ Probar todas las operaciones CRUD
- [ ] ✅ Verificar funcionamiento de paginación y filtros
- [ ] ✅ Documentar casos de uso específicos de la aplicación
🔗 Dependencias
{
"peerDependencies": {
"@angular/common": "20.1.5",
"@angular/core": "20.1.5"
},
"dependencies": {
"tslib": "2.3.0"
}
}📝 Notas de Versión
v0.0.1
- ✅ Implementación inicial del repositorio de módulos
- ✅ Soporte completo para operaciones CRUD
- ✅ Gestión de opciones y permisos por módulo
- ✅ Control de estados y auditoría
- ✅ Paginación avanzada y filtros
- ✅ Arquitectura basada en patrón Repository
- ✅ Configuración simplificada con
forRoot() - ✅ Compatibilidad con Angular 20.1.5
- ✅ Interfaces TypeScript completamente tipadas
- ✅ Documentación completa en español
- ✅ Ejemplos prácticos de implementación
🤝 Contribución
Para contribuir a esta biblioteca:
- Fork el repositorio
- Crear una rama para la nueva funcionalidad
- Implementar los cambios siguiendo los patrones establecidos
- Agregar pruebas unitarias si es necesario
- Enviar un Pull Request con descripción detallada
📞 Soporte
Para soporte técnico o consultas sobre el uso de la biblioteca, contactar al equipo de desarrollo de CBM.
CBM (Contabilidad y Facturación Moderna) - Repositorio de Módulos v0.0.1
