Formatting

This commit is contained in:
2025-08-06 18:37:47 +02:00
parent b838d0f8d6
commit 012ea2b963
22 changed files with 84 additions and 90 deletions

View File

@@ -10,11 +10,11 @@ export function SidebarClient({ children }: { children: ReactNode }) {
if (isMobile) { if (isMobile) {
return ( return (
<div className="flex flex-col w-full"> <div className="flex flex-col w-full">
<div className="p-2 border-b flex items-center gap-1"> <div className="flex gap-1 items-center p-2 border-b">
<SidebarTrigger /> <SidebarTrigger />
<span className="text-xl">Linker</span> <span className="text-xl">Linker</span>
</div> </div>
<div className="flex-1 flex">{children}</div> <div className="flex flex-1">{children}</div>
</div> </div>
) )
} }

View File

@@ -20,7 +20,7 @@ export function SidebarThemeToggle() {
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
<SidebarMenuButton disabled tooltip="Theme"> <SidebarMenuButton disabled tooltip="Theme">
<Sun className="h-4 w-4" /> <Sun className="w-4 h-4" />
<span>Theme</span> <span>Theme</span>
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>
@@ -31,11 +31,11 @@ export function SidebarThemeToggle() {
const getIcon = () => { const getIcon = () => {
switch (theme) { switch (theme) {
case "light": case "light":
return <Sun className="h-4 w-4" /> return <Sun className="w-4 h-4" />
case "dark": case "dark":
return <Moon className="h-4 w-4" /> return <Moon className="w-4 h-4" />
default: default:
return <Monitor className="h-4 w-4" /> return <Monitor className="w-4 h-4" />
} }
} }
@@ -61,20 +61,20 @@ export function SidebarThemeToggle() {
> >
{getIcon()} {getIcon()}
<span>Theme ({getThemeLabel()})</span> <span>Theme ({getThemeLabel()})</span>
<ChevronDown className="ml-auto h-4 w-4" /> <ChevronDown className="ml-auto w-4 h-4" />
</SidebarMenuButton> </SidebarMenuButton>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" side="top"> <DropdownMenuContent align="end" side="top">
<DropdownMenuItem onClick={() => setTheme("light")}> <DropdownMenuItem onClick={() => setTheme("light")}>
<Sun className="h-4 w-4 mr-2" /> <Sun className="mr-2 w-4 h-4" />
Light Light
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}> <DropdownMenuItem onClick={() => setTheme("dark")}>
<Moon className="h-4 w-4 mr-2" /> <Moon className="mr-2 w-4 h-4" />
Dark Dark
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}> <DropdownMenuItem onClick={() => setTheme("system")}>
<Monitor className="h-4 w-4 mr-2" /> <Monitor className="mr-2 w-4 h-4" />
System System
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@@ -76,37 +76,37 @@ export function SidebarUserDropdown() {
size="lg" size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
> >
<Avatar className="h-8 w-8 rounded-lg"> <Avatar className="w-8 h-8 rounded-lg">
<AvatarImage src={user.image || undefined} alt={user.name || "User"} /> <AvatarImage src={user.image || undefined} alt={user.name || "User"} />
<AvatarFallback className="rounded-lg">{userInitials}</AvatarFallback> <AvatarFallback className="rounded-lg">{userInitials}</AvatarFallback>
</Avatar> </Avatar>
<div className="grid flex-1 text-left text-sm leading-tight"> <div className="grid flex-1 text-sm leading-tight text-left">
<span className="truncate font-semibold"> <span className="font-semibold truncate">
{user.name || "User"} {user.name || "User"}
</span> </span>
<span className="truncate text-xs text-muted-foreground"> <span className="text-xs truncate text-muted-foreground">
{user.email} {user.email}
</span> </span>
</div> </div>
</SidebarMenuButton> </SidebarMenuButton>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent <DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" className="rounded-lg w-[--radix-dropdown-menu-trigger-width] min-w-56"
side={state === "collapsed" ? "right" : "bottom"} side={state === "collapsed" ? "right" : "bottom"}
align="end" align="end"
sideOffset={4} sideOffset={4}
> >
<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 gap-2 items-center py-1.5 px-1 text-sm text-left">
<Avatar className="h-8 w-8 rounded-lg"> <Avatar className="w-8 h-8 rounded-lg">
<AvatarImage src={user.image || undefined} alt={user.name || "User"} /> <AvatarImage src={user.image || undefined} alt={user.name || "User"} />
<AvatarFallback className="rounded-lg">{userInitials}</AvatarFallback> <AvatarFallback className="rounded-lg">{userInitials}</AvatarFallback>
</Avatar> </Avatar>
<div className="grid flex-1 text-left text-sm leading-tight"> <div className="grid flex-1 text-sm leading-tight text-left">
<span className="truncate font-semibold"> <span className="font-semibold truncate">
{user.name || "User"} {user.name || "User"}
</span> </span>
<span className="truncate text-xs text-muted-foreground"> <span className="text-xs truncate text-muted-foreground">
{user.email} {user.email}
</span> </span>
</div> </div>
@@ -114,7 +114,7 @@ export function SidebarUserDropdown() {
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={handleSignOut}> <DropdownMenuItem onClick={handleSignOut}>
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 w-4 h-4" />
<span>Log out</span> <span>Log out</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@@ -12,7 +12,7 @@ export function StatsCard({ title, value, icon, description }: StatsCardProps) {
return ( return (
<Card> <Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row justify-between items-center pb-2 space-y-0">
<CardTitle className="text-sm font-medium">{title}</CardTitle> <CardTitle className="text-sm font-medium">{title}</CardTitle>
{icon} {icon}
</CardHeader> </CardHeader>

View File

@@ -46,7 +46,7 @@ function AdvancedUrlForm() {
return ( return (
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4"> <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
control={form.control} control={form.control}
@@ -84,7 +84,7 @@ function AdvancedUrlForm() {
control={form.control} control={form.control}
name="forwardQueryParams" name="forwardQueryParams"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4"> <FormItem className="flex justify-between items-center p-4 rounded-lg border">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel className="text-base"> <FormLabel className="text-base">
Forward Query Parameters Forward Query Parameters
@@ -106,7 +106,7 @@ function AdvancedUrlForm() {
control={form.control} control={form.control}
name="crawlable" name="crawlable"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4"> <FormItem className="flex justify-between items-center p-4 rounded-lg border">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel className="text-base"> <FormLabel className="text-base">
Crawlable Crawlable
@@ -237,7 +237,7 @@ function EditUrlForm({ data }: { data: typeof urls.$inferSelect }) {
return ( return (
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4"> <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
control={form.control} control={form.control}
@@ -275,7 +275,7 @@ function EditUrlForm({ data }: { data: typeof urls.$inferSelect }) {
control={form.control} control={form.control}
name="forwardQueryParams" name="forwardQueryParams"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4"> <FormItem className="flex justify-between items-center p-4 rounded-lg border">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel className="text-base"> <FormLabel className="text-base">
Forward Query Parameters Forward Query Parameters
@@ -297,7 +297,7 @@ function EditUrlForm({ data }: { data: typeof urls.$inferSelect }) {
control={form.control} control={form.control}
name="crawlable" name="crawlable"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-4"> <FormItem className="flex justify-between items-center p-4 rounded-lg border">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel className="text-base"> <FormLabel className="text-base">
Crawlable Crawlable

View File

@@ -47,7 +47,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Slug Slug
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -62,7 +62,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
URL URL
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -94,7 +94,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Visits Visits
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -118,7 +118,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Max Visits Max Visits
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -136,7 +136,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Expires Expires
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -155,7 +155,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Created Created
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -173,7 +173,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Updated Updated
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -191,7 +191,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Crawlable Crawlable
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -199,7 +199,7 @@ const columns: ColumnDef<UrlRecord>[] = [
const crawlable = row.getValue("crawlable") as boolean const crawlable = row.getValue("crawlable") as boolean
return ( return (
<div className="flex justify-center"> <div className="flex justify-center">
{crawlable ? <Check className="h-4 w-4 text-green-600" /> : <X className="h-4 w-4 text-red-600" />} {crawlable ? <Check className="w-4 h-4 text-green-600" /> : <X className="w-4 h-4 text-red-600" />}
</div> </div>
) )
} }
@@ -213,7 +213,7 @@ const columns: ColumnDef<UrlRecord>[] = [
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")} onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
> >
Forward Params Forward Params
<ArrowUpDown className="ml-2 h-4 w-4" /> <ArrowUpDown className="ml-2 w-4 h-4" />
</Button> </Button>
) )
}, },
@@ -221,7 +221,7 @@ const columns: ColumnDef<UrlRecord>[] = [
const forwardQueryParams = row.getValue("forwardQueryParams") as boolean const forwardQueryParams = row.getValue("forwardQueryParams") as boolean
return ( return (
<div className="flex justify-center"> <div className="flex justify-center">
{forwardQueryParams ? <Check className="h-4 w-4 text-green-600" /> : <X className="h-4 w-4 text-red-600" />} {forwardQueryParams ? <Check className="w-4 h-4 text-green-600" /> : <X className="w-4 h-4 text-red-600" />}
</div> </div>
) )
} }
@@ -267,9 +267,9 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) {
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0"> <Button variant="ghost" className="p-0 w-8 h-8">
<span className="sr-only">Open menu</span> <span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" /> <MoreHorizontal className="w-4 h-4" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
@@ -279,12 +279,12 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) {
handleCopy(`${window.location.origin}/r/${urlRecord.slug}`) handleCopy(`${window.location.origin}/r/${urlRecord.slug}`)
}} }}
> >
<Copy className="mr-2 h-4 w-4" /> <Copy className="mr-2 w-4 h-4" />
Copy URL Copy URL
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href={`/dashboard/edit/${urlRecord.id}`}> <Link href={`/dashboard/edit/${urlRecord.id}`}>
<Copy className="mr-2 h-4 w-4" /> <Copy className="mr-2 w-4 h-4" />
Edit URL Edit URL
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
@@ -295,7 +295,7 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) {
handleDelete(urlRecord.id) handleDelete(urlRecord.id)
}} }}
> >
<Trash2 className="mr-2 h-4 w-4" /> <Trash2 className="mr-2 w-4 h-4" />
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
@@ -410,7 +410,7 @@ export function UrlsDataTable({ data }: UrlsDataTableProps) {
</TableBody> </TableBody>
</Table> </Table>
</div> </div>
<div className="flex items-center justify-end space-x-2 py-4"> <div className="flex justify-end items-center py-4 space-x-2">
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{table.getFilteredRowModel().rows.length} row(s) total. {table.getFilteredRowModel().rows.length} row(s) total.
</div> </div>

View File

@@ -11,7 +11,7 @@ export default async function DashboardCreatePage() {
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div> <div>
<h1 className="text-2xl font-bold text-foreground mb-2">Create Short Link</h1> <h1 className="mb-2 text-2xl font-bold text-foreground">Create Short Link</h1>
</div> </div>
<UrlFormCard /> <UrlFormCard />
</div> </div>

View File

@@ -4,8 +4,8 @@ import Link from "next/link"
export default function NotFound() { export default function NotFound() {
return ( return (
<div className="container mx-auto py-8"> <div className="container py-8 mx-auto">
<div className="max-w-2xl mx-auto"> <div className="mx-auto max-w-2xl">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>URL Not Found</CardTitle> <CardTitle>URL Not Found</CardTitle>
@@ -18,7 +18,7 @@ export default function NotFound() {
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
This could happen if: This could happen if:
</p> </p>
<ul className="list-disc list-inside text-sm text-muted-foreground space-y-1"> <ul className="space-y-1 text-sm list-disc list-inside text-muted-foreground">
<li>The link has been deleted</li> <li>The link has been deleted</li>
<li>You don&apos;t have permission to edit this link</li> <li>You don&apos;t have permission to edit this link</li>
<li>The link ID is invalid</li> <li>The link ID is invalid</li>

View File

@@ -25,7 +25,7 @@ export default async function EditPage({
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div> <div>
<h1 className="text-2xl font-bold text-foreground mb-2">Edit Short Link</h1> <h1 className="mb-2 text-2xl font-bold text-foreground">Edit Short Link</h1>
</div> </div>
<UrlFormCard editMode data={url} /> <UrlFormCard editMode data={url} />
</div> </div>

View File

@@ -11,9 +11,9 @@ export default function DashboardLayout({
return ( return (
<SidebarProvider> <SidebarProvider>
<SidebarClient> <SidebarClient>
<div className="min-h-screen flex w-full"> <div className="flex w-full min-h-screen">
<DashboardSidebar /> <DashboardSidebar />
<main className="flex-1 overflow-auto"> <main className="overflow-auto flex-1">
{children} {children}
</main> </main>
</div> </div>

View File

@@ -14,8 +14,8 @@ export default async function DashboardListPage() {
return ( return (
<div className="p-6"> <div className="p-6">
<div className="mb-6"> <div className="mb-6">
<h1 className="text-2xl font-bold text-foreground mb-2 block">URLs</h1> <h1 className="block mb-2 text-2xl font-bold text-foreground">URLs</h1>
<h1 className="text-muted-foreground block">Manage all your shortened URLs.</h1> <h1 className="block text-muted-foreground">Manage all your shortened URLs.</h1>
</div> </div>
<UrlsDataTable data={urls} /> <UrlsDataTable data={urls} />
</div> </div>

View File

@@ -13,7 +13,6 @@ export default async function Dashboard() {
const stats = await getDashboardStats() const stats = await getDashboardStats()
// Determine the most visited URL display value
const mostVisitedDisplay = stats.mostVisitedUrl const mostVisitedDisplay = stats.mostVisitedUrl
? stats.mostVisitedUrl.visitCount > 0 ? stats.mostVisitedUrl.visitCount > 0
? `${stats.mostVisitedUrl.title || stats.mostVisitedUrl.slug || "Untitled"} (${stats.mostVisitedUrl.visitCount})` ? `${stats.mostVisitedUrl.title || stats.mostVisitedUrl.slug || "Untitled"} (${stats.mostVisitedUrl.visitCount})`
@@ -22,24 +21,24 @@ export default async function Dashboard() {
return ( return (
<div className="p-6"> <div className="p-6">
<h1 className="text-2xl font-bold text-foreground mb-4 block">Dashboard</h1> <h1 className="block mb-4 text-2xl font-bold text-foreground">Dashboard</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6"> <div className="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2 lg:grid-cols-3">
<StatsCard <StatsCard
title="Shortened URLs" title="Shortened URLs"
value={stats.totalUrls} value={stats.totalUrls}
icon={<LinkIcon className="h-4 w-4 text-muted-foreground" />} icon={<LinkIcon className="w-4 h-4 text-muted-foreground" />}
description="Total number of shortened links" description="Total number of shortened links"
/> />
<StatsCard <StatsCard
title="Total Visits" title="Total Visits"
value={stats.totalVisits} value={stats.totalVisits}
icon={<MousePointerClick className="h-4 w-4 text-muted-foreground" />} icon={<MousePointerClick className="w-4 h-4 text-muted-foreground" />}
description="Combined visits across all links" description="Combined visits across all links"
/> />
<StatsCard <StatsCard
title="Most Visited URL" title="Most Visited URL"
value={mostVisitedDisplay} value={mostVisitedDisplay}
icon={<TrendingUp className="h-4 w-4 text-muted-foreground" />} icon={<TrendingUp className="w-4 h-4 text-muted-foreground" />}
description="Most popular shortened link" description="Most popular shortened link"
/> />
</div> </div>

View File

@@ -10,18 +10,18 @@ export default function PublicLayout({
}) { }) {
return ( return (
<> <>
<nav className="bg-background shadow-sm border-b border-border"> <nav className="border-b shadow-sm bg-background border-border">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16"> <div className="flex justify-between items-center h-16">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<Link href="/"> <Link href="/">
<h1 className="text-xl font-bold text-foreground hover:text-muted-foreground transition-colors cursor-pointer"> <h1 className="text-xl font-bold transition-colors cursor-pointer text-foreground hover:text-muted-foreground">
Linker Linker
</h1> </h1>
</Link> </Link>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex gap-2 items-center">
<ThemeToggle /> <ThemeToggle />
<UserDropdown /> <UserDropdown />
</div> </div>

View File

@@ -2,7 +2,7 @@ import Link from "next/link"
export default function NotFound() { export default function NotFound() {
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className="flex justify-center items-center min-h-screen">
<div className="text-center"> <div className="text-center">
<h1 className="text-6xl font-bold text-gray-900 dark:text-gray-100">404</h1> <h1 className="text-6xl font-bold text-gray-900 dark:text-gray-100">404</h1>
<h2 className="mt-4 text-2xl font-semibold text-gray-700 dark:text-gray-300"> <h2 className="mt-4 text-2xl font-semibold text-gray-700 dark:text-gray-300">
@@ -13,7 +13,7 @@ export default function NotFound() {
</p> </p>
<Link <Link
href="/" href="/"
className="mt-6 inline-block rounded-lg bg-blue-600 px-6 py-3 text-white hover:bg-blue-700 transition-colors" className="inline-block py-3 px-6 mt-6 text-white bg-blue-600 rounded-lg transition-colors hover:bg-blue-700"
> >
Go Home Go Home
</Link> </Link>

View File

@@ -38,13 +38,13 @@ export function OAuthSignInButton({
{isLoading ? {isLoading ?
( (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className="mr-2 w-4 h-4 animate-spin" />
Signing in... Signing in...
</> </>
) : ) :
( (
<> <>
<AuthentikIcon className="mr-2 h-5 w-5" /> <AuthentikIcon className="mr-2 w-5 h-5" />
Sign in with Authentik Sign in with Authentik
</> </>
)} )}

View File

@@ -11,12 +11,12 @@ export default async function SignInPage() {
} }
return ( return (
<div className="min-h-screen flex items-center justify-center bg-background p-4"> <div className="flex justify-center items-center p-4 min-h-screen bg-background">
<Card className="w-full max-w-md shadow-xl"> <Card className="w-full max-w-md shadow-xl">
<CardHeader className="space-y-1 text-center"> <CardHeader className="space-y-1 text-center">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="p-3 bg-primary/10 rounded-full"> <div className="p-3 rounded-full bg-primary/10">
<LogIn className="h-8 w-8 text-primary" /> <LogIn className="w-8 h-8 text-primary" />
</div> </div>
</div> </div>
<CardTitle className="text-2xl font-bold">Welcome back</CardTitle> <CardTitle className="text-2xl font-bold">Welcome back</CardTitle>

View File

@@ -28,11 +28,11 @@ export function DatePicker({
!value && "text-muted-foreground" !value && "text-muted-foreground"
)} )}
> >
<CalendarIcon className="mr-2 h-4 w-4" /> <CalendarIcon className="mr-2 w-4 h-4" />
{value ? format(value, "PPP") : <span>Pick a date</span>} {value ? format(value, "PPP") : <span>Pick a date</span>}
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start"> <PopoverContent className="p-0 w-auto" align="start">
<Calendar <Calendar
mode="single" mode="single"
selected={value ?? undefined} selected={value ?? undefined}

View File

@@ -44,15 +44,15 @@ export function ThemeToggle() {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}> <DropdownMenuItem onClick={() => setTheme("light")}>
<Sun className="mr-2 h-4 w-4" /> <Sun className="mr-2 w-4 h-4" />
<span>Light</span> <span>Light</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}> <DropdownMenuItem onClick={() => setTheme("dark")}>
<Moon className="mr-2 h-4 w-4" /> <Moon className="mr-2 w-4 h-4" />
<span>Dark</span> <span>Dark</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}> <DropdownMenuItem onClick={() => setTheme("system")}>
<Monitor className="mr-2 h-4 w-4" /> <Monitor className="mr-2 w-4 h-4" />
<span>System</span> <span>System</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@@ -63,8 +63,8 @@ export function UserDropdown({ className }: UserDropdownProps) {
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full"> <Button variant="ghost" className="relative w-8 h-8 rounded-full">
<Avatar className="h-8 w-8"> <Avatar className="w-8 h-8">
<AvatarImage src={user.image || undefined} alt={user.name || "User"} /> <AvatarImage src={user.image || undefined} alt={user.name || "User"} />
<AvatarFallback>{userInitials}</AvatarFallback> <AvatarFallback>{userInitials}</AvatarFallback>
</Avatar> </Avatar>
@@ -84,13 +84,13 @@ export function UserDropdown({ className }: UserDropdownProps) {
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href="/dashboard"> <Link href="/dashboard">
<Settings className="mr-2 h-4 w-4" /> <Settings className="mr-2 w-4 h-4" />
<span>Dashboard</span> <span>Dashboard</span>
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={handleSignOut}> <DropdownMenuItem onClick={handleSignOut}>
<LogOut className="mr-2 h-4 w-4" /> <LogOut className="mr-2 w-4 h-4" />
<span>Log out</span> <span>Log out</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@@ -68,7 +68,7 @@ export async function createAdvanceUrl(unsafeData: unknown): Promise<Response> {
...data, ...data,
slug: data.slug ? data.slug.trim() : undefined, slug: data.slug ? data.slug.trim() : undefined,
title: data.title?.length === 0 ? await getWebsiteTitle(data.url) : data.title, title: data.title?.length === 0 ? await getWebsiteTitle(data.url) : data.title,
maxVisits: data.maxVisits ? data.maxVisits : null, maxVisits: data.maxVisits ? data.maxVisits : null
}) })
return { return {

View File

@@ -4,13 +4,8 @@ import { urls, visits } from "../drizzle/schema"
export async function getDashboardStats() { export async function getDashboardStats() {
try { try {
// Get count of shortened URLs
const [urlsCount] = await db.select({ count: count() }).from(urls) const [urlsCount] = await db.select({ count: count() }).from(urls)
// Get count of total visits
const [visitsCount] = await db.select({ count: count() }).from(visits) const [visitsCount] = await db.select({ count: count() }).from(visits)
// Get most visited URL
const mostVisitedUrl = await db const mostVisitedUrl = await db
.select({ .select({
id: urls.id, id: urls.id,

View File

@@ -1,15 +1,15 @@
import crypto from "node:crypto" import crypto from "node:crypto"
import { env } from "./env/server"
import { z } from "zod" import { z } from "zod"
import { env } from "./env/server"
const gravatarSchema = z.object({ const gravatarSchema = z.object({
avatar_url: z.string().url(), avatar_url: z.string().url()
}) })
export async function getGravatar(email: string) { export async function getGravatar(email: string) {
const baseUrl = "https://api.gravatar.com/v3/profiles" const baseUrl = "https://api.gravatar.com/v3/profiles"
const formattedEmail = email.trim().toLowerCase() const formattedEmail = email.trim().toLowerCase()
const hash = crypto.createHash('sha256').update(formattedEmail).digest('hex') const hash = crypto.createHash("sha256").update(formattedEmail).digest("hex")
const res = await fetch(`${baseUrl}/${hash}`, { const res = await fetch(`${baseUrl}/${hash}`, {
headers: { headers: {