Quellcode durchsuchen

subo cambios para administrativo, pdf, y quedan cambios pendientes

FREDY vor 3 Monaten
Ursprung
Commit
882aa0e355

+ 44 - 18
Back/backendP-Educativa/app/Http/Controllers/Api/RegistroAcademico.php

@@ -363,6 +363,7 @@ public function registroAdministrativo(Request $request)
     $status = 0;
     $rutaS3 = '';
     $uuidArchivo = Str::uuid()->toString();
+
     try {
         $request->validate([
             'idAlumno' => 'required',
@@ -371,7 +372,6 @@ public function registroAdministrativo(Request $request)
 
         $requiereFactura = $request->factura ?? '';
 
-        // Validar si se cargó archivo y se seleccionó "Sí"
         if ($requiereFactura === 'Sí' && $request->hasFile('constanciaFiscal')) {
             $archivo = $request->file('constanciaFiscal');
 
@@ -379,19 +379,35 @@ public function registroAdministrativo(Request $request)
                 throw new \Exception('Archivo inválido');
             }
 
-            $nombreArchivo = time() . '-' . $request->idAlumno . '-' . preg_replace('/[^a-zA-Z0-9._-]/', '', $archivo->getClientOriginalName());
+            $originalName = $archivo->getClientOriginalName();
+
+            if (strlen($originalName) > 250) {
+                throw new \Exception('El nombre del archivo excede los 250 caracteres permitidos.');
+            }
+
+            // Regex actualizado: letras, números, espacios, paréntesis, puntos, guiones, acentos
+            if (!preg_match('/^[a-zA-Z0-9\s._\-\(\)ñÑáéíóúÁÉÍÓÚ]+$/u', $originalName)) {
+                throw new \Exception('El nombre del archivo contiene caracteres no permitidos.');
+            }
+
+            if ($archivo->getClientMimeType() !== 'application/pdf') {
+                throw new \Exception('Solo se permiten archivos PDF.');
+            }
+
+            if ($archivo->getSize() > 5 * 1024 * 1024) {
+                throw new \Exception('El archivo no puede ser mayor a 5MB.');
+            }
+
+            $nombreArchivo = $uuidArchivo . '-' . $request->idAlumno . '-' . $originalName;
 
-            // Subir a S3
             $rutaS3 = 'constancias_fiscales/' . $nombreArchivo;
             Storage::disk('s3')->put($rutaS3, file_get_contents($archivo), 'public');
         }
 
-        // Si seleccionó "No" o no se cargó archivo válido, poner NA
         if ($requiereFactura === 'No' || $rutaS3 === '') {
             $rutaS3 = 'NA';
         }
 
-        // Guardar en base de datos
         $registro = [
             'idEscuela' => $request->idEscuela ?? 'COLEGIOABC',
             'idAlumno' => $request->idAlumno,
@@ -421,9 +437,7 @@ public function registroAdministrativo(Request $request)
 
         $message = 'Registro exitoso';
         $status = 200;
-
     } catch (\Exception $e) {
-        // Borrar archivo si algo falla
         if ($rutaS3 && $rutaS3 !== 'NA' && Storage::disk('s3')->exists($rutaS3)) {
             Storage::disk('s3')->delete($rutaS3);
         }
@@ -441,8 +455,6 @@ public function registroAdministrativo(Request $request)
 
 
 
-
-
 public function eliminarArchivo(Request $request)
 {
     try {
@@ -486,11 +498,12 @@ public function editarRegistroAdmin(Request $request, $id)
     $message = '';
     $status = 0;
     $rutaS3 = '';
+    $uuidArchivo = Str::uuid()->toString();
+
 
     try {
         $idAlumno = base64_decode($id);
 
-        // Buscar el registro existente
         $registroExistente = DB::table('registroadministrativo')
             ->where('idEscuela', 'COLEGIOABC')
             ->where('idAlumno', $idAlumno)
@@ -502,7 +515,6 @@ public function editarRegistroAdmin(Request $request, $id)
 
         $datosActualizar = ['updated_at' => now()];
 
-        // 🔄 Subir nuevo archivo si viene en el request
         if ($request->hasFile('constanciaFiscal')) {
             $archivo = $request->file('constanciaFiscal');
 
@@ -510,16 +522,33 @@ public function editarRegistroAdmin(Request $request, $id)
                 throw new \Exception('Archivo inválido');
             }
 
-            $nombreArchivo = time() . '-' . $idAlumno . '-' . preg_replace('/[^a-zA-Z0-9._-]/', '', $archivo->getClientOriginalName());
+            $originalName = $archivo->getClientOriginalName();
+
+            if (strlen($originalName) > 250) {
+                throw new \Exception('El nombre del archivo excede los 250 caracteres permitidos.');
+            }
+
+            if (!preg_match('/^[a-zA-Z0-9\s._\-\(\)ñÑáéíóúÁÉÍÓÚ]+$/u', $originalName)) {
+                throw new \Exception('El nombre del archivo contiene caracteres no permitidos.');
+            }
+
+            if ($archivo->getClientMimeType() !== 'application/pdf') {
+                throw new \Exception('Solo se permiten archivos PDF.');
+            }
+
+            if ($archivo->getSize() > 5 * 1024 * 1024) {
+                throw new \Exception('El archivo no puede ser mayor a 5MB.');
+            }
+
+            $nombreArchivo = $uuidArchivo . '-' . $idAlumno . '-' . $originalName;
+
             $rutaS3 = 'constancias_fiscales/' . $nombreArchivo;
 
-            // Subir a S3
             Storage::disk('s3')->put($rutaS3, file_get_contents($archivo), 'public');
 
-            // Guardar nueva ruta
             $datosActualizar['RegA_ConstanciaFiscal'] = $rutaS3;
         }
-        // Mapeo de campos
+
         $camposMapeo = [
             'mesInscripcion' => 'RegA_MesInscripcion',
             'gradoCursar' => 'RegA_GradoCursar',
@@ -559,9 +588,7 @@ public function editarRegistroAdmin(Request $request, $id)
 
         $message = 'Actualización exitosa';
         $status = 200;
-
     } catch (\Exception $e) {
-        // Si algo falla, borrar archivo subido para no dejar basura
         if ($rutaS3 && Storage::disk('s3')->exists($rutaS3)) {
             Storage::disk('s3')->delete($rutaS3);
         }
@@ -574,7 +601,6 @@ public function editarRegistroAdmin(Request $request, $id)
 }
 
 
-
     public function getOneRegistroAdmi($id){
         $id = base64_decode($id);
         $registro = DB::table('registroadministrativo')

+ 119 - 54
Back/backendP-Educativa/app/Http/Controllers/PersonalizarController.php

@@ -5,6 +5,10 @@ namespace App\Http\Controllers;
 use App\Models\Personalizar;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Storage;
+
+
 class PersonalizarController extends Controller
 {
     public function show()
@@ -17,6 +21,7 @@ class PersonalizarController extends Controller
                 'personalizacion' => [
                     'PERMENSAJE' => $personalizar->PERMENSAJE,
                     'PERNOMBRE' => $personalizar->PERNOMBRE,
+                    'PERESLOGAN'=> $personalizar ->PERESLOGAN,
                     'PERLOGO' => $imagenUrl,
                     'PERPR_COL_BAR' => $personalizar->PERPR_COL_BAR,
                     'PERSDO_COL_BAR' => $personalizar->PERSDO_COL_BAR,
@@ -37,65 +42,125 @@ class PersonalizarController extends Controller
 
 
     public function update(Request $request)
-    {
-        $personalizar = Personalizar::find(1);
-        if ($personalizar) {            
-            // Actualizar los campos de personalización
-            $personalizar->PERMENSAJE = $request->mensajeBienvenida;
-            $personalizar->PERNOMBRE = $request->nombreColegio;
-            $personalizar->PERPR_COL_BAR = $request->primerColorBarra;
-            $personalizar->PERSDO_COL_BAR = $request->segundoColorBarra;
-            $personalizar->PERTXT_BAR = $request->texturaBarra;
-            $personalizar->PERCOL_LTR_BAR = $request->colorLetraNav;
-            $personalizar->PERPR_COL_TIT = $request->primerColorTitulos;
-            $personalizar->PERVINCULOS = $request->colorVinculos;
-            // Manejar la imagen si se proporciona
+{
+    $personalizar = Personalizar::find(1);
+    if ($personalizar) {
+        // Actualizar campos de personalización
+        $personalizar->PERMENSAJE = $request->mensajeBienvenida;
+        $personalizar->PERNOMBRE = $request->nombreColegio;
+        $personalizar->PERESLOGAN = $request->eslogan;
+        $personalizar->PERPR_COL_BAR = $request->primerColorBarra;
+        $personalizar->PERSDO_COL_BAR = $request->segundoColorBarra;
+        $personalizar->PERTXT_BAR = $request->texturaBarra;
+        $personalizar->PERCOL_LTR_BAR = $request->colorLetraNav;
+        $personalizar->PERPR_COL_TIT = $request->primerColorTitulos;
+        $personalizar->PERVINCULOS = $request->colorVinculos;
+
+        // Función anónima para procesar imagen base64 y subir a S3
+        $procesarImagen = function(?string $imageData, string $nombreArchivo) {
+            if (empty($imageData)) {
+                return null;
+            }
+
+            $fileInfo = explode(';', $imageData)[0];
+            $fileExtension = explode('/', $fileInfo)[1];
+            $allowedExtensions = ['jpeg', 'jpg', 'png'];
+            if (!in_array($fileExtension, $allowedExtensions)) {
+                throw new \Exception('Formato de imagen no soportado. Por favor, utiliza JPEG o PNG.');
+            }
+
+            $imageBase64 = substr($imageData, strpos($imageData, ',') + 1);
+            $imageBase64 = str_replace(' ', '+', $imageBase64);
+
+            // Validar tamaño en bytes (1MB)
+            $decodedSize = strlen(base64_decode($imageBase64));
+            if ($decodedSize > 1 * 1024 * 1024) {
+                throw new \Exception('El tamaño de la imagen excede el límite permitido (1 MB).');
+            }
+
+            $path = 'personalizar/' . $nombreArchivo . '.' . $fileExtension;
+
+            // Subir a S3
+            Storage::disk('s3')->put($path, base64_decode($imageBase64), 'public');
+
+            return $path;
+        };
+
+        try {
+            // Procesar imagen principal (PERLOGO)
             if ($request->has('imagen')) {
-                $imageData = $request->imagen;
-                if (!empty($imageData)) {
-                    // Procesar la imagen solo si se proporciona un dato válido
-                    $fileInfo = explode(';', $imageData)[0];
-                    $fileExtension = explode('/', $fileInfo)[1];
-                    // Validar si es un formato de imagen permitido
-                    $allowedExtensions = ['jpeg', 'jpg', 'png'];
-                    if (!in_array($fileExtension, $allowedExtensions)) {
-                        return response()->json([
-                            'message' => 'Formato de imagen no soportado. Por favor, utiliza JPEG o PNG.',
-                            'status' => 400
-                        ], 400);
-                    }
-                    // Decodificar la imagen base64
-                    $imageData = substr($imageData, strpos($imageData, ',') + 1);
-                    $imageData = str_replace(' ', '+', $imageData);
-                    $maxSize = 1 * 1024 * 1024; // Define el tamaño máximo (1 MB)
-                    if (strlen($imageData) > $maxSize) {
-                        return response()->json([
-                            'message' => 'El tamaño de la imagen excede el límite permitido (1 MB).',
-                            'status' => 400,                           
-                        ], 400);
-                    }
-                    // Nombre fijo para la imagen
-                    $imageName = 'personalizar.' . $fileExtension;
-                    // Guardar la imagen sobrescribiendo la anterior si existe
-                    \Storage::disk('public')->put($imageName, base64_decode($imageData));
-                    // Asignar el nombre de la imagen al modelo
-                    $personalizar->PERLOGO = $imageName;
-                } else {
-                   //Si no hay imagen asignar null
-                    $personalizar->PERLOGO = null;
-                }
+                $rutaImagen = $procesarImagen($request->imagen, 'personalizar');
+                $personalizar->PERLOGO = $rutaImagen;
+            } else {
+                $personalizar->PERLOGO = null;
             }
-            $personalizar->save();
-            return response()->json([
-                'message' => 'Personalización del sistema actualizado',
-                'status' => 200
-            ], 200);
-        } else {
+
+            // Procesar imagen1 (PERLOGO1)
+            if ($request->has('imagen1')) {
+                $rutaImagen1 = $procesarImagen($request->imagen1, 'personalizar1');
+                $personalizar->PERLOGO1 = $rutaImagen1;
+            } else {
+                $personalizar->PERLOGO1 = null;
+            }
+
+            // Procesar imagen2 (PERLOGO2)
+            if ($request->has('imagen2')) {
+                $rutaImagen2 = $procesarImagen($request->imagen2, 'personalizar2');
+                $personalizar->PERLOGO2 = $rutaImagen2;
+            } else {
+                $personalizar->PERLOGO2 = null;
+            }
+        } catch (\Exception $ex) {
             return response()->json([
-                'message' => 'No se encontró la personalización',
-                'status' => 400
+                'message' => $ex->getMessage(),
+                'status' => 400,
             ], 400);
         }
+
+        $personalizar->save();
+
+        return response()->json([
+            'message' => 'Personalización del sistema actualizado',
+            'status' => 200
+        ], 200);
+    } else {
+        return response()->json([
+            'message' => 'No se encontró la personalización',
+            'status' => 400
+        ], 400);
     }
+}
+
+    public function getNombreColegio()
+{
+    try {
+        $personalizacion = DB::table('personalizar')->first();
+
+        return response()->json([
+            'nombreColegio' => $personalizacion->PERNOMBRE ?? 'Sin nombre'
+        ], 200);
+    } catch (\Exception $e) {
+        return response()->json([
+            'error' => 'No se pudo obtener el nombre del colegio',
+            'detalle' => $e->getMessage()
+        ], 500);
+    }
+}
+ public function getEsloganColegio()
+{
+    try {
+        $personalizacion = DB::table('personalizar')->first();
+
+        return response()->json([
+            'eslogan' => $personalizacion->PERESLOGAN ?? 'Sin nombre'
+        ], 200);
+    } catch (\Exception $e) {
+        return response()->json([
+            'error' => 'No se pudo obtener el eslogan del colegio',
+            'detalle' => $e->getMessage()
+        ], 500);
+    }
+}
+
 }
 

+ 2 - 0
Back/backendP-Educativa/routes/api.php

@@ -250,6 +250,8 @@ Route::get('/alumnos/bitacora/{id}', [AlumnosBitacoraController::class, 'index']
     Route::put('habilitarEstadoCalificacion',[RegistroCalicaciones::class,'habilitarEstado']);
 
     Route::get('getAlumnosCalificacion',[RegistroCalicaciones::class,'getCalificacionesMateria']);
+    Route::get('/personalizacion/nombre-colegio', [PersonalizarController::class, 'getNombreColegio']);
+    Route::get('/personalizacion/eslogan-colegio', [PersonalizarController::class, 'getEsloganColegio']);
 
 
     //ADMINISTRADOR DE FORMULARIOS RUTAS

+ 2 - 1
Front/src/app/modules/Administrador/interfaces/Personalizar.interface.ts

@@ -1,6 +1,7 @@
 export interface Personalizar {
     PERMENSAJE: string,
     PERNOMBRE: string,
+    PERESLOGAN:string,
     PERLOGO: string,
     PERPR_COL_BAR: string,
     PERSDO_COL_BAR: string,
@@ -8,4 +9,4 @@ export interface Personalizar {
     PERPR_COL_TIT: string,
     PERCOL_LTR_BAR: string,
     PERVINCULOS: string
-}
+}

+ 1 - 0
Front/src/app/modules/Administrador/pages/layout-page/layout-page.component.ts

@@ -184,6 +184,7 @@ export class LayoutPageComponent {
 
       this._enviarInfo.changeMessage(this.message);
       this._enviarInfo.changeColor(this.firstColorTitles);
+      this._enviarInfo.changeNombreColegio(this.name)
       this._enviarInfo.changeTextColor(this.fontColorBar);
       this._enviarInfo.changeColorLinks(this.links);
     })

+ 14 - 9
Front/src/app/modules/Administrador/pages/personalizar/personalizar.component.css

@@ -7,7 +7,7 @@
 
 .form-personalizar {
     background-color: white;
-    border-radius: 15px;
+
     box-shadow: rgba(13, 102, 204, 0.342) 0 0 10px 7px;
     padding-top: 40px;
     padding-bottom: 40px;
@@ -16,6 +16,7 @@
 
 }
 
+
 .form-personalizar-loader {
     background-color: white;
     border-radius: 15px;
@@ -26,6 +27,10 @@
     padding-left: 20px;
 }
 
+.divRes{
+
+  text-align: center;
+}
 .centrar-buttons {
     display: flex;
     flex-direction: row;
@@ -235,9 +240,9 @@ input[type="file"] {
 }
 
 .button-cancel:hover {
-    
+
     background-color: rgb(204, 2, 2);
-    
+
 }
 
 .button-save {
@@ -251,9 +256,9 @@ input[type="file"] {
 }
 
 .button-save:hover {
-    
+
     background-color: #5581ac;
-    
+
 }
 
 .button-reset {
@@ -267,9 +272,9 @@ input[type="file"] {
 }
 
 .button-reset:hover {
-    
+
     background-color: #c75a2b;
-    
+
 }
 
 
@@ -425,5 +430,5 @@ input[type="radio"] {
         margin-left: 40px;
     }
 
-    
-}
+
+}

+ 127 - 6
Front/src/app/modules/Administrador/pages/personalizar/personalizar.component.html

@@ -3,14 +3,13 @@
     <p class="content" [style.color]="textColor">Personalizar</p>
   </div>
 </div>
-
 <div class="centrar-form fadeIn">
-
-  <mat-spinner *ngIf="isLoading;"></mat-spinner>
+ <mat-spinner *ngIf="isLoading;"></mat-spinner>
+ <div style="background-color: white; align-items: center;" class="divRes" >
+<mat-tab-group  mat-stretch-tabs="false" class="mat-elevation-z4" animationDuration="0ms">
+<mat-tab label="Interfaz Gráfica">
 
   <form class="form-personalizar" *ngIf="!isLoading;" (ngSubmit)="personalizar()" [formGroup]="form">
-
-
     <div class="fila-form">
       <div class="text">Mensaje de Bienvenida: </div>
       <div class="">
@@ -24,6 +23,20 @@
       <div #tooltip1="matTooltip" matTooltip="Mensaje de bienvenida de la pantalla principal"
         matTooltipPosition="right"></div>
     </div>
+     <div class="fila-form">
+      <div class="text">Eslogan: </div>
+      <div class="">
+        <mat-form-field class="input">
+          <input matInput name="Eslogan" formControlName="eslogan"
+            placeholder="Inserte eslogan de la institución">
+        </mat-form-field>
+      </div>
+      <mat-icon class="cursor-pointer mb-10" matSuffix (click)="tooltip1.toggle()"
+        style="color: #f19696;">help</mat-icon>
+      <div #tooltip1="matTooltip" matTooltip="Mensaje de bienvenida de la pantalla principal"
+        matTooltipPosition="right"></div>
+    </div>
+
 
     <div class="fila-form mt-20">
       <div class="text">Nombre del Colegio: </div>
@@ -199,4 +212,112 @@
         <mat-icon class="ml-5">restart_alt</mat-icon></button>
     </div>
   </form>
-</div>
+</mat-tab>
+<mat-tab label="Documentación">
+  <form class="form-personalizar" *ngIf="!isLoading;" (ngSubmit)="personalizar()" [formGroup]="form">
+    <p style="font-size: 30px; color: #888;">Personalizar documentación    <mat-icon style="color: #f19696; margin-left: 5px;">help</mat-icon> </p>
+<div class="fila-form">
+      <div class="text">Mensaje de Bienvenida: </div>
+      <div class="">
+        <mat-form-field class="input">
+          <input matInput name="mensajeBienvenida" formControlName="mensajeBienvenida"
+            placeholder="Bienvenidos al Ciclo Escolar 2024 - 2025" readonly style="background-color: #f5f5f5; color: #888; border-radius: 4px; cursor: not-allowed;"
+>
+        </mat-form-field>
+      </div>
+      <mat-icon class="cursor-pointer mb-10" matSuffix (click)="tooltip1.toggle()"
+        style="color: #f19696;">help</mat-icon>
+      <div #tooltip1="matTooltip" matTooltip="El mensaje de bienvenida se modifica desde la interfaz grafica"
+        matTooltipPosition="right"></div>
+    </div>
+      <div class="fila-form">
+      <div class="text">Eslogan: </div>
+      <div class="">
+        <mat-form-field class="input">
+          <input matInput name="Eslogan" formControlName="eslogan"
+            placeholder="Ingrese el lema de su institución" readonly style="background-color: #f5f5f5; color: #888; border-radius: 4px; cursor: not-allowed;"
+>
+        </mat-form-field>
+      </div>
+      <mat-icon class="cursor-pointer mb-10" matSuffix (click)="tooltip3.toggle()"
+        style="color: #f19696;">help</mat-icon>
+      <div #tooltip3="matTooltip" matTooltip="El eslogan se modifica desde la interfaz grafica"
+        matTooltipPosition="right"></div>
+    </div>
+    <div class="fila-form mt-20">
+      <div class="text">Nombre del Colegio: </div>
+      <div class="flex-row">
+        <mat-form-field class="input">
+          <input name="nombreColegio" formControlName="nombreColegio" matInput placeholder="Colegio ABC" readonly style="background-color: #f5f5f5; color: #888; border-radius: 4px; cursor: not-allowed;"
+>
+          <!-- <mat-hint>Hint</mat-hint> -->
+        </mat-form-field>
+      </div>
+      <mat-icon class="cursor-pointer mb-10" matSuffix (click)="tooltip2.toggle()"
+        style="color: #f19696;">help</mat-icon>
+      <div #tooltip2="matTooltip" matTooltip="El nombre del colegio se cambia desde la interfaz grafica" matTooltipPosition="right"></div>
+    </div>
+
+    <div class="fila-form mt-20">
+      <div class="text">Logo para PDF (izquierdo): </div>
+      <div class="fila-form mb-20">
+        <div class=" flex-col">
+          <div class="justify-end">
+            <label class="custom-file-upload mt-10">
+              <input type="file" name="imagen" accept="image/*" formControlName="imagen"
+                (change)="onFileChange($event)">
+              Subir Archivo
+              <mat-icon>imagesmode</mat-icon>
+            </label>
+          </div>
+          <div class="flex-col mt-5 ">
+            <span class="mr-10" style="color: red;">{{errorPx}}</span>
+            <span class="mr-10" style="color: red;">{{errorSize}}</span>
+            <span class="mr-10" style="color: red;">{{errorType}}</span>
+          </div>
+        </div>
+
+        <div>
+          <img class="img-preview" [src]="url" *ngIf="url" height="200" style="margin-left: 10px;">
+        </div>
+        <mat-icon class="cursor-pointer" matSuffix (click)="tooltip3.toggle()" style="color: #f19696;">help</mat-icon>
+        <div #tooltip3="matTooltip" [matTooltip]="toolTip" matTooltipClass="my-tooltip" matTooltipPosition="right">
+        </div>
+      </div>
+           </div>
+             <div class="fila-form mt-20">
+      <div class="text">Logo para PDF (derecho): </div>
+      <div class="fila-form mb-20">
+        <div class=" flex-col">
+          <div class="justify-end">
+            <label class="custom-file-upload mt-10">
+              <input type="file" name="imagen" accept="image/*" formControlName="imagen"
+                (change)="onFileChange($event)">
+              Subir Archivo
+              <mat-icon>imagesmode</mat-icon>
+            </label>
+          </div>
+          <div class="flex-col mt-5 ">
+            <span class="mr-10" style="color: red;">{{errorPx}}</span>
+            <span class="mr-10" style="color: red;">{{errorSize}}</span>
+            <span class="mr-10" style="color: red;">{{errorType}}</span>
+          </div>
+        </div>
+
+        <div>
+          <img class="img-preview" [src]="url" *ngIf="url" height="200" style="margin-left: 10px;">
+        </div>
+        <mat-icon class="cursor-pointer" matSuffix (click)="tooltip3.toggle()" style="color: #f19696;">help</mat-icon>
+        <div #tooltip3="matTooltip" [matTooltip]="toolTip" matTooltipClass="my-tooltip" matTooltipPosition="right">
+        </div>
+      </div>
+           </div>
+
+               <div class="centrar-buttons mt-40 ml-20">
+      <button type="button" (click)="personalizar()" class="button-save items-center ">Guardar cambios
+        <mat-icon class="ml-5">save</mat-icon></button>
+
+    </div>
+    </form>
+</mat-tab>
+</mat-tab-group>

+ 14 - 3
Front/src/app/modules/Administrador/pages/personalizar/personalizar.component.ts

@@ -19,6 +19,7 @@ export class PersonalizarComponent implements OnInit {
   public color1: string = '';
   public color2: string = '';
   public color3: string = '';
+
   public color4: string = '';
   public color5: string = '';
   public selectedOptionC: string = ''
@@ -55,6 +56,7 @@ export class PersonalizarComponent implements OnInit {
 
   public form = new FormGroup({
     mensajeBienvenida: new FormControl(''),
+    eslogan: new FormControl(''),
     nombreColegio: new FormControl(''),
     imagen: new FormControl(''),
     primerColorBarra: new FormControl(''),
@@ -68,6 +70,7 @@ export class PersonalizarComponent implements OnInit {
   public formReset = new FormGroup({
     mensajeBienvenida: new FormControl('Bienvenidos al Ciclo Escolar 2024-2025'),
     nombreColegio: new FormControl('Colegio ABC'),
+   eslogan: new FormControl('Mi eslogan'),
     imagen: new FormControl(''),
     primerColorBarra: new FormControl('#3C6AB8'),
     segundoColorBarra: new FormControl(''),
@@ -85,6 +88,8 @@ export class PersonalizarComponent implements OnInit {
     '- Peso máximo: 1MB.' + '\n' +
     '- Dimensiones: 800px x 600px.';
 
+  public nombreColegio: string = '';
+
   get formData(): Personalizar {
     this.form.value.imagen = this.url;
     const data = this.form.value as Personalizar;
@@ -104,19 +109,24 @@ export class PersonalizarComponent implements OnInit {
     this._enviarInfo.currentColor.subscribe(color => {
       this.color = color;
     });
-    
+
     this._enviarInfo.currentTextColor.subscribe(textColor => {
       this.textColor = textColor;
     });
-    
+
+
+
+
     this._personalizarService.getOptions().subscribe((response: any) => {
       this.isLoading = false;
       this.data = response.personalizacion;
 
+
       // mostrar data en el formulario
       this.form.controls['mensajeBienvenida'].setValue(this.data.PERMENSAJE);
       this.form.controls['nombreColegio'].setValue(this.data.PERNOMBRE);
-      // this.form.controls['imagen'].setValue(this.data.PERLOGO);
+      this.form.controls['eslogan'].setValue(this.data.PERESLOGAN);
+      this.form.controls['imagen'].setValue(this.data.PERLOGO);
       this.form.controls['primerColorBarra'].setValue(this.data.PERPR_COL_BAR);
       this.form.controls['segundoColorBarra'].setValue(this.data.PERSDO_COL_BAR);
       this.form.controls['texturaBarra'].setValue(this.data.PERTXT_BAR);
@@ -124,6 +134,7 @@ export class PersonalizarComponent implements OnInit {
       this.form.controls['primerColorTitulos'].setValue(this.data.PERPR_COL_TIT);
       this.form.controls['colorVinculos'].setValue(this.data.PERVINCULOS);
 
+
     });
   }
 

+ 39 - 4
Front/src/app/modules/Administrador/services/enviar-info.service.ts

@@ -1,39 +1,74 @@
 import { Injectable } from '@angular/core';
-import { BehaviorSubject, Subject } from 'rxjs';
+import { BehaviorSubject, Subject, Observable } from 'rxjs';
+import { environments } from '../../../../environments/environments';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
 
 @Injectable({
   providedIn: 'root'
 })
 export class EnviarInfoService {
+
+  private URL: string = environments.baseUrl;
+
+  constructor(private _http: HttpClient) { }
+
+  private getHeaders(): HttpHeaders {
+    const token = localStorage.getItem('token') || '';
+    return new HttpHeaders({
+      'Content-Type': 'application/json',
+      'Authorization': `Bearer ${token}`
+    });
+  }
+
   private mensajeBienvenida = new BehaviorSubject<string>('');
+  private nombreColegio = new BehaviorSubject<string>('');
   private color = new BehaviorSubject<string>('');
   private textColor = new BehaviorSubject<string>('');
   private link = new BehaviorSubject<string>('');
+
   currentMensaje = this.mensajeBienvenida.asObservable();
   currentColor = this.color.asObservable();
   currentTextColor = this.textColor.asObservable();
   currentLink = this.link.asObservable();
-  private tablaSource = new Subject<void>();
-
-  constructor() { }
+  currentColegio = this.nombreColegio.asObservable();
 
+  private tablaSource = new Subject<void>();
   tabla$ = this.tablaSource.asObservable();
 
   notifyCambioTabla() {
     this.tablaSource.next();
   }
 
+  changeNombreColegio(name: string) {
+    this.nombreColegio.next(name);
+  }
 
   changeMessage(mensaje: string) {
     this.mensajeBienvenida.next(mensaje);
   }
+
   changeColor(color: string) {
     this.color.next(color);
   }
+
   changeTextColor(textColor: string) {
     this.textColor.next(textColor);
   }
+
   changeColorLinks(links: string) {
     this.link.next(links);
   }
+
+  getNombreColegio(): Observable<any> {
+    return this._http.get(`${this.URL}/personalizacion/nombre-colegio`, {
+      headers: this.getHeaders()
+    });
+  }
+
+   getEsloganColegio(): Observable<any> {
+    return this._http.get(`${this.URL}/personalizacion/eslogan-colegio`, {
+      headers: this.getHeaders()
+    });
+  }
+
 }

+ 9 - 0
Front/src/app/modules/Padres/pages/Registro/registro.component.css

@@ -68,8 +68,14 @@
 
 .font-text {
   font-size: 14px;
+
+
 }
+.font-text2 {
+  font-size: 14px;
+ color: #555555;
 
+}
 .f-z {
   font-size: 14px;
 }
@@ -188,6 +194,9 @@ justify-content: center;
 
   }
 
+
+
+
   .font-text {
     font-size: 13px;
   }

+ 18 - 13
Front/src/app/modules/Padres/pages/Registro/registro.component.html

@@ -11,13 +11,14 @@
     <mat-tab-group mat-stretch-tabs="false" *ngIf="!isLoading" class="mat-elevation-z4"
     (selectedIndexChange)="selected.setValue($event)" [selectedIndex]="selected.value">
             <mat-tab label="Datos Generales">
-                <mat-stepper [linear]="isLinear" #stepper [orientation]="(stepperOrientation | async)!">
-                    <mat-step [stepControl]="form" label="Datos Generales Alumnos">
+                <mat-stepper [linear]="isLinear" #stepper [orientation]="(stepperOrientation | async)!" >
+                    <mat-step [stepControl]="form" label="Datos Generales Alumnos"  style="background-color: aqua;">
                         <div
                             style="display: flex; justify-content: center; align-items: center; flex-direction: column;">
                             <div class="textAdvertencia1">
-                                <p> <mat-icon style="color: #f19696;">info</mat-icon> Debe completar todos los campos e
-                                    ingresar "N/A" en aquellos que no apliquen</p>
+                                <p>  Debe completar todos los campos e
+                                    ingresar "N/A" en aquellos que no apliquen  </p>
+                                    <mat-icon style="color: #f19696;">info</mat-icon>
                             </div>
                             <div class="div-form">
                                 <form [formGroup]="form">
@@ -385,7 +386,7 @@
                                             <input type="text" matInput class="input-Form"
                                                 formControlName="madreDomicilio" readonly>
                                             <mat-hint *ngIf="!isValidField1('madreDomicilio','required')"
-                                                style="color: blue; cursor: pointer;" (click)="Asistente2()">Haga click
+                                                style="color: rgb(0, 0, 255); cursor: pointer;" (click)="Asistente2()">Haga click
                                                 aqui para abrir el Asistente de Captura</mat-hint>
                                             <mat-hint *ngIf="isValidField('madreDomicilio','required')" class="error">El
                                                 campo es obligatorio</mat-hint>
@@ -479,7 +480,7 @@
                             <mat-radio-button value="Si">Si</mat-radio-button>
                             <mat-radio-button value="No">No</mat-radio-button>
                         </mat-radio-group>
-   <mat-hint class="info-message" style="color: orange;  margin-top: 8px; font-size: 12px;">
+   <mat-hint class="info-message" style="color: rgb(255, 0, 0);  margin-top: 10px; font-size: 12px;">
       Si el el alumno es autorizado favor de entregar un oficio del tutor con una firma autorizada a la dirección escolar.
     </mat-hint>
 
@@ -488,9 +489,10 @@
                     <!-- Array dinámico de personas autorizadas -->
                     <div class="personas-autorizadas-container" style="width: 100%; margin-top: 20px;">
                         <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
-                            <h3 class="font-text">Personas Autorizadas para Recoger al Alumno</h3>
+                            <p class="font-text2">Personas Autorizadas para Recoger al Alumno</p>
                             <button type="button" class="btn1 borderNone"
                                     [style.background-color]="color"
+                                    style="width:10%;"
                                     (click)="agregarPersonaAutorizada()">
                                 <mat-icon>person_add</mat-icon> Agregar Persona
                             </button>
@@ -526,13 +528,16 @@
                                         El parentesco es obligatorio
                                     </mat-hint>
                                 </mat-form-field>
+<button
+  *ngIf="personasAutorizadas.length > 1"
+  type="button"
+  class="btn1 borderNone"
+  style="background-color:#FF5C5C; padding: 8px 12px; margin-left: 10px; width:7%; color: white;"
+  (click)="eliminarPersonaAutorizada(i)">
+  <mat-icon>delete</mat-icon>
+  Eliminar
+</button>
 
-                                <button type="button" class="btn1 borderNone"
-                                        style="background-color: red; padding: 8px 12px; margin-left: 10px;"
-                                        (click)="eliminarPersonaAutorizada(i)"
-                                        [disabled]="personasAutorizadas.length <= 1">
-                                    <mat-icon>delete</mat-icon>
-                                </button>
                             </div>
                         </div>
 

+ 54 - 21
Front/src/app/modules/Padres/pages/Registro/registro.component.ts

@@ -456,7 +456,6 @@ get personasAutorizadas(): FormArray {
   agregarPersonaAutorizada() {
   this.personasAutorizadas.push(this.crearPersonaAutorizada());  }
 
-// Función para crear un FormGroup de persona autorizada
 crearPersonaAutorizada(nombre: string = '', telefono: string = '', parentesco: string = ''): FormGroup {
     return this.fb.group({
         nombre: [nombre, [Validators.required, Validators.pattern('^[a-zA-Z0-9áéíóúÁÉÍÓÚñÑ,.][a-zA-Z0-9\\sáéíóúÁÉÍÓÚñÑ,.]*$')]],
@@ -1063,8 +1062,21 @@ formulario() {
     this.form2.get('RFCFactura')?.disable();
     this.form2.get('metedoPago')?.disable();
 }
-  onFileSelected(event: any) {
-    if (this.rutaArchivo && this.rutaArchivo.trim() !== '') {
+nombreInvalido(nombre: string): boolean {
+  const caracteresInvalidos = /[<>:"/\\|?*\u0000-\u001F]/; // incluye control chars
+  const nombresReservados = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\..+)?$/i;
+
+  return (
+    caracteresInvalidos.test(nombre) ||
+    nombre.length > 255 ||
+    nombre.endsWith('.') ||
+    nombre.endsWith(' ') ||
+    nombresReservados.test(nombre)
+  );
+}
+
+onFileSelected(event: any) {
+  if (this.rutaArchivo && this.rutaArchivo.trim() !== '') {
     Swal.fire({
       icon: 'warning',
       title: 'Archivo ya existente',
@@ -1072,27 +1084,48 @@ formulario() {
     });
     return;
   }
-        const file = event.target.files[0];
-        if (file) {
-            if (file.type !== 'application/pdf') {
-                Swal.fire('Error', 'Solo se permiten archivos PDF', 'error');
-                event.target.value = '';
-                this.selectedFile = null;
-                return;
-            }
 
-            if (file.size > 5 * 1024 * 1024) {
-                Swal.fire('Error', 'El archivo no puede ser mayor a 5MB', 'error');
-                event.target.value = '';
-                this.selectedFile = null;
-                return;
-            }
+  const file = event.target.files[0];
 
-            this.selectedFile = file;
-            this.form2.get('constanciaFiscal')?.setValue(file.name);
-            this.cdr.detectChanges();
-        }
+  if (file) {
+    if (file.type !== 'application/pdf') {
+      Swal.fire('Error', 'Solo se permiten archivos PDF', 'error');
+      return;
+    }
+
+    // Validar tamaño
+    if (file.size > 5 * 1024 * 1024) {
+      Swal.fire('Error', 'El archivo no puede ser mayor a 5MB', 'error');
+      return;
     }
+
+    // Validar nombre de archivo (longitud y caracteres reservados)
+    if (this.nombreInvalido(file.name)) {
+      Swal.fire(
+        'Nombre inválido',
+        'El nombre del archivo contiene caracteres no permitidos o está reservado por el sistema.',
+        'error'
+      );
+      return;
+    }
+
+    // Validar caracteres personalizados (tú decías permitir letras, números, puntos, guiones, etc.)
+    const nombreValido = /^[a-zA-Z0-9_.\-ñÑáéíóúÁÉÍÓÚ\s()]+$/.test(file.name);
+    if (!nombreValido) {
+      Swal.fire(
+        'Nombre inválido',
+        'El nombre del archivo contiene caracteres no permitidos. Usa solo letras, números, guiones, guiones bajos y puntos.',
+        'error'
+      );
+      return;
+    }
+
+    this.selectedFile = file;
+    this.form2.get('constanciaFiscal')?.setValue(file.name);
+    this.cdr.detectChanges();
+  }
+}
+
 cargarFormulario() {
   this.isLoading = true;
 

+ 1 - 1
Front/src/app/modules/Padres/pages/Registro/registroAd.component.html

@@ -221,7 +221,7 @@
                                         *ngIf="mostrarEliminarArchivo()"
                                         type="button"
                                         class="btn1 borderNone"
-                                        style="background-color: #f44336; color: white;  width: 10%; padding: 8px 16px;margin-left:75px;"
+                                        style="background-color: #FF5C5C; color: white;  width: 10%; padding: 8px 16px;margin-left:75px;"
                                         (click)="eliminarArchivo()"
                                     >
                                         <mat-icon>delete</mat-icon>

+ 65 - 11
Front/src/app/modules/Padres/pages/registroAlumno/registroAlumno.component.ts

@@ -1,5 +1,5 @@
+import { EnviarInfoService } from './../../../Administrador/services/enviar-info.service';
 import { Component, Injectable, OnInit, ViewChild } from "@angular/core";
-import { EnviarInfoService } from "../../../Administrador/services/enviar-info.service";
 import { MatPaginator, MatPaginatorIntl } from "@angular/material/paginator";
 import { RelacionService } from "../../../Administrador/services/relacion.service";
 import { MatTableDataSource } from "@angular/material/table";
@@ -24,6 +24,10 @@ export class registroAlumnoComponent implements OnInit {
   public color: string = '';
   public textColor: string = '';
   public DataAlumno: any;
+  public nombreColegio: string = '';
+  public eslogan: string = '';
+  public mensajeBienvenida: string = '';
+
   public registros: any;
   public isLoading: boolean = true;
   public displayedColumns: string[] = ['id', 'nombre', 'grado', 'SiguientePaso', 'RegistroAcademico', 'RegistroAdministrativo', 'VersionImpresa'];
@@ -37,7 +41,7 @@ export class registroAlumnoComponent implements OnInit {
     private paginator1: MatPaginatorIntl,
     private _relacionService: RelacionService,
     private _registroService: RegistroAcademicoService,
-    private router: Router
+    private router: Router,
   ) {
     const spanishRangeLabel = (page: number, pageSize: number, length: number): string => {
       if (length === 0 || pageSize === 0) {
@@ -60,12 +64,42 @@ export class registroAlumnoComponent implements OnInit {
   }
 
   ngOnInit(): void {
-    this._enviarInfo.currentTextColor.subscribe(textColor => {
-      this.textColor = textColor;
-    });
-    this._enviarInfo.currentColor.subscribe(color => {
-      this.color = color;
-    });
+  this._enviarInfo.currentTextColor.subscribe(textColor => {
+    this.textColor = textColor;
+  });
+
+  this._enviarInfo.currentColor.subscribe(color => {
+    this.color = color;
+  });
+
+  this._enviarInfo.currentMensaje.subscribe(mensajeBienvenida => {
+    this.mensajeBienvenida = mensajeBienvenida;
+  });
+
+
+  this._enviarInfo.getNombreColegio().subscribe({
+    next: (res) => {
+      this.nombreColegio = res.nombreColegio;
+    },
+    error: (err) => {
+      console.error('Error al obtener nombre del colegio:', err);
+    }
+  });
+
+   this._enviarInfo.getEsloganColegio().subscribe({
+    next: (res) => {
+      this.eslogan = res.eslogan;
+      console.log(this.eslogan);
+
+    },
+    error: (err) => {
+      console.error('Error al obtener nombre del colegio:', err);
+    }
+  });
+
+
+
+    this._enviarInfo.currentMensaje
     this.getAll();
     this.getAllAd();
     this.getAlumnos();
@@ -135,9 +169,10 @@ getAll() {
     tipo === 'ac' ? this.router.navigate(['/homePadres/registro', btoa(data)]) : this.router.navigate(['/homePadres/registroAd', btoa(data)]);
   }
 descargarPDF(id: string) {
+
   forkJoin({
     dataAc: this._registroService.getOneRegistroAcademico(id),
-    dataAd: this._registroService.getOneRegistroAdmin(id)
+    dataAd: this._registroService.getOneRegistroAdmin(id),
   }).subscribe(({ dataAc, dataAd }: any) => {
     const data = dataAc.registro[0];
     const data1 = dataAd.registro[0];
@@ -147,9 +182,28 @@ descargarPDF(id: string) {
     var pageHeight = doc.internal.pageSize.height || doc.internal.pageSize.getHeight();
     var pageWidth = doc.internal.pageSize.width || doc.internal.pageSize.getWidth();
 
-    doc.addImage("assets/img/Encabezado-PDF.jpg", "JPEG", 6, 10, pageWidth - 8, 27);
+    // doc.addImage("assets/img/Encabezado-PDF.jpg", "JPEG", 6, 10, pageWidth - 8, 27);
+
+ // 1. Título principal: Nombre del colegio
+doc.setFontSize(14);
+doc.setFont('helvetica', 'bold');
+doc.text(this.nombreColegio, pageWidth / 2, 20, { align: 'center' });
+
+// 2. Línea horizontal debajo del nombre
+doc.setLineWidth(0.5);
+doc.line(10, 24, pageWidth - 10, 24); // línea justo debajo del nombre
+
+// 3. Eslogan
+doc.setFontSize(12);
+doc.setFont('helvetica', 'italic');
+doc.text(this.eslogan, pageWidth / 2, 30, { align: 'center' });
+
+// 4. Mensaje de bienvenida
+doc.setFontSize(12);
+doc.setFont('helvetica', 'normal');
+doc.text(this.mensajeBienvenida, pageWidth / 2, 38, { align: 'center' });
+
 
-    // TITULO
     doc.setFont("helvetica", "italic");
     doc.setFontSize(10);
     doc.text(`DATOS GENERALES: ALUMNO`, 10, 43);

+ 6 - 6
Front/src/app/modules/Profesor/pages/layout-page/layout-page.component.ts

@@ -28,7 +28,7 @@ export class LayoutPageComponent {
   public lastPing?: any = null;
 
   constructor(
-    private _enviarInfo: EnviarInfoService, 
+    private _enviarInfo: EnviarInfoService,
     private _personalizarService: PersonalizarService,
     private _authService: AuthService,
     private idle: Idle,
@@ -65,7 +65,7 @@ export class LayoutPageComponent {
       this.idle.setIdle(10 * 60); // tiempo de inactividad
       this.idle.setTimeout(5 * 60); // tiempo para cerrar sesión después de pasar setIdle
       this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
-  
+
       // mensaje de cierre de sesión forzado
       this.idle.onTimeout.subscribe(() => {
         this.timeOut = true;
@@ -77,7 +77,7 @@ export class LayoutPageComponent {
         }, (err) => {
           console.log(err);
         });
-  
+
         Swal.fire({
           icon: 'info',
           title: 'Sesión terminada',
@@ -87,7 +87,7 @@ export class LayoutPageComponent {
           confirmButtonText: 'Ok'
         });
       });
-  
+
       // mensaje antes de que te saque
       this.idle.onIdleStart.subscribe(() => {
         Swal.fire({
@@ -103,13 +103,13 @@ export class LayoutPageComponent {
           }
         });
       });
-  
+
       // this.idle.onInterrupt.subscribe(() => {
       //   let hoy = new Date();
       //   hoy.setSeconds(this.timeInterval);
       //   localStorage.setItem('expira', hoy.getTime().toString());
       // });
-  
+
       this.keepalive.onPing.subscribe(() => this.lastPing = new Date());
     }
     // this.checkSession();