feat(shell): personalized sidebar + header with real user and company

- Layout split: (dashboard)/layout.tsx is now async server component that
  fetches active context and passes user/company to (dashboard)/dashboard-shell.tsx
  (client). Redirects to /onboarding if no tenant.
- AppSidebar:
  * Header shows 'İşletmem' + the active company name (companyName from
    tenant_settings), instead of mock 'ShadcnStore / Admin Dashboard'.
  * Nav rebuilt for our modules in Turkish: Genel bakış, Müşteriler,
    Hizmetler, Yazılımlarımız, Takvim, Görevler, Gelir/Gider, Faturalar,
    Çalışma alanı (with submenu), Profil, Plan.
  * Removed SidebarNotification (template promo widget).
  * Accepts user/company props (typed via ShellUser/ShellCompany).
- NavUser:
  * Real user name + email, no more 'ShadcnStore / store@example.com'.
  * Avatar shows initials from name in primary/10 tinted square.
  * Logout wired to signOutAction (server action) via useTransition.
  * Menu items localized (Profil, Plan & Faturalama, Bildirimler, Çıkış yap).
- SiteHeader:
  * Removed Blocks / Landing / GitHub external links (template demo links).
  * Shows company name with Building2 icon between sidebar trigger and
    search trigger.
  * Search trigger moved to right side next to ModeToggle.
