diff --git a/app/admin/share/page.tsx b/app/admin/share/page.tsx new file mode 100644 index 0000000..b393f29 --- /dev/null +++ b/app/admin/share/page.tsx @@ -0,0 +1,185 @@ +import Link from "next/link"; +import { desc, sql } from "drizzle-orm"; +import { ArrowUpRight, Plus } from "lucide-react"; +import { AdminShellServer } from "@/components/admin-shell-server"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { db } from "@/db"; +import { shareTexts } from "@/db/schema"; + +export const dynamic = "force-dynamic"; + +function formatBangkok(date: Date) { + return new Intl.DateTimeFormat("en-GB", { + dateStyle: "medium", + timeStyle: "short", + timeZone: "Asia/Bangkok", + }).format(date); +} + +export default async function AdminSharePage() { + const shares = await db + .select({ + id: shareTexts.id, + authorName: shareTexts.authorName, + source: shareTexts.source, + description: shareTexts.description, + contentPreview: sql`left(${shareTexts.content}, 160)`, + createdAt: shareTexts.createdAt, + }) + .from(shareTexts) + .orderBy(desc(shareTexts.createdAt)); + + return ( + +
+
+
+

Share

+

+ {shares.length.toLocaleString("en-US")} published links +

+
+ +
+ + + + All shares + + Open a public page without loading the full text into this list. + + + +
+ + + + Share + Preview + Author + Source + Created + Open + + + + {shares.length === 0 ? ( + + + No shares yet. + + + ) : ( + shares.map((share) => ( + + + + {share.id} + + + +

+ {share.description || share.contentPreview} +

+
+ {share.authorName} + + + {share.source === "bot" ? "Bot" : "Web"} + + + {formatBangkok(share.createdAt)} + + + +
+ )) + )} +
+
+
+ +
+ {shares.length === 0 ? ( +

+ No shares yet. +

+ ) : ( + shares.map((share) => ( + + + + {share.id} + + + {share.authorName} ยท {formatBangkok(share.createdAt)} + + + +

+ {share.description || share.contentPreview} +

+
+ + {share.source === "bot" ? "Bot" : "Web"} + + +
+
+
+ )) + )} +
+
+
+
+
+ ); +} diff --git a/components/admin-shell.tsx b/components/admin-shell.tsx index 3e8c852..9dc6756 100644 --- a/components/admin-shell.tsx +++ b/components/admin-shell.tsx @@ -15,7 +15,15 @@ import { SidebarInset, SidebarTrigger, } from "@/components/ui/sidebar"; -import { ClipboardList, KeyRound, Server, LayoutDashboard, Upload, LucideIcon } from "lucide-react"; +import { + ClipboardList, + KeyRound, + LayoutDashboard, + LucideIcon, + Server, + Share2, + Upload, +} from "lucide-react"; import { ThemeToggle } from "@/components/theme-toggle"; import { DropdownMenuAvatar, DiscordMemberProfile } from "@/components/avatar"; @@ -40,6 +48,12 @@ const NAV_ITEMS: NavItem[] = [ icon: ClipboardList, isActive: (pathname) => pathname === "/admin/form", }, + { + href: "/admin/share", + label: "Share", + icon: Share2, + isActive: (pathname) => pathname.startsWith("/admin/share"), + }, { href: "/admin/backend", label: "Backend",