This commit is contained in:
2025-08-09 13:45:16 +02:00
parent 298fae916b
commit d322283064
28 changed files with 135 additions and 80 deletions

View File

@@ -0,0 +1,16 @@
type DashBoardTitleProps = {
renderSubtitle: true
subtitle: string
} | {
renderSubtitle?: false
subtitle?: never
}
export default function DashBoardTitle({ title, subtitle, renderSubtitle }: DashBoardTitleProps & { title: string }) {
return (
<div className="pb-6">
<h1 className="mb-2 text-2xl font-bold text-foreground">{title}</h1>
{renderSubtitle && <p className="text-sm text-muted-foreground">{subtitle}</p>}
</div>
)
}

View File

@@ -0,0 +1,7 @@
import { getSession } from "@/lib/auth/session"
export default async function PasswordSettings() {
const {} = await getSession()
return null
}

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { HomeIcon, LayoutDashboard, List, PanelLeft, Plus, User2 } from "lucide-react" import { HomeIcon, KeyRoundIcon, LayoutDashboard, List, PanelLeft, Plus, User2 } from "lucide-react"
import Link from "next/link" import Link from "next/link"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
@@ -20,24 +20,37 @@ import {
import { SidebarThemeToggle } from "./sidebar-theme-toggle" import { SidebarThemeToggle } from "./sidebar-theme-toggle"
import { SidebarUserDropdown } from "./sidebar-user-dropdown" import { SidebarUserDropdown } from "./sidebar-user-dropdown"
const items = [ const dashboardItems = [
{ {
title: "Dashboard", title: "Dashboard",
url: "/dashboard", url: "/admin/dashboard",
icon: LayoutDashboard icon: LayoutDashboard
}, },
{ {
title: "List", title: "List",
url: "/dashboard/list", url: "/admin/dashboard/list",
icon: List icon: List
}, },
{ {
title: "Create", title: "Create",
url: "/dashboard/create", url: "/admin/dashboard/create",
icon: Plus icon: Plus
} }
] ]
const userItems = [
{
title: "Profile",
url: "/admin/user",
icon: User2
},
{
title: "Auth",
url: "/admin/user/auth",
icon: KeyRoundIcon
}
]
export function DashboardSidebar() { export function DashboardSidebar() {
const pathname = usePathname() const pathname = usePathname()
const { toggleSidebar } = useSidebar() const { toggleSidebar } = useSidebar()
@@ -81,7 +94,7 @@ export function DashboardSidebar() {
</SidebarGroupLabel> </SidebarGroupLabel>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{items.map((item) => ( {dashboardItems.map((item) => (
<SidebarMenuItem key={item.title}> <SidebarMenuItem key={item.title}>
<SidebarMenuButton <SidebarMenuButton
asChild asChild
@@ -99,23 +112,25 @@ export function DashboardSidebar() {
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel> <SidebarGroupLabel className="group-data-[collapsible=icon]:hidden">
Other User
</SidebarGroupLabel> </SidebarGroupLabel>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> {userItems.map((item) => (
<SidebarMenuButton <SidebarMenuItem key={item.title}>
asChild <SidebarMenuButton
isActive={pathname === "/dashboard/user"} asChild
tooltip="User Profile" isActive={pathname === item.url}
> tooltip={item.title}
<Link href="/dashboard/user"> >
<User2 /> <Link href={item.url}>
<span>User Profile</span> <item.icon />
</Link> <span>{item.title}</span>
</SidebarMenuButton> </Link>
</SidebarMenuItem> </SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>

View File

@@ -226,12 +226,12 @@ function EditUrlForm({ data }: { data: typeof urls.$inferSelect }) {
toast.error(res.message) toast.error(res.message)
} else { } else {
toast.success(res.message) toast.success(res.message)
router.push("/dashboard/list") router.push("/admin/dashboard/list")
} }
} }
const handleCancel = () => { const handleCancel = () => {
router.push("/dashboard/list") router.push("/admin/dashboard/list")
} }
return ( return (

View File

@@ -283,7 +283,7 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) {
Copy URL Copy URL
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href={`/dashboard/edit/${urlRecord.id}`}> <Link href={`/admin/dashboard/edit/${urlRecord.id}`}>
<Copy className="mr-2 w-4 h-4" /> <Copy className="mr-2 w-4 h-4" />
Edit URL Edit URL
</Link> </Link>

View File

@@ -1,15 +1,15 @@
import { getSession } from "@/lib/auth/session" import { getSession } from "@/lib/auth/session"
import { UrlFormCard } from "../_components/url-form-card" import { UrlFormCard } from "../../_components/url-form-card"
export default async function DashboardCreatePage() { export default async function DashboardCreatePage() {
const { session, redirect } = await getSession() const { session, redirectToSignIn } = await getSession()
if (!session) { if (!session) {
redirect("/sign-in") redirectToSignIn()
} }
return ( return (
<div className="p-6 space-y-6"> <div className="space-y-6">
<div> <div>
<h1 className="mb-2 text-2xl font-bold text-foreground">Create Short Link</h1> <h1 className="mb-2 text-2xl font-bold text-foreground">Create Short Link</h1>
</div> </div>

View File

@@ -25,12 +25,12 @@ export default function NotFound() {
</ul> </ul>
<div className="flex gap-2"> <div className="flex gap-2">
<Button asChild> <Button asChild>
<Link href="/dashboard/list"> <Link href="/admin/dashboard/list">
View All Links View All Links
</Link> </Link>
</Button> </Button>
<Button asChild variant="outline"> <Button asChild variant="outline">
<Link href="/dashboard/create"> <Link href="/admin/dashboard/create">
Create New Link Create New Link
</Link> </Link>
</Button> </Button>

View File

@@ -1,17 +1,17 @@
import { getSession } from "@/lib/auth/session" import { getSession } from "@/lib/auth/session"
import { getUrlById } from "@/lib/db/urls" import { getUrlById } from "@/lib/db/urls"
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { UrlFormCard } from "../../_components/url-form-card" import { UrlFormCard } from "../../../_components/url-form-card"
export default async function EditPage({ export default async function EditPage({
params params
}: { }: {
params: Promise<{ id: string }> params: Promise<{ id: string }>
}) { }) {
const { session, redirect } = await getSession() const { session, redirectToSignIn } = await getSession()
if (!session) { if (!session) {
redirect("/sign-in") redirectToSignIn()
} }
const { id } = await params const { id } = await params
@@ -23,7 +23,7 @@ export default async function EditPage({
} }
return ( return (
<div className="p-6 space-y-6"> <div className="space-y-6">
<div> <div>
<h1 className="mb-2 text-2xl font-bold text-foreground">Edit Short Link</h1> <h1 className="mb-2 text-2xl font-bold text-foreground">Edit Short Link</h1>
</div> </div>

View File

@@ -1,18 +1,18 @@
import { getSession } from "@/lib/auth/session" import { getSession } from "@/lib/auth/session"
import { getAllUrls } from "@/lib/db/urls" import { getAllUrls } from "@/lib/db/urls"
import { UrlsDataTable } from "../_components/urls-data-table" import { UrlsDataTable } from "../../_components/urls-data-table"
export default async function DashboardListPage() { export default async function DashboardListPage() {
const { session, redirect } = await getSession() const { session, redirectToSignIn } = await getSession()
if (!session) { if (!session) {
redirect("/sign-in") redirectToSignIn()
} }
const urls = await getAllUrls() const urls = await getAllUrls()
return ( return (
<div className="p-6"> <div>
<div className="mb-6"> <div className="mb-6">
<h1 className="block mb-2 text-2xl font-bold text-foreground">URLs</h1> <h1 className="block mb-2 text-2xl font-bold text-foreground">URLs</h1>
<h1 className="block text-muted-foreground">Manage all your shortened URLs.</h1> <h1 className="block text-muted-foreground">Manage all your shortened URLs.</h1>

View File

@@ -1,14 +1,14 @@
import { getSession } from "@/lib/auth/session" import { getSession } from "@/lib/auth/session"
import { getDashboardStats } from "@/lib/dashboard/stats" import { getDashboardStats } from "@/lib/dashboard/stats"
import { LinkIcon, MousePointerClick, TrendingUp } from "lucide-react" import { LinkIcon, MousePointerClick, TrendingUp } from "lucide-react"
import { UrlFormCard } from "./_components/simple-url-form-card" import { UrlFormCard } from "../_components/simple-url-form-card"
import { StatsCard } from "./_components/stats-card" import { StatsCard } from "../_components/stats-card"
export default async function Dashboard() { export default async function Dashboard() {
const { session, redirect } = await getSession() const { session, redirectToSignIn } = await getSession()
if (!session) { if (!session) {
redirect("/sign-in") redirectToSignIn()
} }
const stats = await getDashboardStats() const stats = await getDashboardStats()
@@ -20,7 +20,7 @@ export default async function Dashboard() {
: "No URLs" : "No URLs"
return ( return (
<div className="p-6"> <div>
<h1 className="block mb-4 text-2xl font-bold text-foreground">Dashboard</h1> <h1 className="block mb-4 text-2xl font-bold text-foreground">Dashboard</h1>
<div className="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2 lg:grid-cols-3"> <div className="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2 lg:grid-cols-3">
<StatsCard <StatsCard

View File

@@ -13,7 +13,7 @@ export default function DashboardLayout({
<SidebarClient> <SidebarClient>
<div className="flex w-full min-h-screen"> <div className="flex w-full min-h-screen">
<DashboardSidebar /> <DashboardSidebar />
<main className="overflow-auto flex-1"> <main className="overflow-auto flex-1 p-6">
{children} {children}
</main> </main>
</div> </div>

View File

@@ -0,0 +1,22 @@
import { getSession } from "@/lib/auth/session"
import DashBoardTitle from "../../_components/dashboard-title"
import { PasskeyAdd } from "../../_components/passkey"
import PasskeysList from "../../_components/passkeys-list"
export default async function UserAuthPage() {
const { session, redirectToSignIn } = await getSession()
if (!session) {
redirectToSignIn()
}
return (
<div>
<DashBoardTitle title="User Auth" renderSubtitle subtitle="Manage user settings" />
<div className="flex flex-col gap-6">
<PasskeyAdd />
<PasskeysList />
</div>
</div>
)
}

View File

@@ -0,0 +1,18 @@
import { getSession } from "@/lib/auth/session"
import DashBoardTitle from "../_components/dashboard-title"
import UserProfile from "../_components/user-profile"
export default async function UserPage() {
const { session, redirectToSignIn } = await getSession()
if (!session) {
redirectToSignIn()
}
return (
<div>
<DashBoardTitle title="User Profile" renderSubtitle subtitle="Manage Profile" />
<UserProfile />
</div>
)
}

View File

@@ -1,26 +0,0 @@
import { getSession } from "@/lib/auth/session"
import { PasskeyAdd } from "../_components/passkey"
import PasskeysList from "../_components/passkeys-list"
import UserProfile from "../_components/user-profile"
export default async function UserPage() {
const { session, redirect } = await getSession()
if (!session) {
redirect("/login")
}
return (
<div className="p-6">
<div className="pb-6">
<h1 className="block mb-2 text-2xl font-bold text-foreground">User Profile</h1>
<h1 className="block text-muted-foreground">Manage user settings</h1>
</div>
<div className="flex flex-col gap-6">
<UserProfile />
<PasskeyAdd />
<PasskeysList />
</div>
</div>
)
}

View File

@@ -22,7 +22,7 @@ export function OAuthSignInButton({
setIsLoading(true) setIsLoading(true)
const res = await authClient.signIn.oauth2({ const res = await authClient.signIn.oauth2({
providerId: "authentik", providerId: "authentik",
callbackURL: "/dashboard" callbackURL: "/admin/dashboard"
}) })
if (res && res.error) { if (res && res.error) {
@@ -39,7 +39,7 @@ export function OAuthSignInButton({
const res = await authClient.signIn.passkey({ const res = await authClient.signIn.passkey({
fetchOptions: { fetchOptions: {
onSuccess: () => { onSuccess: () => {
router.push("/dashboard") router.push("/admin/dashboard")
} }
} }
}) })

View File

@@ -4,10 +4,10 @@ import { getSession } from "@/lib/auth/session"
import { LogIn } from "lucide-react" import { LogIn } from "lucide-react"
export default async function SignInPage() { export default async function SignInPage() {
const { session, redirect } = await getSession() const { session, redirectToHome } = await getSession()
if (session) { if (session) {
redirect("/dashboard") redirectToHome()
} }
return ( return (

View File

@@ -85,9 +85,9 @@ export function UserDropdown({ className }: UserDropdownProps) {
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href="/dashboard"> <Link href="/admin/dashboard">
<Settings className="mr-2 w-4 h-4" /> <Settings className="mr-2 w-4 h-4" />
<span>Dashboard</span> <span>Admin</span>
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />

View File

@@ -37,7 +37,7 @@ export async function addUrl(unsafeData: unknown): Promise<Response> {
title: await getWebsiteTitle(data.url) title: await getWebsiteTitle(data.url)
}) })
revalidatePath("/dashboard") revalidatePath("/admin/dashboard")
return { return {
error: false, error: false,

View File

@@ -6,9 +6,12 @@ export async function getSession() {
const session = await auth.api.getSession({ const session = await auth.api.getSession({
headers: await headers() headers: await headers()
}) })
const redirectFunc = (path: string) => { function redirectToSignIn() {
redirect(path) redirect("/sign-in")
}
function redirectToHome() {
redirect("/")
} }
return { session, redirect: redirectFunc } return { session, redirectToSignIn, redirectToHome }
} }

View File

@@ -10,4 +10,4 @@ export async function middleware(request: NextRequest) {
return NextResponse.next() return NextResponse.next()
} }
export const config = { matcher: ["/dashboard"] } export const config = { matcher: ["/admin"] }