[Closes #1] [Closes !5] Migrated from PrimeVue to shadcn
All checks were successful
Production Build and Deploy / Build (push) Successful in 52s
Production Build and Deploy / Deploy (push) Successful in 19s

This commit is contained in:
Liviu Burcusel 2025-12-22 16:20:35 +01:00
parent 7b34c27290
commit 0cec9f5afd
Signed by: liviu
GPG key ID: 6CDB37A4AD2C610C
123 changed files with 4685 additions and 3607 deletions

View file

@ -1,21 +1,48 @@
<template>
<div class="layout-wrapper layout-static">
<default-topbar />
<default-sidebar />
<div class="layout-main-container">
<div class="layout-main">
<slot />
</div>
<default-footer />
</div>
<div class="layout-mask animate-fadein"></div>
</div>
<Toast />
</template>
<script setup lang="ts">
import Toast from "primevue/toast";
import DefaultTopbar from "~/layouts/default/Topbar.vue";
import DefaultSidebar from "~/layouts/default/Sidebar.vue";
import DefaultFooter from "~/layouts/default/Footer.vue";
import { SidebarInset, SidebarProvider, SidebarTrigger } from "~/components/ui/sidebar";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "~/components/ui/breadcrumb";
import { Separator } from "~/components/ui/separator";
const currentYear = new Date().getFullYear();
</script>
<template>
<SidebarProvider>
<DefaultSidebar />
<SidebarInset>
<header class="flex h-12 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger class="-ml-1" />
<Separator orientation="vertical" class="mr-2 data-[orientation=vertical]:h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem class="hidden md:block">
<BreadcrumbLink href="#"> Building Your Application </BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator class="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</header>
<main class="flex flex-1 flex-col gap-4 p-4">
<slot />
</main>
<footer class="flex h-12 shrink-0 items-center gap-2 border-b px-4" data-testid="footer">
<div v-if="currentYear === 2025" class="bg-muted/50 flex-1 rounded-xl p-2 text-center">Glowing Fiesta 2025</div>
<div v-else class="bg-muted/50 flex-1 rounded-xl p-2 text-center">Glowing Fiesta 2025 - {{ currentYear }}</div>
</footer>
</SidebarInset>
</SidebarProvider>
</template>

View file

@ -1,10 +0,0 @@
<script setup lang="ts">
const currentYear = new Date().getFullYear();
</script>
<template>
<footer class="layout-footer font-bold">
<div v-if="currentYear === 2025">Glowing Fiesta 2025</div>
<div v-else>Glowing Fiesta 2025 - {{ currentYear }}</div>
<div class="font-light text-emerald-500">(with auto-deploy)</div>
</footer>
</template>

View file

@ -1,35 +1,245 @@
<template>
<div class="layout-sidebar">
<ul class="layout-menu">
<li class="layout-root-menuitem">
<div class="layout-menuitem-root-text">Home</div>
<ul class="layout-submenu">
<li>
<nuxt-link to="/">
<i class="pi pi-home layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Dashboard</span>
</nuxt-link>
</li>
</ul>
</li>
<script setup lang="ts">
import { computed } from "vue";
import {
BadgeCheck,
Bell,
ChevronRight,
ChevronsUpDown,
CreditCard,
BookOpen,
HandCoins,
LogOut,
Settings2,
Sparkles,
SquareTerminal,
} from "lucide-vue-next";
<li class="layout-root-menuitem">
<div class="layout-menuitem-root-text">Configuration</div>
<ul class="layout-submenu">
<li>
<nuxt-link to="/config/account">
<i class="pi pi-user layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Account</span>
</nuxt-link>
</li>
<li>
<nuxt-link to="/config/settings">
<i class="pi pi-cog layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Settings</span>
</nuxt-link>
</li>
</ul>
</li>
</ul>
</div>
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
useSidebar,
type SidebarProps,
} from "~/components/ui/sidebar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/components/ui/collapsible";
import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar";
const data = {
user: {
name: "Liviu",
email: "x.liviu@gmail.com",
avatar: "https://git.burcusel.nl/avatars/bcc51c59d08174c798ba7db59631df6cb820e042679af9098cc03ef68330f486?size=200",
},
navMain: [
{
title: "Playground",
url: "#",
icon: SquareTerminal,
isActive: true,
items: [
{ title: "History", url: "#" },
{ title: "Starred", url: "#" },
{ title: "Settings", url: "#" },
],
},
{
title: "Documentation",
url: "#",
icon: BookOpen,
isActive: true,
items: [
{ title: "Introduction", url: "#" },
{ title: "Get Started", url: "#" },
{ title: "Tutorials", url: "#" },
{ title: "Changelog", url: "#" },
],
},
{
title: "Settings",
url: "#",
icon: Settings2,
isActive: true,
items: [
{ title: "General", url: "#" },
{ title: "Billing", url: "#" },
{ title: "Limits", url: "#" },
],
},
],
};
interface NavItem {
title: string;
url: string;
icon?: any;
isActive?: boolean;
items?: { title: string; url: string }[];
}
interface SidebarLayoutProps extends /* @vue-ignore */ SidebarProps {
navItems?: NavItem[];
}
const props = withDefaults(defineProps<SidebarLayoutProps>(), {
collapsible: "icon",
});
const navMain = computed(() => props.navItems || data.navMain);
const { isMobile } = useSidebar();
</script>
<template>
<Sidebar v-bind="props">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" as-child>
<NuxtLink to="/">
<div
class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"
>
<HandCoins class="size-4" />
</div>
<div class="flex flex-col gap-0.5 leading-none">
<span class="font-bold text-primary">Glowing Fiesta</span>
<span>v1.0.0</span>
</div>
</NuxtLink>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
<Collapsible
v-for="item in navMain"
:key="item.title"
as-child
:default-open="item.isActive"
class="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger as-child>
<SidebarMenuButton :tooltip="item.title">
<component :is="item.icon" v-if="item.icon" class="text-primary" />
<span class="font-bold text-primary">{{ item.title }}</span>
<ChevronRight
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 text-primary"
/>
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
<SidebarMenuSubItem v-for="subItem in item.items" :key="subItem.title">
<SidebarMenuSubButton as-child>
<NuxtLink :to="subItem.url">
<span>{{ subItem.title }}</span>
</NuxtLink>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar class="h-8 w-8 rounded-lg">
<AvatarImage :src="data.user.avatar" :alt="data.user.name" />
<AvatarFallback class="rounded-lg"> LB </AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-medium">{{ data.user.name }}</span>
<span class="truncate text-xs">{{ data.user.email }}</span>
</div>
<ChevronsUpDown class="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
class="w-[--reka-dropdown-menu-trigger-width] min-w-56 rounded-lg"
:side="isMobile ? 'bottom' : 'right'"
align="end"
:side-offset="4"
>
<DropdownMenuLabel class="p-0 font-normal">
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar class="h-8 w-8 rounded-lg">
<AvatarImage :src="data.user.avatar" :alt="data.user.name" />
<AvatarFallback class="rounded-lg">LB</AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{{ data.user.name }}</span>
<span class="truncate text-xs">{{ data.user.email }}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
<SidebarRail></SidebarRail>
</Sidebar>
</template>

