Redesign app layout chrome
linter / quality (pull_request) Successful in 2m0s
tests / ci (8.4) (pull_request) Successful in 1m57s

This commit is contained in:
2026-06-25 01:56:54 +02:00
parent 0f125f17a3
commit 59817ad30f
8 changed files with 477 additions and 312 deletions
+270 -78
View File
@@ -1,6 +1,6 @@
@import 'tailwindcss';
@import "tailwindcss";
@import 'tw-animate-css';
@import "tw-animate-css";
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';
@@ -9,13 +9,69 @@
@theme inline {
--font-sans:
Instrument Sans, ui-sans-serif, system-ui, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-headline-lg:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-headline-lg-mobile:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-headline-md:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-body-md:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-body-sm:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--font-label-uppercase:
Inter, Instrument Sans, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--radius-sm: 0.125rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--spacing-container-margin: 1.5rem;
--spacing-column-gap: 1rem;
--spacing-card-padding: 0.75rem;
--spacing-stack-gap-sm: 0.5rem;
--spacing-stack-gap-md: 0.75rem;
--text-headline-lg: 1.5rem;
--text-headline-lg--line-height: 2rem;
--text-headline-lg--font-weight: 600;
--text-headline-lg--letter-spacing: 0;
--text-headline-md: 1.125rem;
--text-headline-md--line-height: 1.5rem;
--text-headline-md--font-weight: 600;
--text-headline-md--letter-spacing: 0;
--text-headline-lg-mobile: 1.25rem;
--text-headline-lg-mobile--line-height: 1.75rem;
--text-headline-lg-mobile--font-weight: 600;
--text-body-md: 0.875rem;
--text-body-md--line-height: 1.25rem;
--text-body-md--font-weight: 400;
--text-body-sm: 0.8125rem;
--text-body-sm--line-height: 1.125rem;
--text-body-sm--font-weight: 400;
--text-label-uppercase: 0.6875rem;
--text-label-uppercase--line-height: 1rem;
--text-label-uppercase--font-weight: 700;
--text-label-uppercase--letter-spacing: 0.05em;
--shadow-kanban-card: 0 4px 12px rgb(0 0 0 / 0.08);
--shadow-kanban-nav: 0 12px 30px rgb(15 23 42 / 0.06);
--color-background: var(--background);
--color-foreground: var(--foreground);
@@ -59,6 +115,54 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-surface: var(--surface);
--color-surface-dim: var(--surface-dim);
--color-surface-bright: var(--surface-bright);
--color-surface-container-lowest: var(--surface-container-lowest);
--color-surface-container-low: var(--surface-container-low);
--color-surface-container: var(--surface-container);
--color-surface-container-high: var(--surface-container-high);
--color-surface-container-highest: var(--surface-container-highest);
--color-surface-variant: var(--surface-variant);
--color-surface-tint: var(--surface-tint);
--color-on-surface: var(--on-surface);
--color-on-surface-variant: var(--on-surface-variant);
--color-on-background: var(--on-background);
--color-outline: var(--outline);
--color-outline-variant: var(--outline-variant);
--color-primary-container: var(--primary-container);
--color-on-primary: var(--on-primary);
--color-on-primary-container: var(--on-primary-container);
--color-primary-fixed: var(--primary-fixed);
--color-primary-fixed-dim: var(--primary-fixed-dim);
--color-on-primary-fixed: var(--on-primary-fixed);
--color-on-primary-fixed-variant: var(--on-primary-fixed-variant);
--color-secondary-container: var(--secondary-container);
--color-on-secondary: var(--on-secondary);
--color-on-secondary-container: var(--on-secondary-container);
--color-secondary-fixed: var(--secondary-fixed);
--color-secondary-fixed-dim: var(--secondary-fixed-dim);
--color-on-secondary-fixed: var(--on-secondary-fixed);
--color-on-secondary-fixed-variant: var(--on-secondary-fixed-variant);
--color-tertiary: var(--tertiary);
--color-tertiary-container: var(--tertiary-container);
--color-on-tertiary: var(--on-tertiary);
--color-on-tertiary-container: var(--on-tertiary-container);
--color-tertiary-fixed: var(--tertiary-fixed);
--color-tertiary-fixed-dim: var(--tertiary-fixed-dim);
--color-on-tertiary-fixed: var(--on-tertiary-fixed);
--color-on-tertiary-fixed-variant: var(--on-tertiary-fixed-variant);
--color-error: var(--error);
--color-error-container: var(--error-container);
--color-on-error: var(--on-error);
--color-on-error-container: var(--on-error-container);
--color-inverse-surface: var(--inverse-surface);
--color-inverse-on-surface: var(--inverse-on-surface);
--color-inverse-primary: var(--inverse-primary);
--color-priority-high: #ef4444;
--color-priority-medium: #f59e0b;
--color-priority-low: #3b82f6;
}
/*
@@ -83,83 +187,171 @@
body,
html {
--font-sans:
'Instrument Sans', ui-sans-serif, system-ui, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
Inter, "Instrument Sans", ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji";
}
}
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(0 0% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(0 0% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(0 0% 3.9%);
--primary: hsl(0 0% 9%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(0 0% 92.1%);
--secondary-foreground: hsl(0 0% 9%);
--muted: hsl(0 0% 96.1%);
--muted-foreground: hsl(0 0% 45.1%);
--accent: hsl(0 0% 96.1%);
--accent-foreground: hsl(0 0% 9%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(0 0% 92.8%);
--input: hsl(0 0% 89.8%);
--ring: hsl(0 0% 3.9%);
--chart-1: hsl(12 76% 61%);
--chart-2: hsl(173 58% 39%);
--chart-3: hsl(197 37% 24%);
--chart-4: hsl(43 74% 66%);
--chart-5: hsl(27 87% 67%);
--radius: 0.5rem;
--sidebar-background: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(0 0% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(0 0% 94%);
--sidebar-accent-foreground: hsl(0 0% 30%);
--sidebar-border: hsl(0 0% 91%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--sidebar: hsl(0 0% 98%);
--background: #f7f9fb;
--foreground: #191c1e;
--card: #ffffff;
--card-foreground: #191c1e;
--popover: #ffffff;
--popover-foreground: #191c1e;
--primary: #006e2f;
--primary-foreground: #ffffff;
--secondary: #d0e1fb;
--secondary-foreground: #0b1c30;
--muted: #f2f4f6;
--muted-foreground: #3d4a3d;
--accent: #e6e8ea;
--accent-foreground: #191c1e;
--destructive: #ba1a1a;
--destructive-foreground: #ffffff;
--border: #bccbb9;
--input: #bccbb9;
--ring: #006e2f;
--chart-1: #22c55e;
--chart-2: #3b82f6;
--chart-3: #f59e0b;
--chart-4: #ef4444;
--chart-5: #505f76;
--radius: 0.25rem;
--surface: #f7f9fb;
--surface-dim: #d8dadc;
--surface-bright: #f7f9fb;
--surface-container-lowest: #ffffff;
--surface-container-low: #f2f4f6;
--surface-container: #eceef0;
--surface-container-high: #e6e8ea;
--surface-container-highest: #e0e3e5;
--surface-variant: #e0e3e5;
--surface-tint: #006e2f;
--on-surface: #191c1e;
--on-surface-variant: #3d4a3d;
--on-background: #191c1e;
--outline: #6d7b6c;
--outline-variant: #bccbb9;
--primary-container: #22c55e;
--on-primary: #ffffff;
--on-primary-container: #004b1e;
--primary-fixed: #6bff8f;
--primary-fixed-dim: #4ae176;
--on-primary-fixed: #002109;
--on-primary-fixed-variant: #005321;
--secondary-container: #d0e1fb;
--on-secondary: #ffffff;
--on-secondary-container: #54647a;
--secondary-fixed: #d3e4fe;
--secondary-fixed-dim: #b7c8e1;
--on-secondary-fixed: #0b1c30;
--on-secondary-fixed-variant: #38485d;
--tertiary: #9e4036;
--tertiary-container: #ff8b7c;
--on-tertiary: #ffffff;
--on-tertiary-container: #76231b;
--tertiary-fixed: #ffdad5;
--tertiary-fixed-dim: #ffb4a9;
--on-tertiary-fixed: #410001;
--on-tertiary-fixed-variant: #7f2a21;
--error: #ba1a1a;
--error-container: #ffdad6;
--on-error: #ffffff;
--on-error-container: #93000a;
--inverse-surface: #2d3133;
--inverse-on-surface: #eff1f3;
--inverse-primary: #4ae176;
--sidebar-background: #f2f4f6;
--sidebar-foreground: #191c1e;
--sidebar-primary: #006e2f;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #e6e8ea;
--sidebar-accent-foreground: #191c1e;
--sidebar-border: #bccbb9;
--sidebar-ring: #006e2f;
--sidebar: #f2f4f6;
}
.dark {
--background: hsl(0 0% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(0 0% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(0 0% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(0 0% 9%);
--secondary: hsl(0 0% 14.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(0 0% 16.08%);
--muted-foreground: hsl(0 0% 63.9%);
--accent: hsl(0 0% 14.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 84% 60%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(0 0% 14.9%);
--input: hsl(0 0% 14.9%);
--ring: hsl(0 0% 83.1%);
--chart-1: hsl(220 70% 50%);
--chart-2: hsl(160 60% 45%);
--chart-3: hsl(30 80% 55%);
--chart-4: hsl(280 65% 60%);
--chart-5: hsl(340 75% 55%);
--sidebar-background: hsl(0 0% 7%);
--sidebar-foreground: hsl(0 0% 95.9%);
--sidebar-primary: hsl(360, 100%, 100%);
--sidebar-primary-foreground: hsl(0 0% 100%);
--sidebar-accent: hsl(0 0% 15.9%);
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
--sidebar-border: hsl(0 0% 15.9%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--sidebar: hsl(240 5.9% 10%);
--background: #0f172a;
--foreground: #eff1f3;
--card: #111827;
--card-foreground: #eff1f3;
--popover: #111827;
--popover-foreground: #eff1f3;
--primary: #4ae176;
--primary-foreground: #002109;
--secondary: #1f2937;
--secondary-foreground: #eff1f3;
--muted: #1e293b;
--muted-foreground: #cbd5e1;
--accent: #1e293b;
--accent-foreground: #eff1f3;
--destructive: #f87171;
--destructive-foreground: #ffffff;
--border: #334155;
--input: #334155;
--ring: #4ae176;
--chart-1: #4ae176;
--chart-2: #60a5fa;
--chart-3: #fbbf24;
--chart-4: #f87171;
--chart-5: #b7c8e1;
--surface: #0f172a;
--surface-dim: #020617;
--surface-bright: #111827;
--surface-container-lowest: #111827;
--surface-container-low: #1e293b;
--surface-container: #263445;
--surface-container-high: #334155;
--surface-container-highest: #475569;
--surface-variant: #334155;
--surface-tint: #4ae176;
--on-surface: #eff1f3;
--on-surface-variant: #cbd5e1;
--on-background: #eff1f3;
--outline: #94a3b8;
--outline-variant: #334155;
--primary-container: #14532d;
--on-primary: #002109;
--on-primary-container: #bbf7d0;
--primary-fixed: #6bff8f;
--primary-fixed-dim: #4ae176;
--on-primary-fixed: #002109;
--on-primary-fixed-variant: #005321;
--secondary-container: #1e3a5f;
--on-secondary: #ffffff;
--on-secondary-container: #d0e1fb;
--secondary-fixed: #d3e4fe;
--secondary-fixed-dim: #b7c8e1;
--on-secondary-fixed: #0b1c30;
--on-secondary-fixed-variant: #38485d;
--tertiary: #ffb4a9;
--tertiary-container: #7f2a21;
--on-tertiary: #410001;
--on-tertiary-container: #ffdad5;
--tertiary-fixed: #ffdad5;
--tertiary-fixed-dim: #ffb4a9;
--on-tertiary-fixed: #410001;
--on-tertiary-fixed-variant: #ffb4a9;
--error: #f87171;
--error-container: #7f1d1d;
--on-error: #ffffff;
--on-error-container: #fee2e2;
--inverse-surface: #eff1f3;
--inverse-on-surface: #191c1e;
--inverse-primary: #006e2f;
--sidebar-background: #111827;
--sidebar-foreground: #eff1f3;
--sidebar-primary: #4ae176;
--sidebar-primary-foreground: #002109;
--sidebar-accent: #1e293b;
--sidebar-accent-foreground: #eff1f3;
--sidebar-border: #334155;
--sidebar-ring: #4ae176;
--sidebar: #111827;
}
@layer base {
+1 -1
View File
@@ -20,7 +20,7 @@ const className = computed(() => props.class);
</SidebarInset>
<main
v-else
class="mx-auto flex h-full w-full max-w-7xl flex-1 flex-col gap-4 rounded-xl"
class="flex h-full w-full flex-1 flex-col gap-4"
:class="className"
>
<slot />
+187 -217
View File
@@ -1,9 +1,16 @@
<script setup lang="ts">
import { Link, usePage } from '@inertiajs/vue3';
import { BookOpen, Folder, LayoutGrid, Menu, Search } from 'lucide-vue-next';
import {
Folder,
LayoutGrid,
Layers3,
Menu,
Search,
Shield,
Users,
} from 'lucide-vue-next';
import { computed } from 'vue';
import AppLogo from '@/components/AppLogo.vue';
import AppLogoIcon from '@/components/AppLogoIcon.vue';
import Breadcrumbs from '@/components/Breadcrumbs.vue';
import { Button } from '@/components/ui/button';
import {
@@ -11,12 +18,6 @@ import {
DropdownMenuContent,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuList,
navigationMenuTriggerStyle,
} from '@/components/ui/navigation-menu';
import {
Sheet,
SheetContent,
@@ -24,246 +25,215 @@ import {
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import UserAvatar from '@/components/ui/avatar/UserAvatar.vue';
import UserMenuContent from '@/components/UserMenuContent.vue';
import { useCurrentUrl } from '@/composables/useCurrentUrl';
import { toUrl } from '@/lib/utils';
import { dashboard } from '@/routes/index';
import { dashboard } from '@/routes';
import { index as adminUsers } from '@/routes/admin/users';
import { index as projectTemplatesIndex } from '@/routes/project-templates';
import { index as projectsIndex } from '@/routes/projects';
import { index as teamsIndex } from '@/routes/teams';
import type { BreadcrumbItem, NavItem } from '@/types';
import UserAvatar from '@/components/ui/avatar/UserAvatar.vue';
type Props = {
breadcrumbs?: BreadcrumbItem[];
};
const props = withDefaults(defineProps<Props>(), {
breadcrumbs: () => [],
});
const props = withDefaults(
defineProps<{
breadcrumbs?: BreadcrumbItem[];
}>(),
{
breadcrumbs: () => [],
},
);
const page = usePage();
const auth = computed(() => page.props.auth);
const { isCurrentUrl, whenCurrentUrl } = useCurrentUrl();
const { isCurrentOrParentUrl } = useCurrentUrl();
const activeItemStyles =
'text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100';
const mainNavItems = computed<NavItem[]>(() => {
const items: NavItem[] = [
{
title: 'Dashboard',
href: dashboard(),
icon: LayoutGrid,
},
{
title: 'Projects',
href: projectsIndex(),
icon: Folder,
},
];
const mainNavItems: NavItem[] = [
{
title: 'Dashboard',
href: dashboard(),
icon: LayoutGrid,
},
];
if (page.props.auth.permissions.projectCreate) {
items.push({
title: 'Project templates',
href: projectTemplatesIndex(),
icon: Layers3,
});
}
const rightNavItems: NavItem[] = [
{
title: 'Repository',
href: 'https://github.com/laravel/vue-starter-kit',
icon: Folder,
},
{
title: 'Documentation',
href: 'https://laravel.com/docs/starter-kits#vue',
icon: BookOpen,
},
];
if (page.props.auth.permissions.globalAdmin) {
items.push({
title: 'Users',
href: adminUsers(),
icon: Shield,
});
}
if (page.props.auth.permissions.canManageTeams) {
items.push({
title: 'Teams',
href: teamsIndex(),
icon: Users,
});
}
return items;
});
const navItemClasses = (item: NavItem): string =>
[
'inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium transition-colors duration-200',
isCurrentOrParentUrl(item.href)
? 'bg-primary text-primary-foreground shadow-[0_8px_20px_rgba(0,110,47,0.16)]'
: 'text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface',
].join(' ');
</script>
<template>
<div>
<div class="border-sidebar-border/80 border-b">
<div class="mx-auto flex h-16 items-center px-4 md:max-w-7xl">
<!-- Mobile Menu -->
<div class="lg:hidden">
<Sheet>
<SheetTrigger :as-child="true">
<Button
variant="ghost"
size="icon"
class="mr-2 h-9 w-9"
>
<Menu class="h-5 w-5" />
</Button>
</SheetTrigger>
<SheetContent side="left" class="w-[300px] p-6">
<SheetTitle class="sr-only"
>Navigation menu</SheetTitle
>
<SheetHeader class="flex justify-start text-left">
<AppLogoIcon
class="size-6 fill-current text-black dark:text-white"
/>
</SheetHeader>
<div
class="flex h-full flex-1 flex-col justify-between space-y-4 py-6"
>
<nav class="-mx-3 space-y-1">
<Link
v-for="item in mainNavItems"
:key="item.title"
:href="item.href"
class="hover:bg-accent flex items-center gap-x-3 rounded-lg px-3 py-2 text-sm font-medium"
:class="
whenCurrentUrl(
item.href,
activeItemStyles,
)
"
>
<component
v-if="item.icon"
:is="item.icon"
class="h-5 w-5"
/>
{{ item.title }}
</Link>
</nav>
<div class="flex flex-col space-y-4">
<a
v-for="item in rightNavItems"
:key="item.title"
:href="toUrl(item.href)"
target="_blank"
rel="noopener noreferrer"
class="flex items-center space-x-2 text-sm font-medium"
>
<component
v-if="item.icon"
:is="item.icon"
class="h-5 w-5"
/>
<span>{{ item.title }}</span>
</a>
</div>
</div>
</SheetContent>
</Sheet>
</div>
<Link :href="dashboard()" class="flex items-center gap-x-2">
<AppLogo />
</Link>
<!-- Desktop Menu -->
<div class="hidden h-full lg:flex lg:flex-1">
<NavigationMenu class="ml-10 flex h-full items-stretch">
<NavigationMenuList
class="flex h-full items-stretch space-x-2"
<header
class="border-outline-variant/80 bg-surface/90 shadow-kanban-nav sticky top-0 z-50 border-b backdrop-blur-md"
>
<div
class="sm:px-container-margin flex h-16 w-full items-center gap-3 px-4"
>
<Sheet>
<SheetTrigger :as-child="true">
<Button
variant="ghost"
size="icon"
class="text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface size-9 rounded-lg lg:hidden"
>
<Menu class="size-5" />
<span class="sr-only">Open navigation</span>
</Button>
</SheetTrigger>
<SheetContent
side="left"
class="border-outline-variant bg-surface w-[320px] p-0"
>
<SheetTitle class="sr-only">Navigation menu</SheetTitle>
<SheetHeader class="border-outline-variant border-b p-4">
<Link
:href="dashboard()"
class="flex items-center gap-3"
>
<NavigationMenuItem
v-for="(item, index) in mainNavItems"
:key="index"
class="relative flex h-full items-center"
<AppLogo />
</Link>
</SheetHeader>
<div class="flex flex-col gap-5 p-4">
<div class="relative">
<Search
class="text-on-surface-variant pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2"
/>
<input
disabled
aria-label="Search"
placeholder="Coming soon"
class="border-outline-variant bg-surface-container-low text-on-surface placeholder:text-on-surface-variant h-10 w-full rounded-lg border py-2 pl-9 pr-3 text-sm disabled:cursor-not-allowed disabled:opacity-100"
type="text"
/>
</div>
<nav class="flex flex-col gap-1" aria-label="Main">
<Link
v-for="item in mainNavItems"
:key="item.title"
:href="item.href"
:class="navItemClasses(item)"
>
<Link
:class="[
navigationMenuTriggerStyle(),
whenCurrentUrl(
item.href,
activeItemStyles,
),
'h-9 cursor-pointer px-3',
]"
:href="item.href"
>
<component
v-if="item.icon"
:is="item.icon"
class="mr-2 h-4 w-4"
/>
{{ item.title }}
</Link>
<div
v-if="isCurrentUrl(item.href)"
class="absolute bottom-0 left-0 h-0.5 w-full translate-y-px bg-black dark:bg-white"
></div>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
<component
v-if="item.icon"
:is="item.icon"
class="size-4"
/>
{{ item.title }}
</Link>
</nav>
</div>
</SheetContent>
</Sheet>
<Link
:href="dashboard()"
class="flex shrink-0 items-center gap-2"
aria-label="KanbanGit dashboard"
>
<AppLogo />
</Link>
<nav
class="ml-3 hidden min-w-0 flex-1 items-center gap-1 lg:flex"
aria-label="Main"
>
<Link
v-for="item in mainNavItems"
:key="item.title"
:href="item.href"
:class="navItemClasses(item)"
>
<component
v-if="item.icon"
:is="item.icon"
class="size-4"
/>
<span class="truncate">{{ item.title }}</span>
</Link>
</nav>
<div class="ml-auto flex items-center gap-3">
<div class="relative hidden sm:block">
<Search
class="text-on-surface-variant pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2"
/>
<input
disabled
aria-label="Search"
placeholder="Coming soon"
class="border-outline-variant bg-surface-container-low text-on-surface placeholder:text-on-surface-variant h-10 w-48 rounded-lg border py-2 pl-9 pr-3 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-100 md:w-64"
type="text"
/>
</div>
<div class="ml-auto flex items-center space-x-2">
<div class="relative flex items-center space-x-1">
<DropdownMenu>
<DropdownMenuTrigger :as-child="true">
<Button
variant="ghost"
size="icon"
class="group h-9 w-9 cursor-pointer"
class="focus-visible:ring-primary/30 hover:bg-surface-container-high size-10 rounded-full p-1 focus-visible:ring-2"
>
<Search
class="size-5 opacity-80 group-hover:opacity-100"
/>
<UserAvatar rounded />
<span class="sr-only">Open user menu</span>
</Button>
<div class="hidden space-x-1 lg:flex">
<template
v-for="item in rightNavItems"
:key="item.title"
>
<TooltipProvider :delay-duration="0">
<Tooltip>
<TooltipTrigger>
<Button
variant="ghost"
size="icon"
as-child
class="group h-9 w-9 cursor-pointer"
>
<a
:href="toUrl(item.href)"
target="_blank"
rel="noopener noreferrer"
>
<span class="sr-only">{{
item.title
}}</span>
<component
:is="item.icon"
class="size-5 opacity-80 group-hover:opacity-100"
/>
</a>
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{{ item.title }}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</template>
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger :as-child="true">
<Button
variant="ghost"
size="icon"
class="focus-within:ring-primary relative size-10 w-auto rounded-full p-1 focus-within:ring-2"
>
<UserAvatar rounded />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" class="w-56">
<UserMenuContent :user="auth.user" />
</DropdownMenuContent>
</DropdownMenu>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
class="border-outline-variant bg-popover w-56"
>
<UserMenuContent :user="auth.user" />
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div
v-if="props.breadcrumbs.length > 1"
class="border-sidebar-border/70 flex w-full border-b"
class="border-outline-variant/70 bg-surface-container-low/70 border-t"
>
<div
class="mx-auto flex h-12 w-full items-center justify-start px-4 text-neutral-500 md:max-w-7xl"
class="text-on-surface-variant sm:px-container-margin flex h-11 w-full items-center px-4 text-sm"
>
<Breadcrumbs :breadcrumbs="breadcrumbs" />
</div>
</div>
</div>
</header>
</template>
+5 -5
View File
@@ -4,13 +4,13 @@ import AppLogoIcon from '@/components/AppLogoIcon.vue';
<template>
<div
class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-md"
class="bg-primary text-primary-foreground flex aspect-square size-9 items-center justify-center rounded-lg shadow-[0_8px_24px_rgba(0,110,47,0.18)]"
>
<AppLogoIcon class="size-5 fill-current text-white dark:text-black" />
<AppLogoIcon class="size-5 fill-current" />
</div>
<div class="ml-1 grid flex-1 text-left text-sm">
<span class="mb-0.5 truncate font-semibold leading-tight"
>Laravel Starter Kit</span
>
<span class="text-primary truncate text-sm font-semibold leading-tight">
KanbanGit
</span>
</div>
</template>
+7 -7
View File
@@ -15,15 +15,15 @@ defineProps<Props>();
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 40 42"
viewBox="0 0 32 32"
:class="className"
v-bind="$attrs"
aria-hidden="true"
>
<path
fill="currentColor"
fill-rule="evenodd"
clip-rule="evenodd"
d="M17.2 5.633 8.6.855 0 5.633v26.51l16.2 9 16.2-9v-8.442l7.6-4.223V9.856l-8.6-4.777-8.6 4.777V18.3l-5.6 3.111V5.633ZM38 18.301l-5.6 3.11v-6.157l5.6-3.11V18.3Zm-1.06-7.856-5.54 3.078-5.54-3.079 5.54-3.078 5.54 3.079ZM24.8 18.3v-6.157l5.6 3.111v6.158L24.8 18.3Zm-1 1.732 5.54 3.078-13.14 7.302-5.54-3.078 13.14-7.3v-.002Zm-16.2 7.89 7.6 4.222V38.3L2 30.966V7.92l5.6 3.111v16.892ZM8.6 9.3 3.06 6.222 8.6 3.143l5.54 3.08L8.6 9.3Zm21.8 15.51-13.2 7.334V38.3l13.2-7.334v-6.156ZM9.6 11.034l5.6-3.11v14.6l-5.6 3.11v-14.6Z"
/>
<rect x="4" y="5" width="7" height="22" rx="2" />
<rect x="13" y="5" width="7" height="14" rx="2" opacity="0.82" />
<rect x="22" y="5" width="6" height="18" rx="2" opacity="0.64" />
<path d="M14 24h7v3h-7z" opacity="0.82" />
<path d="M22 26h6v1h-6z" opacity="0.64" />
</svg>
</template>
+4 -1
View File
@@ -15,7 +15,10 @@ const isOpen = usePage().props.sidebarOpen;
</script>
<template>
<div v-if="variant === 'header'" class="flex min-h-screen w-full flex-col">
<div
v-if="variant === 'header'"
class="bg-background text-foreground flex min-h-screen w-full flex-col"
>
<slot />
</div>
<SidebarProvider v-else :default-open="isOpen">
+2 -2
View File
@@ -76,12 +76,12 @@ const mainNavItems = computed<NavItem[]>(() => {
const footerNavItems: NavItem[] = [
{
title: 'Repository',
href: 'https://github.com/laravel/vue-starter-kit',
href: 'https://git.twyco.de/OpenSource/KanbanGit',
icon: FolderGit2,
},
{
title: 'Documentation',
href: 'https://laravel.com/docs/starter-kits#vue',
href: 'https://git.twyco.de/OpenSource/KanbanGit/src/branch/main/README.md',
icon: BookOpen,
},
];
+1 -1
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import AppLayout from '@/layouts/app/AppSidebarLayout.vue';
import AppLayout from '@/layouts/app/AppHeaderLayout.vue';
import type { BreadcrumbItem } from '@/types';
type Props = {