From 64c3bbb1d7fd58b2a692e814e1a5d6b26c0b1d8d Mon Sep 17 00:00:00 2001
From: Hanzo_dev <2002samudiojohan@gmail.com>
Date: Sat, 21 Mar 2026 15:08:05 -0500
Subject: [PATCH] =?UTF-8?q?feat:=20optimizaci=C3=B3n=20de=20im=C3=A1genes?=
=?UTF-8?q?=20(WebP=20y=20AppImage),=20Lazy=20Load=20y=20LCP=20(Fase=201?=
=?UTF-8?q?=20y=202)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
frontend/index.html | 1 +
frontend/package-lock.json | 16 ++++
frontend/package.json | 1 +
frontend/src/components/AppImage.vue | 88 ++++++++++++++++++++++
frontend/src/views/AdminBusinessEditor.vue | 22 +++++-
frontend/src/views/CouponsView.vue | 11 +--
frontend/src/views/FavoritesView.vue | 16 ++--
frontend/src/views/LandingView.vue | 2 +-
8 files changed, 137 insertions(+), 20 deletions(-)
create mode 100644 frontend/src/components/AppImage.vue
diff --git a/frontend/index.html b/frontend/index.html
index a572ac0..09c3543 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -20,6 +20,7 @@
+
SIB - Sistema de Transporte
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 326ed49..fae051e 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -15,6 +15,7 @@
"@googlemaps/js-api-loader": "^2.0.2",
"@supabase/supabase-js": "^2.97.0",
"@tailwindcss/vite": "^4.2.0",
+ "browser-image-compression": "^2.0.2",
"chart.js": "^4.5.1",
"html2canvas": "^1.4.1",
"jspdf": "^4.1.0",
@@ -4016,6 +4017,15 @@
"node": "18 || 20 || >=22"
}
},
+ "node_modules/browser-image-compression": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/browser-image-compression/-/browser-image-compression-2.0.2.tgz",
+ "integrity": "sha512-pBLlQyUf6yB8SmmngrcOw3EoS4RpQ1BcylI3T9Yqn7+4nrQTXJD4sJDe5ODnJdrvNMaio5OicFo75rDyJD2Ucw==",
+ "license": "MIT",
+ "dependencies": {
+ "uzip": "0.20201231.0"
+ }
+ },
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
@@ -8230,6 +8240,12 @@
"base64-arraybuffer": "^1.0.2"
}
},
+ "node_modules/uzip": {
+ "version": "0.20201231.0",
+ "resolved": "https://registry.npmjs.org/uzip/-/uzip-0.20201231.0.tgz",
+ "integrity": "sha512-OZeJfZP+R0z9D6TmBgLq2LHzSSptGMGDGigGiEe0pr8UBe/7fdflgHlHBNDASTXB5jnFuxHpNaJywSg8YFeGng==",
+ "license": "MIT"
+ },
"node_modules/vite": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index f5f2174..524fd2e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -16,6 +16,7 @@
"@googlemaps/js-api-loader": "^2.0.2",
"@supabase/supabase-js": "^2.97.0",
"@tailwindcss/vite": "^4.2.0",
+ "browser-image-compression": "^2.0.2",
"chart.js": "^4.5.1",
"html2canvas": "^1.4.1",
"jspdf": "^4.1.0",
diff --git a/frontend/src/components/AppImage.vue b/frontend/src/components/AppImage.vue
new file mode 100644
index 0000000..4e1a4d8
--- /dev/null
+++ b/frontend/src/components/AppImage.vue
@@ -0,0 +1,88 @@
+
+
+
![]()
+
+
+
+
+
+
diff --git a/frontend/src/views/AdminBusinessEditor.vue b/frontend/src/views/AdminBusinessEditor.vue
index d30aac1..557498d 100644
--- a/frontend/src/views/AdminBusinessEditor.vue
+++ b/frontend/src/views/AdminBusinessEditor.vue
@@ -4,6 +4,22 @@ import { useRouter, useRoute } from 'vue-router';
import { businessService } from '@/services/businessService';
import type { Business } from '@/types';
import { useAuthStore } from '@/stores/auth';
+import imageCompression from 'browser-image-compression';
+
+async function compressImage(file: File) {
+ const options = {
+ maxSizeMB: 0.5,
+ maxWidthOrHeight: 1200,
+ useWebWorker: true,
+ fileType: 'image/webp'
+ };
+ try {
+ return await imageCompression(file, options);
+ } catch (err) {
+ console.error('Error compressing image', err);
+ return file; // fallback to original
+ }
+}
const router = useRouter();
const route = useRoute();
@@ -145,7 +161,8 @@ async function saveBusiness() {
if (galleryFiles.value.length > 0) {
const uploadedUrls: string[] = [];
for (const gFile of galleryFiles.value) {
- const url = await businessService.uploadImage(gFile);
+ const compressedFile = await compressImage(gFile) as File;
+ const url = await businessService.uploadImage(compressedFile);
uploadedUrls.push(url);
}
// Combinar las URL existentes ya serializadas + las nuevas subidas
@@ -154,7 +171,8 @@ async function saveBusiness() {
}
if (selectedFile.value) {
- formData.append('image', selectedFile.value);
+ const compressedMain = await compressImage(selectedFile.value) as File;
+ formData.append('image', compressedMain);
}
if (isEditing.value && businessForm.value.id) {
diff --git a/frontend/src/views/CouponsView.vue b/frontend/src/views/CouponsView.vue
index e53011d..7120e48 100644
--- a/frontend/src/views/CouponsView.vue
+++ b/frontend/src/views/CouponsView.vue
@@ -1,10 +1,10 @@
-