View file

@ -1,56 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
const isDarkTheme = ref(false);
const toggleDarkMode = () => {
isDarkTheme.value = !isDarkTheme.value;
document.documentElement.classList.toggle("app-dark");
};
const toggleMenu = () => {
document.getElementsByClassName("layout-wrapper")[0]?.classList.toggle("layout-static-inactive");
};
</script>
<template>
<nav class="layout-topbar">
<div class="layout-topbar-logo-container">
<button class="layout-menu-button layout-topbar-action" @click="toggleMenu">
<i class="pi pi-bars"></i>
</button>
<nuxt-link to="/" class="layout-topbar-logo">
<img src="@/assets/images/logo.svg" alt="Git like tree using red, white-ish and blue colours." />
<span>Glowing Fiesta</span>
</nuxt-link>
</div>
<div class="layout-topbar-actions">
<div class="layout-config-menu">
<button type="button" class="layout-topbar-action layout-topbar-action-highlight" @click="toggleDarkMode">
<i :class="['pi', { 'pi-moon': isDarkTheme, 'pi-sun': !isDarkTheme }]"></i>
</button>
</div>
<Divider layout="vertical" />
<div class="layout-topbar-menu hidden lg:block">
<div class="layout-topbar-menu-content">
<button type="button" class="layout-topbar-action">
<i class="pi pi-calendar"></i>
<span>Calendar</span>
</button>
<button type="button" class="layout-topbar-action">
<i class="pi pi-inbox"></i>
<span>Messages</span>
</button>
<button type="button" class="layout-topbar-action">
<i class="pi pi-user"></i>
<span>Profile</span>
</button>
</div>
</div>
</div>
</nav>
</template>