Files
plantillas-proyectos/frontend/src/lib/components/sidebar/app-launcher.svelte
Kevin_Ramirez bdd089954b
Some checks failed
Build Producción & Push a Harbor / test (push) Failing after 3s
Build Producción & Push a Harbor / build (push) Has been skipped
Aduanasoft/plantillas-proyectos/pipeline/head There was a failure building this commit
feat: plantilla base workspace SaaS
2026-07-21 13:59:00 -05:00

82 lines
2.7 KiB
Svelte

<script lang="ts">
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
import LayoutGridIcon from '@lucide/svelte/icons/layout-grid';
import { workspaceAppsStore, type WorkspaceApp } from '$lib/stores/workspace-apps.svelte';
// Abrir la app seleccionada:
// - Mismo origin (es esta misma app): si trae `active_system`, cambio de sistema local
// recargando la página actual con ese parámetro (sin rebotar al Workspace). Si no, ir al dashboard.
// - Otro origin: navegar a su entrada (login_url); su propio login resuelve el SSO vía Workspace
// y aterriza en la app.
function openApp(app: WorkspaceApp) {
let target: URL;
try {
target = new URL(app.url);
} catch {
window.location.assign(app.url);
return;
}
if (target.origin !== window.location.origin) {
window.location.assign(app.url);
return;
}
const system = target.searchParams.get('active_system');
if (system) {
const current = new URL(window.location.href);
current.searchParams.set('active_system', system);
window.location.assign(current.pathname + current.search);
} else {
window.location.assign('/dashboard');
}
}
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<button
{...props}
class="inline-flex size-10 shrink-0 select-none items-center justify-center rounded-md
text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground
transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring
data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
title="Aplicaciones"
aria-label="Abrir selector de aplicaciones"
>
<LayoutGridIcon class="size-5" />
</button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content
class="w-64 rounded-xl p-3"
align="end"
side="bottom"
sideOffset={8}
>
<p class="mb-3 px-1 text-xs font-medium text-muted-foreground">Tus aplicaciones</p>
<div class="grid grid-cols-2 gap-2">
{#each workspaceAppsStore.apps as app (app.id)}
<button
onclick={() => openApp(app)}
class="flex flex-col items-center gap-1.5 rounded-lg p-3 text-center transition-colors hover:bg-accent"
>
<div class="flex size-10 items-center justify-center overflow-hidden rounded-xl bg-primary/10 text-primary">
{#if app.iconUrl}
<img src={app.iconUrl} alt={app.name} class="size-full object-cover" />
{:else}
<LayoutGridIcon class="size-5" />
{/if}
</div>
<span class="text-sm font-medium leading-tight">{app.name}</span>
{#if app.slug}
<span class="text-xs text-muted-foreground">{app.slug}</span>
{/if}
</button>
{/each}
</div>
</DropdownMenu.Content>
</DropdownMenu.Root>