Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-03-07 23:09:13 -06:00
parent 2ac07b55b6
commit 5478a88f2b
14 changed files with 799 additions and 170 deletions

View File

@@ -16,6 +16,7 @@
// Obtener datos del usuario desde el contexto (viene de Keycloak vía +layout.server.ts)
const userData = getContext<any>('user');
const userTenants = getContext<{ id: number; name: string; slug: string }[]>('userTenants') ?? [];
// Obtener datos del sidebar con traducciones
const sidebarData = getSidebarData();
@@ -42,7 +43,7 @@
<NavProjects projects={data.projects} />
</Sidebar.Content>
<Sidebar.Footer>
<NavUser user={data.user} />
<NavUser user={data.user} tenants={userTenants} />
</Sidebar.Footer>
<Sidebar.Rail />
</Sidebar.Root>

View File

@@ -11,6 +11,9 @@
import LanguagesIcon from '@lucide/svelte/icons/languages';
import MoonIcon from '@lucide/svelte/icons/moon';
import SunIcon from '@lucide/svelte/icons/sun';
import ArrowLeftRightIcon from '@lucide/svelte/icons/arrow-left-right';
import BuildingIcon from '@lucide/svelte/icons/building';
import CheckIcon from '@lucide/svelte/icons/check';
import { logout } from '$lib/auth';
import { cookieName } from '$lib/paraglide/runtime';
import { page } from '$app/state';
@@ -19,9 +22,18 @@
import { getBackendAssetUrl } from '$lib/utils';
import AppVersion from '$lib/components/app-version.svelte';
let { user }: { user: { name: string; email: string; avatar: string } } = $props();
let { user, tenants = [] }: {
user: { name: string; email: string; avatar: string };
tenants: { id: number; name: string; slug: string }[];
} = $props();
const sidebar = useSidebar();
// Tenant activo (viene en el JWT como atributo tenant_slug)
let currentTenantSlug = $derived((page.data.user as any)?.tenant_slug ?? '');
// Estado de cambio de tenant
let switchingTenant = $state(false);
// URL completa del avatar
let avatarUrl = $derived(getBackendAssetUrl(user.avatar) || '/avatars/default.jpg');
@@ -59,6 +71,26 @@
await logout();
}
async function switchTenant(slug: string) {
if (slug === currentTenantSlug || switchingTenant) return;
switchingTenant = true;
try {
const res = await fetch('/api-sveltekit/auth/switch-tenant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tenant_slug: slug })
});
if (res.ok) {
// Recargar el dashboard con los nuevos tokens
window.location.href = '/dashboard';
} else {
console.error('Error al cambiar de organización');
}
} finally {
switchingTenant = false;
}
}
function navigateToAccount() {
goto('/dashboard/account');
}
@@ -169,6 +201,40 @@
{/if}
</DropdownMenu.Item>
<DropdownMenu.Separator />
{#if tenants.length > 1}
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger class="gap-2">
{#if switchingTenant}
<svg class="animate-spin size-4 shrink-0" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
</svg>
{:else}
<ArrowLeftRightIcon class="size-4" />
{/if}
Cambiar organización
</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent class="min-w-44">
<DropdownMenu.Label class="text-xs text-muted-foreground pb-1">
Mis organizaciones
</DropdownMenu.Label>
{#each tenants as tenant (tenant.id)}
<DropdownMenu.Item
class="gap-2 cursor-pointer"
disabled={switchingTenant}
onclick={() => switchTenant(tenant.slug)}
>
<BuildingIcon class="size-4 shrink-0 text-muted-foreground" />
<span class="flex-1 truncate">{tenant.name}</span>
{#if tenant.slug === currentTenantSlug}
<CheckIcon class="size-3.5 shrink-0 text-blue-600" />
{/if}
</DropdownMenu.Item>
{/each}
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
{/if}
<DropdownMenu.Separator />
<DropdownMenu.Item onclick={handleLogout}>
<LogOutIcon />
Log out