feat: move share index to public route
This commit is contained in:
@@ -1,185 +0,0 @@
|
|||||||
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<string>`left(${shareTexts.content}, 160)`,
|
|
||||||
createdAt: shareTexts.createdAt,
|
|
||||||
})
|
|
||||||
.from(shareTexts)
|
|
||||||
.orderBy(desc(shareTexts.createdAt));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AdminShellServer>
|
|
||||||
<main className="mx-auto flex w-full max-w-7xl flex-col gap-6 p-4 pb-12 md:p-6">
|
|
||||||
<header className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Share</h1>
|
|
||||||
<p className="mt-1 text-muted-foreground">
|
|
||||||
{shares.length.toLocaleString("en-US")} published links
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Button asChild>
|
|
||||||
<Link href="/share">
|
|
||||||
<Plus data-icon="inline-start" />
|
|
||||||
Create share
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>All shares</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Open a public page without loading the full text into this list.
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="hidden border md:block">
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>Share</TableHead>
|
|
||||||
<TableHead>Preview</TableHead>
|
|
||||||
<TableHead>Author</TableHead>
|
|
||||||
<TableHead>Source</TableHead>
|
|
||||||
<TableHead>Created</TableHead>
|
|
||||||
<TableHead className="text-right">Open</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{shares.length === 0 ? (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
colSpan={6}
|
|
||||||
className="h-24 text-center text-muted-foreground"
|
|
||||||
>
|
|
||||||
No shares yet.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
) : (
|
|
||||||
shares.map((share) => (
|
|
||||||
<TableRow key={share.id}>
|
|
||||||
<TableCell className="font-mono font-medium">
|
|
||||||
<Link
|
|
||||||
href={`/share/${share.id}`}
|
|
||||||
className="hover:underline"
|
|
||||||
>
|
|
||||||
{share.id}
|
|
||||||
</Link>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<p className="max-w-md truncate text-muted-foreground">
|
|
||||||
{share.description || share.contentPreview}
|
|
||||||
</p>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{share.authorName}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
share.source === "bot" ? "secondary" : "outline"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{share.source === "bot" ? "Bot" : "Web"}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{formatBangkok(share.createdAt)}</TableCell>
|
|
||||||
<TableCell className="text-right">
|
|
||||||
<Button variant="ghost" size="icon-sm" asChild>
|
|
||||||
<Link
|
|
||||||
href={`/share/${share.id}`}
|
|
||||||
aria-label={`Open share ${share.id}`}
|
|
||||||
>
|
|
||||||
<ArrowUpRight />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 md:hidden">
|
|
||||||
{shares.length === 0 ? (
|
|
||||||
<p className="py-8 text-center text-sm text-muted-foreground">
|
|
||||||
No shares yet.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
shares.map((share) => (
|
|
||||||
<Card key={share.id} size="sm">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="font-mono">
|
|
||||||
<Link href={`/share/${share.id}`}>{share.id}</Link>
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
{share.authorName} · {formatBangkok(share.createdAt)}
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="line-clamp-2 text-sm text-muted-foreground">
|
|
||||||
{share.description || share.contentPreview}
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center justify-between gap-3">
|
|
||||||
<Badge
|
|
||||||
variant={
|
|
||||||
share.source === "bot" ? "secondary" : "outline"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{share.source === "bot" ? "Bot" : "Web"}
|
|
||||||
</Badge>
|
|
||||||
<Button variant="outline" size="sm" asChild>
|
|
||||||
<Link href={`/share/${share.id}`}>
|
|
||||||
Open
|
|
||||||
<ArrowUpRight data-icon="inline-end" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</main>
|
|
||||||
</AdminShellServer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { desc, sql } from "drizzle-orm";
|
||||||
|
import { ArrowUpRight, Plus } from "lucide-react";
|
||||||
|
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 AllSharesPage() {
|
||||||
|
const shares = await db
|
||||||
|
.select({
|
||||||
|
id: shareTexts.id,
|
||||||
|
authorName: shareTexts.authorName,
|
||||||
|
source: shareTexts.source,
|
||||||
|
description: shareTexts.description,
|
||||||
|
contentPreview: sql<string>`left(${shareTexts.content}, 160)`,
|
||||||
|
createdAt: shareTexts.createdAt,
|
||||||
|
})
|
||||||
|
.from(shareTexts)
|
||||||
|
.orderBy(desc(shareTexts.createdAt));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col gap-6">
|
||||||
|
<header className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight">Share</h1>
|
||||||
|
<p className="mt-1 text-muted-foreground">
|
||||||
|
{shares.length.toLocaleString("th-TH")} ลิงก์สาธารณะ
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button asChild>
|
||||||
|
<Link href="/share">
|
||||||
|
<Plus data-icon="inline-start" />
|
||||||
|
สร้างลิงก์ใหม่
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>ข้อความที่แชร์ทั้งหมด</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
เลือกข้อความเพื่อเปิดหน้าสาธารณะ
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="hidden border md:block">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Share</TableHead>
|
||||||
|
<TableHead>Preview</TableHead>
|
||||||
|
<TableHead>Author</TableHead>
|
||||||
|
<TableHead>Source</TableHead>
|
||||||
|
<TableHead>Created</TableHead>
|
||||||
|
<TableHead className="text-right">Open</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{shares.length === 0 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={6}
|
||||||
|
className="h-24 text-center text-muted-foreground"
|
||||||
|
>
|
||||||
|
ยังไม่มีข้อความที่แชร์
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
|
shares.map((share) => (
|
||||||
|
<TableRow key={share.id}>
|
||||||
|
<TableCell className="font-mono font-medium">
|
||||||
|
<Link
|
||||||
|
href={`/share/${share.id}`}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
|
{share.id}
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<p className="max-w-md truncate text-muted-foreground">
|
||||||
|
{share.description || share.contentPreview}
|
||||||
|
</p>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{share.authorName}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge
|
||||||
|
variant={
|
||||||
|
share.source === "bot" ? "secondary" : "outline"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{share.source === "bot" ? "Bot" : "Web"}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{formatBangkok(share.createdAt)}</TableCell>
|
||||||
|
<TableCell className="text-right">
|
||||||
|
<Button variant="ghost" size="icon-sm" asChild>
|
||||||
|
<Link
|
||||||
|
href={`/share/${share.id}`}
|
||||||
|
aria-label={`Open share ${share.id}`}
|
||||||
|
>
|
||||||
|
<ArrowUpRight />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-3 md:hidden">
|
||||||
|
{shares.length === 0 ? (
|
||||||
|
<p className="py-8 text-center text-sm text-muted-foreground">
|
||||||
|
ยังไม่มีข้อความที่แชร์
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
shares.map((share) => (
|
||||||
|
<Card key={share.id} size="sm">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="font-mono">
|
||||||
|
<Link href={`/share/${share.id}`}>{share.id}</Link>
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{share.authorName} · {formatBangkok(share.createdAt)}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="line-clamp-2 text-sm text-muted-foreground">
|
||||||
|
{share.description || share.contentPreview}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<Badge
|
||||||
|
variant={
|
||||||
|
share.source === "bot" ? "secondary" : "outline"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{share.source === "bot" ? "Bot" : "Web"}
|
||||||
|
</Badge>
|
||||||
|
<Button variant="outline" size="sm" asChild>
|
||||||
|
<Link href={`/share/${share.id}`}>
|
||||||
|
เปิด
|
||||||
|
<ArrowUpRight data-icon="inline-end" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { ArrowLeft, Plus } from "lucide-react";
|
import { ArrowLeft, List, Plus } from "lucide-react";
|
||||||
import { DropdownMenuAvatar } from "@/components/avatar";
|
import { DropdownMenuAvatar } from "@/components/avatar";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ThemeToggle } from "@/components/theme-toggle";
|
import { ThemeToggle } from "@/components/theme-toggle";
|
||||||
@@ -36,6 +36,12 @@ export default async function ShareLayout({ children }: { children: React.ReactN
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
<Button variant="ghost" size="sm" asChild>
|
||||||
|
<Link href="/share/all">
|
||||||
|
<List data-icon="inline-start" />
|
||||||
|
ทั้งหมด
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
<Button variant="outline" size="sm" asChild>
|
<Button variant="outline" size="sm" asChild>
|
||||||
<Link href="/share">
|
<Link href="/share">
|
||||||
<Plus data-icon="inline-start" />
|
<Plus data-icon="inline-start" />
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
LucideIcon,
|
LucideIcon,
|
||||||
Server,
|
Server,
|
||||||
Share2,
|
|
||||||
Upload,
|
Upload,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { ThemeToggle } from "@/components/theme-toggle";
|
import { ThemeToggle } from "@/components/theme-toggle";
|
||||||
@@ -48,12 +47,6 @@ const NAV_ITEMS: NavItem[] = [
|
|||||||
icon: ClipboardList,
|
icon: ClipboardList,
|
||||||
isActive: (pathname) => pathname === "/admin/form",
|
isActive: (pathname) => pathname === "/admin/form",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
href: "/admin/share",
|
|
||||||
label: "Share",
|
|
||||||
icon: Share2,
|
|
||||||
isActive: (pathname) => pathname.startsWith("/admin/share"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
href: "/admin/backend",
|
href: "/admin/backend",
|
||||||
label: "Backend",
|
label: "Backend",
|
||||||
|
|||||||
Reference in New Issue
Block a user