Selaa lähdekoodia

creo componente

FREDY 4 kuukautta sitten
vanhempi
commit
2f3f5f6584

+ 14 - 0
Front/src/app/modules/Administrador/administrador.module.ts

@@ -27,6 +27,13 @@ import { ModalNiveleducativoEdit } from './pages/niveles-educativos/niveles-educ
 import { CircularesComponent, ModalCircular, ModalCircularesEdit, ModalExtraInfo, ModalStats } from './pages/circulares/circulares.component';
 import { ConsultarRegistroAcayAdmiComponent } from './pages/consultarRegistroAcayAdmi/consultar-registro-acay-admi.component';
 import { SeguimientoComponent } from './pages/seguimientoUsuarios/seguimiento.component';
+import {Component} from '@angular/core';
+import {FormControl, FormsModule} from '@angular/forms';
+import {MatTabsModule} from '@angular/material/tabs';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatButtonModule} from '@angular/material/button';
+import {MatInputModule} from '@angular/material/input';
+import {MatFormFieldModule} from '@angular/material/form-field';
 
 
 @NgModule({
@@ -85,6 +92,13 @@ import { SeguimientoComponent } from './pages/seguimientoUsuarios/seguimiento.co
     AdminRoutingModule,
     SharedModule,
     MaterialModule,
+      MatFormFieldModule,
+    MatInputModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatButtonModule,
+    MatCheckboxModule,
+    MatTabsModule,
     ReactiveFormsModule,
     FullCalendarModule,
     NgxEditorModule.forRoot({

+ 1 - 1
Front/src/app/modules/Administrador/components/cards/cards.component.css

@@ -186,4 +186,4 @@
         margin-right: 0;
         margin-bottom: 50px;
     }
-}
+}

+ 708 - 57
Front/src/app/modules/Administrador/pages/admin-form/admin-form.component.css

@@ -1,98 +1,749 @@
+/* admin-form.component.css */
 
 .centrar {
     margin-top: 4%;
     line-height: auto;
     text-align: center;
     width: 100%;
-    margin-right: 30px;
-    padding: 20px;
+    margin-right: 50px;
+
+}
+
+.fondo {
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.content {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0;
+  text-align: center;
+}
+
+.container {
+  display: grid;
+  grid-template-columns: 1fr 2fr 1fr;
+  gap: 20px;
+  padding: 20px;
+  height: calc(100vh - 120px);
+}
+
+.container-formConfig,
+.container-form,
+.container-formPropiedades {
+  background: white;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+  overflow-y: auto;
 }
-.container{
-display: flex;
-padding: 50px;
-justify-items: center;
+
+.title-form {
+  font-size: 18px;
+  font-weight: bold;
+  margin-bottom: 20px;
+  color: #333;
+  border-bottom: 2px solid #e0e0e0;
+  padding-bottom: 10px;
+}
+
+.config-section {
+  margin-bottom: 25px;
+}
+
+.text {
+  font-weight: 500;
+  margin-bottom: 8px;
+  color: #555;
+}
+
+.form__input {
+  width: 90%;
+  padding: 12px;
+  border: 2px solid #e0e0e0;
+  border-radius: 6px;
+  font-size: 14px;
+  transition: border-color 0.3s ease;
+}
+
+.form__input:focus {
+  outline: none;
+  border-color: #007bff;
+  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
+}
+
+.form__input-small {
+  width: 80px;
+  padding: 8px;
+  border: 2px solid #e0e0e0;
+  border-radius: 4px;
+  font-size: 14px;
+  text-align: center;
+}
+
+.grid-config {
+  display: flex;
+  gap: 20px;
+  align-items: center;
 }
 
-.container-form{
-  padding:20px;
-  background-color:white;
-  border-radius: 15px;
-  width: 40%;
+.input-group {
   display: flex;
   flex-direction: column;
+  gap: 5px;
+}
+
+.input-group label {
+  font-size: 12px;
+  font-weight: 500;
+  color: #666;
+}
+
+.elements-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 10px;
+}
+
+.element-btn {
+  padding: 12px;
+  background: #f8f9fa;
+  border: 2px solid #e0e0e0;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  font-size: 12px;
+  font-weight: 500;
+}
+
+.element-btn:hover {
+  background: #e9ecef;
+  border-color: #007bff;
+  transform: translateY(-2px);
+}
+
+.btn-generate {
+  width: 100%;
+  padding: 12px;
+  background: #007bff;
+  color: white;
+  border: none;
+  border-radius: 6px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+}
+
+.btn-generate:hover {
+  background: #0056b3;
+}
+
+.form-grid {
+  display: grid;
+  gap: 10px;
+  padding: 20px;
+  border: 2px dashed #e0e0e0;
+  border-radius: 8px;
+  min-height: 400px;
+}
+
+.grid-row {
+  display: contents;
+}
+
+.grid-cell {
+  min-height: 120px;
+  border: 2px solid #e0e0e0;
+  border-radius: 6px;
+  display: flex;
   align-items: center;
-height: 600px;
-margin: 20px;
-margin-top: -20px;
-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
+  justify-content: center;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  position: relative;
+  padding: 10px;
+}
+
+.grid-cell:hover {
+  border-color: #007bff;
+  background: #f8f9fa;
+}
+
+.grid-cell.selected {
+  border-color: #007bff;
+  background: #e3f2fd;
+  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
+}
+
+.grid-cell.has-element {
+  background: #e8f5e8;
+  border-color: #28a745;
+}
+
+.grid-cell.has-element:hover {
+  background: #d4edda;
+}
+
+.cell-element {
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  gap: 8px;
+  padding: 10px;
+  width: 100%;
+  text-align: left;
+}
+
+.element-label {
+  font-size: 12px;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 4px;
+}
 
+.form-element {
+  width: 100%;
+  padding: 6px 8px;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  font-size: 11px;
+  background: white;
+  opacity: 0.8;
 }
-.container-formConfig{
-  padding:20px;
-  background-color:white;
-  border-radius: 15px;
-  width: 25%;
+
+.radio-group,
+.checkbox-item {
   display: flex;
   flex-direction: column;
+  gap: 4px;
+}
+
+.radio-item {
+  display: flex;
   align-items: center;
-height: 600px;
-margin: 20px;
-margin-top: -20px;
-box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
+  gap: 4px;
+  font-size: 10px;
+}
+
+.radio-item input[type="radio"] {
+  width: auto;
+  margin: 0;
+}
+
+.checkbox-item {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 4px;
+  font-size: 10px;
+}
+
+.checkbox-item input[type="checkbox"] {
+  width: auto;
+  margin: 0;
+}
+
+.checkbox-item label {
+  margin: 0;
+  font-size: 10px;
+}
+
+.remove-btn {
+  position: absolute;
+  top: -8px;
+  right: -8px;
+  width: 20px;
+  height: 20px;
+  border-radius: 50%;
+  background: #dc3545;
+  color: white;
+  border: none;
+  cursor: pointer;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: background-color 0.3s ease;
+}
 
+.remove-btn:hover {
+  background: #c82333;
 }
-.container-formPropiedades{
-  padding:20px;
-  background-color:white;
-  border-radius: 15px;
-  width: 20%;
+
+.cell-placeholder {
   display: flex;
   flex-direction: column;
   align-items: center;
-  height: 300px;
-  top: 50px !important;
-  margin: 20px;
-  margin-top: -20px;
-  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
+  gap: 5px;
+  color: #999;
+  font-size: 12px;
+}
+
+.cell-placeholder span {
+  font-weight: bold;
+}
+
+.cell-placeholder small {
+  font-size: 10px;
+  font-style: italic;
+}
+
+.properties-panel {
+  padding: 15px;
+  background: #f8f9fa;
+  border-radius: 6px;
+  border: 1px solid #e0e0e0;
+  max-height: 600px;
+  overflow-y: auto;
+}
+
+.properties-panel h4 {
+  margin: 0 0 15px 0;
+  color: #007bff;
+  font-size: 16px;
+  border-bottom: 1px solid #e0e0e0;
+  padding-bottom: 8px;
+}
+
+.property-group {
+  margin-bottom: 15px;
+}
+
+.property-group label {
+  display: block;
+  margin-bottom: 5px;
+  font-size: 12px;
+  font-weight: 500;
+  color: #555;
+}
+
+.property-input {
+  width: 100%;
+  padding: 8px;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  font-size: 12px;
+  transition: border-color 0.3s ease;
+}
+
+.property-input:focus {
+  outline: none;
+  border-color: #007bff;
+  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
+}
+
+.property-input-small {
+  width: 80px;
+  padding: 6px;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  font-size: 12px;
+  text-align: center;
+}
+
+.number-range {
+  display: flex;
+  gap: 10px;
+}
+
+.number-range > div {
+  flex: 1;
+}
+
+.checkbox-label {
+  display: flex !important;
+  align-items: center;
+  gap: 8px;
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.checkbox-label input[type="checkbox"] {
+  width: auto;
+  margin: 0;
+}
+
+.options-list {
+  border: 1px solid #e0e0e0;
+  border-radius: 4px;
+  padding: 10px;
+  background: white;
+}
+
+.option-item {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 8px;
+  align-items: center;
+}
+
+.option-item .property-input {
+  flex: 1;
+  margin-bottom: 0;
+}
+
+.remove-option-btn {
+  width: 24px;
+  height: 24px;
+  border: none;
+  background: #dc3545;
+  color: white;
+  border-radius: 50%;
+  cursor: pointer;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: background-color 0.3s ease;
+}
+
+.remove-option-btn:hover {
+  background: #c82333;
+}
+
+.add-option-btn {
+  width: 100%;
+  padding: 8px;
+  background: #28a745;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  font-size: 12px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+}
+
+.add-option-btn:hover {
+  background: #218838;
+}
+
+.fadeIn {
+  animation: fadeIn 0.5s ease-in;
+}
+/* Estilos adicionales para campos de ayuda */
+
+/* Contenedor para input con ayuda */
+.input-with-help {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+}
+.example-input-label,
+.example-add-tab-button,
+.example-delete-tab-button {
+  margin: 8px;
+}
 
+.input-with-help label,
+.input-with-help p {
+  margin: 0;
+  flex: 1;
 }
 
-.title-form{
+/* Botón de ayuda */
+.help-btn {
+  background: #007bff;
+  border: none;
+  border-radius: 50%;
+  width: 22px;
+  height: 22px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: all 0.2s ease;
+  flex-shrink: 0;
+}
+
+.help-btn:hover {
+  background: #0056b3;
+  transform: scale(1.1);
+}
+
+.help-icon {
+  color: white;
+  font-size: 12px;
   font-weight: bold;
- font-size: 20px;
+  font-style: normal;
 }
-.text{
-  margin-right: 200px;
-  padding: 20px;
+
+/* Tooltip de ayuda */
+.help-tooltip {
+  background: #f8f9fa;
+  border: 1px solid #dee2e6;
+  border-radius: 8px;
+  padding: 12px;
+  margin-bottom: 10px;
+  font-size: 13px;
+  color: #495057;
+  line-height: 1.4;
+  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+  position: relative;
 }
-.form__input {
-  font-family: 'Roboto', sans-serif;
-  color: #333;
-  font-size: 1.2rem;
-  padding: 1rem 1rem;
-  border-radius: 0.2rem;
-  background-color: rgb(255, 255, 255);
+
+.help-tooltip::before {
+  content: "💡";
+  margin-right: 6px;
+}
+
+/* Contenedor para elementos disponibles */
+.element-btn-container {
+  position: relative;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+}
+
+.element-btn {
+  flex: 1;
+  padding: 10px 15px;
+  background: #28a745;
+  color: white;
   border: none;
-  width: 90%;
+  border-radius: 5px;
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+  font-size: 14px;
+}
+
+.element-btn:hover {
+  background: #218838;
+}
+
+.element-help-btn {
+  background: #17a2b8;
+  border: none;
+  border-radius: 50%;
+  width: 20px;
+  height: 20px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: all 0.2s ease;
+  flex-shrink: 0;
+}
+
+.element-help-btn:hover {
+  background: #138496;
+  transform: scale(1.1);
+}
+
+.element-help-btn .help-icon {
+  font-size: 11px;
+}
+
+.element-tooltip {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  right: 0;
+  z-index: 1000;
+  margin-top: 5px;
+}
+
+/* Etiquetas de campo mejoradas */
+.element-label-container {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  margin-bottom: 5px;
+}
+
+.element-label {
+  font-weight: 500;
+  color: #333;
+  font-size: 14px;
+  margin: 0;
+}
+
+.required-indicator {
+  color: #dc3545;
+  font-weight: bold;
+}
+
+.element-description {
   display: block;
-  border-bottom: 0.3rem solid transparent;
-  transition: all 0.3s;
+  margin-top: 4px;
+  color: #6c757d;
+  font-style: italic;
+  font-size: 12px;
+  line-height: 1.3;
 }
 
-input {
-  width: 100%;
-  padding: 5px;
+/* Mejorar el aspecto del placeholder */
+.cell-placeholder {
+  text-align: center;
+  padding: 20px;
+  color: #6c757d;
+  border: 2px dashed #dee2e6;
+  border-radius: 8px;
+  transition: all 0.2s ease;
+}
+
+.cell-placeholder:hover {
+  border-color: #007bff;
+  background-color: #f8f9fa;
+}
+
+.cell-placeholder span {
+  display: block;
+  font-weight: 500;
+  margin-bottom: 4px;
+}
+
+.cell-placeholder small {
+  font-size: 11px;
+  color: #868e96;
+}
+
+/* Panel de propiedades vacío */
+.empty-properties {
+  text-align: center;
+  padding: 40px 20px;
+  color: #6c757d;
+}
+
+.empty-icon {
+  font-size: 48px;
+  margin-bottom: 16px;
+}
+
+.empty-properties p {
+  margin: 0 0 8px 0;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.empty-properties small {
+  font-size: 13px;
+  color: #868e96;
+}
+
+/* Mejorar botones de opciones */
+.add-option-btn {
+  background: #28a745;
+  color: white;
   border: none;
   border-radius: 5px;
-  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+  padding: 8px 12px;
+  cursor: pointer;
+  font-size: 13px;
+  transition: background-color 0.2s ease;
+  margin-top: 8px;
+}
+
+.add-option-btn:hover {
+  background: #218838;
+}
+
+.remove-option-btn {
+  background: #dc3545;
+  color: white;
+  border: none;
+  border-radius: 3px;
+  width: 24px;
+  height: 24px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: background-color 0.2s ease;
+}
+
+.remove-option-btn:hover {
+  background: #c82333;
+}
+
+/* Mejorar botón de eliminar campo */
+.remove-btn {
+  position: absolute;
+  top: 5px;
+  right: 5px;
+  background: #dc3545;
+  color: white;
+  border: none;
+  border-radius: 50%;
+  width: 24px;
+  height: 24px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
   font-size: 16px;
-  transition: box-shadow 0.3s ease, transform 0.2s ease;
+  font-weight: bold;
+  transition: all 0.2s ease;
+  z-index: 10;
 }
 
-input:hover {
-  box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
-  transform: scale(1.02);
+.remove-btn:hover {
+  background: #c82333;
+  transform: scale(1.1);
 }
 
+/* Responsive adjustments */
+@media (max-width: 768px) {
+  .input-with-help {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 4px;
+  }
+
+  .element-btn-container {
+    flex-direction: column;
+    gap: 8px;
+  }
+
+  .element-tooltip {
+    position: static;
+    margin-top: 8px;
+  }
+
+  .help-btn {
+    width: 20px;
+    height: 20px;
+  }
+
+  .help-icon {
+    font-size: 11px;
+  }
+}
+@keyframes fadeIn {
+  from { opacity: 0; transform: translateY(-10px); }
+  to { opacity: 1; transform: translateY(0); }
+}
 
+/* Responsive */
+@media (max-width: 1200px) {
+  .container {
+    grid-template-columns: 1fr;
+    grid-template-rows: auto auto 1fr;
+  }
 
+  .elements-grid {
+    grid-template-columns: repeat(3, 1fr);
+  }
+}
+
+@media (max-width: 768px) {
+  .container {
+    padding: 10px;
+    gap: 10px;
+  }
+
+  .form-grid {
+    padding: 10px;
+  }
+
+  .grid-cell {
+    min-height: 100px;
+  }
+
+  .elements-grid {
+    grid-template-columns: 1fr;
+  }
+
+}

+ 469 - 10
Front/src/app/modules/Administrador/pages/admin-form/admin-form.component.html

@@ -1,27 +1,486 @@
+<!-- admin-form.component.html -->
 <div class="centrar fadeIn">
   <div class="fondo" [style.background-color]="color">
-    <p class="content" [style.color]="textColor">Creacion de Formularios</p>
+    <p class="content" [style.color]="textColor">Creador de Formularios</p>
   </div>
 </div>
 
 <div class="container">
+  <!-- Configuration Panel -->
+  <div class="container-formConfig">
+    <div class="title-form">Configuración General</div>
 
+    <!-- Form Title -->
+    <div class="config-section">
+      <div class="input-with-help">
+        <p class="text">Título del formulario</p>
+        <button class="help-btn" (click)="showHelp('formTitle')" type="button">
+          <i class="help-icon">?</i>
+        </button>
+      </div>
+      <div class="help-tooltip" *ngIf="showTooltip === 'formTitle'">
+        {{getHelpMessage('formTitle')}}
+      </div>
+      <input
+        type="text"
+        class="form__input"
+        placeholder="Ej: Formulario de Contacto"
+        [(ngModel)]="formTitle">
+    </div>
 
-  <div class="container-formConfig">
+    <!-- Grid Configuration -->
+    <div class="config-section">
+      <div class="input-with-help">
+        <p class="text">Diseño de la Cuadrícula</p>
+        <button class="help-btn" (click)="showHelp('gridConfig')" type="button">
+          <i class="help-icon">?</i>
+        </button>
+      </div>
+      <div class="help-tooltip" *ngIf="showTooltip === 'gridConfig'">
+        Define cómo se organizarán los campos en tu formulario. Puedes crear una cuadrícula personalizada.
+      </div>
+      <div class="grid-config">
+        <div class="input-group">
+          <div class="input-with-help">
+            <label>Filas:</label>
+            <button class="help-btn" (click)="showHelp('rows')" type="button">
+              <i class="help-icon">?</i>
+            </button>
+          </div>
+          <div class="help-tooltip" *ngIf="showTooltip === 'rows'">
+            {{getHelpMessage('rows')}}
+          </div>
+          <input
+            type="number"
+            class="form__input-small"
+            [(ngModel)]="rows"
+            (ngModelChange)="onGridConfigChange()"
+            min="1"
+            max="10">
+        </div>
+        <div class="input-group">
+          <div class="input-with-help">
+            <label>Columnas:</label>
+            <button class="help-btn" (click)="showHelp('columns')" type="button">
+              <i class="help-icon">?</i>
+            </button>
+          </div>
+          <div class="help-tooltip" *ngIf="showTooltip === 'columns'">
+            {{getHelpMessage('columns')}}
+          </div>
+          <input
+            type="number"
+            class="form__input-small"
+            [(ngModel)]="columns"
+            (ngModelChange)="onGridConfigChange()"
+            min="1"
+            max="10">
+        </div>
+      </div>
+    </div>
 
-    <div class="title-form">Configuracion deL Formulario </div>
-    <form action="">
-  <p class="text">Titulo del formulario</p>
+    <!-- Available Elements -->
+    <div class="config-section" *ngIf="showElementSelector">
+      <div class="input-with-help">
+        <p class="text">Tipos de Campos Disponibles</p>
+        <button class="help-btn" (click)="showHelp('availableElements')" type="button">
+          <i class="help-icon">?</i>
+        </button>
+      </div>
+      <div class="help-tooltip" *ngIf="showTooltip === 'availableElements'">
+        Selecciona el tipo de campo que quieres agregar a tu formulario. Cada tipo sirve para recopilar información diferente.
+      </div>
+      <div class="elements-grid">
+        <div class="element-btn-container" *ngFor="let element of availableElements">
+          <button
+            class="element-btn"
+            (click)="addElementToCell(element)">
+            {{element.label}}
+          </button>
+          <button
+            class="element-help-btn"
+            (click)="showHelp(element.type + 'Field')"
+            type="button">
+            <i class="help-icon">?</i>
+          </button>
+          <div class="help-tooltip element-tooltip" *ngIf="showTooltip === element.type + 'Field'">
+            {{getHelpMessage(element.type + 'Field')}}
+          </div>
+        </div>
+      </div>
+    </div>
 
-  <input type="text" class="form__input" placeholder="Titulo del Formulario">
-  </form>
+    <!-- Generate JSON Button -->
+    <div class="config-section">
+      <div class="input-with-help">
+        <button class="btn-generate" (click)="logConfiguration()">
+          Generar Configuración del Formulario
+        </button>
+        <button class="help-btn" (click)="showHelp('generateJson')" type="button">
+          <i class="help-icon">?</i>
+        </button>
+      </div>
+      <div class="help-tooltip" *ngIf="showTooltip === 'generateJson'">
+        Genera el código de configuración de tu formulario que podrás usar para implementarlo.
+      </div>
+    </div>
   </div>
+
+  <!-- Form Grid -->
   <div class="container-form">
+    <div class="title-form">Vista Previa del Formulario</div>
+    <div class="form-grid" [style.grid-template-columns]="'repeat(' + columns + ', 1fr)'">
+      <div
+        *ngFor="let row of grid; let i = index"
+        class="grid-row">
+        <div
+          *ngFor="let cell of row; let j = index"
+          class="grid-cell"
+          [class.selected]="cell.selected"
+          [class.has-element]="cell.element"
+          (click)="selectCell(cell)"
+          (dblclick)="selectElementForEditing(cell)">
+
+          <!-- Show element if exists -->
+          <div *ngIf="cell.element" class="cell-element">
+            <div class="element-label-container">
+              <label class="element-label">{{cell.element.label}}</label>
+              <span class="required-indicator" *ngIf="cell.element.required">*</span>
+            </div>
+
+            <!-- Text Input -->
+            <input
+              *ngIf="cell.element.type === 'text'"
+              type="text"
+              class="form-element"
+              [placeholder]="cell.element.placeholder"
+              [required]="cell.element.required"
+              disabled>
+
+            <!-- Number Input -->
+            <input
+              *ngIf="cell.element.type === 'number'"
+              type="number"
+              class="form-element"
+              [placeholder]="cell.element.placeholder"
+              [min]="cell.element.min"
+              [max]="cell.element.max"
+              [required]="cell.element.required"
+              disabled>
+
+            <!-- Email Input -->
+            <input
+              *ngIf="cell.element.type === 'email'"
+              type="email"
+              class="form-element"
+              [placeholder]="cell.element.placeholder"
+              [required]="cell.element.required"
+              disabled>
+
+            <!-- Date Input -->
+            <input
+              *ngIf="cell.element.type === 'date'"
+              type="date"
+              class="form-element"
+              [required]="cell.element.required"
+              disabled>
+
+            <!-- Select -->
+            <select
+              *ngIf="cell.element.type === 'select'"
+              class="form-element"
+              [required]="cell.element.required"
+              disabled>
+              <option value="">Selecciona una opción</option>
+              <option *ngFor="let option of cell.element.options || []" [value]="option">
+                {{option}}
+              </option>
+            </select>
+
+            <!-- Radio Buttons -->
+            <div *ngIf="cell.element.type === 'radio'" class="radio-group">
+              <div *ngFor="let option of cell.element.options || []; let idx = index" class="radio-item">
+                <input
+                  type="radio"
+                  [name]="cell.element.name"
+                  [id]="cell.element.id + '_' + idx"
+                  [value]="option"
+                  disabled>
+                <label [for]="cell.element.id + '_' + idx">{{option}}</label>
+              </div>
+            </div>
+
+            <!-- Checkbox -->
+            <div *ngIf="cell.element.type === 'checkbox'" class="checkbox-item">
+              <input
+                type="checkbox"
+                [id]="cell.element.id"
+                [required]="cell.element.required"
+                disabled>
+              <label [for]="cell.element.id">{{cell.element.label}}</label>
+            </div>
+
+            <!-- Textarea -->
+            <textarea
+              *ngIf="cell.element.type === 'textarea'"
+              class="form-element"
+              [placeholder]="cell.element.placeholder"
+              [required]="cell.element.required"
+              disabled
+              rows="3">
+            </textarea>
+
+            <!-- Description -->
+            <small class="element-description" *ngIf="cell.element.description">
+              {{cell.element.description}}
+            </small>
 
-    <div class="title-form"> Formulario</div>
+            <button
+              class="remove-btn"
+              (click)="removeElementFromCell(cell); $event.stopPropagation()"
+              title="Eliminar campo">
+              ×
+            </button>
+          </div>
+
+          <!-- Show placeholder if empty -->
+          <div *ngIf="!cell.element" class="cell-placeholder">
+            <span>Posición {{i + 1}},{{j + 1}}</span>
+            <small>Haz clic para agregar un campo</small>
+          </div>
+        </div>
+      </div>
+    </div>
   </div>
-   <div class="container-formPropiedades">
 
-    <div class="title-form">Propiedades </div>
+  <!-- Properties Panel -->
+  <div class="container-formPropiedades">
+    <div class="title-form">Propiedades del Campo</div>
+
+    <div *ngIf="editingElement" class="properties-panel">
+      <h4>Editando: {{editingElement.label}}</h4>
+
+      <!-- Common Properties -->
+      <div class="property-group">
+        <div class="input-with-help">
+          <label>Etiqueta del Campo:</label>
+          <button class="help-btn" (click)="showHelp('label')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'label'">
+          {{getHelpMessage('label')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.label"
+          placeholder="Ej: Nombre completo">
+      </div>
+
+      <div class="property-group">
+        <div class="input-with-help">
+          <label>Identificador del Campo:</label>
+          <button class="help-btn" (click)="showHelp('fieldId')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'fieldId'">
+          {{getHelpMessage('fieldId')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.id"
+          placeholder="Se genera automáticamente">
+      </div>
+
+      <div class="property-group">
+        <div class="input-with-help">
+          <label>Nombre del Campo:</label>
+          <button class="help-btn" (click)="showHelp('fieldName')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'fieldName'">
+          {{getHelpMessage('fieldName')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.name"
+          placeholder="nombre_campo">
+      </div>
+
+      <div class="property-group">
+        <div class="input-with-help">
+          <label class="checkbox-label">
+            <input
+              type="checkbox"
+              [(ngModel)]="editingElement.required">
+            Campo obligatorio
+          </label>
+          <button class="help-btn" (click)="showHelp('required')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'required'">
+          {{getHelpMessage('required')}}
+        </div>
+      </div>
+
+      <!-- Placeholder for text-based inputs -->
+      <div class="property-group" *ngIf="editingElement.type === 'text' || editingElement.type === 'email' || editingElement.type === 'textarea' || editingElement.type === 'number'">
+        <div class="input-with-help">
+          <label>Texto de Ayuda:</label>
+          <button class="help-btn" (click)="showHelp('placeholder')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'placeholder'">
+          {{getHelpMessage('placeholder')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.placeholder"
+          placeholder="Ej: Escribe tu nombre aquí">
+      </div>
+
+      <!-- Min/Max for number inputs -->
+      <div *ngIf="editingElement.type === 'number'" class="property-group">
+        <div class="number-range">
+          <div>
+            <div class="input-with-help">
+              <label>Valor Mínimo:</label>
+              <button class="help-btn" (click)="showHelp('minValue')" type="button">
+                <i class="help-icon">?</i>
+              </button>
+            </div>
+            <div class="help-tooltip" *ngIf="showTooltip === 'minValue'">
+              {{getHelpMessage('minValue')}}
+            </div>
+            <input
+              type="number"
+              class="property-input-small"
+              [(ngModel)]="editingElement.min"
+              placeholder="0">
+          </div>
+          <div>
+            <div class="input-with-help">
+              <label>Valor Máximo:</label>
+              <button class="help-btn" (click)="showHelp('maxValue')" type="button">
+                <i class="help-icon">?</i>
+              </button>
+            </div>
+            <div class="help-tooltip" *ngIf="showTooltip === 'maxValue'">
+              {{getHelpMessage('maxValue')}}
+            </div>
+            <input
+              type="number"
+              class="property-input-small"
+              [(ngModel)]="editingElement.max"
+              placeholder="100">
+          </div>
+        </div>
+      </div>
+
+      <!-- Pattern for text inputs -->
+      <div class="property-group" *ngIf="editingElement.type === 'text'">
+        <div class="input-with-help">
+          <label>Formato Específico:</label>
+          <button class="help-btn" (click)="showHelp('pattern')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'pattern'">
+          {{getHelpMessage('pattern')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.pattern"
+          placeholder="Ej: [0-9]{3}-[0-9]{3}-[0-9]{4} para teléfono">
+      </div>
+
+      <!-- Options for select and radio -->
+      <div class="property-group" *ngIf="(editingElement.type === 'select' || editingElement.type === 'radio') && editingElement.options">
+        <div class="input-with-help">
+          <label>Opciones Disponibles:</label>
+          <button class="help-btn" (click)="showHelp('options')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'options'">
+          {{getHelpMessage('options')}}
+        </div>
+        <div class="options-list">
+          <div *ngFor="let option of editingElement.options; let i = index; trackBy: trackByIndex" class="option-item">
+            <input
+              type="text"
+              class="property-input"
+              [value]="option"
+              (input)="updateOption(i, $event)"
+              placeholder="Nombre de la opción">
+            <button
+              class="remove-option-btn"
+              (click)="removeOption(i)"
+              title="Eliminar opción">
+              ×
+            </button>
+          </div>
+          <button
+            class="add-option-btn"
+            (click)="addOption()">
+            + Agregar nueva opción
+          </button>
+        </div>
+      </div>
+
+      <!-- Description -->
+      <div class="property-group">
+        <div class="input-with-help">
+          <label>Descripción o Instrucciones:</label>
+          <button class="help-btn" (click)="showHelp('description')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'description'">
+          {{getHelpMessage('description')}}
+        </div>
+        <textarea
+          class="property-input"
+          [(ngModel)]="editingElement.description"
+          placeholder="Ej: Por favor ingresa tu número de teléfono con código de área"
+          rows="2">
+        </textarea>
+      </div>
+
+      <!-- Default value -->
+      <div class="property-group">
+        <div class="input-with-help">
+          <label>Valor Predeterminado:</label>
+          <button class="help-btn" (click)="showHelp('defaultValue')" type="button">
+            <i class="help-icon">?</i>
+          </button>
+        </div>
+        <div class="help-tooltip" *ngIf="showTooltip === 'defaultValue'">
+          {{getHelpMessage('defaultValue')}}
+        </div>
+        <input
+          type="text"
+          class="property-input"
+          [(ngModel)]="editingElement.value"
+          placeholder="Valor que aparecerá por defecto">
+      </div>
+    </div>
+
+    <div *ngIf="!editingElement" class="properties-panel">
+      <div class="empty-properties">
+        <div class="empty-icon">⚙️</div>
+        <p>Selecciona un campo para editarlo</p>
+        <small>Haz doble clic en cualquier campo del formulario para ver sus propiedades</small>
+      </div>
+    </div>
   </div>
 </div>

+ 218 - 0
Front/src/app/modules/Administrador/pages/admin-form/admin-form.component.ts

@@ -1,6 +1,34 @@
+// admin-form.component.ts
 import { Component, OnInit } from '@angular/core';
 import { EnviarInfoService } from '../../services/enviar-info.service';
 
+interface GridCell {
+  row: number;
+  col: number;
+  selected: boolean;
+  element?: FormElement;
+}
+
+interface FormElement {
+  type: 'text' | 'select' | 'radio' | 'checkbox' | 'textarea' | 'number' | 'email' | 'date';
+  label: string;
+  placeholder?: string;
+  required: boolean;
+  options?: string[]; // Para select y radio
+  id?: string;
+  name?: string;
+  value?: string;
+  min?: number;
+  max?: number;
+  pattern?: string;
+  description?: string;
+}
+
+interface HelpTooltip {
+  field: string;
+  message: string;
+}
+
 @Component({
   selector: 'app-admin-form',
   templateUrl: './admin-form.component.html',
@@ -10,6 +38,59 @@ export class AdminFormComponent implements OnInit {
   public color: string = '';
   public textColor: string = '';
 
+  // Grid configuration
+  public rows: number = 3;
+  public columns: number = 3;
+  public grid: GridCell[][] = [];
+
+  // Form configuration
+  public formTitle: string = '';
+
+  // Available form elements
+  public availableElements: FormElement[] = [
+    { type: 'text', label: 'Texto Corto', required: false, placeholder: 'Escribe aquí...', id: '', name: '' },
+    { type: 'select', label: 'Lista de Opciones', required: false, options: ['Opción 1', 'Opción 2', 'Opción 3'], id: '', name: '' },
+    { type: 'radio', label: 'Selección Única', required: false, options: ['Opción A', 'Opción B', 'Opción C'], id: '', name: '' },
+    { type: 'checkbox', label: 'Casilla de Verificación', required: false, id: '', name: '' },
+    { type: 'textarea', label: 'Texto Largo', required: false, placeholder: 'Texto extenso...', id: '', name: '' },
+    { type: 'number', label: 'Número', required: false, placeholder: '0', min: 0, max: 100, id: '', name: '' },
+    { type: 'email', label: 'Correo Electrónico', required: false, placeholder: 'correo@ejemplo.com', id: '', name: '' },
+    { type: 'date', label: 'Fecha', required: false, id: '', name: '' }
+  ];
+
+  // Selected cell for element assignment
+  public selectedCell: GridCell | null = null;
+  public showElementSelector: boolean = false;
+  public editingElement: FormElement | null = null;
+
+  // Help tooltips
+  public helpTooltips: HelpTooltip[] = [
+    { field: 'formTitle', message: 'Nombre que aparecerá en la parte superior del formulario' },
+    { field: 'rows', message: 'Número de filas horizontales en la cuadrícula (máximo 10)' },
+    { field: 'columns', message: 'Número de columnas verticales en la cuadrícula (máximo 10)' },
+    { field: 'label', message: 'Texto que se mostrará como título del campo' },
+    { field: 'fieldId', message: 'Identificador único del campo (se genera automáticamente)' },
+    { field: 'fieldName', message: 'Nombre interno del campo para procesar los datos' },
+    { field: 'required', message: 'Si está marcado, el usuario debe completar este campo obligatoriamente' },
+    { field: 'placeholder', message: 'Texto de ayuda que aparece dentro del campo cuando está vacío' },
+    { field: 'minValue', message: 'Valor mínimo permitido para este número' },
+    { field: 'maxValue', message: 'Valor máximo permitido para este número' },
+    { field: 'pattern', message: 'Formato específico que debe seguir el texto (ejemplo: solo números, solo letras)' },
+    { field: 'options', message: 'Lista de opciones disponibles para que el usuario seleccione' },
+    { field: 'description', message: 'Descripción adicional o instrucciones para el usuario' },
+    { field: 'defaultValue', message: 'Valor que aparecerá por defecto cuando se cargue el formulario' },
+    { field: 'textShort', message: 'Campo para escribir texto corto como nombres, títulos, etc.' },
+    { field: 'optionsList', message: 'Lista desplegable donde el usuario puede elegir una opción' },
+    { field: 'singleSelection', message: 'Botones circulares donde solo se puede seleccionar una opción' },
+    { field: 'checkboxField', message: 'Casilla que se puede marcar o desmarcar (Sí/No)' },
+    { field: 'textLong', message: 'Campo para escribir texto extenso como comentarios, descripciones, etc.' },
+    { field: 'numberField', message: 'Campo que solo acepta números' },
+    { field: 'emailField', message: 'Campo especial para direcciones de correo electrónico' },
+    { field: 'dateField', message: 'Selector de fecha con calendario' }
+  ];
+
+  public showTooltip: string = '';
+
   constructor(private _enviarInfo: EnviarInfoService) {}
 
   ngOnInit(): void {
@@ -20,5 +101,142 @@ export class AdminFormComponent implements OnInit {
     this._enviarInfo.currentColor.subscribe(color => {
       this.color = color;
     });
+
+    this.generateGrid();
+  }
+
+  // Generate the grid based on rows and columns
+  generateGrid(): void {
+    this.grid = [];
+    for (let i = 0; i < this.rows; i++) {
+      const row: GridCell[] = [];
+      for (let j = 0; j < this.columns; j++) {
+        row.push({
+          row: i,
+          col: j,
+          selected: false
+        });
+      }
+      this.grid.push(row);
+    }
+  }
+
+  // Update grid when rows or columns change
+  onGridConfigChange(): void {
+    if (this.rows > 0 && this.columns > 0 && this.rows <= 10 && this.columns <= 10) {
+      this.generateGrid();
+    }
+  }
+
+  // Select a cell in the grid
+  selectCell(cell: GridCell): void {
+    // Deselect all cells
+    this.grid.forEach(row => {
+      row.forEach(c => c.selected = false);
+    });
+
+    // Select the clicked cell
+    cell.selected = true;
+    this.selectedCell = cell;
+    this.showElementSelector = true;
+  }
+
+  // Add an element to the selected cell
+  addElementToCell(element: FormElement): void {
+    if (this.selectedCell) {
+      // Create a copy with unique ID
+      const newElement = {
+        ...element,
+        id: `campo_${this.selectedCell.row}_${this.selectedCell.col}`,
+        name: `campo_${this.selectedCell.row}_${this.selectedCell.col}`
+      };
+      this.selectedCell.element = newElement;
+      this.showElementSelector = false;
+      this.editingElement = newElement;
+    }
+  }
+
+  // Remove element from cell
+  removeElementFromCell(cell: GridCell): void {
+    delete cell.element;
+    cell.selected = false;
+    this.editingElement = null;
+  }
+
+  // Select element for editing
+  selectElementForEditing(cell: GridCell): void {
+    if (cell.element) {
+      this.selectedCell = cell;
+      this.editingElement = cell.element;
+      this.showElementSelector = false;
+
+      // Deselect all other cells
+      this.grid.forEach(row => {
+        row.forEach(c => c.selected = false);
+      });
+      cell.selected = true;
+    }
+  }
+
+  // Add option to select/radio elements
+  addOption(): void {
+    if (this.editingElement && (this.editingElement.type === 'select' || this.editingElement.type === 'radio')) {
+      if (!this.editingElement.options) {
+        this.editingElement.options = [];
+      }
+      this.editingElement.options.push('Nueva opción');
+    }
+  }
+
+  // Remove option from select/radio elements
+  removeOption(index: number): void {
+    if (this.editingElement && this.editingElement.options && this.editingElement.options.length > index) {
+      this.editingElement.options.splice(index, 1);
+    }
+  }
+
+  // Update option value
+  updateOption(index: number, event: any): void {
+    if (this.editingElement && this.editingElement.options && this.editingElement.options.length > index) {
+      this.editingElement.options[index] = event.target.value;
+    }
+  }
+
+  // Track by function for options
+  trackByIndex(index: number): number {
+    return index;
+  }
+
+  // Show help tooltip
+  showHelp(field: string): void {
+    this.showTooltip = this.showTooltip === field ? '' : field;
+  }
+
+  // Get help message for a field
+  getHelpMessage(field: string): string {
+    const tooltip = this.helpTooltips.find(t => t.field === field);
+    return tooltip ? tooltip.message : '';
+  }
+
+  // Generate JSON configuration
+  generateJSON(): string {
+    const config = {
+      formTitle: this.formTitle,
+      grid: {
+        rows: this.rows,
+        columns: this.columns,
+        elements: this.grid.flat().filter(cell => cell.element).map(cell => ({
+          position: { row: cell.row, col: cell.col },
+          element: cell.element
+        }))
+      }
+    };
+
+    return JSON.stringify(config, null, 2);
+  }
+
+  // Log the current configuration
+  logConfiguration(): void {
+    console.log('Form Configuration:', this.generateJSON());
   }
 }

+ 105 - 0
Front/src/app/modules/Administrador/pages/admin-form/field-options-dialog.component.ts

@@ -0,0 +1,105 @@
+import { Component, Inject } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormGroup, FormArray, FormControl, ReactiveFormsModule } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+
+@Component({
+  selector: 'app-field-options-dialog',
+  standalone: true,
+  imports: [
+    CommonModule,
+    MatDialogModule,
+    ReactiveFormsModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatButtonModule,
+    MatIconModule
+  ],
+  template: `
+    <h2 mat-dialog-title>Editar Opciones</h2>
+    <mat-dialog-content>
+      <form [formGroup]="optionsForm">
+        <div formArrayName="options">
+          <div *ngFor="let option of options.controls; let i = index" class="option-row">
+            <mat-form-field appearance="outline" class="flex-grow">
+              <input matInput [formControlName]="i" placeholder="Opción {{ i + 1 }}">
+            </mat-form-field>
+            <button mat-icon-button color="warn" (click)="removeOption(i)">
+              <mat-icon>delete</mat-icon>
+            </button>
+          </div>
+        </div>
+      </form>
+      <button mat-button color="primary" (click)="addOption()">
+        <mat-icon>add</mat-icon>
+        Agregar Opción
+      </button>
+    </mat-dialog-content>
+    <mat-dialog-actions align="end">
+      <button mat-button (click)="onCancel()">Cancelar</button>
+      <button mat-raised-button color="primary" (click)="onSave()">Guardar</button>
+    </mat-dialog-actions>
+  `,
+  styles: [`
+    .option-row {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      margin-bottom: 8px;
+    }
+
+    .flex-grow {
+      flex: 1;
+    }
+
+    mat-dialog-content {
+      max-height: 400px;
+      overflow: auto;
+    }
+  `]
+})
+export class FieldOptionsDialogComponent {
+  optionsForm: FormGroup;
+
+  constructor(
+    private fb: FormBuilder,
+    private dialogRef: MatDialogRef<FieldOptionsDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: string[] = []
+  ) {
+    this.optionsForm = this.fb.group({
+      options: this.fb.array([])
+    });
+
+    if (data?.length) {
+      data.forEach(option => this.addOption(option));
+    } else {
+      this.addOption();
+    }
+  }
+
+  get options(): FormArray {
+    return this.optionsForm.get('options') as FormArray;
+  }
+
+  addOption(value: string = ''): void {
+    this.options.push(new FormControl(value));
+  }
+
+  removeOption(index: number): void {
+    this.options.removeAt(index);
+  }
+
+  onCancel(): void {
+    this.dialogRef.close();
+  }
+
+  onSave(): void {
+    if (this.optionsForm.valid) {
+      this.dialogRef.close(this.options.value);
+    }
+  }
+}

+ 3 - 2
Front/src/app/modules/Administrador/pages/configuracion/configuracion.component.css

@@ -11,13 +11,14 @@
     text-align: center;
     width: 100%;
     margin-right: 50px;
+
 }
 
 
 @media only screen and (max-width: 600px) {
 
 
-    
+
     .centrar {
         width: 100%;
         margin-top: 18%;
@@ -27,4 +28,4 @@
         margin-right: 0;
     }
 
-}
+}

+ 2 - 2
Front/src/app/modules/Administrador/pages/configuracion/configuracion.component.ts

@@ -85,7 +85,7 @@ export class ConfiguracionComponent {
 
     this.route = this._router.url;
     // let primerColorTitulos = localStorage.getItem('primerColorTitulos') || ''
-    // this.primerColorTitulos = primerColorTitulos;  
+    // this.primerColorTitulos = primerColorTitulos;
 
     this._enviarInfo.currentTextColor.subscribe(textColor => {
       this.textColor = textColor;
@@ -107,7 +107,7 @@ export class ConfiguracionComponent {
   }
 
   ScreenSize() {
-    this.widowWidth = window.innerWidth;    
+    this.widowWidth = window.innerWidth;
   }
 
 }