feat : update

This commit is contained in:
2026-08-27 14:11:20 +00:00 Unverified
parent eee932259f
commit 992d3f94f9
61 changed files with 76 additions and 55 deletions
@@ -12,7 +12,7 @@ import { Switch } from "@/components/ui/switch";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { toast } from "sonner";
import { updateForm } from "@/app/admin/form/actions";
import { updateForm } from "@/app/(ui)/admin/form/actions";
import {
createQuestion,
updateQuestion,
@@ -8,7 +8,7 @@ import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { toast } from "sonner";
import { testDiscordWebhook, updateForm } from "@/app/admin/form/actions";
import { testDiscordWebhook, updateForm } from "@/app/(ui)/admin/form/actions";
import { Loader2, ChevronDown, Send } from "lucide-react";
import { getGuildRoles } from "@/app/actions/discord";
import type { DiscordRole } from "@/lib/discord/discord";
@@ -45,7 +45,7 @@ export default async function AdminFormLayout(
return (
<SidebarProvider>
<div className="flex min-h-svh w-full bg-background text-foreground">
<div className="flex min-h-svh w-full bg-transparent text-foreground">
<FormAdminSidebar
formId={params.id}
formTitle={form.title}
@@ -1,7 +1,7 @@
import { db } from "@/db";
import { notFound } from "next/navigation";
import { Badge } from "@/components/ui/badge";
import { FormFillerClient } from "@/app/form/client";
import { FormFillerClient } from "@/app/(ui)/form/client";
export const dynamic = "force-dynamic";
@@ -42,7 +42,7 @@ import { stripHtml } from "@/lib/utils";
import { HtmlDisplay } from "@/components/html-display";
import { formatStoredAnswer } from "@/lib/form/form-answer-values";
import { normalizeStoredAnswerSnapshot } from "@/lib/form/submission-intake";
import { updateSubmissionAdminNote } from "@/app/admin/form/actions";
import { updateSubmissionAdminNote } from "@/app/(ui)/admin/form/actions";
import {
useDebouncedAutoSave,
} from "@/hooks/use-debounced-autosave";
@@ -40,7 +40,7 @@ import { GuardedLink } from "@/components/guarded-link";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { getResponseNavigation } from "@/app/admin/form/actions";
import { getResponseNavigation } from "@/app/(ui)/admin/form/actions";
type ResponseNavigationData = Awaited<ReturnType<typeof getResponseNavigation>>;
+25
View File
@@ -2,8 +2,33 @@
import { useState, type ReactNode } from "react";
import { ArrowLeft, ArrowRight } from "lucide-react";
import { Toaster } from "sonner";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import { PageProgress } from "@/components/page-progress";
import { ThemeProvider } from "@/components/theme-provider";
import { TooltipProvider } from "@/components/ui/tooltip";
import { VersionCheck } from "@/components/version-check";
export function UiProviders({ children }: { children: ReactNode }) {
return (
<>
<PageProgress />
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
<TooltipProvider>
<Toaster richColors closeButton position="bottom-right" theme="dark" />
<VersionCheck />
{children}
</TooltipProvider>
</ThemeProvider>
</>
);
}
export function LinkCarousel({
primary,
@@ -47,7 +47,7 @@ export default async function FormLayout({
}
return (
<div className="relative flex min-h-screen w-full flex-1 flex-col overflow-x-hidden bg-background text-foreground">
<div className="relative flex min-h-svh w-full flex-1 flex-col overflow-x-hidden">
<header className="relative z-10 flex shrink-0 items-center justify-between border-b border-border bg-background/90 px-6 py-3 backdrop-blur">
<Link href="/">
<span className="font-semibold text-sm">
+24
View File
@@ -0,0 +1,24 @@
import Image from "next/image";
import Background from "@/public/background/space.webp";
import { UiProviders } from "./client";
export default function UiLayout({ children }: { children: React.ReactNode }) {
return (
<UiProviders>
<div className="relative isolate min-h-svh overflow-x-clip text-foreground">
<Image
src={Background}
alt=""
priority
sizes="100vw"
className="pointer-events-none fixed inset-0 -z-20 size-full object-cover opacity-40"
/>
<div
aria-hidden="true"
className="pointer-events-none fixed inset-0 -z-10 bg-background/40"
/>
{children}
</div>
</UiProviders>
);
}
@@ -4,7 +4,7 @@ export default function LeaderboardLayout({
children: React.ReactNode;
}>) {
return (
<main className="relative min-h-screen overflow-hidden bg-background text-foreground">
<main className="relative min-h-svh overflow-hidden">
{children}
</main>
);
+1 -9
View File
@@ -8,7 +8,6 @@ import { Finger_Paint } from "next/font/google";
import { connection } from "next/server";
import { getFollowerCounts } from "@/lib/followers";
import { Share2, Trophy } from "lucide-react";
import Background from "@/public/background/space.webp";
import {
formatCount,
getCountForLink,
@@ -128,18 +127,11 @@ export default async function Home() {
return (
<main className="relative flex min-h-screen flex-col items-center justify-center bg-background p-4 text-foreground">
<main className="relative flex min-h-svh flex-col items-center justify-center p-4">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<Image
src={Background}
alt="Background"
fill
priority
className="fixed top-0 left-0 z-0 h-fit min-h-dvh w-full object-cover opacity-40"
/>
<Card className="relative z-10 w-full max-w-sm bg-card/90 backdrop-blur md:max-w-lg" role="article">
<CardHeader className="flex flex-col items-center gap-4 text-center">
<div className="relative w-24 h-24 overflow-hidden rounded-full">
@@ -13,7 +13,7 @@ export const metadata: Metadata = {
export default function PrivacyPolicyPage() {
return (
<main className="min-h-screen flex flex-col items-center p-4 md:p-8">
<main className="flex flex-col items-center p-4 md:p-8">
<div className="w-full max-w-2xl space-y-6">
<div className="flex items-center gap-2">
<Link href="/">
@@ -9,7 +9,7 @@ export default function ProfileLayout({
children: React.ReactNode;
}>) {
return (
<main className="relative min-h-screen overflow-hidden bg-background text-foreground">
<main className="relative min-h-svh overflow-hidden">
<div className="relative mx-auto flex w-full max-w-5xl flex-col gap-6 px-4 py-6 sm:px-6 lg:py-10">
<nav aria-label="ทางลัด" className="flex items-center justify-between gap-3">
<Button variant="ghost" asChild>
@@ -10,7 +10,7 @@ import {
CopyShareActions,
DeleteCommentButton,
DeleteShareButton,
} from "@/app/share/[id]/client";
} from "./client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import Link from "next/link";
import { getServerSession } from "next-auth";
import { ArrowLeft, List, Plus } from "lucide-react";
import { ArrowLeft } from "lucide-react";
import { DropdownMenuAvatar } from "@/components/avatar";
import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/theme-toggle";
@@ -22,7 +22,7 @@ export default async function ShareLayout({ children }: { children: React.ReactN
: null;
return (
<div className="min-h-screen bg-background text-foreground">
<div className="min-h-svh">
<header className="sticky top-0 z-10 border-b bg-background/90 backdrop-blur">
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-4 sm:px-6">
<div className="flex items-center gap-3">
@@ -2,7 +2,7 @@ import { getServerSession } from "next-auth";
import { LoginDialog } from "@/components/login-dialog";
import { authOptions } from "@/lib/auth/auth-options";
import { getSessionDiscordId } from "@/lib/auth/auth";
import { ShareUploadClient } from "@/app/share/client";
import { ShareUploadClient } from "./client";
export const dynamic = "force-dynamic";
+1 -1
View File
@@ -12,7 +12,7 @@ export const metadata: Metadata = {
export default function TermsOfServicePage() {
return (
<main className="min-h-screen flex flex-col items-center p-4 md:p-8">
<main className="flex flex-col items-center p-4 md:p-8">
<div className="w-full max-w-2xl space-y-6">
<div className="flex items-center gap-2">
<Link href="/">
+2 -26
View File
@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import { Anuphan, Inter } from "next/font/google";
import "./globals.css";
import { cn } from "@/lib/utils";
import { config } from "@/lib/config";
const inter = Inter({
@@ -65,12 +64,6 @@ export const metadata: Metadata = {
},
};
import { TooltipProvider } from "@/components/ui/tooltip";
import { Toaster } from "sonner";
import { ThemeProvider } from "@/components/theme-provider";
import { PageProgress } from "@/components/page-progress";
import { VersionCheck } from "@/components/version-check";
export default function RootLayout({
children,
}: Readonly<{
@@ -79,28 +72,11 @@ export default function RootLayout({
return (
<html
lang="th"
className={cn(
"h-full",
"antialiased",
inter.variable,
anuphan.variable,
)}
className={`dark h-full antialiased ${inter.variable} ${anuphan.variable}`}
suppressHydrationWarning
>
<body className="font-app" suppressHydrationWarning>
<PageProgress />
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
<TooltipProvider>
<Toaster richColors closeButton position="bottom-right" theme="dark" />
<VersionCheck />
{children}
</TooltipProvider>
</ThemeProvider>
{children}
</body>
</html>
);
+6 -2
View File
@@ -99,9 +99,13 @@ export function AdminShell({
}) {
const pathname = usePathname();
if (/^\/admin\/form\/[^/]+/.test(pathname)) {
return children;
}
return (
<SidebarProvider>
<div className="flex min-h-screen w-full bg-background text-foreground">
<div className="flex min-h-svh w-full bg-transparent text-foreground">
<Sidebar variant="sidebar" collapsible="icon">
<SidebarHeader className="border-b border-border py-4 px-4">
<Link href="/admin">
@@ -157,7 +161,7 @@ export function AdminShell({
</SidebarContent>
</Sidebar>
<SidebarInset>
<SidebarInset className="bg-transparent">
<div className="md:hidden p-2">
<SidebarTrigger />
</div>
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Trash2, Loader2 } from "lucide-react";
import { deleteForm } from "@/app/admin/form/actions";
import { deleteForm } from "@/app/(ui)/admin/form/actions";
import { deleteSubmission, deleteAllSubmissions } from "@/app/actions/submissions";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
+2 -2
View File
@@ -35,8 +35,8 @@ describe("share UI contracts", () => {
test("keeps the extracted login dialog non-dismissible and shared", async () => {
const [dialog, sharePage, formPage] = await Promise.all([
Bun.file(new URL("../../components/login-dialog.tsx", import.meta.url)).text(),
Bun.file(new URL("../../app/share/page.tsx", import.meta.url)).text(),
Bun.file(new URL("../../app/form/[id]/page.tsx", import.meta.url)).text(),
Bun.file(new URL("../../app/(ui)/share/page.tsx", import.meta.url)).text(),
Bun.file(new URL("../../app/(ui)/form/[id]/page.tsx", import.meta.url)).text(),
]);
expect(dialog).toContain("เข้าสู่ระบบก่อนกรอกแบบฟอร์ม");