Commit 192cafd0 authored by Alfonso Rafael Solis Rangel's avatar Alfonso Rafael Solis Rangel
Browse files

Obtener reportes de las listas terminado

parent 297cd7c4
Loading
Loading
Loading
Loading
+145 −0
Original line number Diff line number Diff line
<?php

namespace App\Exports;

use App\Models\Lista;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;

class ListasExport implements FromCollection, WithHeadings
{
    protected $extraFields;
    protected $lista_id;

    public function __construct($lista_id, $extraFields = [])
    {
        $this->lista_id = $lista_id;
        $this->extraFields = $extraFields;
    }

    public function collection()
    {
        $lista = Lista::with(['grupos', 'subgrupos', 'contactos'])->find($this->lista_id);
        $contactos = new Collection();
        $contactos = $contactos->merge($lista->contactos);

        foreach ($lista->grupos as $grupo) {
            $contactos = $contactos->merge($grupo->contactos);

            foreach ($grupo->subgrupos as $subgrupo) {
                $contactos = $contactos->merge($subgrupo->contactos);
            }
        }
        foreach($lista->subgrupos as $subgrupo){
            $contactos = $contactos->merge($subgrupo->contactos);
        }

        $contactos = $contactos->unique('id');

        return $contactos->map(function($contacto){
            return $this->formatContactoData($contacto);
        });
    }

    public function headings(): array
    {
        $defaultHeadings = ['Profesión', 'Nombre', 'Apellido Paterno', 'Apellido Materno', 'Cargo', 'Descripción del cargo', 'Grupo', 'Subgrupo'];
        $extraHeadings = $this->getExtraHeadings();
        return array_merge($defaultHeadings, $extraHeadings);
    }

    private function formatContactoData($contacto)
    {
        $defaultData = [
            'Profesión' => $contacto->profesion->abreviatura ?? 'N/A',
            'Nombre' => $contacto->nombre,
            'Apellido Paterno' => $contacto->ap_paterno,
            'Apellido Materno' => $contacto->ap_materno ?? '',
            'Cargo' => $contacto->cargo->nombre ?? 'N/A',
            'Descripción del cargo' => $contacto->cargo_desc ?? 'N/A',
            'Grupo' => $contacto->grupos->pluck('nombre')[0] ?? 'N/A',
            'Subgrupo' => $contacto->subgrupos->pluck('nombre')[0] ?? 'N/A',
        ];

        $extraData = [];
        $fields = $this->extraFields;

        if (in_array('todos', $fields)) {
            $fields = ['domicilio', 'cumpleanos', 'telefonos', 'conyuge', 'email'];
        }

        foreach ($fields as $field) {
            switch ($field) {
                case 'domicilio':
                    $extraData = array_merge($extraData, [
                        'Domicilio Oficial' => $contacto->domicilio_oficial ?? 'N/A',
                        'Código Postal' => $contacto->codigo_postal ?? 'N/A',
                        'Localidad Oficial' => $contacto->localidad_oficial ?? 'N/A',
                        'Municipio Oficial' => $contacto->municipio_oficial ?? 'N/A',
                        'Estado' => $contacto->estado ?? 'N/A',
                        'País' => $contacto->pais ?? 'N/A',
                    ]);
                    break;
                case 'cumpleanos':
                    $extraData['Cumpleaños'] = $contacto->mes_cump && $contacto->dia_cump
                        ? $contacto->mes_cump . '/' . $contacto->dia_cump
                        : 'N/A';
                    break;
                case 'telefonos':
                    $extraData = array_merge($extraData, [
                        'Número de teléfono' => $contacto->telefonos->pluck('numero')->join(', ') ?? 'N/A',
                        'Tipo' => $contacto->telefonos->pluck('tipo')->join(', ') ?? 'N/A',
                        'Estatus' => $contacto->telefonos->pluck('estatus')->join(', ') ?? 'N/A',
                        'Extensión' => $contacto->telefonos->pluck('ext')->join(', ') ?? 'N/A',
                        'Observaciones' => $contacto->telefonos->pluck('observaciones')->join(', ') ?? 'N/A',
                    ]);
                    break;
                case 'conyuge':
                    $extraData['Cónyuge'] = $contacto->pareja
                        ? $contacto->pareja->nombre . ' ' . $contacto->pareja->ap_paterno . ' ' . $contacto->pareja->ap_materno
                        : 'N/A';
                    break;
                case 'email':
                    $extraData = array_merge($extraData, [
                        'Correo Electrónico' => $contacto->correos->pluck('correo_electronico')->join(', ') ?? 'N/A',
                        'Tipo de Correo Electrónico' => $contacto->correos->pluck('tipo_correo_electronico')->join(', ') ?? 'N/A',
                    ]);
                    break;
            }
        }

        return array_merge($defaultData, $extraData);
    }

