feat: dashboard on landing page
This commit is contained in:
parent
4a748fe14a
commit
8857f489bb
|
|
@ -13,6 +13,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@morev/vue-transitions": "^2.3.6",
|
"@morev/vue-transitions": "^2.3.6",
|
||||||
"@tanstack/vue-table": "^8.9.3",
|
"@tanstack/vue-table": "^8.9.3",
|
||||||
|
"@unovis/ts": "^1.2.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||||
"@vueuse/core": "^10.2.1",
|
"@vueuse/core": "^10.2.1",
|
||||||
"class-variance-authority": "^0.6.1",
|
"class-variance-authority": "^0.6.1",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
layout: docs
|
|
||||||
---
|
|
||||||
|
|
||||||
docs laytou please?
|
|
||||||
|
|
@ -2,4 +2,8 @@
|
||||||
home: true
|
home: true
|
||||||
---
|
---
|
||||||
|
|
||||||
this is main content
|
<script setup>
|
||||||
|
import Dashboard from "@/examples/dashboard/Example.vue"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dashboard />
|
||||||
218
apps/www/src/examples/dashboard/Example.vue
Normal file
218
apps/www/src/examples/dashboard/Example.vue
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Overview from './components/Overview.vue'
|
||||||
|
import DateRangePicker from './components/DateRangePicker.vue'
|
||||||
|
import MainNav from './components/MainNav.vue'
|
||||||
|
import RecentSales from './components/RecentSales.vue'
|
||||||
|
import Search from './components/Search.vue'
|
||||||
|
import TeamSwitcher from './components/TeamSwitcher.vue'
|
||||||
|
import UserNav from './components/UserNav.vue'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/lib/registry/default/ui/card'
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/tabs'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="md:hidden">
|
||||||
|
<img
|
||||||
|
src="/examples/dashboard-light.png"
|
||||||
|
width="1280"
|
||||||
|
height="866"
|
||||||
|
alt="Dashboard"
|
||||||
|
class="block dark:hidden"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/examples/dashboard-dark.png"
|
||||||
|
width="{1280}"
|
||||||
|
height="{866}"
|
||||||
|
alt="Dashboard"
|
||||||
|
class="hidden dark:block"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="hidden flex-col md:flex">
|
||||||
|
<div class="border-b">
|
||||||
|
<div class="flex h-16 items-center px-4">
|
||||||
|
<TeamSwitcher />
|
||||||
|
<MainNav class="mx-6" />
|
||||||
|
<div class="ml-auto flex items-center space-x-4">
|
||||||
|
<Search />
|
||||||
|
<UserNav />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 space-y-4 p-8 pt-6">
|
||||||
|
<div class="flex items-center justify-between space-y-2">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight">
|
||||||
|
Dashboard
|
||||||
|
</h2>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<DateRangePicker />
|
||||||
|
<Button>Download</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Tabs default-value="overview" class="space-y-4">
|
||||||
|
<TabsList>
|
||||||
|
<TabsTrigger value="overview">
|
||||||
|
Overview
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="analytics" disabled>
|
||||||
|
Analytics
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="reports" disabled>
|
||||||
|
Reports
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="notifications" disabled>
|
||||||
|
Notifications
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="overview" class="space-y-4">
|
||||||
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle class="text-sm font-medium">
|
||||||
|
Total Revenue
|
||||||
|
</CardTitle>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
class="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
||||||
|
</svg>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div class="text-2xl font-bold">
|
||||||
|
$45,231.89
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
+20.1% from last month
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle class="text-sm font-medium">
|
||||||
|
Subscriptions
|
||||||
|
</CardTitle>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
class="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="9" cy="7" r="4" />
|
||||||
|
<path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
|
||||||
|
</svg>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div class="text-2xl font-bold">
|
||||||
|
+2350
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
+180.1% from last month
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle class="text-sm font-medium">
|
||||||
|
Sales
|
||||||
|
</CardTitle>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
class="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<rect width="20" height="14" x="2" y="5" rx="2" />
|
||||||
|
<path d="M2 10h20" />
|
||||||
|
</svg>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div class="text-2xl font-bold">
|
||||||
|
+12,234
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
+19% from last month
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle class="text-sm font-medium">
|
||||||
|
Active Now
|
||||||
|
</CardTitle>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
class="h-4 w-4 text-muted-foreground"
|
||||||
|
>
|
||||||
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
||||||
|
</svg>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div class="text-2xl font-bold">
|
||||||
|
+573
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
+201 since last hour
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
|
||||||
|
<Card class="col-span-4">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Overview</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent class="pl-2">
|
||||||
|
<Overview />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card class="col-span-3">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Sales</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
You made 265 sales this month.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<RecentSales />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { addDays, format } from 'date-fns'
|
||||||
|
import { Calendar as CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { Calendar } from '@/lib/registry/default/ui/calendar'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
const date = ref({
|
||||||
|
start: new Date(2023, 0, 20),
|
||||||
|
end: addDays(new Date(2023, 0, 20), 20),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('grid gap-2', $attrs.class ?? '')">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
id="date"
|
||||||
|
:variant="'outline'"
|
||||||
|
:class="cn(
|
||||||
|
'w-[260px] justify-start text-left font-normal',
|
||||||
|
!date && 'text-muted-foreground',
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{{ date.start ? (
|
||||||
|
date.end ? `${format(date.start, 'LLL dd, y')} - ${format(date.end, 'LLL dd, y')}`
|
||||||
|
: format(date.start, 'LLL dd, y')
|
||||||
|
) : 'Pick a date' }}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0" :align="'end'" :avoid-collisions="true">
|
||||||
|
<Calendar
|
||||||
|
v-model.range="date"
|
||||||
|
:columns="2"
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
34
apps/www/src/examples/dashboard/components/MainNav.vue
Normal file
34
apps/www/src/examples/dashboard/components/MainNav.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<nav
|
||||||
|
:class="cn('flex items-center space-x-4 lg:space-x-6', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href="/examples/dashboard"
|
||||||
|
class="text-sm font-medium transition-colors hover:text-primary"
|
||||||
|
>
|
||||||
|
Overview
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/examples/dashboard"
|
||||||
|
class="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
||||||
|
>
|
||||||
|
Customers
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/examples/dashboard"
|
||||||
|
class="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
||||||
|
>
|
||||||
|
Products
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/examples/dashboard"
|
||||||
|
class="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
100
apps/www/src/examples/dashboard/components/Overview.vue
Normal file
100
apps/www/src/examples/dashboard/components/Overview.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Axis, StackedBar, XYContainer } from '@unovis/ts'
|
||||||
|
import { nextTick, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: 'Jan',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Feb',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mar',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Apr',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'May',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Jun',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Jul',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Aug',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sep',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Oct',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nov',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dec',
|
||||||
|
total: Math.floor(Math.random() * 5000) + 1000,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const container = ref<HTMLElement>()
|
||||||
|
|
||||||
|
let chart: XYContainer<typeof data[0]>
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!container.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const bar = new StackedBar<typeof data[0]>({
|
||||||
|
x: (d, i) => i,
|
||||||
|
y: d => d.total,
|
||||||
|
color: '#adfa1d',
|
||||||
|
roundedCorners: 4,
|
||||||
|
barPadding: 0.15,
|
||||||
|
})
|
||||||
|
|
||||||
|
chart = new XYContainer(container.value, {
|
||||||
|
components: [bar],
|
||||||
|
height: '350px',
|
||||||
|
margin: { left: 20, right: 20 },
|
||||||
|
xAxis: new Axis({
|
||||||
|
type: 'x',
|
||||||
|
numTicks: data.length,
|
||||||
|
tickFormat: (index: number) => data[index].name,
|
||||||
|
gridLine: false,
|
||||||
|
tickLine: false,
|
||||||
|
color: '#888888',
|
||||||
|
}),
|
||||||
|
yAxis: new Axis({
|
||||||
|
type: 'y',
|
||||||
|
tickFormat: (value: number) => `$${value}`,
|
||||||
|
gridLine: false,
|
||||||
|
domainLine: false,
|
||||||
|
tickLine: false,
|
||||||
|
color: '#888888',
|
||||||
|
}),
|
||||||
|
}, data)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="container" />
|
||||||
|
</template>
|
||||||
97
apps/www/src/examples/dashboard/components/RecentSales.vue
Normal file
97
apps/www/src/examples/dashboard/components/RecentSales.vue
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from '@/lib/registry/default/ui/avatar'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-8">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar class="h-9 w-9">
|
||||||
|
<AvatarImage src="/avatars/01.png" alt="Avatar" />
|
||||||
|
<AvatarFallback>OM</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div class="ml-4 space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
Olivia Martin
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
olivia.martin@email.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto font-medium">
|
||||||
|
+$1,999.00
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar class="flex h-9 w-9 items-center justify-center space-y-0 border">
|
||||||
|
<AvatarImage src="/avatars/02.png" alt="Avatar" />
|
||||||
|
<AvatarFallback>JL</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div class="ml-4 space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
Jackson Lee
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
jackson.lee@email.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto font-medium">
|
||||||
|
+$39.00
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar class="h-9 w-9">
|
||||||
|
<AvatarImage src="/avatars/03.png" alt="Avatar" />
|
||||||
|
<AvatarFallback>IN</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div class="ml-4 space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
Isabella Nguyen
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
isabella.nguyen@email.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto font-medium">
|
||||||
|
+$299.00
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar class="h-9 w-9">
|
||||||
|
<AvatarImage src="/avatars/04.png" alt="Avatar" />
|
||||||
|
<AvatarFallback>WK</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div class="ml-4 space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
William Kim
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
will@email.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto font-medium">
|
||||||
|
+$99.00
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Avatar class="h-9 w-9">
|
||||||
|
<AvatarImage src="/avatars/05.png" alt="Avatar" />
|
||||||
|
<AvatarFallback>SD</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div class="ml-4 space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
Sofia Davis
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
sofia.davis@email.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto font-medium">
|
||||||
|
+$39.00
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
13
apps/www/src/examples/dashboard/components/Search.vue
Normal file
13
apps/www/src/examples/dashboard/components/Search.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Input } from '@/lib/registry/default/ui/input'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
type="search"
|
||||||
|
placeholder="Search..."
|
||||||
|
class="md:w-[100px] lg:w-[300px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
194
apps/www/src/examples/dashboard/components/TeamSwitcher.vue
Normal file
194
apps/www/src/examples/dashboard/components/TeamSwitcher.vue
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import CaretSortIcon from '~icons/radix-icons/caret-sort'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from '@/lib/registry/default/ui/avatar'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/lib/registry/default/ui/dialog'
|
||||||
|
import { Input } from '@/lib/registry/default/ui/input'
|
||||||
|
import { Label } from '@/lib/registry/default/ui/label'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/lib/registry/default/ui/select'
|
||||||
|
|
||||||
|
const groups = [
|
||||||
|
{
|
||||||
|
label: 'Personal Account',
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
label: 'Alicia Koch',
|
||||||
|
value: 'personal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Teams',
|
||||||
|
teams: [
|
||||||
|
{
|
||||||
|
label: 'Acme Inc.',
|
||||||
|
value: 'acme-inc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Monsters Inc.',
|
||||||
|
value: 'monsters',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
type Team = (typeof groups)[number]['teams'][number]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const showNewTeamDialog = ref(false)
|
||||||
|
const selectedTeam = ref<Team>(groups[0].teams[0])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-model:open="showNewTeamDialog">
|
||||||
|
<Popover v-model:open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
aria-expanded="open"
|
||||||
|
aria-label="Select a team"
|
||||||
|
:class="cn('w-[200px] justify-between', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<Avatar class="mr-2 h-5 w-5">
|
||||||
|
<AvatarImage
|
||||||
|
:src="`https://avatar.vercel.sh/${selectedTeam.value}.png`"
|
||||||
|
:alt="selectedTeam.label"
|
||||||
|
/>
|
||||||
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
{{ selectedTeam.label }}
|
||||||
|
<CaretSortIcon class="ml-auto h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-[200px] p-0">
|
||||||
|
<!-- <Command>
|
||||||
|
<CommandList>
|
||||||
|
<CommandInput placeholder="Search team..." />
|
||||||
|
<CommandEmpty>No team found.</CommandEmpty>
|
||||||
|
{groups.map((group) => (
|
||||||
|
<CommandGroup key={group.label} heading={group.label}>
|
||||||
|
{group.teams.map((team) => (
|
||||||
|
<CommandItem
|
||||||
|
key={team.value}
|
||||||
|
onSelect={() => {
|
||||||
|
setSelectedTeam(team)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
<Avatar class="mr-2 h-5 w-5">
|
||||||
|
<AvatarImage
|
||||||
|
src={`https://avatar.vercel.sh/${team.value}.png`}
|
||||||
|
alt={team.label}
|
||||||
|
class="grayscale"
|
||||||
|
/>
|
||||||
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
{team.label}
|
||||||
|
<CheckIcon
|
||||||
|
class={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
selectedTeam.value === team.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
))}
|
||||||
|
</CommandList>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandList>
|
||||||
|
<CommandGroup>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<CommandItem
|
||||||
|
onSelect={() => {
|
||||||
|
setOpen(false)
|
||||||
|
setShowNewTeamDialog(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusCircledIcon class="mr-2 h-5 w-5" />
|
||||||
|
Create Team
|
||||||
|
</CommandItem>
|
||||||
|
</DialogTrigger>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command> -->
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Create team</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Add a new team to manage products and customers.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div>
|
||||||
|
<div class="space-y-4 py-2 pb-4">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="name">Team name</Label>
|
||||||
|
<Input id="name" placeholder="Acme Inc." />
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="plan">Subscription plan</Label>
|
||||||
|
<Select>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a plan" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="free">
|
||||||
|
<span class="font-medium">Free</span> -{" "}
|
||||||
|
<span class="text-muted-foreground">
|
||||||
|
Trial for two weeks
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="pro">
|
||||||
|
<span class="font-medium">Pro</span> -{" "}
|
||||||
|
<span class="text-muted-foreground">
|
||||||
|
$9/month per user
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" @click="showNewTeamDialog = false">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit">
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
64
apps/www/src/examples/dashboard/components/UserNav.vue
Normal file
64
apps/www/src/examples/dashboard/components/UserNav.vue
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from '@/lib/registry/default/ui/avatar'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/dropdown-menu'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="ghost" class="relative h-8 w-8 rounded-full">
|
||||||
|
<Avatar class="h-8 w-8">
|
||||||
|
<AvatarImage src="/avatars/01.png" alt="@shadcn" />
|
||||||
|
<AvatarFallback>SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent class="w-56" align="end" force-mount>
|
||||||
|
<DropdownMenuLabel class="font-normal">
|
||||||
|
<div class="flex flex-col space-y-1">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
shadcn
|
||||||
|
</p>
|
||||||
|
<p class="text-xs leading-none text-muted-foreground">
|
||||||
|
m@example.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Profile
|
||||||
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Billing
|
||||||
|
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Settings
|
||||||
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>New Team</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Log out
|
||||||
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</template>
|
||||||
981
pnpm-lock.yaml
981
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user