|
|
@@ -204,29 +204,41 @@ constructor(
|
|
|
) {}
|
|
|
|
|
|
|
|
|
- ngOnInit() {
|
|
|
- this._enviarInfo.currentTextColor.subscribe(color => this.textColor = color);
|
|
|
- this._enviarInfo.currentColor.subscribe(color => this.color = color);
|
|
|
- this.addTab();
|
|
|
- this.route.queryParams.subscribe(params => {
|
|
|
+ // MÉTODO QUE SE EJECUTA AL INICIAR EL COMPONENTE
|
|
|
+ngOnInit() {
|
|
|
+ // SUSCRIPCIÓN PARA RECIBIR COLOR DEL TEXTO DESDE EL SERVICIO
|
|
|
+ this._enviarInfo.currentTextColor.subscribe(color => this.textColor = color);
|
|
|
+
|
|
|
+ // SUSCRIPCIÓN PARA RECIBIR COLOR DE FONDO DESDE EL SERVICIO
|
|
|
+ this._enviarInfo.currentColor.subscribe(color => this.color = color);
|
|
|
+
|
|
|
+ // AÑADIR UNA NUEVA TAB POR DEFECTO AL INICIAR
|
|
|
+ this.addTab();
|
|
|
+
|
|
|
+ // LEER EL ID DEL FORMULARIO SI VIENE POR QUERY PARAMS PARA CARGARLO
|
|
|
+ this.route.queryParams.subscribe(params => {
|
|
|
const id = params['id'];
|
|
|
if (id) {
|
|
|
this.cargarFormulario(+id);
|
|
|
- console.log('ID recibido:', id);
|
|
|
+ console.log('ID RECIBIDO:', id);
|
|
|
}
|
|
|
});
|
|
|
- // this.loadSavedForms();
|
|
|
- }
|
|
|
- initializeForm(): void {
|
|
|
+
|
|
|
+ // this.loadSavedForms(); // OPCIONAL: CARGAR FORMULARIOS GUARDADOS AL INICIO
|
|
|
+}
|
|
|
+
|
|
|
+// MÉTODO PARA INICIALIZAR EL FORMULARIO DINÁMICO CON TODOS LOS CONTROLES
|
|
|
+initializeForm(): void {
|
|
|
const formControls: { [key: string]: FormControl } = {};
|
|
|
|
|
|
+ // ITERAR POR TODAS LAS TABS Y SUS CUADRÍCULAS
|
|
|
for (const tab of this.tabs) {
|
|
|
for (let rowIndex = 0; rowIndex < tab.grid.length; rowIndex++) {
|
|
|
const row = tab.grid[rowIndex];
|
|
|
for (let colIndex = 0; colIndex < row.length; colIndex++) {
|
|
|
const cell = row[colIndex];
|
|
|
if (cell?.element) {
|
|
|
- // Usar tab directamente aquí ya que estamos iterando sobre this.tabs
|
|
|
+ // CREAR EL NOMBRE DEL CONTROL Y AGREGARLO AL FORM
|
|
|
const controlName = this.getControlName(tab, { ...cell, row: rowIndex, col: colIndex });
|
|
|
formControls[controlName] = new FormControl('');
|
|
|
}
|
|
|
@@ -234,8 +246,11 @@ constructor(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ASIGNAR LOS CONTROLES AL FORM GROUP PRINCIPAL
|
|
|
this.form = this.fb.group(formControls);
|
|
|
}
|
|
|
+
|
|
|
+// MÉTODO PARA OBTENER UN NOMBRE DE CONTROL SEGURO (SI NO HAY TAB ACTIVA)
|
|
|
getSafeControlName(cell: any, rowIndex: number, colIndex: number): string {
|
|
|
if (!this.currentTab) {
|
|
|
return `campo_${rowIndex}_${colIndex}`;
|
|
|
@@ -244,173 +259,178 @@ getSafeControlName(cell: any, rowIndex: number, colIndex: number): string {
|
|
|
}
|
|
|
|
|
|
|
|
|
- // MÉTODOS PARA MANEJAR LA API
|
|
|
-
|
|
|
- loadSavedForms() {
|
|
|
- this.isLoading = true;
|
|
|
- this.formApiService.getForms().subscribe({
|
|
|
- next: (forms) => {
|
|
|
- this.savedForms = forms;
|
|
|
- this.isLoading = false;
|
|
|
- },
|
|
|
- error: (error) => {
|
|
|
- console.error('Error al cargar formularios:', error);
|
|
|
- this.saveStatus = 'Error al cargar formularios';
|
|
|
- this.isLoading = false;
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+// CARGAR FORMULARIOS GUARDADOS
|
|
|
+loadSavedForms() {
|
|
|
+ this.isLoading = true;
|
|
|
+ this.formApiService.getForms().subscribe({
|
|
|
+ next: (forms) => {
|
|
|
+ this.savedForms = forms;
|
|
|
+ this.isLoading = false;
|
|
|
+ },
|
|
|
+ error: (error) => {
|
|
|
+ console.error('ERROR AL CARGAR FORMULARIOS:', error);
|
|
|
+ this.saveStatus = 'ERROR AL CARGAR FORMULARIOS';
|
|
|
+ this.isLoading = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
- saveForm() {
|
|
|
+// GUARDAR FORMULARIO NUEVO O EXISTENTE
|
|
|
+saveForm() {
|
|
|
+ // VALIDAR QUE HAYA TÍTULO
|
|
|
if (!this.formTitle.trim()) {
|
|
|
- alert('Por favor, ingresa un título para el formulario');
|
|
|
+ alert('POR FAVOR, INGRESA UN TÍTULO PARA EL FORMULARIO');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-// ESTO ES UNA VALIDACION EN CASO DE QUE EL USUARIO MALLOGRE PONER UN 0 EN LA CONFIGURACION DE LAS FILAS O COLUMNAS
|
|
|
+ // VALIDAR QUE NINGUNA TAB TENGA FILAS O COLUMNAS EN 0
|
|
|
const tabConError = this.tabs.find(tab => tab.rows < 1 || tab.columns < 1);
|
|
|
if (tabConError) {
|
|
|
- alert(` La sección "${tabConError.name}" tiene un número inválido de filas o columnas.\n\n
|
|
|
- No se permite 0.\n Usa al menos 1 fila y 1 columna.`);
|
|
|
+ alert(`LA SECCIÓN "${tabConError.name}" TIENE UN NÚMERO INVÁLIDO DE FILAS O COLUMNAS.\n\nNO SE PERMITE 0.\nUSA AL MENOS 1 FILA Y 1 COLUMNA.`);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Guardar la cuadrícula del tab activo actual
|
|
|
+ // GUARDAR LA CUADRÍCULA DEL TAB ACTIVO ACTUAL
|
|
|
if (this.currentTab) {
|
|
|
this.currentTab.grid = [...this.grid];
|
|
|
}
|
|
|
|
|
|
- //Variable para guardar la configuracion del servicio
|
|
|
+ // CONSTRUIR CONFIGURACIÓN PARA EL SERVICIO
|
|
|
const config = this.buildFormConfiguration();
|
|
|
this.isLoading = true;
|
|
|
- this.saveStatus = 'Guardando...';
|
|
|
+ this.saveStatus = 'GUARDANDO...';
|
|
|
|
|
|
+ // LLAMAR AL SERVICIO PARA GUARDAR EL FORMULARIO
|
|
|
this.formApiService.createForm(config).subscribe({
|
|
|
next: (response) => {
|
|
|
- console.log('Formulario guardado:', response);
|
|
|
+ console.log('FORMULARIO GUARDADO:', response);
|
|
|
this.currentFormId = response.id;
|
|
|
- this.saveStatus = 'Formulario guardado exitosamente';
|
|
|
+ this.saveStatus = 'FORMULARIO GUARDADO EXITOSAMENTE';
|
|
|
this.isLoading = false;
|
|
|
this.router.navigate(['/homeAdmin/verFormulario']);
|
|
|
},
|
|
|
error: (error) => {
|
|
|
- console.error('Error al guardar formulario:', error);
|
|
|
- this.saveStatus = 'Error al guardar el formulario';
|
|
|
+ console.error('ERROR AL GUARDAR FORMULARIO:', error);
|
|
|
+ this.saveStatus = 'ERROR AL GUARDAR EL FORMULARIO';
|
|
|
this.isLoading = false;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+// CARGAR UN FORMULARIO EXISTENTE POR ID
|
|
|
+loadForm(formId: number) {
|
|
|
+ this.isLoading = true;
|
|
|
+ this.formApiService.getForm(formId).subscribe({
|
|
|
+ next: (formData) => {
|
|
|
+ this.loadFormConfiguration(formData);
|
|
|
+ this.currentFormId = formId;
|
|
|
+ this.isLoading = false;
|
|
|
+ },
|
|
|
+ error: (error) => {
|
|
|
+ console.error('ERROR AL CARGAR FORMULARIO:', error);
|
|
|
+ this.saveStatus = 'ERROR AL CARGAR EL FORMULARIO';
|
|
|
+ this.isLoading = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- loadForm(formId: number) {
|
|
|
- this.isLoading = true;
|
|
|
- this.formApiService.getForm(formId).subscribe({
|
|
|
- next: (formData) => {
|
|
|
- this.loadFormConfiguration(formData);
|
|
|
- this.currentFormId = formId;
|
|
|
- this.isLoading = false;
|
|
|
- },
|
|
|
- error: (error) => {
|
|
|
- console.error('Error al cargar formulario:', error);
|
|
|
- this.saveStatus = 'Error al cargar el formulario';
|
|
|
- this.isLoading = false;
|
|
|
- }
|
|
|
- });
|
|
|
+// PUBLICAR FORMULARIO (SOLO UNO PUEDE ESTAR PUBLICADO)
|
|
|
+publishForm(formId: number) {
|
|
|
+ if (!confirm('¿ESTÁS SEGURO DE QUE QUIERES PUBLICAR ESTE FORMULARIO?')) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- publishForm(formId: number) {
|
|
|
- if (!confirm('¿Estás seguro de que quieres publicar este formulario?')) {
|
|
|
- return;
|
|
|
+ this.isLoading = true;
|
|
|
+ this.formApiService.publishForm(formId).subscribe({
|
|
|
+ next: (response) => {
|
|
|
+ this.saveStatus = 'FORMULARIO PUBLICADO EXITOSAMENTE';
|
|
|
+ this.loadSavedForms();
|
|
|
+ this.isLoading = false;
|
|
|
+
|
|
|
+ // LIMPIAR MENSAJE DESPUÉS DE 3 SEGUNDOS
|
|
|
+ setTimeout(() => {
|
|
|
+ this.saveStatus = '';
|
|
|
+ }, 3000);
|
|
|
+ },
|
|
|
+ error: (error) => {
|
|
|
+ console.error('ERROR AL PUBLICAR FORMULARIO:', error);
|
|
|
+ this.saveStatus = 'ERROR AL PUBLICAR EL FORMULARIO';
|
|
|
+ this.isLoading = false;
|
|
|
}
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// CONSTRUIR LA CONFIGURACIÓN DEL FORMULARIO PARA EL SERVICIO
|
|
|
+buildFormConfiguration(): FormConfiguration {
|
|
|
+ return {
|
|
|
+ title: this.formTitle,
|
|
|
+ tabs: this.tabs.map(tab => ({
|
|
|
+ id: tab.id,
|
|
|
+ title: tab.name,
|
|
|
+ rows: tab.rows,
|
|
|
+ columns: tab.columns,
|
|
|
+ elements: this.extractElementsFromGrid(tab.grid)
|
|
|
+ }))
|
|
|
+ };
|
|
|
+}
|
|
|
|
|
|
- this.isLoading = true;
|
|
|
- this.formApiService.publishForm(formId).subscribe({
|
|
|
- next: (response) => {
|
|
|
- this.saveStatus = 'Formulario publicado exitosamente';
|
|
|
- this.loadSavedForms();
|
|
|
- this.isLoading = false;
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- this.saveStatus = '';
|
|
|
- }, 3000);
|
|
|
- },
|
|
|
- error: (error) => {
|
|
|
- console.error('Error al publicar formulario:', error);
|
|
|
- this.saveStatus = 'Error al publicar el formulario';
|
|
|
- this.isLoading = false;
|
|
|
+// CARGAR CONFIGURACIÓN DEL FORMULARIO DESDE LA API
|
|
|
+loadFormConfiguration(formData: any) {
|
|
|
+ this.formTitle = formData.title || '';
|
|
|
+ this.tabs = [];
|
|
|
+
|
|
|
+ if (formData.tabs && formData.tabs.length > 0) {
|
|
|
+ formData.tabs.forEach((tabData: any) => {
|
|
|
+ const newTab: Tab = {
|
|
|
+ id: tabData.id,
|
|
|
+ name: tabData.title,
|
|
|
+ active: false,
|
|
|
+ rows: tabData.rows,
|
|
|
+ columns: tabData.columns,
|
|
|
+ grid: this.buildNewGrid(tabData.rows, tabData.columns)
|
|
|
+ };
|
|
|
+
|
|
|
+ // CARGAR ELEMENTOS EN LA CUADRÍCULA
|
|
|
+ if (tabData.elements && tabData.elements.length > 0) {
|
|
|
+ tabData.elements.forEach((elementData: any) => {
|
|
|
+ const { row, col } = elementData.position;
|
|
|
+ if (row < newTab.rows && col < newTab.columns) {
|
|
|
+ newTab.grid[row][col].element = { ...elementData.element };
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ this.tabs.push(newTab);
|
|
|
});
|
|
|
- }
|
|
|
|
|
|
- buildFormConfiguration(): FormConfiguration {
|
|
|
- return {
|
|
|
- title: this.formTitle,
|
|
|
- tabs: this.tabs.map(tab => ({
|
|
|
- id: tab.id,
|
|
|
- title: tab.name,
|
|
|
- rows: tab.rows,
|
|
|
- columns: tab.columns,
|
|
|
- elements: this.extractElementsFromGrid(tab.grid)
|
|
|
- }))
|
|
|
- };
|
|
|
+ this.switchToTab(this.tabs[0]);
|
|
|
+ this.initializeForm();
|
|
|
+ } else {
|
|
|
+ this.addTab();
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- loadFormConfiguration(formData: any) {
|
|
|
- this.formTitle = formData.title || '';
|
|
|
+// CREAR UN NUEVO FORMULARIO (SE PIERDEN CAMBIOS NO GUARDADOS)
|
|
|
+newForm() {
|
|
|
+ if (confirm('¿ESTÁS SEGURO DE QUE QUIERES CREAR UN NUEVO FORMULARIO? SE PERDERÁN LOS CAMBIOS NO GUARDADOS.')) {
|
|
|
+ this.formTitle = '';
|
|
|
+ this.currentFormId = null;
|
|
|
this.tabs = [];
|
|
|
-
|
|
|
- if (formData.tabs && formData.tabs.length > 0) {
|
|
|
- formData.tabs.forEach((tabData: any) => {
|
|
|
- const newTab: Tab = {
|
|
|
- id: tabData.id,
|
|
|
- name: tabData.title,
|
|
|
- active: false,
|
|
|
- rows: tabData.rows,
|
|
|
- columns: tabData.columns,
|
|
|
- grid: this.buildNewGrid(tabData.rows, tabData.columns)
|
|
|
- };
|
|
|
-
|
|
|
- // Cargar elementos en el grid
|
|
|
- if (tabData.elements && tabData.elements.length > 0) {
|
|
|
- tabData.elements.forEach((elementData: any) => {
|
|
|
- const { row, col } = elementData.position;
|
|
|
- if (row < newTab.rows && col < newTab.columns) {
|
|
|
- newTab.grid[row][col].element = { ...elementData.element };
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- this.tabs.push(newTab);
|
|
|
- });
|
|
|
-
|
|
|
- this.switchToTab(this.tabs[0]);
|
|
|
- this.initializeForm();
|
|
|
- } else {
|
|
|
- this.addTab();
|
|
|
- }
|
|
|
+ this.addTab();
|
|
|
+ this.saveStatus = '';
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- newForm() {
|
|
|
- if (confirm('¿Estás seguro de que quieres crear un nuevo formulario? Se perderán los cambios no guardados.')) {
|
|
|
- this.formTitle = '';
|
|
|
- this.currentFormId = null;
|
|
|
- this.tabs = [];
|
|
|
- this.addTab();
|
|
|
- this.saveStatus = '';
|
|
|
- }
|
|
|
+// ELIMINAR UN FORMULARIO EXISTENTE
|
|
|
+deleteForm(formId: number) {
|
|
|
+ if (!confirm('¿ESTÁS SEGURO DE QUE QUIERES ELIMINAR ESTE FORMULARIO? ESTA ACCIÓN NO SE PUEDE DESHACER.')) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- deleteForm(formId: number) {
|
|
|
- if (!confirm('¿Estás seguro de que quieres eliminar este formulario? Esta acción no se puede deshacer.')) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+ // AQUÍ IRÍA LLAMADO AL SERVICIO PARA ELIMINAR
|
|
|
+ this.loadSavedForms();
|
|
|
+}
|
|
|
|
|
|
- this.loadSavedForms();
|
|
|
- }
|
|
|
|
|
|
// MÉTODOS EXISTENTES (sin cambios)
|
|
|
|
|
|
@@ -423,186 +443,195 @@ getSafeControlName(cell: any, rowIndex: number, colIndex: number): string {
|
|
|
}
|
|
|
|
|
|
// TABS
|
|
|
- addTab(rows = this.defaultRows, columns = this.defaultColumns) {
|
|
|
- const newTab: Tab = {
|
|
|
- id: `tab_${this.tabs.length + 1}`,
|
|
|
- name: `Sección ${this.tabs.length + 1}`,
|
|
|
- active: false,
|
|
|
- rows,
|
|
|
- columns,
|
|
|
- grid: this.buildNewGrid(rows, columns)
|
|
|
- };
|
|
|
- this.tabs.push(newTab);
|
|
|
- this.switchToTab(newTab);
|
|
|
- }
|
|
|
+ // ====================== TABS / SECCIONES ======================
|
|
|
+
|
|
|
+// AÑADE UNA NUEVA TAB (SECCIÓN) AL FORMULARIO
|
|
|
+addTab(rows = this.defaultRows, columns = this.defaultColumns) {
|
|
|
+ const newTab: Tab = {
|
|
|
+ id: `tab_${this.tabs.length + 1}`, // ID ÚNICO
|
|
|
+ name: `Sección ${this.tabs.length + 1}`, // NOMBRE POR DEFECTO
|
|
|
+ active: false,
|
|
|
+ rows,
|
|
|
+ columns,
|
|
|
+ grid: this.buildNewGrid(rows, columns) // GENERAR GRID VACÍO
|
|
|
+ };
|
|
|
+ this.tabs.push(newTab); // AGREGAR A ARRAY DE TABS
|
|
|
+ this.switchToTab(newTab); // ACTIVAR LA TAB NUEVA
|
|
|
+}
|
|
|
|
|
|
- removeTab() {
|
|
|
- if (this.tabs.length <= 1) return;
|
|
|
+// ELIMINA LA TAB ACTIVA
|
|
|
+removeTab() {
|
|
|
+ if (this.tabs.length <= 1) return; // SI SOLO QUEDA 1 TAB, NO HACE NADA
|
|
|
|
|
|
- const activeIndex = this.tabs.findIndex(tab => tab.active);
|
|
|
- if (activeIndex === -1) return;
|
|
|
+ const activeIndex = this.tabs.findIndex(tab => tab.active);
|
|
|
+ if (activeIndex === -1) return;
|
|
|
|
|
|
- this.tabs.splice(activeIndex, 1);
|
|
|
- const newActiveIndex = activeIndex === 0 ? 0 : activeIndex - 1;
|
|
|
- this.tabs.forEach((tab, index) => {
|
|
|
- tab.active = index === newActiveIndex;
|
|
|
- });
|
|
|
- }
|
|
|
+ this.tabs.splice(activeIndex, 1); // ELIMINAR TAB ACTIVA
|
|
|
+ const newActiveIndex = activeIndex === 0 ? 0 : activeIndex - 1;
|
|
|
|
|
|
+ // ACTIVAR TAB ADYACENTE
|
|
|
+ this.tabs.forEach((tab, index) => {
|
|
|
+ tab.active = index === newActiveIndex;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// CAMBIA A LA TAB SELECCIONADA
|
|
|
switchToTab(tab: Tab) {
|
|
|
- if (this.currentTab) this.currentTab.grid = [...this.grid];
|
|
|
+ if (this.currentTab) this.currentTab.grid = [...this.grid]; // GUARDAR GRID ACTUAL
|
|
|
this.tabs.forEach(t => t.active = false);
|
|
|
tab.active = true;
|
|
|
this.currentTab = tab;
|
|
|
- this.grid = [...tab.grid];
|
|
|
- this.clearSelection();
|
|
|
- this.buildFormGroup();
|
|
|
- this.cdr.detectChanges();
|
|
|
+ this.grid = [...tab.grid]; // CARGAR GRID DE LA TAB
|
|
|
+ this.clearSelection(); // LIMPIAR SELECCIÓN DE CELDAS
|
|
|
+ this.buildFormGroup(); // RECONSTRUIR FORM GROUP PARA EL TAB
|
|
|
+ this.cdr.detectChanges(); // DETECTAR CAMBIOS
|
|
|
}
|
|
|
+
|
|
|
+// ====================== FORM GROUP DINÁMICO ======================
|
|
|
+
|
|
|
+// CONSTRUYE EL FORM GROUP BASADO EN LA CUADRÍCULA ACTUAL
|
|
|
buildFormGroup() {
|
|
|
const group: any = {};
|
|
|
this.grid.forEach((row, i) => {
|
|
|
row.forEach((cell, j) => {
|
|
|
if (cell.element && cell.element.name) {
|
|
|
const controlName = this.getSafeControlName(cell, i, j);
|
|
|
- group[controlName] = new FormControl('');
|
|
|
+ group[controlName] = new FormControl(''); // CONTROL VACÍO
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
this.form = new FormGroup(group);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+// TRACKBY PARA RENDEREAR FILAS Y CELDAS EFICIENTEMENTE
|
|
|
trackByRowIndex(index: number, row: GridCell[]): number {
|
|
|
return index;
|
|
|
}
|
|
|
-
|
|
|
trackByCellIndex(index: number, cell: GridCell): string {
|
|
|
return `${cell.row}_${cell.col}`;
|
|
|
}
|
|
|
|
|
|
+// ACTUALIZA EL NOMBRE DE UNA TAB
|
|
|
+updateTabName(tab: Tab, name: string) {
|
|
|
+ tab.name = name;
|
|
|
+}
|
|
|
|
|
|
- updateTabName(tab: Tab, name: string) {
|
|
|
- tab.name = name;
|
|
|
- }
|
|
|
-
|
|
|
- // GRID
|
|
|
- buildNewGrid(rows: number, columns: number): GridCell[][] {
|
|
|
- return Array.from({ length: rows }, (_, r) =>
|
|
|
- Array.from({ length: columns }, (_, c) => ({
|
|
|
- row: r, col: c, selected: false
|
|
|
- }))
|
|
|
- );
|
|
|
- }
|
|
|
+// ====================== GRID ======================
|
|
|
|
|
|
- updateTabDimensions(tab: Tab, newRows: number, newColumns: number) {
|
|
|
- const oldGrid = tab.grid.map(row => row.map(cell => ({ ...cell, element: cell.element ? { ...cell.element } : undefined })));
|
|
|
+// CREA UN GRID VACÍO
|
|
|
+buildNewGrid(rows: number, columns: number): GridCell[][] {
|
|
|
+ return Array.from({ length: rows }, (_, r) =>
|
|
|
+ Array.from({ length: columns }, (_, c) => ({
|
|
|
+ row: r, col: c, selected: false
|
|
|
+ }))
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
- tab.rows = newRows;
|
|
|
- tab.columns = newColumns;
|
|
|
- tab.grid = this.buildNewGrid(newRows, newColumns);
|
|
|
+// ACTUALIZA DIMENSIONES DE UNA TAB (FILAS Y COLUMNAS)
|
|
|
+updateTabDimensions(tab: Tab, newRows: number, newColumns: number) {
|
|
|
+ const oldGrid = tab.grid.map(row => row.map(cell => ({ ...cell, element: cell.element ? { ...cell.element } : undefined })));
|
|
|
+ tab.rows = newRows;
|
|
|
+ tab.columns = newColumns;
|
|
|
+ tab.grid = this.buildNewGrid(newRows, newColumns);
|
|
|
|
|
|
- this.preserveElementsInNewGrid(oldGrid, tab.grid, newRows, newColumns);
|
|
|
+ // PRESERVAR ELEMENTOS YA EXISTENTES DENTRO DEL NUEVO GRID
|
|
|
+ this.preserveElementsInNewGrid(oldGrid, tab.grid, newRows, newColumns);
|
|
|
|
|
|
- if (this.currentTab?.id === tab.id) {
|
|
|
- this.grid = tab.grid.map(row => row.map(cell => ({ ...cell, element: cell.element ? { ...cell.element } : undefined })));
|
|
|
- }
|
|
|
+ if (this.currentTab?.id === tab.id) {
|
|
|
+ this.grid = tab.grid.map(row => row.map(cell => ({ ...cell, element: cell.element ? { ...cell.element } : undefined })));
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- preserveElementsInNewGrid(oldGrid: GridCell[][], newGrid: GridCell[][], maxR: number, maxC: number) {
|
|
|
- for (let r = 0; r < oldGrid.length; r++) {
|
|
|
- for (let c = 0; c < oldGrid[r]?.length; c++) {
|
|
|
- const cell = oldGrid[r][c];
|
|
|
- if (cell?.element && r < maxR && c < maxC) {
|
|
|
- newGrid[r][c].element = { ...cell.element };
|
|
|
- }
|
|
|
+// PRESERVA ELEMENTOS EN EL GRID AL CAMBIAR DIMENSIONES
|
|
|
+preserveElementsInNewGrid(oldGrid: GridCell[][], newGrid: GridCell[][], maxR: number, maxC: number) {
|
|
|
+ for (let r = 0; r < oldGrid.length; r++) {
|
|
|
+ for (let c = 0; c < oldGrid[r]?.length; c++) {
|
|
|
+ const cell = oldGrid[r][c];
|
|
|
+ if (cell?.element && r < maxR && c < maxC) {
|
|
|
+ newGrid[r][c].element = { ...cell.element };
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- onTabDimensionsChange(event: { tab: Tab, rows: number, columns: number }) {
|
|
|
- this.updateTabDimensions(event.tab, event.rows, event.columns);
|
|
|
- }
|
|
|
-
|
|
|
- buildGrid() {
|
|
|
- if (this.currentTab) {
|
|
|
- this.currentTab.grid = this.grid = this.buildNewGrid(this.currentTab.rows, this.currentTab.columns);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- verifyGridIntegrity(grid: GridCell[][]): boolean {
|
|
|
- return grid.every((row, r) => row.every((cell, c) => {
|
|
|
- cell.row = r;
|
|
|
- cell.col = c;
|
|
|
- return true;
|
|
|
- }));
|
|
|
- }
|
|
|
+// ESCUCHA CAMBIOS DE DIMENSIONES DESDE EL PANEL DE CONFIGURACIÓN
|
|
|
+onTabDimensionsChange(event: { tab: Tab, rows: number, columns: number }) {
|
|
|
+ this.updateTabDimensions(event.tab, event.rows, event.columns);
|
|
|
+}
|
|
|
|
|
|
- // FORM CONFIG
|
|
|
- onFormTitleChange(title: string) {
|
|
|
- this.formTitle = title;
|
|
|
+// RECONSTRUYE EL GRID DE LA TAB ACTUAL
|
|
|
+buildGrid() {
|
|
|
+ if (this.currentTab) {
|
|
|
+ this.currentTab.grid = this.grid = this.buildNewGrid(this.currentTab.rows, this.currentTab.columns);
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- onRowsChange(rows: number) {
|
|
|
- if (this.currentTab) this.updateTabDimensions(this.currentTab, rows, this.currentTab.columns);
|
|
|
- }
|
|
|
+// VERIFICA INTEGRIDAD DEL GRID (FILAS Y COLUMNAS CORRECTAS)
|
|
|
+verifyGridIntegrity(grid: GridCell[][]): boolean {
|
|
|
+ return grid.every((row, r) => row.every((cell, c) => {
|
|
|
+ cell.row = r;
|
|
|
+ cell.col = c;
|
|
|
+ return true;
|
|
|
+ }));
|
|
|
+}
|
|
|
|
|
|
- onColumnsChange(cols: number) {
|
|
|
- if (this.currentTab) this.updateTabDimensions(this.currentTab, this.currentTab.rows, cols);
|
|
|
- }
|
|
|
+// ====================== FORM CONFIG ======================
|
|
|
+onFormTitleChange(title: string) { this.formTitle = title; }
|
|
|
+onRowsChange(rows: number) { if (this.currentTab) this.updateTabDimensions(this.currentTab, rows, this.currentTab.columns); }
|
|
|
+onColumnsChange(cols: number) { if (this.currentTab) this.updateTabDimensions(this.currentTab, this.currentTab.rows, cols); }
|
|
|
+onShowTooltipChange(field: string) { this.showTooltip = field; }
|
|
|
|
|
|
- onShowTooltipChange(field: string) {
|
|
|
- this.showTooltip = field;
|
|
|
- }
|
|
|
+// ====================== SERIALIZACIÓN ======================
|
|
|
+logConfiguration() {
|
|
|
+ const config = this.buildFormConfiguration();
|
|
|
+ console.log('CONFIGURACIÓN COMPLETA:', config);
|
|
|
+ return config;
|
|
|
+}
|
|
|
|
|
|
- // SERIALIZACIÓN
|
|
|
- logConfiguration() {
|
|
|
- const config = this.buildFormConfiguration();
|
|
|
- console.log('Configuración completa:', config);
|
|
|
- return config;
|
|
|
- }
|
|
|
+extractElementsFromGrid(grid: GridCell[][]) {
|
|
|
+ return grid.flatMap(row => row
|
|
|
+ .filter(cell => cell.element)
|
|
|
+ .map(cell => ({
|
|
|
+ position: { row: cell.row, col: cell.col },
|
|
|
+ element: this.serializeElement(cell.element!)
|
|
|
+ }))
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
- extractElementsFromGrid(grid: GridCell[][]) {
|
|
|
- return grid.flatMap(row => row
|
|
|
- .filter(cell => cell.element)
|
|
|
- .map(cell => ({
|
|
|
- position: { row: cell.row, col: cell.col },
|
|
|
- element: this.serializeElement(cell.element!)
|
|
|
- }))
|
|
|
- );
|
|
|
- }
|
|
|
+// SERIALIZA UN ELEMENTO INDIVIDUAL PARA GUARDAR EN API
|
|
|
+serializeElement(el: FormElement) {
|
|
|
+ const base: any = { type: el.type, label: el.label, required: el.required };
|
|
|
+ if (el.placeholder) base.placeholder = el.placeholder;
|
|
|
+ if (el.id) base.id = el.id;
|
|
|
+ if (el.name) base.name = el.name;
|
|
|
+ if (el.value) base.value = el.value;
|
|
|
+ if (el.description) base.description = el.description;
|
|
|
+ if (el.options?.length) base.options = el.options;
|
|
|
+ if (el.min !== undefined) base.min = el.min;
|
|
|
+ if (el.max !== undefined) base.max = el.max;
|
|
|
+ if (el.pattern) base.pattern = el.pattern;
|
|
|
+ return base;
|
|
|
+}
|
|
|
|
|
|
- serializeElement(el: FormElement) {
|
|
|
- const base: any = {
|
|
|
- type: el.type, label: el.label, required: el.required
|
|
|
- };
|
|
|
- if (el.placeholder) base.placeholder = el.placeholder;
|
|
|
- if (el.id) base.id = el.id;
|
|
|
- if (el.name) base.name = el.name;
|
|
|
- if (el.value) base.value = el.value;
|
|
|
- if (el.description) base.description = el.description;
|
|
|
- if (el.options?.length) base.options = el.options;
|
|
|
- if (el.min !== undefined) base.min = el.min;
|
|
|
- if (el.max !== undefined) base.max = el.max;
|
|
|
- if (el.pattern) base.pattern = el.pattern;
|
|
|
- return base;
|
|
|
- }
|
|
|
+// ====================== INTERACCIÓN CELDAS ======================
|
|
|
|
|
|
- // INTERACCIÓN CON CELDAS Y CAMPOS
|
|
|
- selectCell(cell: GridCell) {
|
|
|
- this.clearSelection();
|
|
|
- cell.selected = true;
|
|
|
- this.selectedCell = cell;
|
|
|
- this.showElementSelector = true;
|
|
|
- }
|
|
|
+// SELECCIONA UNA CELDA
|
|
|
+selectCell(cell: GridCell) {
|
|
|
+ this.clearSelection();
|
|
|
+ cell.selected = true;
|
|
|
+ this.selectedCell = cell;
|
|
|
+ this.showElementSelector = true;
|
|
|
+}
|
|
|
|
|
|
- clearSelection() {
|
|
|
- this.grid.forEach(row => row.forEach(c => c.selected = false));
|
|
|
- this.selectedCell = null;
|
|
|
- this.showElementSelector = false;
|
|
|
- this.editingElement = null;
|
|
|
- }
|
|
|
+// LIMPIA SELECCIÓN DE CELDAS Y ELEMENTOS
|
|
|
+clearSelection() {
|
|
|
+ this.grid.forEach(row => row.forEach(c => c.selected = false));
|
|
|
+ this.selectedCell = null;
|
|
|
+ this.showElementSelector = false;
|
|
|
+ this.editingElement = null;
|
|
|
+}
|
|
|
|
|
|
+// CONFIRMA IDENTIFICADOR DEL NUEVO ELEMENTO
|
|
|
confirmarIdentificador() {
|
|
|
if (this.nombreIdentificadorControl.invalid || !this.pendingTemplate) {
|
|
|
this.nombreIdentificadorControl.markAsTouched();
|
|
|
@@ -616,14 +645,10 @@ confirmarIdentificador() {
|
|
|
element.name = nombreClave;
|
|
|
element.label = this.capitalizeWords(nombreClave.replace(/_/g, ' '));
|
|
|
|
|
|
- // Validaciones según tipo
|
|
|
+ // APLICA VALIDACIONES SEGÚN TIPO
|
|
|
switch (element.type) {
|
|
|
case 'email':
|
|
|
- Object.assign(element, {
|
|
|
- required: true,
|
|
|
- pattern: '^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$',
|
|
|
- placeholder: 'ejemplo@correo.com'
|
|
|
- });
|
|
|
+ Object.assign(element, { required: true, pattern: '^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$', placeholder: 'ejemplo@correo.com' });
|
|
|
break;
|
|
|
case 'number':
|
|
|
Object.assign(element, { required: true, min: 1, max: 100 });
|
|
|
@@ -632,12 +657,11 @@ confirmarIdentificador() {
|
|
|
element.required = true;
|
|
|
element.min = element.min ?? 1;
|
|
|
element.max = element.max ?? 20;
|
|
|
- if (!element.pattern) {
|
|
|
- element.pattern = '^\\b(\\w+\\b\\s*){1,20}$';
|
|
|
- }
|
|
|
+ if (!element.pattern) element.pattern = '^\\b(\\w+\\b\\s*){1,20}$';
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ // ASIGNA ELEMENTO A LA CELDA SELECCIONADA
|
|
|
this.selectedCell!.element = element;
|
|
|
this.editingElement = element;
|
|
|
this.showElementSelector = false;
|
|
|
@@ -646,12 +670,12 @@ confirmarIdentificador() {
|
|
|
this.pendingTemplate = null;
|
|
|
}
|
|
|
|
|
|
+// AGREGA ELEMENTO A LA CELDA (ABRE MODAL SI YA HAY ELEMENTO)
|
|
|
addElementToCell(template: FormElement) {
|
|
|
if (!this.selectedCell || this.selectedCell.element) {
|
|
|
- this.showModal = true;
|
|
|
+ this.showModal = true; // MODAL AVISO DE CELDA OCUPADA
|
|
|
return;
|
|
|
- };
|
|
|
-
|
|
|
+ }
|
|
|
this.pendingTemplate = template;
|
|
|
this.nombreIdentificadorControl.reset();
|
|
|
this.showModalIdentificador = true;
|
|
|
@@ -659,6 +683,8 @@ addElementToCell(template: FormElement) {
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
+
|
|
|
// // Solicita un "nombre clave" legible para el input, ejemplo: "nombre_alumno"
|
|
|
// let nombreClave = prompt('Ingresa el identificador único para este campo (ej. nombre_alumno):');
|
|
|
// if (!nombreClave) {
|
|
|
@@ -706,6 +732,8 @@ addElementToCell(template: FormElement) {
|
|
|
this.showModal = false;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// DEVUELVE EL NOMBRE DEL CONTROL DE FORMULARIO PARA UNA CELDA
|
|
|
getControlName(tab: Tab | null, cell: any): string {
|
|
|
const row = cell.row ?? 0;
|
|
|
const col = cell.col ?? 0;
|
|
|
@@ -713,57 +741,68 @@ getControlName(tab: Tab | null, cell: any): string {
|
|
|
const rawName = cell.element?.name;
|
|
|
const cleaned = rawName?.trim().replace(/\s+/g, '_').toLowerCase();
|
|
|
|
|
|
+ // SI NO HAY NOMBRE DEFINIDO, USAR FORMATO POR DEFECTO "campo_row_col"
|
|
|
return cleaned && cleaned !== '' ? cleaned : `campo_${row}_${col}`;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+// CAPITALIZA CADA PALABRA DE UN TEXTO
|
|
|
capitalizeWords(text: string): string {
|
|
|
return text.replace(/\b\w/g, c => c.toUpperCase());
|
|
|
}
|
|
|
|
|
|
|
|
|
- updateCurrentTabGrid() {
|
|
|
- if (this.currentTab) this.currentTab.grid = [...this.grid];
|
|
|
- }
|
|
|
+// ACTUALIZA EL GRID DE LA TAB ACTIVA CON LOS CAMBIOS DEL GRID VISIBLE
|
|
|
+updateCurrentTabGrid() {
|
|
|
+ if (this.currentTab) this.currentTab.grid = [...this.grid];
|
|
|
+}
|
|
|
|
|
|
- removeElementFromCell(cell: GridCell) {
|
|
|
- cell.element = undefined;
|
|
|
+// ELIMINA EL ELEMENTO DE UNA CELDA
|
|
|
+removeElementFromCell(cell: GridCell) {
|
|
|
+ cell.element = undefined;
|
|
|
+ this.updateCurrentTabGrid();
|
|
|
+}
|
|
|
+
|
|
|
+// SELECCIONA UN ELEMENTO PARA EDITARLO EN EL PANEL DE PROPIEDADES
|
|
|
+selectElementForEditing(cell: GridCell) {
|
|
|
+ this.editingElement = cell.element || null;
|
|
|
+}
|
|
|
+
|
|
|
+// ACTUALIZA EL ELEMENTO EDITADO DESDE EL PANEL DE PROPIEDADES
|
|
|
+onEditingElementChange(updated: FormElement) {
|
|
|
+ if (this.selectedCell) {
|
|
|
+ this.selectedCell.element = updated;
|
|
|
this.updateCurrentTabGrid();
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- selectElementForEditing(cell: GridCell) {
|
|
|
- this.editingElement = cell.element || null;
|
|
|
+// ACTUALIZA EL ELEMENTO EDITADO DIRECTAMENTE (sin necesidad de selección de celda)
|
|
|
+onElementChange(updated: FormElement) {
|
|
|
+ if (this.editingElement) {
|
|
|
+ Object.assign(this.editingElement, updated);
|
|
|
+ this.updateCurrentTabGrid();
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- onEditingElementChange(updated: FormElement) {
|
|
|
- if (this.selectedCell) {
|
|
|
- this.selectedCell.element = updated;
|
|
|
- this.updateCurrentTabGrid();
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- onElementChange(updated: FormElement) {
|
|
|
- if (this.editingElement) {
|
|
|
- Object.assign(this.editingElement, updated);
|
|
|
- this.updateCurrentTabGrid();
|
|
|
- }
|
|
|
- }
|
|
|
+// CARGA UN FORMULARIO EXISTENTE DESDE LA API PARA EDITARLO
|
|
|
cargarFormulario(id: number): void {
|
|
|
this.formService.getFormById(id).subscribe(data => {
|
|
|
this.formData = data;
|
|
|
console.log('Form a editar:', this.formData);
|
|
|
|
|
|
+ // CARGA LA CONFIGURACIÓN DEL FORMULARIO EN EL COMPONENTE
|
|
|
this.loadFormConfiguration(this.formData.form.configuration);
|
|
|
this.formTitle = this.formData.form.title;
|
|
|
this.currentFormId = this.formData.form.id;
|
|
|
|
|
|
- // Inicializa el formulario reactivo basado en los campos
|
|
|
+ // INICIALIZA EL FORMULARIO REACTIVO CON LOS CAMPOS
|
|
|
this.initializeForm();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
- updateForm() {
|
|
|
+// ACTUALIZA UN FORMULARIO YA EXISTENTE
|
|
|
+updateForm() {
|
|
|
if (!this.formTitle.trim()) {
|
|
|
alert('Por favor, ingresa un título para el formulario');
|
|
|
return;
|
|
|
@@ -774,12 +813,14 @@ cargarFormulario(id: number): void {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // VALIDACIÓN DE DIMENSIONES MÍNIMAS
|
|
|
const tabConError = this.tabs.find(tab => tab.rows < 1 || tab.columns < 1);
|
|
|
if (tabConError) {
|
|
|
alert(`La sección "${tabConError.name}" tiene dimensiones inválidas (mínimo 1 fila y 1 columna).`);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // ACTUALIZA EL GRID DEL TAB ACTUAL
|
|
|
if (this.currentTab) {
|
|
|
this.currentTab.grid = [...this.grid];
|
|
|
}
|
|
|
@@ -788,13 +829,13 @@ cargarFormulario(id: number): void {
|
|
|
this.isLoading = true;
|
|
|
this.saveStatus = 'Actualizando formulario...';
|
|
|
|
|
|
+ // LLAMADA A API PARA ACTUALIZAR
|
|
|
this.formService.updateForm(this.currentFormId, config).subscribe({
|
|
|
next: (response) => {
|
|
|
console.log('Formulario actualizado:', response);
|
|
|
this.saveStatus = 'Formulario actualizado correctamente';
|
|
|
this.isLoading = false;
|
|
|
- this.router.navigate(['/homeAdmin/verFormulario']);
|
|
|
-
|
|
|
+ this.router.navigate(['/homeAdmin/verFormulario']);
|
|
|
},
|
|
|
error: (error) => {
|
|
|
console.error('Error al actualizar formulario:', error);
|
|
|
@@ -803,6 +844,9 @@ cargarFormulario(id: number): void {
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+// DECIDE SI SE DEBE GUARDAR O ACTUALIZAR
|
|
|
saveOrUpdateForm() {
|
|
|
if (this.currentFormId) {
|
|
|
this.updateForm();
|
|
|
@@ -811,8 +855,7 @@ saveOrUpdateForm() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
+}
|
|
|
|
|
|
// joinCell(cell: GridCell, rowSpan: number = 1, colSpan: number = 1) {
|
|
|
// if (!this.currentTab) return;
|