    private function getExtraHeadings()
    {
        $headings = [];
        $fields = $this->extraFields;

        if (in_array('todos', $fields)) {
            $fields = ['domicilio', 'cumpleanos', 'telefonos', 'conyuge', 'email'];
        }

        foreach ($fields as $field) {
            switch ($field) {
                case 'domicilio':
                    $headings = array_merge($headings, ['Domicilio Oficial', 'Código Postal', 'Localidad Oficial', 'Municipio Oficial', 'Estado', 'País']);
                    break;
                case 'cumpleanos':
                    $headings[] = 'Cumpleaños';
                    break;
                case 'telefonos':
                    $headings = array_merge($headings, ['Número de teléfono', 'Tipo', 'Estatus', 'Extensión', 'Observaciones']);
                    break;
                case 'conyuge':
                    $headings[] = 'Cónyuge';
                    break;
                case 'email':
                    $headings = array_merge($headings, ['Correo Electrónico', 'Tipo de Correo Electrónico']);
                    break;
            }
        }
        return $headings;
    }
}
+77 −26
Original line number Diff line number Diff line
@@ -10,10 +10,12 @@
use Illuminate\Http\Request;

use App\Exports\ContactosExport;
use App\Exports\ListasExport;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use ZipArchive;
use Illuminate\Support\Str;

class ReporteController extends Controller
{
@@ -102,11 +104,12 @@ public function export(Request $request)
            'mesFin' => $request->input('mesFin'),
            'diaFin' => $request->input('diaFin'),
        ];
        try{
            $export = new ContactosExport($contactIds, $extraFields, $filters);
            if($request->archivo == 'excel'){
                return Excel::download($export, 'contactos.xlsx');
            }else if($request->archivo == 'pdf'){
            $pdf = Pdf::loadView('adminGen.contactos.contactos_pdf', ['contactos' => $export->collection()])->setPaper('A1', 'landscape');;
                $pdf = Pdf::loadView('adminGen.contactos.contactos_pdf', ['contactos' => $export->collection()])->setPaper('A1', 'landscape');
                return $pdf->download('contactos.pdf');
            }else if($request->archivo == 'ambos'){
                $excelPath = storage_path('app/public/contactos.xlsx');
@@ -128,8 +131,12 @@ public function export(Request $request)
                // Descargar archivo ZIP
                return response()->download($zipPath)->deleteFileAfterSend(true);
            }else{
            return response()->json(['Error, No seleccionaste un archivo de exportación valido.'], 400);
                return response()->json(['Error, no seleccionaste un archivo de exportación valido.'], 400);
            }
        }catch(\Exception $e){
            return response()->json(['message'=> $e->getMessage()], 500);
        }
        
    }

    public function getSubgrupos(Int $id)
