- Added a step in the CI/CD pipeline to generate a version based on the branch and commit history. - Updated Dockerfile to accept the application version as an argument. - Modified the configuration to retrieve the application version from the environment variable. - Created a new API endpoint to expose the application version. - Added a new Svelte component to display the application version in the UI.
184 lines
5.9 KiB
Svelte
184 lines
5.9 KiB
Svelte
<script lang="ts">
|
|
import * as Avatar from "$lib/components/ui/avatar/index.js";
|
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
|
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
|
import { useSidebar } from "$lib/components/ui/sidebar/index.js";
|
|
import BadgeCheckIcon from "@lucide/svelte/icons/badge-check";
|
|
import BellIcon from "@lucide/svelte/icons/bell";
|
|
import ChevronsUpDownIcon from "@lucide/svelte/icons/chevrons-up-down";
|
|
import CreditCardIcon from "@lucide/svelte/icons/credit-card";
|
|
import LogOutIcon from "@lucide/svelte/icons/log-out";
|
|
import LanguagesIcon from "@lucide/svelte/icons/languages";
|
|
import MoonIcon from "@lucide/svelte/icons/moon";
|
|
import SunIcon from "@lucide/svelte/icons/sun";
|
|
import { logout } from "$lib/auth";
|
|
import { cookieName } from "$lib/paraglide/runtime";
|
|
import { page } from "$app/state";
|
|
import { goto } from "$app/navigation";
|
|
import { browser } from "$app/environment";
|
|
import { getBackendAssetUrl } from "$lib/utils";
|
|
import AppVersion from "$lib/components/app-version.svelte";
|
|
|
|
let { user }: { user: { name: string; email: string; avatar: string } } = $props();
|
|
const sidebar = useSidebar();
|
|
|
|
// URL completa del avatar
|
|
let avatarUrl = $derived(getBackendAssetUrl(user.avatar) || '/avatars/default.jpg');
|
|
|
|
// Iniciales del usuario (2 primeras letras)
|
|
let initials = $derived(user.name.slice(0, 2).toUpperCase());
|
|
|
|
// Estado reactivo del idioma actual
|
|
let currentLocale = $derived(page.data.locale || 'en');
|
|
|
|
// Estado reactivo del tema actual
|
|
let isDarkMode = $state(false);
|
|
|
|
// Inicializar el estado del tema al montar el componente
|
|
$effect(() => {
|
|
if (browser) {
|
|
// Cargar la preferencia guardada o usar el valor actual del HTML
|
|
const savedTheme = localStorage.getItem('theme');
|
|
if (savedTheme) {
|
|
isDarkMode = savedTheme === 'dark';
|
|
if (savedTheme === 'dark') {
|
|
document.documentElement.classList.add('dark');
|
|
} else {
|
|
document.documentElement.classList.remove('dark');
|
|
}
|
|
} else {
|
|
// Si no hay preferencia guardada, usar el valor actual
|
|
isDarkMode = document.documentElement.classList.contains('dark');
|
|
// Guardar el estado actual
|
|
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');
|
|
}
|
|
}
|
|
});
|
|
|
|
async function handleLogout() {
|
|
await logout();
|
|
}
|
|
|
|
function navigateToAccount() {
|
|
goto('/dashboard/account');
|
|
}
|
|
|
|
function toggleLanguage() {
|
|
if (!browser) return;
|
|
|
|
// Leer la cookie actual para obtener el idioma real
|
|
const cookies = document.cookie.split(';').map(c => c.trim());
|
|
const localeCookie = cookies.find(c => c.startsWith(`${cookieName}=`));
|
|
const current = localeCookie ? localeCookie.split('=')[1] : 'en';
|
|
|
|
// Alternar el idioma
|
|
const newLocale = current === 'en' ? 'es' : 'en';
|
|
|
|
// Establecer la cookie del idioma
|
|
document.cookie = `${cookieName}=${newLocale}; path=/; max-age=34560000; SameSite=Lax`;
|
|
|
|
// Recargar la página para que el servidor procese el nuevo idioma
|
|
window.location.reload();
|
|
}
|
|
|
|
function toggleTheme() {
|
|
if (!browser) return;
|
|
|
|
const html = document.documentElement;
|
|
const newTheme = html.classList.contains('dark') ? 'light' : 'dark';
|
|
|
|
if (newTheme === 'dark') {
|
|
html.classList.add('dark');
|
|
} else {
|
|
html.classList.remove('dark');
|
|
}
|
|
|
|
// Guardar la preferencia en localStorage
|
|
localStorage.setItem('theme', newTheme);
|
|
isDarkMode = newTheme === 'dark';
|
|
}
|
|
</script>
|
|
|
|
<Sidebar.Menu>
|
|
<Sidebar.MenuItem>
|
|
<DropdownMenu.Root>
|
|
<DropdownMenu.Trigger>
|
|
{#snippet child({ props })}
|
|
<Sidebar.MenuButton
|
|
size="lg"
|
|
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
|
{...props}
|
|
>
|
|
<Avatar.Root class="size-8 rounded-lg">
|
|
<Avatar.Image src={avatarUrl} alt={user.name} />
|
|
<Avatar.Fallback class="rounded-lg">{initials}</Avatar.Fallback>
|
|
</Avatar.Root>
|
|
<div class="grid flex-1 text-left text-sm leading-tight">
|
|
<span class="truncate font-medium">{user.name}</span>
|
|
<span class="truncate text-xs">{user.email}</span>
|
|
</div>
|
|
<ChevronsUpDownIcon class="ml-auto size-4" />
|
|
</Sidebar.MenuButton>
|
|
{/snippet}
|
|
</DropdownMenu.Trigger>
|
|
<DropdownMenu.Content
|
|
class="w-(--bits-dropdown-menu-anchor-width) min-w-56 rounded-lg"
|
|
side={sidebar.isMobile ? "bottom" : "right"}
|
|
align="end"
|
|
sideOffset={4}
|
|
>
|
|
<DropdownMenu.Label class="p-0 font-normal">
|
|
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
<Avatar.Root class="size-8 rounded-lg">
|
|
<Avatar.Image src={avatarUrl} alt={user.name} />
|
|
<Avatar.Fallback class="rounded-lg">{initials}</Avatar.Fallback>
|
|
</Avatar.Root>
|
|
<div class="grid flex-1 text-left text-sm leading-tight">
|
|
<span class="truncate font-medium">{user.name}</span>
|
|
<span class="truncate text-xs">{user.email}</span>
|
|
</div>
|
|
</div>
|
|
</DropdownMenu.Label>
|
|
<DropdownMenu.Separator />
|
|
<DropdownMenu.Group>
|
|
<DropdownMenu.Item onclick={navigateToAccount}>
|
|
<BadgeCheckIcon />
|
|
Account
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Item>
|
|
<CreditCardIcon />
|
|
Billing
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Item>
|
|
<BellIcon />
|
|
Notifications
|
|
</DropdownMenu.Item>
|
|
</DropdownMenu.Group>
|
|
<DropdownMenu.Separator />
|
|
<DropdownMenu.Item onclick={toggleLanguage}>
|
|
<LanguagesIcon />
|
|
Language: {currentLocale.toUpperCase()}
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Item onclick={toggleTheme}>
|
|
{#if isDarkMode}
|
|
<SunIcon />
|
|
Light Mode
|
|
{:else}
|
|
<MoonIcon />
|
|
Dark Mode
|
|
{/if}
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Separator />
|
|
<DropdownMenu.Item onclick={handleLogout}>
|
|
<LogOutIcon />
|
|
Log out
|
|
</DropdownMenu.Item>
|
|
<DropdownMenu.Separator />
|
|
<div class="px-2 py-2">
|
|
<AppVersion />
|
|
</div>
|
|
</DropdownMenu.Content>
|
|
</DropdownMenu.Root>
|
|
</Sidebar.MenuItem>
|
|
</Sidebar.Menu>
|