Merge pull request 'feature/data-entry' (#185) from feature/data-entry into development
Reviewed-on: ADUANASOFT/anexo76#185
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||
import type { CustomsBroker } from "./columns.js";
|
||||
import DetailsDialog from "./details-dialog.svelte";
|
||||
import DeleteDialog from "./delete-dialog.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import EllipsisIcon from '@lucide/svelte/icons/ellipsis';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import type { CustomsBroker } from './columns.js';
|
||||
import DetailsDialog from './details-dialog.svelte';
|
||||
import DeleteDialog from './delete-dialog.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let {
|
||||
let {
|
||||
broker,
|
||||
onSuccess
|
||||
}: {
|
||||
}: {
|
||||
broker: CustomsBroker;
|
||||
onSuccess?: () => void;
|
||||
} = $props();
|
||||
@@ -32,10 +32,6 @@
|
||||
showDetailsDialog = true;
|
||||
}
|
||||
|
||||
function handleEdit() {
|
||||
showEditDialog = true;
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
showDeleteDialog = true;
|
||||
}
|
||||
@@ -43,33 +39,27 @@
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="relative h-8 w-8 p-0"
|
||||
>
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="relative h-8 w-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Item onclick={handleCopyKey}>
|
||||
Copiar clave
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleCopyKey}>Copiar clave</DropdownMenu.Item>
|
||||
{#if broker.tax_id}
|
||||
<DropdownMenu.Item onclick={handleCopyTaxId}>
|
||||
Copiar RFC
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleCopyTaxId}>Copiar RFC</DropdownMenu.Item>
|
||||
{/if}
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>
|
||||
Ver detalles
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={() => goto(`/dashboard/customs_brokers/edit/${broker.broker_key}`)}>
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>Ver detalles</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
onclick={() => goto(`/dashboard/customs_brokers/edit/${broker.broker_key}`)}
|
||||
>
|
||||
Editar
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleDelete} class="text-destructive">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { authStore, currentUser } from '$lib/auth';
|
||||
import * as Sheet from '$lib/components/ui/sheet';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
@@ -60,7 +59,8 @@
|
||||
title: '',
|
||||
content: '',
|
||||
updated_at: '',
|
||||
last_editor: ''
|
||||
last_editor: '',
|
||||
content_type: 'markdown'
|
||||
}; // Mock for UI
|
||||
}
|
||||
|
||||
@@ -100,8 +100,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
loadArticles();
|
||||
$effect(() => {
|
||||
if (helpStore.isOpen && articles.length === 0 && !isLoading) {
|
||||
loadArticles();
|
||||
}
|
||||
});
|
||||
|
||||
// Simple markdown renderer fallback if marked is not available
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
class="w-full max-w-2xl rounded-xl bg-white p-6 shadow-2xl dark:bg-gray-900 text-gray-900 dark:text-gray-100 max-h-[80vh] overflow-y-auto"
|
||||
role="document"
|
||||
bind:this={modalRef}
|
||||
role="presentation"
|
||||
onkeydown={handleFocusTrap}
|
||||
>
|
||||
<div
|
||||
@@ -83,16 +84,19 @@
|
||||
<!-- Global Navigation -->
|
||||
<div>
|
||||
<h3
|
||||
class="mb-3 text-sm font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400"
|
||||
class="mb-3 text-sm font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
Global Navigation (Alt)
|
||||
</h3>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
<div
|
||||
class="space-y-2 max-h-64 overflow-y-auto pr-1"
|
||||
role="region"
|
||||
aria-label="Global Navigation shortcuts"
|
||||
bind:this={globalList}
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
role="region"
|
||||
aria-label="Global Navigation Shortcuts"
|
||||
onkeydown={(event) => handleArrowScroll(event, globalList)}
|
||||
>
|
||||
{#each Object.entries(GLOBAL_CONF) as [key, route]}
|
||||
@@ -118,19 +122,22 @@
|
||||
<!-- Local Actions -->
|
||||
<div>
|
||||
<h3
|
||||
class="mb-3 text-sm font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400"
|
||||
class="mb-3 text-sm font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
Active Actions (Alt+Shift)
|
||||
</h3>
|
||||
{#if localShortcuts.length === 0}
|
||||
<p class="text-sm italic text-gray-400">No specific actions for this view.</p>
|
||||
<p class="text-sm text-gray-400 italic">No specific actions for this view.</p>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
<div
|
||||
class="space-y-2 max-h-64 overflow-y-auto pr-1"
|
||||
role="region"
|
||||
aria-label="Active Actions shortcuts"
|
||||
bind:this={localList}
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
role="region"
|
||||
aria-label="Local Action Shortcuts"
|
||||
onkeydown={(event) => handleArrowScroll(event, localList)}
|
||||
>
|
||||
{#each localShortcuts as shortcut}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import * as Collapsible from "$lib/components/ui/collapsible/index.js";
|
||||
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||
import { useSidebar } from "$lib/components/ui/sidebar/context.svelte.js";
|
||||
import ChevronRight from "@lucide/svelte/icons/chevron-right";
|
||||
import * as Collapsible from '$lib/components/ui/collapsible/index.js';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import { useSidebar } from '$lib/components/ui/sidebar/context.svelte.js';
|
||||
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||
|
||||
let {
|
||||
items,
|
||||
items
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
@@ -37,30 +37,30 @@
|
||||
}
|
||||
|
||||
function handleTriggerEnter(title: string) {
|
||||
if (sidebar.state !== "collapsed") return;
|
||||
if (sidebar.state !== 'collapsed') return;
|
||||
activeTitle = title;
|
||||
}
|
||||
|
||||
function handleTriggerLeave(event: PointerEvent, title: string) {
|
||||
if (sidebar.state !== "collapsed") return;
|
||||
|
||||
if (sidebar.state !== 'collapsed') return;
|
||||
|
||||
// Si nos movemos al contenido (o nos quedamos en el trigger), no cerramos
|
||||
if (shouldKeepOpen(event, title)) return;
|
||||
|
||||
|
||||
activeTitle = null;
|
||||
}
|
||||
|
||||
function handleContentEnter(title: string) {
|
||||
if (sidebar.state !== "collapsed") return;
|
||||
if (sidebar.state !== 'collapsed') return;
|
||||
activeTitle = title;
|
||||
}
|
||||
|
||||
function handleContentLeave(event: PointerEvent, title: string) {
|
||||
if (sidebar.state !== "collapsed") return;
|
||||
|
||||
if (sidebar.state !== 'collapsed') return;
|
||||
|
||||
// Si nos movemos de vuelta al trigger (o dentro del contenido), no cerramos
|
||||
if (shouldKeepOpen(event, title)) return;
|
||||
|
||||
|
||||
activeTitle = null;
|
||||
}
|
||||
|
||||
@@ -81,23 +81,26 @@
|
||||
<Sidebar.Menu>
|
||||
{#each items as item (item.title)}
|
||||
{#if item.items && item.items.length > 0}
|
||||
{#if sidebar.state === "collapsed"}
|
||||
{#if sidebar.state === 'collapsed'}
|
||||
<!-- Sidebar Colapsado: Dropdown controlado por eventos estrictos -->
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root
|
||||
open={activeTitle === item.title}
|
||||
onOpenChange={(v) => onOpenChange(v, item.title)}
|
||||
modal={false}
|
||||
>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<div
|
||||
id={`trigger-${item.title}`}
|
||||
class="relative z-30 w-full flex justify-center"
|
||||
class="relative z-30 flex w-full justify-center"
|
||||
onpointerenter={() => handleTriggerEnter(item.title)}
|
||||
onpointerleave={(e) => handleTriggerLeave(e, item.title)}
|
||||
>
|
||||
<Sidebar.MenuButton {...props} tooltipContent={null} class="justify-center">
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
tooltipContent={undefined}
|
||||
class="justify-center"
|
||||
>
|
||||
{#if item.icon}
|
||||
<item.icon />
|
||||
{:else}
|
||||
@@ -113,7 +116,7 @@
|
||||
side="right"
|
||||
align="start"
|
||||
sideOffset={0}
|
||||
class="z-50 w-64 rounded-lg p-0 shadow-lg overflow-visible"
|
||||
class="z-50 w-64 overflow-visible rounded-lg p-0 shadow-lg"
|
||||
id={`content-${item.title}`}
|
||||
onpointerenter={() => handleContentEnter(item.title)}
|
||||
onpointerleave={(e) => handleContentLeave(e, item.title)}
|
||||
@@ -123,7 +126,9 @@
|
||||
Posicionado con right-full para estar exactamente donde el trigger termina (offset 0).
|
||||
Usamos w-8 h-8 para coincidir con un botón de tamaño estándar de sidebar.
|
||||
-->
|
||||
<div class="absolute right-full top-0 flex items-center justify-center bg-sidebar-accent text-sidebar-accent-foreground rounded-l-lg w-8 h-8 shadow-none border border-sidebar-border border-r-0 z-50">
|
||||
<div
|
||||
class="absolute top-0 right-full z-50 flex h-8 w-8 items-center justify-center rounded-l-lg border border-r-0 border-sidebar-border bg-sidebar-accent text-sidebar-accent-foreground shadow-none"
|
||||
>
|
||||
{#if item.icon}
|
||||
<item.icon class="size-4 shrink-0" />
|
||||
{:else}
|
||||
@@ -134,20 +139,29 @@
|
||||
<!--
|
||||
Panel Principal
|
||||
-->
|
||||
<div class="w-full h-full p-1 pointer-events-auto bg-popover rounded-lg rounded-tl-none border border-sidebar-border ml-[0px]">
|
||||
|
||||
<div
|
||||
class="pointer-events-auto ml-[0px] h-full w-full rounded-lg rounded-tl-none border border-sidebar-border bg-popover p-1"
|
||||
>
|
||||
<!-- Título en el panel principal -->
|
||||
<div class="px-2 py-2 text-sm font-medium border-b text-sidebar-foreground truncate">
|
||||
<div
|
||||
class="truncate border-b px-2 py-2 text-sm font-medium text-sidebar-foreground"
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
|
||||
|
||||
<DropdownMenu.DropdownMenuGroup class="mt-1 max-h-80 overflow-y-auto">
|
||||
{#each item.items as subItem (subItem.title)}
|
||||
<DropdownMenu.DropdownMenuItem asChild>
|
||||
<a href={subItem.url} class="flex w-full items-center gap-2 overflow-hidden">
|
||||
<span class="truncate">{subItem.title}</span>
|
||||
</a>
|
||||
</DropdownMenu.DropdownMenuItem>
|
||||
<DropdownMenu.Item>
|
||||
{#snippet child({ props })}
|
||||
<a
|
||||
{...props}
|
||||
href={subItem.url}
|
||||
class="flex w-full items-center gap-2 overflow-hidden"
|
||||
>
|
||||
<span class="truncate">{subItem.title}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
</DropdownMenu.DropdownMenuGroup>
|
||||
</div>
|
||||
@@ -209,4 +223,3 @@
|
||||
{/each}
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Group>
|
||||
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
<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";
|
||||
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());
|
||||
|
||||
@@ -65,34 +65,34 @@
|
||||
|
||||
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 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';
|
||||
@@ -123,7 +123,7 @@
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-(--bits-dropdown-menu-anchor-width) min-w-56 rounded-lg"
|
||||
side={sidebar.isMobile ? "bottom" : "right"}
|
||||
side={sidebar.isMobile ? 'bottom' : 'right'}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
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 ChevronsUpDownIcon from "@lucide/svelte/icons/chevrons-up-down";
|
||||
import BuildingIcon from "@lucide/svelte/icons/building";
|
||||
import CheckIcon from "@lucide/svelte/icons/check";
|
||||
import { companyStore } from "$lib/stores/company.svelte";
|
||||
import { getBackendAssetUrl } from "$lib/utils";
|
||||
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 ChevronsUpDownIcon from '@lucide/svelte/icons/chevrons-up-down';
|
||||
import BuildingIcon from '@lucide/svelte/icons/building';
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import { getBackendAssetUrl } from '$lib/utils';
|
||||
|
||||
const sidebar = useSidebar();
|
||||
|
||||
@@ -14,95 +14,93 @@
|
||||
let activeCompanyLogoUrl = $derived(
|
||||
companyStore.activeCompany?.logo
|
||||
? getBackendAssetUrl(
|
||||
`v1/a76/company/${companyStore.activeCompany.id}/logo/image?t=${Date.now()}`
|
||||
)
|
||||
`v1/a76/company/${companyStore.activeCompany.id}/logo/image?t=${Date.now()}`
|
||||
)
|
||||
: null
|
||||
);
|
||||
|
||||
// Iniciales de la compañía activa (2 primeras letras)
|
||||
let activeCompanyInitials = $derived(
|
||||
companyStore.activeCompany?.name?.slice(0, 2).toUpperCase() || "CO"
|
||||
companyStore.activeCompany?.name?.slice(0, 2).toUpperCase() || 'CO'
|
||||
);
|
||||
|
||||
// Fallback en degradé cuando no hay logo cargado
|
||||
const fallbackBg =
|
||||
"radial-gradient(circle at 30% 30%, rgba(0,0,0,0.08), rgba(0,0,0,0.12)), linear-gradient(135deg, rgba(99,102,241,0.12), rgba(14,165,233,0.18))";
|
||||
'radial-gradient(circle at 30% 30%, rgba(0,0,0,0.08), rgba(0,0,0,0.12)), linear-gradient(135deg, rgba(99,102,241,0.12), rgba(14,165,233,0.18))';
|
||||
|
||||
const logoBg = $derived(
|
||||
activeCompanyLogoUrl
|
||||
? `url(${activeCompanyLogoUrl})`
|
||||
: fallbackBg
|
||||
);
|
||||
const logoBg = $derived(activeCompanyLogoUrl ? `url(${activeCompanyLogoUrl})` : fallbackBg);
|
||||
</script>
|
||||
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
size="lg"
|
||||
class="relative overflow-hidden data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground group-has-data-[state=collapsed]/sidebar-wrapper:aspect-square group-has-data-[state=collapsed]/sidebar-wrapper:h-10 group-has-data-[state=collapsed]/sidebar-wrapper:w-10 group-has-data-[state=collapsed]/sidebar-wrapper:rounded-lg group-has-data-[state=collapsed]/sidebar-wrapper:justify-center group-has-data-[state=collapsed]/sidebar-wrapper:gap-0"
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
size="lg"
|
||||
class="relative overflow-hidden group-has-data-[state=collapsed]/sidebar-wrapper:aspect-square group-has-data-[state=collapsed]/sidebar-wrapper:h-10 group-has-data-[state=collapsed]/sidebar-wrapper:w-10 group-has-data-[state=collapsed]/sidebar-wrapper:justify-center group-has-data-[state=collapsed]/sidebar-wrapper:gap-0 group-has-data-[state=collapsed]/sidebar-wrapper:rounded-lg data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<div
|
||||
class="flex aspect-square size-8 items-center justify-center overflow-hidden rounded-lg bg-sidebar-primary text-sidebar-primary-foreground group-has-data-[state=collapsed]/sidebar-wrapper:hidden"
|
||||
>
|
||||
<div
|
||||
class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg overflow-hidden group-has-data-[state=collapsed]/sidebar-wrapper:hidden"
|
||||
>
|
||||
{#if activeCompanyLogoUrl}
|
||||
<img
|
||||
src={activeCompanyLogoUrl}
|
||||
alt={companyStore.activeCompany?.name || "Company"}
|
||||
class="size-full object-cover"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = "none";
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<span class="text-sm font-semibold text-white">{activeCompanyInitials}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight min-w-0 group-has-data-[state=collapsed]/sidebar-wrapper:hidden">
|
||||
<span class="truncate font-medium">
|
||||
{companyStore.activeCompany?.name || "Seleccionar compañía"}
|
||||
{#if activeCompanyLogoUrl}
|
||||
<img
|
||||
src={activeCompanyLogoUrl}
|
||||
alt={companyStore.activeCompany?.name || 'Company'}
|
||||
class="size-full object-cover"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<span class="text-sm font-semibold text-white">{activeCompanyInitials}</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
class="grid min-w-0 flex-1 text-left text-sm leading-tight group-has-data-[state=collapsed]/sidebar-wrapper:hidden"
|
||||
>
|
||||
<span class="truncate font-medium">
|
||||
{companyStore.activeCompany?.name || 'Seleccionar compañía'}
|
||||
</span>
|
||||
{#if companyStore.activeCompany?.rfc}
|
||||
<span class="truncate text-xs text-muted-foreground">
|
||||
{companyStore.activeCompany.rfc}
|
||||
</span>
|
||||
{#if companyStore.activeCompany?.rfc}
|
||||
<span class="truncate text-xs text-muted-foreground">
|
||||
{companyStore.activeCompany.rfc}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<ChevronsUpDownIcon class="ml-auto size-4 group-has-data-[state=collapsed]/sidebar-wrapper:hidden" />
|
||||
{/if}
|
||||
</div>
|
||||
<ChevronsUpDownIcon
|
||||
class="ml-auto size-4 group-has-data-[state=collapsed]/sidebar-wrapper:hidden"
|
||||
/>
|
||||
|
||||
<!-- Isotipo compacto visible solo en modo colapsado -->
|
||||
<div
|
||||
class="relative z-10 hidden size-8 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-foreground text-sm font-semibold shadow-sm ring-1 ring-sidebar-border/40 group-has-data-[state=collapsed]/sidebar-wrapper:flex overflow-hidden"
|
||||
>
|
||||
{#if activeCompanyLogoUrl}
|
||||
<img
|
||||
src={activeCompanyLogoUrl}
|
||||
alt={companyStore.activeCompany?.name || "Company"}
|
||||
class="size-full object-cover"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = "none";
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
{activeCompanyInitials}
|
||||
{/if}
|
||||
</div>
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<!-- Isotipo compacto visible solo en modo colapsado -->
|
||||
<div
|
||||
class="relative z-10 hidden size-8 items-center justify-center overflow-hidden rounded-md bg-sidebar-primary text-sm font-semibold text-sidebar-foreground shadow-sm ring-1 ring-sidebar-border/40 group-has-data-[state=collapsed]/sidebar-wrapper:flex"
|
||||
>
|
||||
{#if activeCompanyLogoUrl}
|
||||
<img
|
||||
src={activeCompanyLogoUrl}
|
||||
alt={companyStore.activeCompany?.name || 'Company'}
|
||||
class="size-full object-cover"
|
||||
onerror={(e) => {
|
||||
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
{activeCompanyInitials}
|
||||
{/if}
|
||||
</div>
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-(--bits-dropdown-menu-anchor-width) min-w-56 rounded-lg"
|
||||
align="start"
|
||||
side={sidebar.isMobile ? "bottom" : "right"}
|
||||
side={sidebar.isMobile ? 'bottom' : 'right'}
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenu.Label class="text-muted-foreground text-xs">
|
||||
Mis Compañías
|
||||
</DropdownMenu.Label>
|
||||
|
||||
<DropdownMenu.Label class="text-xs text-muted-foreground">Mis Compañías</DropdownMenu.Label>
|
||||
|
||||
{#if companyStore.loading}
|
||||
<DropdownMenu.Item disabled class="gap-2 p-2">
|
||||
<span class="text-muted-foreground">Cargando...</span>
|
||||
@@ -113,25 +111,28 @@
|
||||
</DropdownMenu.Item>
|
||||
{:else}
|
||||
{#each companyStore.companies as company, index (company.id)}
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => companyStore.setActiveCompany(company)}
|
||||
class="gap-2 p-2 cursor-pointer"
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => companyStore.setActiveCompany(company)}
|
||||
class="cursor-pointer gap-2 p-2"
|
||||
>
|
||||
<div class="flex size-6 items-center justify-center rounded-md border overflow-hidden">
|
||||
<div
|
||||
class="flex size-6 items-center justify-center overflow-hidden rounded-md border"
|
||||
>
|
||||
{#if company.logo}
|
||||
<img
|
||||
src={getBackendAssetUrl(`v1/a76/company/${company.id}/logo/image`)}
|
||||
<img
|
||||
src={getBackendAssetUrl(`v1/a76/company/${company.id}/logo/image`)}
|
||||
alt={company.name}
|
||||
class="size-full rounded object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<span class="text-xs font-semibold">{company.name.slice(0, 2).toUpperCase()}</span>
|
||||
<span class="text-xs font-semibold">{company.name.slice(0, 2).toUpperCase()}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-1 flex-col min-w-0">
|
||||
<span class="font-medium truncate">{company.name}</span>
|
||||
<div class="flex min-w-0 flex-1 flex-col">
|
||||
<span class="truncate font-medium">{company.name}</span>
|
||||
{#if company.rfc}
|
||||
<span class="text-xs text-muted-foreground truncate">{company.rfc}</span>
|
||||
<span class="truncate text-xs text-muted-foreground">{company.rfc}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if companyStore.activeCompany?.id === company.id}
|
||||
|
||||
@@ -1,47 +1,45 @@
|
||||
<script lang="ts" module>
|
||||
import { tv, type VariantProps } from "tailwind-variants";
|
||||
import { tv, type VariantProps } from 'tailwind-variants';
|
||||
|
||||
export const sidebarMenuButtonVariants = tv({
|
||||
base: "peer/menu-button outline-hidden ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground group-has-data-[sidebar=menu-action]/menu-item:pr-8 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
base: 'peer/menu-button outline-hidden ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground group-has-data-[sidebar=menu-action]/menu-item:pr-8 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
||||
outline:
|
||||
"bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",
|
||||
'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)]'
|
||||
},
|
||||
size: {
|
||||
default: "h-8 text-sm",
|
||||
sm: "h-7 text-xs",
|
||||
lg: "group-data-[collapsible=icon]:p-0! h-12 text-sm",
|
||||
},
|
||||
default: 'h-8 text-sm',
|
||||
sm: 'h-7 text-xs',
|
||||
lg: 'group-data-[collapsible=icon]:p-0! h-12 text-sm'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type SidebarMenuButtonVariant = VariantProps<
|
||||
typeof sidebarMenuButtonVariants
|
||||
>["variant"];
|
||||
export type SidebarMenuButtonSize = VariantProps<typeof sidebarMenuButtonVariants>["size"];
|
||||
export type SidebarMenuButtonVariant = VariantProps<typeof sidebarMenuButtonVariants>['variant'];
|
||||
export type SidebarMenuButtonSize = VariantProps<typeof sidebarMenuButtonVariants>['size'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
|
||||
import { cn, type WithElementRef, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
import { mergeProps } from "bits-ui";
|
||||
import type { ComponentProps, Snippet } from "svelte";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { useSidebar } from "./context.svelte.js";
|
||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||
import { cn, type WithElementRef, type WithoutChildrenOrChild } from '$lib/utils.js';
|
||||
import { mergeProps } from 'bits-ui';
|
||||
import type { ComponentProps, Snippet } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { useSidebar } from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
child,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
isActive = false,
|
||||
tooltipContent,
|
||||
tooltipContentProps,
|
||||
@@ -59,11 +57,11 @@
|
||||
|
||||
const buttonProps = $derived({
|
||||
class: cn(sidebarMenuButtonVariants({ variant, size }), className),
|
||||
"data-slot": "sidebar-menu-button",
|
||||
"data-sidebar": "menu-button",
|
||||
"data-size": size,
|
||||
"data-active": isActive,
|
||||
...restProps,
|
||||
'data-slot': 'sidebar-menu-button',
|
||||
'data-sidebar': 'menu-button',
|
||||
'data-size': size,
|
||||
'data-active': isActive,
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -90,10 +88,10 @@
|
||||
<Tooltip.Content
|
||||
side="right"
|
||||
align="center"
|
||||
hidden={sidebar.state !== "collapsed" || sidebar.isMobile}
|
||||
hidden={sidebar.state !== 'collapsed' || sidebar.isMobile}
|
||||
{...tooltipContentProps}
|
||||
>
|
||||
{#if typeof tooltipContent === "string"}
|
||||
{#if typeof tooltipContent === 'string'}
|
||||
{tooltipContent}
|
||||
{:else if tooltipContent}
|
||||
{@render tooltipContent()}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { Tooltip as TooltipPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { Tooltip as TooltipPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
sideOffset = 0,
|
||||
side = "top",
|
||||
side = 'top',
|
||||
children,
|
||||
arrowClasses,
|
||||
...restProps
|
||||
@@ -22,7 +22,7 @@
|
||||
{sideOffset}
|
||||
{side}
|
||||
class={cn(
|
||||
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--bits-tooltip-content-transform-origin) z-50 w-fit text-balance rounded-md px-3 py-1.5 text-xs",
|
||||
'animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--bits-tooltip-content-transform-origin) rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
@@ -32,11 +32,11 @@
|
||||
{#snippet child({ props })}
|
||||
<div
|
||||
class={cn(
|
||||
"bg-primary z-50 size-2.5 rotate-45 rounded-[2px]",
|
||||
"data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]",
|
||||
"data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]",
|
||||
"data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2",
|
||||
"data-[side=left]:-translate-y-[calc(50%_-_3px)]",
|
||||
'z-50 size-2.5 rotate-45 rounded-[2px] bg-primary',
|
||||
'data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]',
|
||||
'data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]',
|
||||
'data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2',
|
||||
'data-[side=left]:-translate-y-[calc(50%_-_3px)]',
|
||||
arrowClasses
|
||||
)}
|
||||
{...props}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user