@@ -137,4 +144,48 @@ public function getSubgrupos(Int $id)
        $grupo = Grupos::find($id);
        return response()->json($grupo->subgrupos);
    }

    public function exportListas(Request $request)
    {
        try{
            if(is_null($request->archivo)){
                return response()->json(['Error, No seleccionaste un archivo de exportación valido.'], 400);
            }
    
            $extraFields = $request->input('exportar', []);
            if(!$extraFields){
                return response()->json(['Error, No seleccionaste campos a exportar.'], 400);
            }
            $lista_id = $request->input('id');
    
            $export = new ListasExport($lista_id, $extraFields);
            $uuid = Str::uuid()->toString();
            if($request->archivo == 'excel'){
                return Excel::download($export, $uuid . '.xlsx');
            }else if($request->archivo == 'pdf'){
                $pdf = Pdf::loadView('adminGen.contactos.contactos_pdf', ['contactos' => $export->collection()])->setPaper('A1', 'landscape');
                return $pdf->download($uuid.'.pdf');
            }else if($request->archivo == 'ambos'){
                $excelPath = storage_path('app/public/contactos.xlsx');
                $pdfPath = storage_path('app/public/contactos.pdf');

                Excel::store($export, 'public/contactos.xlsx');
                $pdf = Pdf::loadView('adminGen.contactos.contactos_pdf', ['contactos' => $export->collection()])->setPaper('A1', 'landscape');
                Storage::put('public/contactos.pdf', $pdf->output());
                $zipPath = storage_path('app/public/'.$uuid.'.zip');
                $zip = new ZipArchive;
                if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
                    $zip->addFile($excelPath, $uuid.'.xlsx');
                    $zip->addFile($pdfPath, $uuid.'.pdf');
                    $zip->close();
                }

                return response()->download($zipPath)->deleteFileAfterSend(true);
            }else{
                return response()->json(['message' => 'Error, no seleccionaste un archivo de exportación valido.'], 400);
            }
        }catch(\Exception $e){
            return response()->json(['message' => $e->getMessage()], 500);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -34,6 +34,6 @@ public function listas(): BelongsToMany

    public function contactos(): BelongsToMany
    {
        return $this->belongsToMany(Contacto::class, 'contactos_subgrupos', 'contacto_id', 'subgrupo_id');
        return $this->belongsToMany(Contacto::class, 'contactos_subgrupos', 'subgrupo_id', 'contacto_id');
    }
}
+201 −1
Original line number Diff line number Diff line
@@ -14,6 +14,58 @@
        </ul>
        <div class="pt-5">
            <div class="grid grid-cols-1 lg:grid-cols-3 xl:grid-cols-4 gap-5 mb-5">
                <div class="panel lg:col-span-4 xl:col-span-4">
                    <div class="mb-5">
                        <h5 class="font-semibold text-lg dark:text-white-light">Exportar datos</h5>
                    </div>
                    <div class="mb-5">
                        <div class="text-[#515365] dark:text-white-light font-semibold p-4 bg-white rounded-lg shadow">
                            <form method="GET" action="" class="space-y-4" onsubmit="event.preventDefault(); logData();">
                                <!-- Contenedor de selects alineados -->
                                <div class="grid grid-cols-2 gap-4 items-center">

                                    <!-- Tipo de archivo a exportar -->
                                    <div>
                                        <label for="archivo" class="text-gray-700 font-semibold block mb-1">Tipo de archivo:</label>
                                        <select id="archivo" class="w-full px-3 py-2 bg-white border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
                                            <option value="" disabled selected>Selecciona el formato de archivo a exportar</option>
                                            <option value="ambos">Ambos</option>
                                            <option value="pdf">PDF</option>
                                            <option value="excel">Excel</option>
                                        </select>
                                    </div>

                                    <!-- Campos a exportar -->
                                    <div>
                                        <label for="exportar" class="text-gray-700 font-semibold block mb-1">Campos:</label>
                                        <div class="flex items-center space-x-2">
                                            <select id="exportar" class="w-full px-3 py-2 bg-white border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500">
                                                <option value="" disabled selected>Selecciona los campos a exportar</option>
                                                <option value="todos">Todos</option>
                                                <option value="conyuge">Cónyuge</option>
                                                <option value="domicilio">Domicilio</option>
                                                <option value="cumpleanos">Cumpleaños</option>
                                                <option value="email">Correo electrónico</option>
                                                <option value="telefonos">Teléfonos</option>
                                            </select>
                                            <button type="button" onclick="agregarEtiqueta('exportar', 'exportarAgregados')" class="bg-blue-500 text-white px-3 py-2 rounded-md shadow hover:bg-blue-600 transition">
                                                Agregar
                                            </button>
                                        </div>
                                    </div>
                                    <div id="exportarAgregados" class="flex flex-wrap gap-2 mt-2"></div>
                                </div>

                                <!-- Botón para aplicar filtros -->
                                <div class="flex justify-end">
                                    <button type="submit" class="px-4 py-2 bg-blue-500 text-white font-semibold rounded-md shadow hover:bg-blue-600 transition">
                                        Aplicar Filtros
                                    </button>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="panel lg:col-span-2 xl:col-span-2">
                    <div class="mb-5">
                        <h5 class="font-semibold text-lg dark:text-white-light">Grupos</h5>