- Dropped UpgradeToProButton from the shell (template promo).
- Deleted dead-code src/components/layouts/base-layout.tsx (unused alt
  layout that wasn't compatible with the new AppSidebar props).
This commit is contained in:
kovakmedya
2026-04-30 03:43:00 +03:00
parent 8a7742af1b
commit 0a280fd3a3
6 changed files with 301 additions and 458 deletions
+93
View File
@@ -0,0 +1,93 @@
"use client";
import React from "react";
import { AppSidebar } from "@/components/app-sidebar";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar";
import { ThemeCustomizer, ThemeCustomizerTrigger } from "@/components/theme-customizer";
import { useSidebarConfig } from "@/hooks/use-sidebar-config";
export type ShellUser = {
id: string;
name: string;
email: string;
};
export type ShellCompany = {
id: string;
name: string;
};
export function DashboardShell({
user,
company,
children,
}: {
user: ShellUser;
company: ShellCompany;
children: React.ReactNode;
}) {
const [themeCustomizerOpen, setThemeCustomizerOpen] = React.useState(false);
const { config } = useSidebarConfig();
return (
<SidebarProvider
style={
{
"--sidebar-width": "16rem",
"--sidebar-width-icon": "3rem",
"--header-height": "calc(var(--spacing) * 14)",
} as React.CSSProperties
}
className={config.collapsible === "none" ? "sidebar-none-mode" : ""}
>
{config.side === "left" ? (
<>
<AppSidebar
user={user}
company={company}
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
<SidebarInset>
<SiteHeader company={company} />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">{children}</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
</>
) : (
<>
<SidebarInset>
<SiteHeader company={company} />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">{children}</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
<AppSidebar
user={user}
company={company}
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
</>
)}
<ThemeCustomizerTrigger onClick={() => setThemeCustomizerOpen(true)} />
<ThemeCustomizer
open={themeCustomizerOpen}
onOpenChange={setThemeCustomizerOpen}
/>
</SidebarProvider>
);
}
+19 -68
View File
@@ -1,78 +1,29 @@
"use client"; import { redirect } from "next/navigation";
import React from "react"; import { getActiveContext } from "@/lib/appwrite/active-context";
import { AppSidebar } from "@/components/app-sidebar"; import { DashboardShell } from "./dashboard-shell";
import { SiteHeader } from "@/components/site-header";
import { SiteFooter } from "@/components/site-footer";
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar";
import { ThemeCustomizer, ThemeCustomizerTrigger } from "@/components/theme-customizer";
import { UpgradeToProButton } from "@/components/upgrade-to-pro-button";
import { useSidebarConfig } from "@/hooks/use-sidebar-config";
export default function DashboardLayout({ export default async function DashboardLayout({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const [themeCustomizerOpen, setThemeCustomizerOpen] = React.useState(false); const ctx = await getActiveContext();
const { config } = useSidebarConfig(); if (!ctx) redirect("/onboarding");
const company = {
id: ctx.tenantId,
name: ctx.settings?.companyName ?? "Çalışma alanı",
};
const user = {
id: ctx.user.id,
name: ctx.user.name || ctx.user.email,
email: ctx.user.email,
};
return ( return (
<SidebarProvider <DashboardShell user={user} company={company}>
style={{ {children}
"--sidebar-width": "16rem", </DashboardShell>
"--sidebar-width-icon": "3rem",
"--header-height": "calc(var(--spacing) * 14)",
} as React.CSSProperties}
className={config.collapsible === "none" ? "sidebar-none-mode" : ""}
>
{config.side === "left" ? (
<>
<AppSidebar
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
{children}
</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
</>
) : (
<>
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
{children}
</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
<AppSidebar
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
</>
)}
{/* Theme Customizer */}
<ThemeCustomizerTrigger onClick={() => setThemeCustomizerOpen(true)} />
<ThemeCustomizer
open={themeCustomizerOpen}
onOpenChange={setThemeCustomizerOpen}
/>
<UpgradeToProButton />
</SidebarProvider>
); );
} }
+119 -203
View File
@@ -1,27 +1,24 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { import {
LayoutPanelLeft, Briefcase,
LayoutDashboard,
Mail,
CheckSquare,
MessageCircle,
Calendar, Calendar,
Shield, CheckSquare,
AlertTriangle,
Settings,
HelpCircle,
CreditCard, CreditCard,
LayoutTemplate, FileText,
LayoutDashboard,
Package,
Receipt,
Settings,
Users, Users,
} from "lucide-react" Wallet,
import Link from "next/link" } from "lucide-react";
import { Logo } from "@/components/logo" import Link from "next/link";
import { SidebarNotification } from "@/components/sidebar-notification"
import { NavMain } from "@/components/nav-main" import { Logo } from "@/components/logo";
import { NavUser } from "@/components/nav-user" import { NavMain } from "@/components/nav-main";
import { NavUser } from "@/components/nav-user";
import { import {
Sidebar, Sidebar,
SidebarContent, SidebarContent,
@@ -30,186 +27,106 @@ import {
SidebarMenu, SidebarMenu,
SidebarMenuButton, SidebarMenuButton,
SidebarMenuItem, SidebarMenuItem,
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar";
const data = { import type { ShellCompany, ShellUser } from "@/app/(dashboard)/dashboard-shell";
user: {
name: "ShadcnStore", const navGroups = [
email: "store@example.com", {
avatar: "", label: "Genel",
items: [
{
title: "Genel bakış",
url: "/dashboard",
icon: LayoutDashboard,
},
],
}, },
navGroups: [ {
{ label: "İşletme",
label: "Dashboards", items: [
items: [ {
{ title: "Müşteriler",
title: "Dashboard 1", url: "/customers",
url: "/dashboard", icon: Users,
icon: LayoutDashboard, },
}, {
{ title: "Hizmetler",
title: "Dashboard 2", url: "/services",
url: "/dashboard-2", icon: Briefcase,
icon: LayoutPanelLeft, },
}, {
], title: "Yazılımlarımız",
}, url: "/software",
{ icon: Package,
label: "Apps", },
items: [ ],
{ },
title: "Mail", {
url: "/mail", label: "Operasyon",
icon: Mail, items: [
}, {
{ title: "Takvim",
title: "Tasks", url: "/calendar",
url: "/tasks", icon: Calendar,
icon: CheckSquare, },
}, {
{ title: "Görevler",
title: "Chat", url: "/tasks",
url: "/chat", icon: CheckSquare,
icon: MessageCircle, },
}, ],
{ },
title: "Calendar", {
url: "/calendar", label: "Finans",
icon: Calendar, items: [
}, {
{ title: "Gelir / Gider",
title: "Users", url: "/finance",
url: "/users", icon: Wallet,
icon: Users, },
}, {
], title: "Faturalar",
}, url: "/invoices",
{ icon: Receipt,
label: "Pages", },
items: [ ],
{ },
title: "Landing", {
url: "/landing", label: "Hesap",
target: "_blank", items: [
icon: LayoutTemplate, {
}, title: "Çalışma alanı",
{ url: "/settings/workspace",
title: "Auth Pages", icon: Settings,
url: "#", items: [
icon: Shield, { title: "Şirket bilgileri", url: "/settings/workspace" },
items: [ { title: "Ekip üyeleri", url: "/settings/members" },
{ { title: "Faturalama", url: "/settings/billing" },
title: "Sign In 1", ],
url: "/sign-in", },
}, {
{ title: "Profil",
title: "Sign In 2", url: "/settings/account",
url: "/sign-in-2", icon: FileText,
}, },
{ {
title: "Sign In 3", title: "Plan",
url: "/sign-in-3", url: "/pricing",
}, icon: CreditCard,
{ },
title: "Sign Up 1", ],
url: "/sign-up", },
}, ];
{
title: "Sign Up 2",
url: "/sign-up-2",
},
{
title: "Sign Up 3",
url: "/sign-up-3",
},
{
title: "Forgot Password 1",
url: "/forgot-password",
},
{
title: "Forgot Password 2",
url: "/forgot-password-2",
},
{
title: "Forgot Password 3",
url: "/forgot-password-3",
}
],
},
{
title: "Errors",
url: "#",
icon: AlertTriangle,
items: [
{
title: "Unauthorized",
url: "/errors/unauthorized",
},
{
title: "Forbidden",
url: "/errors/forbidden",
},
{
title: "Not Found",
url: "/errors/not-found",
},
{
title: "Internal Server Error",
url: "/errors/internal-server-error",
},
{
title: "Under Maintenance",
url: "/errors/under-maintenance",
},
],
},
{
title: "Settings",
url: "#",
icon: Settings,
items: [
{
title: "User Settings",
url: "/settings/user",
},
{
title: "Account Settings",
url: "/settings/account",
},
{
title: "Plans & Billing",
url: "/settings/billing",
},
{
title: "Appearance",
url: "/settings/appearance",
},
{
title: "Notifications",
url: "/settings/notifications",
},
{
title: "Connections",
url: "/settings/connections",
},
],
},
{
title: "FAQs",
url: "/faqs",
icon: HelpCircle,
},
{
title: "Pricing",
url: "/pricing",
icon: CreditCard,
},
],
},
],
}
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) { export function AppSidebar({
user,
company,
...props
}: React.ComponentProps<typeof Sidebar> & {
user: ShellUser;
company: ShellCompany;
}) {
return ( return (
<Sidebar {...props}> <Sidebar {...props}>
<SidebarHeader> <SidebarHeader>
@@ -217,12 +134,12 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<SidebarMenuItem> <SidebarMenuItem>
<SidebarMenuButton size="lg" asChild> <SidebarMenuButton size="lg" asChild>
<Link href="/dashboard"> <Link href="/dashboard">
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground"> <div className="bg-primary text-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<Logo size={24} className="text-current" /> <Logo size={20} className="text-current" />
</div> </div>
<div className="grid flex-1 text-left text-sm leading-tight"> <div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">ShadcnStore</span> <span className="truncate font-medium">İşletmem</span>
<span className="truncate text-xs">Admin Dashboard</span> <span className="text-muted-foreground truncate text-xs">{company.name}</span>
</div> </div>
</Link> </Link>
</SidebarMenuButton> </SidebarMenuButton>
@@ -230,14 +147,13 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</SidebarMenu> </SidebarMenu>
</SidebarHeader> </SidebarHeader>
<SidebarContent> <SidebarContent>
{data.navGroups.map((group) => ( {navGroups.map((group) => (
<NavMain key={group.label} label={group.label} items={group.items} /> <NavMain key={group.label} label={group.label} items={group.items} />
))} ))}
</SidebarContent> </SidebarContent>
<SidebarFooter> <SidebarFooter>
<SidebarNotification /> <NavUser user={user} />
<NavUser user={data.user} />
</SidebarFooter> </SidebarFooter>
</Sidebar> </Sidebar>
) );
} }
-105
View File
@@ -1,105 +0,0 @@
"use client"
import * as React from "react"
import { AppSidebar } from "@/components/app-sidebar"
import { SiteHeader } from "@/components/site-header"
import { SiteFooter } from "@/components/site-footer"
import { ThemeCustomizer, ThemeCustomizerTrigger } from "@/components/theme-customizer"
import { UpgradeToProButton } from "@/components/upgrade-to-pro-button"
import { useSidebarConfig } from "@/hooks/use-sidebar-config"
import {
SidebarInset,
SidebarProvider,
} from "@/components/ui/sidebar"
interface BaseLayoutProps {
children: React.ReactNode
title?: string
description?: string
}
export function BaseLayout({ children, title, description }: BaseLayoutProps) {
const [themeCustomizerOpen, setThemeCustomizerOpen] = React.useState(false)
const { config } = useSidebarConfig()
return (
<SidebarProvider
style={
{
"--sidebar-width": "16rem",
"--sidebar-width-icon": "3rem",
"--header-height": "calc(var(--spacing) * 14)",
} as React.CSSProperties
}
className={config.collapsible === "none" ? "sidebar-none-mode" : ""}
>
{config.side === "left" ? (
<>
<AppSidebar
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
{title && (
<div className="px-4 lg:px-6">
<div className="flex flex-col gap-2">
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
{description && (
<p className="text-muted-foreground">{description}</p>
)}
</div>
</div>
)}
{children}
</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
</>
) : (
<>
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col">
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
{title && (
<div className="px-4 lg:px-6">
<div className="flex flex-col gap-2">
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
{description && (
<p className="text-muted-foreground">{description}</p>
)}
</div>
</div>
)}
{children}
</div>
</div>
</div>
<SiteFooter />
</SidebarInset>
<AppSidebar
variant={config.variant}
collapsible={config.collapsible}
side={config.side}
/>
</>
)}
{/* Theme Customizer */}
<ThemeCustomizerTrigger onClick={() => setThemeCustomizerOpen(true)} />
<ThemeCustomizer
open={themeCustomizerOpen}
onOpenChange={setThemeCustomizerOpen}
/>
<UpgradeToProButton />
</SidebarProvider>
)
}
+40 -33
View File
@@ -1,15 +1,15 @@
"use client" "use client";
import { useTransition } from "react";
import { import {
BellDot,
CircleUser,
CreditCard, CreditCard,
EllipsisVertical, EllipsisVertical,
LogOut, LogOut,
BellDot, } from "lucide-react";
CircleUser, import Link from "next/link";
} from "lucide-react"
import Link from "next/link"
import { Logo } from "@/components/logo"
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -18,24 +18,33 @@ import {
DropdownMenuLabel, DropdownMenuLabel,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu" } from "@/components/ui/dropdown-menu";
import { import {
SidebarMenu, SidebarMenu,
SidebarMenuButton, SidebarMenuButton,
SidebarMenuItem, SidebarMenuItem,
useSidebar, useSidebar,
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar";
import { signOutAction } from "@/lib/appwrite/auth-actions";
function initials(name: string) {
const parts = name.trim().split(/\s+/).slice(0, 2);
return parts.map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
}
export function NavUser({ export function NavUser({
user, user,
}: { }: {
user: { user: { name: string; email: string };
name: string
email: string
avatar: string
}
}) { }) {
const { isMobile } = useSidebar() const { isMobile } = useSidebar();
const [isPending, startTransition] = useTransition();
const handleSignOut = () => {
startTransition(async () => {
await signOutAction();
});
};
return ( return (
<SidebarMenu> <SidebarMenu>
@@ -46,14 +55,12 @@ export function NavUser({
size="lg" size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer" className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground cursor-pointer"
> >
<div className="flex h-8 w-8 items-center justify-center rounded-lg"> <div className="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg text-sm font-medium">
< Logo size={28} /> {initials(user.name)}
</div> </div>
<div className="grid flex-1 text-left text-sm leading-tight"> <div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span> <span className="truncate font-medium">{user.name}</span>
<span className="text-muted-foreground truncate text-xs"> <span className="text-muted-foreground truncate text-xs">{user.email}</span>
{user.email}
</span>
</div> </div>
<EllipsisVertical className="ml-auto size-4" /> <EllipsisVertical className="ml-auto size-4" />
</SidebarMenuButton> </SidebarMenuButton>
@@ -66,14 +73,12 @@ export function NavUser({
> >
<DropdownMenuLabel className="p-0 font-normal"> <DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<div className="h-8 w-8 rounded-lg"> <div className="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg text-sm font-medium">
< Logo size={28} /> {initials(user.name)}
</div> </div>
<div className="grid flex-1 text-left text-sm leading-tight"> <div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span> <span className="truncate font-medium">{user.name}</span>
<span className="text-muted-foreground truncate text-xs"> <span className="text-muted-foreground truncate text-xs">{user.email}</span>
{user.email}
</span>
</div> </div>
</div> </div>
</DropdownMenuLabel> </DropdownMenuLabel>
@@ -82,32 +87,34 @@ export function NavUser({
<DropdownMenuItem asChild className="cursor-pointer"> <DropdownMenuItem asChild className="cursor-pointer">
<Link href="/settings/account"> <Link href="/settings/account">
<CircleUser /> <CircleUser />
Account Profil
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild className="cursor-pointer"> <DropdownMenuItem asChild className="cursor-pointer">
<Link href="/settings/billing"> <Link href="/settings/billing">
<CreditCard /> <CreditCard />
Billing Plan & Faturalama
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild className="cursor-pointer"> <DropdownMenuItem asChild className="cursor-pointer">
<Link href="/settings/notifications"> <Link href="/settings/notifications">
<BellDot /> <BellDot />
Notifications Bildirimler
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuGroup> </DropdownMenuGroup>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild className="cursor-pointer"> <DropdownMenuItem
<Link href="/sign-in"> onClick={handleSignOut}
<LogOut /> disabled={isPending}
Log out className="cursor-pointer"
</Link> >
<LogOut />
{isPending ? "Çıkış yapılıyor..." : "Çıkış yap"}
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</SidebarMenuItem> </SidebarMenuItem>
</SidebarMenu> </SidebarMenu>
) );
} }
+30 -49
View File
@@ -1,26 +1,29 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { Button } from "@/components/ui/button" import { Building2 } from "lucide-react";
import { Separator } from "@/components/ui/separator"
import { SidebarTrigger } from "@/components/ui/sidebar"
import { CommandSearch, SearchTrigger } from "@/components/command-search"
import { ModeToggle } from "@/components/mode-toggle"
export function SiteHeader() { import { CommandSearch, SearchTrigger } from "@/components/command-search";
const [searchOpen, setSearchOpen] = React.useState(false) import { ModeToggle } from "@/components/mode-toggle";
import { Separator } from "@/components/ui/separator";
import { SidebarTrigger } from "@/components/ui/sidebar";
import type { ShellCompany } from "@/app/(dashboard)/dashboard-shell";
export function SiteHeader({ company }: { company?: ShellCompany }) {
const [searchOpen, setSearchOpen] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
const down = (e: KeyboardEvent) => { const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) { if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault() e.preventDefault();
setSearchOpen((open) => !open) setSearchOpen((open) => !open);
} }
} };
document.addEventListener("keydown", down) document.addEventListener("keydown", down);
return () => document.removeEventListener("keydown", down) return () => document.removeEventListener("keydown", down);
}, []) }, []);
return ( return (
<> <>
@@ -31,45 +34,23 @@ export function SiteHeader() {
orientation="vertical" orientation="vertical"
className="mx-2 data-[orientation=vertical]:h-4" className="mx-2 data-[orientation=vertical]:h-4"
/> />
<div className="flex-1 max-w-sm">
<SearchTrigger onClick={() => setSearchOpen(true)} /> {company && (
</div> <div className="text-muted-foreground hidden items-center gap-1.5 text-sm md:flex">
<Building2 className="size-3.5" />
<span className="max-w-[260px] truncate">{company.name}</span>
</div>
)}
<div className="ml-auto flex items-center gap-2"> <div className="ml-auto flex items-center gap-2">
<Button variant="ghost" asChild size="sm" className="hidden sm:flex"> <div className="hidden w-full max-w-sm md:block">
<a <SearchTrigger onClick={() => setSearchOpen(true)} />
href="https://shadcnstore.com/blocks" </div>
rel="noopener noreferrer"
target="_blank"
className="dark:text-foreground"
>
Blocks
</a>
</Button>
<Button variant="ghost" asChild size="sm" className="hidden sm:flex">
<a
href="/landing"
rel="noopener noreferrer"
target="_blank"
className="dark:text-foreground"
>
Landing Page
</a>
</Button>
<Button variant="ghost" asChild size="sm" className="hidden sm:flex">
<a
href="https://github.com/silicondeck/shadcn-dashboard-landing-template"
rel="noopener noreferrer"
target="_blank"
className="dark:text-foreground"
>
GitHub
</a>
</Button>
<ModeToggle /> <ModeToggle />
</div> </div>
</div> </div>
</header> </header>
<CommandSearch open={searchOpen} onOpenChange={setSearchOpen} /> <CommandSearch open={searchOpen} onOpenChange={setSearchOpen} />
</> </>
) );
} }