nav activa con activeTab, SectionCards dinámicas, fondo light 0.95 oklch
This commit is contained in:
@@ -23,6 +23,14 @@ import {
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
defineProps<{
|
||||
activeTab?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:activeTab': [value: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,12 +42,18 @@ import {
|
||||
</div>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain />
|
||||
<NavDocuments />
|
||||
<NavMain
|
||||
:active-tab="activeTab"
|
||||
@update:active-tab="emit('update:activeTab', $event)"
|
||||
/>
|
||||
<NavDocuments
|
||||
:active-tab="activeTab"
|
||||
@update:active-tab="emit('update:activeTab', $event)"
|
||||
/>
|
||||
<NavSecondary :items="[]" class="mt-auto" />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<NavUser />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -26,6 +26,14 @@ const documentItems = [
|
||||
{ name: 'nav.wordAssistant', icon: IconFileWord, id: 'word-assistant' },
|
||||
{ name: 'nav.templates', icon: IconFileDescription, id: 'templates' },
|
||||
]
|
||||
|
||||
defineProps<{
|
||||
activeTab?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:activeTab': [value: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -33,13 +41,14 @@ const documentItems = [
|
||||
<SidebarGroupLabel>{{ t('nav.documents') }}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in documentItems" :key="item.id">
|
||||
<SidebarMenuButton as-child>
|
||||
<a href="#">
|
||||
<component :is="item.icon" />
|
||||
<span>{{ t(item.name) }}</span>
|
||||
</a>
|
||||
<SidebarMenuButton
|
||||
:is-active="activeTab === item.id"
|
||||
@click="emit('update:activeTab', item.id)"
|
||||
>
|
||||
<component :is="item.icon" />
|
||||
<span>{{ t(item.name) }}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -30,6 +30,11 @@ const mainNavItems = [
|
||||
|
||||
defineProps<{
|
||||
items?: { title: string; url: string; icon?: Component }[]
|
||||
activeTab?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:activeTab': [value: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -49,7 +54,11 @@ defineProps<{
|
||||
</SidebarMenu>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in mainNavItems" :key="item.id">
|
||||
<SidebarMenuButton :tooltip="t(item.title)">
|
||||
<SidebarMenuButton
|
||||
:tooltip="t(item.title)"
|
||||
:is-active="activeTab === item.id"
|
||||
@click="emit('update:activeTab', item.id)"
|
||||
>
|
||||
<component :is="item.icon" v-if="item.icon" />
|
||||
<span>{{ t(item.title) }}</span>
|
||||
</SidebarMenuButton>
|
||||
@@ -57,4 +66,4 @@ defineProps<{
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -10,95 +10,41 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
|
||||
interface CardData {
|
||||
label: string
|
||||
value: string
|
||||
trend: string
|
||||
up: boolean
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
cards?: CardData[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
<Card class="@container/card">
|
||||
<Card v-for="(card, i) in cards" :key="i" class="@container/card">
|
||||
<CardHeader>
|
||||
<CardDescription>Total Revenue</CardDescription>
|
||||
<CardDescription>{{ card.label }}</CardDescription>
|
||||
<CardTitle class="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
||||
$1,250.00
|
||||
{{ card.value }}
|
||||
</CardTitle>
|
||||
<CardAction>
|
||||
<Badge variant="outline">
|
||||
<IconTrendingUp />
|
||||
+12.5%
|
||||
<IconTrendingUp v-if="card.up" />
|
||||
<IconTrendingDown v-else />
|
||||
{{ card.trend }}
|
||||
</Badge>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter class="flex-col items-start gap-1.5 text-sm">
|
||||
<div class="line-clamp-1 flex gap-2 font-medium">
|
||||
Trending up this month <IconTrendingUp class="size-4" />
|
||||
{{ card.up ? 'Trending up' : 'Trending down' }}
|
||||
</div>
|
||||
<div class="text-muted-foreground">
|
||||
Visitors for the last 6 months
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card class="@container/card">
|
||||
<CardHeader>
|
||||
<CardDescription>New Customers</CardDescription>
|
||||
<CardTitle class="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
||||
1,234
|
||||
</CardTitle>
|
||||
<CardAction>
|
||||
<Badge variant="outline">
|
||||
<IconTrendingDown />
|
||||
-20%
|
||||
</Badge>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter class="flex-col items-start gap-1.5 text-sm">
|
||||
<div class="line-clamp-1 flex gap-2 font-medium">
|
||||
Down 20% this period <IconTrendingDown class="size-4" />
|
||||
</div>
|
||||
<div class="text-muted-foreground">
|
||||
Acquisition needs attention
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card class="@container/card">
|
||||
<CardHeader>
|
||||
<CardDescription>Active Accounts</CardDescription>
|
||||
<CardTitle class="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
||||
45,678
|
||||
</CardTitle>
|
||||
<CardAction>
|
||||
<Badge variant="outline">
|
||||
<IconTrendingUp />
|
||||
+12.5%
|
||||
</Badge>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter class="flex-col items-start gap-1.5 text-sm">
|
||||
<div class="line-clamp-1 flex gap-2 font-medium">
|
||||
Strong user retention <IconTrendingUp class="size-4" />
|
||||
</div>
|
||||
<div class="text-muted-foreground">
|
||||
Engagement exceed targets
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card class="@container/card">
|
||||
<CardHeader>
|
||||
<CardDescription>Growth Rate</CardDescription>
|
||||
<CardTitle class="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
|
||||
4.5%
|
||||
</CardTitle>
|
||||
<CardAction>
|
||||
<Badge variant="outline">
|
||||
<IconTrendingUp />
|
||||
+4.5%
|
||||
</Badge>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter class="flex-col items-start gap-1.5 text-sm">
|
||||
<div class="line-clamp-1 flex gap-2 font-medium">
|
||||
Steady performance increase <IconTrendingUp class="size-4" />
|
||||
</div>
|
||||
<div class="text-muted-foreground">
|
||||
Meets growth projections
|
||||
{{ card.label }}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
@@ -149,9 +149,13 @@ const groupedResults = computed(() => {
|
||||
<path d="M12 14.3l7.37 -7.37" />
|
||||
<path d="M12 19.6l8.85 -8.85" />
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-[18px]">
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4.5">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
|
||||
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
|
||||
<path d="M12 3l0 18" />
|
||||
<path d="M12 9l4.65 -4.65" />
|
||||
<path d="M12 14.3l7.37 -7.37" />
|
||||
<path d="M12 19.6l8.85 -8.85" />
|
||||
</svg>
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user