@@ -93,5 +145,153 @@ class="btn btn-primary">
                </a>
            </div>
        </div>
        <div id="loader-container" class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75 hidden">
            <span id="loader" class="animate-spin border-8 border-gray-300 border-l-[#780005] border-r-[#780005] rounded-full w-14 h-14"></span>
        </div>
    </div>

    <link rel="stylesheet" href="{{ Vite::asset('resources/css/highlight.min.css') }}">
    <script src="/assets/js/highlight.min.js"></script>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <link rel="stylesheet" href="/assets/css/jquery.toast.css">
    <script src="/assets/js/jquery.toast.js"></script>
    <script>
        let exportarSeleccion = [];
        function agregarEtiqueta(selectId, containerId){
            const select = document.getElementById(selectId);
            const selectedOption = select.options[select.selectedIndex];

            if(selectedOption && selectedOption.value){
                const container = document.getElementById(containerId);
                const etiquetaId = `${selectId}-${selectedOption.value}`;

                if(!document.getElementById(etiquetaId)){
                    switch(selectId){
                        case 'exportar':
                            exportarSeleccion.push(selectedOption.value);
                            break;
                        default:
                            break;
                    }

                    const etiqueta = document.createElement('div');
                    etiqueta.id = etiquetaId;
                    etiqueta.classList.add('inline-block', 'bg-gray-200', 'px-4', 'py-2', 'rounded', 'mr-2', 'mt-2');
                    etiqueta.innerHTML = `${selectedOption.text} <button type="button" onclick="eliminarEtiqueta('${etiquetaId}')" class="text-red-500">x</button>`;

                    const inputHidden = document.createElement('input');
                    inputHidden.type = 'hidden';
                    inputHidden.name = `${selectId}[]`;
                    inputHidden.value = selectedOption.value;
                    etiqueta.appendChild(inputHidden);

                    container.appendChild(etiqueta);
                    select,selectedIndex = 0;
                }
            }
        }

        function eliminarEtiqueta(etiquetaId){
            const etiqueta = document.getElementById(etiquetaId);
            if(etiqueta){
                etiqueta.remove();

                const [selectId, value] = etiquetaId.split('-');
                switch(selectId){
                    case 'exportar':
                        exportarSeleccion = exportarSeleccion.filter(item => item !== value);
                        break;
                    default:
                        break;
                }
            }
        }

        function logData(){
            const select = document.getElementById('archivo');
            const selectedOption = select.options[select.selectedIndex];

            const data = {
                archivo: selectedOption.value,
                exportar: exportarSeleccion,
                id: {{$lista->id}},
            };

            document.getElementById('loader-container').classList.remove('hidden');
            document.querySelector('form').classList.add('pointer-events-none');

            fetch('/reportes/listas/exportar', {
                method: 'POST',
                body: JSON.stringify(data),
                headers:{
                    "Content-Type": "application/json; charset=utf-8",
                    "X-CSRF-TOKEN": "{{ csrf_token() }}"
                }
            })
            .then(response => {
                if(!response.ok){
                    return response.json().then(err => {
                        throw err;
                    });
                }

                const contentDisposition = response.headers.get('Content-Disposition');
                let filename = "reporte.xlsx";
                if(contentDisposition){
                    const match = contentDisposition.match(/filename="?([^"]+)"?/);
                    if(match && match[1]){
                        filename = match[1];
                    }
                }

                return response.blob().then(blob => ({blob, filename}));
            })
            .then(({blob, filename}) => {
                const url = window.URL.createObjectURL(blob);
                const link = document.createElement('a');
                link.href = url;
                link.download = filename;
                document.body.appendChild(link);
                link.click();
                link.remove();
                window.URL.revokeObjectURL(url);
            })
            .catch(err => {
                showToast(err, 'Error.', 'error');
            })
            .finally(() => {
                document.getElementById('loader-container').classList.add('hidden');
                document.querySelector('form').classList.remove('pointer-events-none');
            });
        }


        function showToast(message, heading, icon) {
            $.toast({
                heading: heading,
                text: message,
                showHideTransition: 'slide',
                icon: icon,
                position: 'top-right',
                loader: false,
                hideAfter: 10000, 
                allowToastClose: true,
                textColor: '#676767',
            });
        }

        document.addEventListener('DOMContentLoaded', function() {
            @if(session('success'))
            // Muestra el mensaje de éxito utilizando showMessage
                showToast("{{ session('success') }}", 'Exito.', 'success')
            @endif

            @if($errors->any())
                @foreach($errors->all() as $error)
                    showToast(`{{ $error }}`, 'Error.', 'error');
                @endforeach
            @endif
        });
    </script>
</x-layout.default>
 No newline at end of file