feat : update
This commit is contained in:
@@ -1,182 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -69,6 +69,29 @@ export async function getXpLeaderboardPage(
|
|||||||
return buildXpLeaderboard(rows, requestedPage, searchQuery);
|
return buildXpLeaderboard(rows, requestedPage, searchQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMemberXpStats(userId: string) {
|
||||||
|
const database = getLeaderboardDatabase();
|
||||||
|
const [sourceRows, leaderboard] = await Promise.all([
|
||||||
|
database
|
||||||
|
.select({
|
||||||
|
level: userLevels.level,
|
||||||
|
totalXp: userLevels.xp,
|
||||||
|
})
|
||||||
|
.from(userLevels)
|
||||||
|
.where(eq(userLevels.userId, userId))
|
||||||
|
.limit(1),
|
||||||
|
getXpLeaderboardPage(1, userId),
|
||||||
|
]);
|
||||||
|
const rankedRow = leaderboard.rows.find((row) => row.userId === userId);
|
||||||
|
const sourceRow = sourceRows[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
level: sourceRow?.level ?? 0,
|
||||||
|
totalXp: sourceRow?.totalXp ?? "0",
|
||||||
|
rank: rankedRow?.rank ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function getVoiceLeaderboardPage(
|
export async function getVoiceLeaderboardPage(
|
||||||
currentChannelIds: string[],
|
currentChannelIds: string[],
|
||||||
requestedPage: number,
|
requestedPage: number,
|
||||||
@@ -108,3 +131,21 @@ export async function getVoiceLeaderboardPage(
|
|||||||
searchQuery,
|
searchQuery,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMemberVoiceStats(
|
||||||
|
userId: string,
|
||||||
|
currentChannelIds: string[],
|
||||||
|
) {
|
||||||
|
const leaderboard = await getVoiceLeaderboardPage(
|
||||||
|
currentChannelIds,
|
||||||
|
1,
|
||||||
|
undefined,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
const rankedRow = leaderboard.rows.find((row) => row.userId === userId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalSeconds: rankedRow?.totalSeconds ?? "0",
|
||||||
|
rank: rankedRow?.rank ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+2
-14
@@ -11,7 +11,7 @@ const nextConfig: NextConfig = {
|
|||||||
root: __dirname,
|
root: __dirname,
|
||||||
},
|
},
|
||||||
serverExternalPackages: ["bun"],
|
serverExternalPackages: ["bun"],
|
||||||
allowedDevOrigins: ['test.gunshiz.top'],
|
allowedDevOrigins: ['test.gunshiz.top', 'astral.neko-piranha.ts.net'],
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
@@ -21,19 +21,7 @@ const nextConfig: NextConfig = {
|
|||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'cdn.discordapp.com',
|
hostname: 'cdn.discordapp.com',
|
||||||
},
|
}
|
||||||
{
|
|
||||||
protocol: 'https',
|
|
||||||
hostname: 'p16-sign-sg.tiktokcdn.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocol: 'https',
|
|
||||||
hostname: 'p16-sign.tiktokcdn-us.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocol: 'https',
|
|
||||||
hostname: '*.tiktokcdn.com',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user