diff --git a/app/Http/Controllers/Dashboard/AtencionController.php b/app/Http/Controllers/Dashboard/AtencionController.php new file mode 100644 index 0000000..7e08bc6 --- /dev/null +++ b/app/Http/Controllers/Dashboard/AtencionController.php @@ -0,0 +1,237 @@ +only(['start', 'end', 'type', 'action', 'period', 'charts_only']); + $input = array_merge(['action' => 'index', 'type' => 'api'], $query); + + $startDate = $input['start'] ?? date('Y-01-01'); + $endDate = $input['end'] ?? date('Y-12-31'); + $period = $input['period'] ?? null; + $chartsOnly = $input['charts_only'] ?? false; + + // Cache key para diferentes tipos de peticiones + $cacheKey = 'atencion_' . md5(serialize([ + 'start' => $startDate, + 'end' => $endDate, + 'period' => $period, + 'charts_only' => $chartsOnly + ])); + + // Intentar obtener desde cache (2 minutos) + if (Cache::has($cacheKey)) { + return response()->json(Cache::get($cacheKey), 200) + ->header('Content-Type', 'application/json'); + } + + try { + // Si solo necesitamos gráficas, hacer menos peticiones + if ($chartsOnly) { + $result = $this->getChartsData($startDate, $endDate); + } else { + $result = $this->getFullData($startDate, $endDate, $period); + } + + // Cachear resultado por 2 minutos + Cache::put($cacheKey, $result, 120); + + return response()->json($result, 200) + ->header('Content-Type', 'application/json'); + } catch (\Exception $e) { + Log::error('Error en la consulta de atención', ['exception' => $e->getMessage()]); + return response()->json(['error' => 'Error en la consulta de atención'], 500); + } + } + + private function getChartsData($startDate, $endDate) + { + $baseParams = [ + 'start_date' => $startDate, + 'end_date' => $endDate, + 'type' => 'api', + 'action' => 'index', + ]; + + $baseUrl = 'https://apoyos.comalcalco.gob.mx/beneficiaries/stats-by-date-range'; + + // Solo hacer la petición principal para gráficas + $response = Http::timeout(15)->get($baseUrl, $baseParams); + + if (!$response->successful()) { + throw new \Exception('Error del servicio principal'); + } + + return $response->json(); + } + + private function getFullData($startDate, $endDate, $period) + { + $baseParams = [ + 'start_date' => $startDate, + 'end_date' => $endDate, + 'type' => 'api', + 'action' => 'index', + ]; + + $urls = [ + 'main' => 'https://apoyos.comalcalco.gob.mx/beneficiaries/stats-by-date-range', + 'counts' => 'https://apoyos.comalcalco.gob.mx/beneficiaries/counts-by-date', + 'dashboard' => 'https://apoyos.comalcalco.gob.mx/beneficiaries/dashboard/api', + 'dashboard_servicio' => 'https://apoyos.comalcalco.gob.mx/beneficiaries/dashboard/api?type=servicio' + ]; + + // Hacer peticiones en paralelo usando HTTP Pool + $responses = Http::pool(fn($pool) => [ + $pool->timeout(15)->get($urls['main'], $baseParams), + $pool->timeout(15)->get($urls['counts'], [ + 'start_date' => $startDate, + 'end_date' => $endDate, + ]), + $pool->timeout(15)->get($urls['dashboard']), + $pool->timeout(15)->get($urls['dashboard_servicio']) + ]); + + $mainData = []; + $countsAc = []; + $dashboardData = []; + $dashboardServicioData = []; + + // Procesar respuestas + if ($responses[0]->successful()) { + $mainData = $responses[0]->json(); + } else { + Log::error('Error en petición principal', ['status' => $responses[0]->status()]); + } + + if ($responses[1]->successful()) { + $countsAc = $responses[1]->json(); + } else { + Log::error('Error en petición counts', ['status' => $responses[1]->status()]); + } + + if ($responses[2]->successful()) { + $dashboardData = $responses[2]->json(); + } else { + Log::error('Error en petición dashboard', ['status' => $responses[2]->status()]); + } + + if ($responses[3]->successful()) { + $dashboardServicioData = $responses[3]->json(); + } else { + Log::error('Error en petición dashboard servicio', ['status' => $responses[3]->status()]); + } + + // Combinar todos los resultados + return array_merge( + is_array($mainData) ? $mainData : [], + [ + 'counts' => $countsAc, + 'dashboard' => $dashboardData, + 'dashboard_servicio' => $dashboardServicioData, + ] + ); + } + + + public function exportExcel(Request $request) + { + try { + // Obtener parámetros necesarios para la API + $params = $request->only([ + 'start_date', + 'end_date', + 'department' + ]); + + // Validaciones básicas + if (!isset($params['start_date']) || !isset($params['end_date'])) { + return response()->json([ + 'error' => 'Las fechas son requeridas' + ], 400); + } + + if (!isset($params['department']) || $params['department'] === '') { + return response()->json([ + 'error' => 'El departamento es requerido' + ], 400); + } + + // Validar que department sea 1 o 3 + if (!in_array((int)$params['department'], [1, 3])) { + return response()->json([ + 'error' => 'El departamento debe ser Atención Ciudadana o DIF' + ], 400); + } + + $url = 'https://apoyos.comalcalco.gob.mx/api/beneficiaries/export-excel'; + + // Hacer la petición a la API externa + $response = Http::withoutVerifying() + ->timeout(60) + ->get($url, $params); + + if (!$response->successful()) { + Log::error('Error al exportar Excel', [ + 'status' => $response->status(), + 'body' => $response->body(), + 'params' => $params + ]); + + return response()->json([ + 'error' => 'Error al generar el archivo Excel: ' . $response->body() + ], $response->status()); + } + + // Verificar que la respuesta sea realmente un Excel + $contentType = $response->header('Content-Type'); + + if (!str_contains($contentType, 'spreadsheet') && !str_contains($contentType, 'excel') && !str_contains($contentType, 'octet-stream')) { + Log::error('Respuesta no es un archivo Excel', [ + 'url' => $url, + 'params' => $params, + 'status' => $response->status(), + 'content_type' => $contentType, + 'body_preview' => substr($response->body(), 0, 500) + ]); + + return response()->json([ + 'error' => 'La API externa no devolvió un archivo Excel válido. URL: ' . $url . ' | Status: ' . $response->status() + ], 400); + } + + // Nombre del departamento para el archivo + $departmentName = $params['department'] == 1 ? 'AtencionCiudadana' : 'DIF'; + + // Retornar el archivo Excel directamente con headers correctos + return response($response->body(), 200, [ + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'Content-Disposition' => 'attachment; filename="beneficiarios_' . $departmentName . '_' . + $params['start_date'] . '_' . $params['end_date'] . '.xlsx"', + 'Content-Length' => strlen($response->body()), + 'Cache-Control' => 'no-cache, no-store, must-revalidate', + 'Pragma' => 'no-cache', + 'Expires' => '0' + ]); + } catch (\Exception $e) { + Log::error('Error en exportExcel', [ + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + 'params' => $request->all() + ]); + + return response()->json([ + 'error' => 'Error interno del servidor: ' . $e->getMessage() + ], 500); + } + } +} diff --git a/app/Http/Controllers/Dashboard/ObrasController.php b/app/Http/Controllers/Dashboard/ObrasController.php new file mode 100644 index 0000000..b1b7455 --- /dev/null +++ b/app/Http/Controllers/Dashboard/ObrasController.php @@ -0,0 +1,70 @@ +only(['start', 'end', 'type', 'action']); + $query = array_merge($query, ['action' => 'index', 'type' => 'api'], $query); + + // Caché por 2 minutos (datos más dinámicos) + $cacheKey = "obras_data_" . md5(serialize($query)); + + if ($cachedData = Cache::get($cacheKey)) { + return response()->json($cachedData, 200) + ->header('Content-Type', 'application/json'); + } + + try { + $response = Http::timeout(5)->get('https://obras-information.comalcalco.gob.mx/api/controller.php', $query); + + if (! $response->successful()) { + Log::warning('Obras service error', [ + 'status' => $response->status(), + 'body' => $response->body(), + 'query' => $query + ]); + return response()->json(['error' => 'External service error'], $response->status()); + } + + $mainData = $response->json(); + + $counterQuery = array_merge($query, ['action' => 'getCounters']); + $countersResponse = Http::timeout(5)->get('https://obras-information.comalcalco.gob.mx/api/controller.php', $counterQuery); + + $countersData = []; + if ($countersResponse->successful()) { + $countersData = $countersResponse->json(); + } + + $usersQuery = array_merge($query, ['action' => 'actionsOfSupervisors']); + $userResponse = Http::timeout(5)->get('https://obras-information.comalcalco.gob.mx/api/controller.php', $usersQuery); + + $usersData = []; + if($userResponse->successful()) { + $usersData = $userResponse->json(); + } + + // Combinar ambas respuestas + $combinedData = array_merge($mainData, ['counters' => $countersData, 'users' => $usersData]); + + // Cachear por 2 minutos + Cache::put($cacheKey, $combinedData, now()->addMinutes(2)); + + return response()->json($combinedData, $response->status()) + ->header('Content-Type', $response->header('Content-Type', 'application/json')); + } catch (\Throwable $e) { + Log::error('Proxy error: '.$e->getMessage(), [ + 'query' => $query, + 'trace' => $e->getTraceAsString() + ]); + return response()->json(['error' => 'Unable to contact external service'], 502); + } + } +} diff --git a/app/Http/Controllers/Dashboard/TramiteController.php b/app/Http/Controllers/Dashboard/TramiteController.php new file mode 100644 index 0000000..214f539 --- /dev/null +++ b/app/Http/Controllers/Dashboard/TramiteController.php @@ -0,0 +1,54 @@ +only(['start', 'end', 'type']); + $query = array_merge(['type' => 'api'], $query); + + // Caché por 2 minutos (datos más dinámicos) + $cacheKey = "tramites_data_" . md5(serialize($query)); + + if ($cachedData = Cache::get($cacheKey)) { + return response()->json($cachedData, 200) + ->header('Content-Type', 'application/json'); + } + + try { + $response = Http::timeout(20)->get('https://tramites.comalcalco.gob.mx/reporte-especial', $query); + + if (!$response->successful()) { + Log::warning('Tramites service error', [ + 'status' => $response->status(), + 'body' => $response->body(), + 'query' => $query + ]); + return response()->json(['error' => 'External service error'], $response->status()); + } + + $data = $response->json(); + + // Cachear por 2 minutos + Cache::put($cacheKey, $data, now()->addMinutes(2)); + + return response()->json($data, $response->status()) + ->header('Content-Type', $response->header('Content-Type', 'application/json')); + + } catch (\Throwable $e) { + Log::error('Proxy error: ' . $e->getMessage(), [ + 'query' => $query, + 'trace' => $e->getTraceAsString() + ]); + return response()->json(['error' => 'Unable to contact external service'], 502); + } + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f5b4862..581b2b7 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider * * @var string */ - public const HOME = '/dashboard/welcome'; + public const HOME = '/tramites'; /** * Define your route model bindings, pattern filters, and other route configuration. diff --git a/colors.json.example b/colors.json.example index fd28bbe..d317205 100644 --- a/colors.json.example +++ b/colors.json.example @@ -7,12 +7,12 @@ "main-on":"#000", "main-dark":"#1E1F1C", "main-dark-on":"#fff", - "primary":"#111827", + "primary":"#621132", "primary-on":"#fff", - "primary-dark":"#000", + "primary-dark":"#621132", "primary-dark-on":"#fff", - "secondary":"#374151", - "secondary-dark":"#989A9C", + "secondary":"#7d1a42", + "secondary-dark":"#7d1a42", "white":"#FFFFFF", "white-dark":"#FFFFFF", "success":"#22C55E", @@ -23,4 +23,4 @@ "danger-dark":"#EF4444", "info":"#3B82F6", "info-dark":"#3B82F6" -} \ No newline at end of file +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index bfd99be..2e86407 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -10,15 +10,15 @@ /** * Siembra usuarios de forma segura - * + * * Siembra usuarios sin la necesidad de escribir una contraseña que quede registrada en el código. * El usuario será generado con una contraseña que puede ser consultado en el log. Esto permite * evitar el escribir las contraseñas en código. Esto permite que los usuarios de prueba sean seguros * y de proyecto en proyecto no sea una brecha de seguridad tener contraseñas dentro del código en caso * de que este pueda llegar a ser vulnerado. - * + * * @author Moisés de Jesús Cortés Castellanos - * + * * @version 1.0.0 */ class UserSeeder extends Seeder @@ -35,36 +35,28 @@ public function run() [ $developerEmail, $developerPass - ] = UserSecureSupport::new('developer@notsoweb.com'); - + ] = UserSecureSupport::new('developer@golsystems.com'); + User::create([ 'name' => 'Developer', - 'paternal' => 'Notsoweb', + 'paternal' => 'golsystems', 'email' => $developerEmail, 'phone' => '5631809090', 'password' => $developerPass, ])->assignRole('developer'); - + // Usuario administrador [ $adminEmail, $adminPass - ] = UserSecureSupport::new('admin@notsoweb.com'); - + ] = UserSecureSupport::new('admin@comalcalco.com'); + User::create([ 'name' => 'Administrador', - 'paternal' => 'Notsoweb', + 'paternal' => 'comalcalco', 'email' => $adminEmail, 'password' => $adminPass ])->assignRole('admin'); - - // Usuario de prueba - User::create([ - 'name' => 'Demo', - 'paternal' => 'Notsoweb', - 'email' => 'demo@notsoweb.com', - 'password' => Hash::make('Demo') - ]); }); } } diff --git a/package-lock.json b/package-lock.json index 9fbf9e7..e292790 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,22 @@ { - "name": "template-laravel-vuejs", + "name": "maquetador-graficas", "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { + "@googlemaps/js-api-loader": "^1.16.10", "@inertiajs/vue3": "^1.0.0-beta.2", "@soketi/soketi": "^1.6.0", "@vueuse/core": "^9.6.0", + "chart.js": "^4.5.0", "dotenv": "^16.3.1", "express": "^4.18.2", "laravel-echo": "^1.14.2", "pusher-js": "^7.5.0", "sweetalert2": "^11.4.8", "toastr": "^2.1.4", + "vue-chartjs": "^5.3.2", "vue-i18n": "^9.2.2", "vue-multiselect": "^3.0.0-alpha.2" }, @@ -406,6 +409,12 @@ "node": ">=12" } }, + "node_modules/@googlemaps/js-api-loader": { + "version": "1.16.10", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.10.tgz", + "integrity": "sha512-c2erv2k7P2ilYzMmtYcMgAR21AULosQuUHJbStnrvRk2dG93k5cqptDrh9A8p+ZNlyhiqEOgHW7N9PAizdUM7Q==", + "license": "Apache-2.0" + }, "node_modules/@inertiajs/core": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-1.0.14.tgz", @@ -523,6 +532,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz", @@ -1678,6 +1693,18 @@ "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==" }, + "node_modules/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -5451,6 +5478,16 @@ } } }, + "node_modules/vue-chartjs": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz", + "integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==", + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.1.1", + "vue": "^3.0.0-0 || ^2.7.0" + } + }, "node_modules/vue-i18n": { "version": "9.7.1", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.7.1.tgz", diff --git a/package.json b/package.json index cf5cb41..3466eea 100644 --- a/package.json +++ b/package.json @@ -19,15 +19,18 @@ "vue": "^3.2.31" }, "dependencies": { + "@googlemaps/js-api-loader": "^1.16.10", "@inertiajs/vue3": "^1.0.0-beta.2", "@soketi/soketi": "^1.6.0", "@vueuse/core": "^9.6.0", + "chart.js": "^4.5.0", "dotenv": "^16.3.1", "express": "^4.18.2", "laravel-echo": "^1.14.2", "pusher-js": "^7.5.0", "sweetalert2": "^11.4.8", "toastr": "^2.1.4", + "vue-chartjs": "^5.3.2", "vue-i18n": "^9.2.2", "vue-multiselect": "^3.0.0-alpha.2" } diff --git a/resources/css/app.css b/resources/css/app.css index e609f68..b0a29ba 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -18,12 +18,12 @@ @font-face { src: url(./google-icons/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'); } -/* Scrollbar */ +/* Scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } - + ::-webkit-scrollbar-thumb { background: #777777; border-radius: 4px; @@ -61,7 +61,7 @@ .btn-icon-secondary { } .header-icon { - @apply hover:text-yellow-500 focus:text-yellow-600 hover:scale-105 hover:transition hover:duration-300; + @apply hover:text-primary focus:text-primary hover:scale-105 hover:transition hover:duration-300; } .input-primary { diff --git a/resources/js/Components/Dashboard/Charts/Bars.vue b/resources/js/Components/Dashboard/Charts/Bars.vue new file mode 100644 index 0000000..2c4513a --- /dev/null +++ b/resources/js/Components/Dashboard/Charts/Bars.vue @@ -0,0 +1,34 @@ + + + diff --git a/resources/js/Components/Dashboard/Charts/Lines.vue b/resources/js/Components/Dashboard/Charts/Lines.vue new file mode 100644 index 0000000..9b5a718 --- /dev/null +++ b/resources/js/Components/Dashboard/Charts/Lines.vue @@ -0,0 +1,32 @@ + + + diff --git a/resources/js/Components/Dashboard/Charts/Pie.vue b/resources/js/Components/Dashboard/Charts/Pie.vue new file mode 100644 index 0000000..3d4073a --- /dev/null +++ b/resources/js/Components/Dashboard/Charts/Pie.vue @@ -0,0 +1,29 @@ + + + diff --git a/resources/js/Components/Dashboard/Charts/StackedBar.vue b/resources/js/Components/Dashboard/Charts/StackedBar.vue new file mode 100644 index 0000000..2ebd952 --- /dev/null +++ b/resources/js/Components/Dashboard/Charts/StackedBar.vue @@ -0,0 +1,42 @@ + + + diff --git a/resources/js/Components/Dashboard/Footer.vue b/resources/js/Components/Dashboard/Footer.vue new file mode 100644 index 0000000..0219b96 --- /dev/null +++ b/resources/js/Components/Dashboard/Footer.vue @@ -0,0 +1,125 @@ + diff --git a/resources/js/Components/Dashboard/Form/DateRange.vue b/resources/js/Components/Dashboard/Form/DateRange.vue new file mode 100644 index 0000000..aace826 --- /dev/null +++ b/resources/js/Components/Dashboard/Form/DateRange.vue @@ -0,0 +1,118 @@ + + + diff --git a/resources/js/Components/Dashboard/Maps.vue b/resources/js/Components/Dashboard/Maps.vue new file mode 100644 index 0000000..8fffb6a --- /dev/null +++ b/resources/js/Components/Dashboard/Maps.vue @@ -0,0 +1,694 @@ + + diff --git a/resources/js/Components/Dashboard/Modal/ExportModal.vue b/resources/js/Components/Dashboard/Modal/ExportModal.vue new file mode 100644 index 0000000..28149bf --- /dev/null +++ b/resources/js/Components/Dashboard/Modal/ExportModal.vue @@ -0,0 +1,223 @@ + + + diff --git a/resources/js/Components/Dashboard/Skeleton/Header.vue b/resources/js/Components/Dashboard/Skeleton/Header.vue index dba38f1..97ccfce 100644 --- a/resources/js/Components/Dashboard/Skeleton/Header.vue +++ b/resources/js/Components/Dashboard/Skeleton/Header.vue @@ -30,7 +30,7 @@ const notifications = ref(notificationCtl.notifications); // Otras variables const userId = router.page.props.user.id; -// +// const darkModeStatus = ref(theme); // Métodos @@ -137,7 +137,7 @@ onMounted(()=>{ :title="$t('users.menu')" class="flex items-center space-x-4 text-sm border-2 border-transparent rounded-full focus:outline-none transition" > - {
{{ $page.props.user.name }}
- + API Tokens @@ -169,4 +169,4 @@ onMounted(()=>{ - \ No newline at end of file + diff --git a/resources/js/Layouts/AppLayout.vue b/resources/js/Layouts/AppLayout.vue new file mode 100644 index 0000000..54bf20b --- /dev/null +++ b/resources/js/Layouts/AppLayout.vue @@ -0,0 +1,88 @@ + + + diff --git a/resources/js/Layouts/DashboardLayout.vue b/resources/js/Layouts/DashboardLayout.vue index 0b72714..cbacbaa 100644 --- a/resources/js/Layouts/DashboardLayout.vue +++ b/resources/js/Layouts/DashboardLayout.vue @@ -35,7 +35,7 @@ onMounted(()=> { :title="title" />
- -
diff --git a/resources/js/Pages/App/AccesoRapido.vue b/resources/js/Pages/App/AccesoRapido.vue new file mode 100644 index 0000000..e63b29c --- /dev/null +++ b/resources/js/Pages/App/AccesoRapido.vue @@ -0,0 +1,145 @@ + + + diff --git a/resources/js/Pages/App/AtencionCiudadana.vue b/resources/js/Pages/App/AtencionCiudadana.vue new file mode 100644 index 0000000..77f38f9 --- /dev/null +++ b/resources/js/Pages/App/AtencionCiudadana.vue @@ -0,0 +1,1018 @@ + + + diff --git a/resources/js/Pages/App/Obras.vue b/resources/js/Pages/App/Obras.vue new file mode 100644 index 0000000..9212c29 --- /dev/null +++ b/resources/js/Pages/App/Obras.vue @@ -0,0 +1,1488 @@ + + diff --git a/resources/js/Pages/App/Tramites.vue b/resources/js/Pages/App/Tramites.vue new file mode 100644 index 0000000..c0be88e --- /dev/null +++ b/resources/js/Pages/App/Tramites.vue @@ -0,0 +1,581 @@ + + + diff --git a/resources/js/Pages/Auth/Login.vue b/resources/js/Pages/Auth/Login.vue index 36ce378..0b82c48 100644 --- a/resources/js/Pages/Auth/Login.vue +++ b/resources/js/Pages/Auth/Login.vue @@ -35,7 +35,7 @@ const submit = () => { @@ -63,7 +63,7 @@ const submit = () => { />
- - + --> {{ $t('auth.forgotPassword.ask') }} diff --git a/resources/js/app.js b/resources/js/app.js index 4670131..a972537 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -22,9 +22,7 @@ window.Notify = new Notify(); window.sessionFresh = new SessionFresh(); window.Swal = Swal; window.TwScreen = new TailwindScreen(); -window.darkMode = darkMode; -bootTheme(); bootSidebar(); createInertiaApp({ @@ -34,8 +32,8 @@ createInertiaApp({ title: (title) => `${title} - ${appName}`, resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), setup({ el, App, props, plugin }) { - return createApp({ - render: () => h(App, props) + return createApp({ + render: () => h(App, props) }) .use(plugin) .use(ZiggyVue, Ziggy) diff --git a/routes/api.php b/routes/api.php index eb6fa48..e4a4a92 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,5 +1,8 @@ get('/user', function (Request $request) { return $request->user(); }); + +Route::middleware('api')->group(function () { + Route::get('/reporte-obras', [ObrasController::class, 'Obras'])->middleware(['throttle:60,1']); + Route::get('/reporte-especial', [TramiteController::class, 'tramiteEspecial'])->middleware(['throttle:60,1']); + Route::get('/reporte-atencion', [AtencionController::class, 'Atencion'])->middleware(['throttle:60,1']); +}); diff --git a/routes/web.php b/routes/web.php index 2ad00e1..8bb2b6d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ middleware([ + 'auth:sanctum', + 'verified', + config('jetstream.auth_session') + ])->group(function () { + Route::inertia('/tramites', 'App/Tramites')->name('tramites'); + Route::inertia('/obras', 'App/Obras')->name('obras'); + Route::inertia('/atencion', 'App/AtencionCiudadana')->name('atencion'); + Route::get('/api/export-excel', [AtencionController::class, 'exportExcel']); +}); /** * Rutas del Dashboard - * + * * El dashboard es el panel de los usuarios de forma general */ Route::prefix('dashboard')->name('dashboard.')->middleware([ - 'auth:sanctum', - 'verified', - config('jetstream.auth_session') + 'auth:sanctum', + 'verified', + config('jetstream.auth_session') ])->group(function () { - Route::get('/welcome', [IndexController::class, 'index'])->name('index'); - Route::inertia('/changelogs', 'Dashboard/Changelogs')->name('changelogs'); - Route::inertia('/help', 'Dashboard/Help')->name('help'); + Route::get('/welcome', [IndexController::class, 'index'])->name('index'); + Route::inertia('/changelogs', 'Dashboard/Changelogs')->name('changelogs'); + Route::inertia('/help', 'Dashboard/Help')->name('help'); - # Log de Acciones - Route::resource('histories', HistoryLogController::class)->only([ - 'index', - 'store' - ]); + # Log de Acciones + Route::resource('histories', HistoryLogController::class)->only([ + 'index', + 'store' + ]); - Route::resource('notifications', NotificationController::class); - Route::prefix('/users')->name('users.')->group(function() - { - Route::get('/notifications', [UserController::class, 'getNotifications'])->name('notifications'); - }); + Route::resource('notifications', NotificationController::class); + Route::prefix('/users')->name('users.')->group(function () { + Route::get('/notifications', [UserController::class, 'getNotifications'])->name('notifications'); + }); }); /** * Rutas de administrador - * + * * Estas ubicaciones son del administrador, sin embargo el desarrollador * puede acceder a ellas. */ Route::prefix('admin')->name('admin.')->middleware([ - 'auth:sanctum', - config('jetstream.auth_session') + 'auth:sanctum', + config('jetstream.auth_session') ])->group(function () { - Route::resource('users', UserController::class); + Route::resource('users', UserController::class); - Route::prefix('/users')->name('users.')->group(function() - { - Route::get('{user}/settings', [UserController::class, 'settings'])->name('settings'); - Route::post('/password', [UserController::class, 'updatePassword'])->name('password'); - Route::post('/syncRoles', [UserController::class, 'syncRoles'])->name('syncRoles'); - }); + Route::prefix('/users')->name('users.')->group(function () { + Route::get('{user}/settings', [UserController::class, 'settings'])->name('settings'); + Route::post('/password', [UserController::class, 'updatePassword'])->name('password'); + Route::post('/syncRoles', [UserController::class, 'syncRoles'])->name('syncRoles'); + }); }); /** * Rutas solo del desarrollador - * + * * Son ubicaciones o funciones que pueden llegar a ser muy sensibles en el sistema, por lo que * solo el desarrollador debe de ser capaz de modificarlas o actualizarlas. */ Route::prefix('developer')->name('developer.')->middleware([ - 'auth:sanctum', - config('jetstream.auth_session') + 'auth:sanctum', + config('jetstream.auth_session') ])->group(function () { - Route::resource('roles', RoleController::class); + Route::resource('roles', RoleController::class); }); /** * Elementos de la plantilla - * + * * Estos son elementos que existen y pueden ser usados en la plantilla, vienen ejemplos de uso. - * + * * Estas rutas pueden ser comentadas o eliminadas cuando se finalice un proyecto. Por default estan ocultas * en el dashboard. */ Route::prefix('examples')->name('examples.')->middleware([ - 'auth:sanctum', - 'verified', - config('jetstream.auth_session') + 'auth:sanctum', + 'verified', + config('jetstream.auth_session') ])->group(function () { - Route::get('/', [ExampleIndexController::class, 'index'])->name('index'); -}); \ No newline at end of file + Route::get('/', [ExampleIndexController::class, 'index'])->name('index'); +}); diff --git a/tailwind.config.js b/tailwind.config.js index aa7b36d..19f562c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -16,7 +16,7 @@ module.exports = { theme: { extend: { fontFamily: { - sans: ['Nunito', ...defaultTheme.fontFamily.sans], + sans: ['Inter', 'ui-sans-serif', 'system-ui', 'Segoe UI', 'Roboto'], 'google-icon':['Material Icons'], 'google-icon-outlined':['Material Symbols Outlined'] },