diff --git a/nuxt4/app/components/auth/ApplicationsList.vue b/nuxt4/app/components/auth/ApplicationsList.vue
index 4e3437c..3fc666a 100644
--- a/nuxt4/app/components/auth/ApplicationsList.vue
+++ b/nuxt4/app/components/auth/ApplicationsList.vue
@@ -186,6 +186,13 @@ const getAppIconUrl = (app: Application): string | null => {
return null
}
+ // Intentar primero ícono local basado en slug
+ if (app.slug) {
+ const localIconPath = `/app-icons/${app.slug}.svg`
+ // Retornar path local - si falla se manejará en handleIconError
+ return localIconPath
+ }
+
// Si tiene meta_icon de Authentik, usarlo
if (app.icon) {
// Si es una URL completa
@@ -216,8 +223,47 @@ const handleIconError = (event: Event, app: Application) => {
const imgElement = event.target as HTMLImageElement
const currentSrc = imgElement.src
- // Marcar como fallido
- failedIcons.value.add(app.pk)
+ // Cascada de fallback para íconos:
+ // 1. Ícono local (/app-icons/${slug}.svg)
+ // 2. Meta_icon de Authentik
+ // 3. apple-touch-icon.png del dominio
+ // 4. favicon.ico del dominio
+ // 5. Ícono por defecto
+
+ // Si estábamos intentando el ícono local, intentar meta_icon o apple-touch-icon
+ if (currentSrc.includes('/app-icons/')) {
+ failedIcons.value.delete(app.pk) // Permitir siguiente intento
+
+ if (app.icon) {
+ // Tiene meta_icon de Authentik
+ if (app.icon.startsWith('http://') || app.icon.startsWith('https://')) {
+ imgElement.src = app.icon
+ } else {
+ try {
+ const url = new URL(app.launchUrl)
+ imgElement.src = `${url.origin}${app.icon.startsWith('/') ? '' : '/'}${app.icon}`
+ } catch {
+ // Si falla el parsing de URL, intentar apple-touch-icon
+ try {
+ const url = new URL(app.launchUrl)
+ imgElement.src = `${url.origin}/apple-touch-icon.png`
+ } catch {
+ failedIcons.value.add(app.pk)
+ }
+ }
+ }
+ return
+ }
+
+ // No tiene meta_icon, intentar apple-touch-icon
+ try {
+ const url = new URL(app.launchUrl)
+ imgElement.src = `${url.origin}/apple-touch-icon.png`
+ return
+ } catch {
+ failedIcons.value.add(app.pk)
+ }
+ }
// Si estábamos intentando apple-touch-icon, intentar favicon.ico
if (currentSrc.includes('apple-touch-icon')) {
@@ -227,11 +273,12 @@ const handleIconError = (event: Event, app: Application) => {
failedIcons.value.delete(app.pk) // Dar otra oportunidad
return
} catch {
- // Si falla, el v-else mostrará el icono por defecto
+ failedIcons.value.add(app.pk)
}
}
- // Ocultar la imagen para que se muestre el icono por defecto
+ // Marcar como fallido y ocultar imagen para mostrar ícono por defecto
+ failedIcons.value.add(app.pk)
imgElement.style.display = 'none'
}
diff --git a/nuxt4/public/app-icons/airbyte.svg b/nuxt4/public/app-icons/airbyte.svg
new file mode 100644
index 0000000..68f552a
--- /dev/null
+++ b/nuxt4/public/app-icons/airbyte.svg
@@ -0,0 +1,11 @@
+
diff --git a/nuxt4/public/app-icons/devserver.svg b/nuxt4/public/app-icons/devserver.svg
new file mode 100644
index 0000000..c0dafaf
--- /dev/null
+++ b/nuxt4/public/app-icons/devserver.svg
@@ -0,0 +1,8 @@
+
diff --git a/nuxt4/public/app-icons/metabase.svg b/nuxt4/public/app-icons/metabase.svg
new file mode 100644
index 0000000..8459c49
--- /dev/null
+++ b/nuxt4/public/app-icons/metabase.svg
@@ -0,0 +1,5 @@
+
diff --git a/nuxt4/public/app-icons/portainer.svg b/nuxt4/public/app-icons/portainer.svg
new file mode 100644
index 0000000..d9601e0
--- /dev/null
+++ b/nuxt4/public/app-icons/portainer.svg
@@ -0,0 +1,7 @@
+
diff --git a/nuxt4/public/app-icons/supabase.svg b/nuxt4/public/app-icons/supabase.svg
new file mode 100644
index 0000000..1a5e876
--- /dev/null
+++ b/nuxt4/public/app-icons/supabase.svg
@@ -0,0 +1,10 @@
+
diff --git a/nuxt4/public/app-icons/traefik.svg b/nuxt4/public/app-icons/traefik.svg
new file mode 100644
index 0000000..54c5265
--- /dev/null
+++ b/nuxt4/public/app-icons/traefik.svg
@@ -0,0 +1,5 @@
+
diff --git a/nuxt4/public/app-icons/ubuntu.svg b/nuxt4/public/app-icons/ubuntu.svg
new file mode 100644
index 0000000..2f4cd32
--- /dev/null
+++ b/nuxt4/public/app-icons/ubuntu.svg
@@ -0,0 +1,7 @@
+