feat : use cdn
CI / Verify (push) Successful in 1m44s
CI / Build immutable images and deploy (push) Successful in 2m54s

This commit is contained in:
2026-10-01 22:28:22 +07:00 Unverified
parent 9fc12c1874
commit 61dfc9c9e5
9 changed files with 18 additions and 57 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Skeleton } from "@/components/ui/skeleton";
export function CommissionSlipDialog({ ticketId }: { ticketId: string }) {
export function CommissionSlipDialog({ src }: { src: string }) {
const [imageState, setImageState] = useState<"loading" | "loaded" | "error">("loading");
return <Dialog>
@@ -17,7 +17,7 @@ export function CommissionSlipDialog({ ticketId }: { ticketId: string }) {
{imageState === "loading" && <Skeleton className="h-80 w-full" aria-label="กำลังโหลดสลิป" />}
{imageState === "error" && <p className="text-sm text-destructive">โหลดสลิปไม่สำเร็จ</p>}
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={`/api/commission/tickets/${ticketId}/slip`} alt="สลิปการชำระเงิน"
<img src={src} alt="สลิปการชำระเงิน"
onLoad={() => setImageState("loaded")} onError={() => setImageState("error")}
className={imageState === "loaded" ? "max-h-[calc(90svh-7rem)] w-full object-contain" : "hidden"} />
</DialogContent>
+5 -5
View File
@@ -21,7 +21,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
type ChatMessage = { id: string; authorId: string; authorName: string; text: string | null;
imageObjectKey: string | null; createdAt: Date; reactions: { userId: string; emoji: string }[] };
imageUrl: string | null; createdAt: Date; reactions: { userId: string; emoji: string }[] };
type OptimisticMessage = { localId: string; serverId: string | null; text: string; imageUrl: string | null;
status: "sending" | "sent" };
type OptimisticReaction = { active: boolean; status: "sending" | "sent" };
@@ -313,10 +313,10 @@ export function CommissionTicketChat({ ticketId, userId, status, messages, admin
</AvatarFallback></Avatar></MessageAvatar>}
<MessageContent className={cn("w-auto max-w-[min(82%,36rem)] gap-1", mine ? "items-end" : "items-start")}>
<MessageHeader className="px-1">{mine ? "คุณ" : message.authorName}</MessageHeader>
<Bubble variant={message.imageObjectKey ? "ghost" : mine ? "muted" : "secondary"} className="max-w-full">
<BubbleContent className={cn("max-w-full", message.imageObjectKey ? "border-0 bg-transparent p-0 shadow-none" : "px-4 py-2.5 shadow-sm", mine ? "rounded-br-md" : "rounded-bl-md")}>
{message.text && <p className={cn("whitespace-pre-wrap", mine && "text-foreground", message.imageObjectKey && "pb-2")}>{message.text}</p>}
{message.imageObjectKey && <CommissionMessageImage src={`/api/commission/tickets/${ticketId}/images/${message.id}`} />}
<Bubble variant={message.imageUrl ? "ghost" : mine ? "muted" : "secondary"} className="max-w-full">
<BubbleContent className={cn("max-w-full", message.imageUrl ? "border-0 bg-transparent p-0 shadow-none" : "px-4 py-2.5 shadow-sm", mine ? "rounded-br-md" : "rounded-bl-md")}>
{message.text && <p className={cn("whitespace-pre-wrap", mine && "text-foreground", message.imageUrl && "pb-2")}>{message.text}</p>}
{message.imageUrl && <CommissionMessageImage src={message.imageUrl} />}
</BubbleContent>
</Bubble>
{counts.length > 0 && <div className={cn("flex flex-wrap gap-1", mine && "justify-end")}>
+5 -2
View File
@@ -1,6 +1,7 @@
import { notFound } from "next/navigation";
import { getCommissionLabels } from "@/lib/commission/catalog";
import { getCommissionTicket } from "@/lib/commission/tickets";
import { publicMediaUrl } from "@/lib/media/storage";
import { HttpError } from "@/lib/security/http";
import { CommissionRequestSummary } from "./request-summary";
import { CommissionTicketChat } from "./ticket-chat";
@@ -31,9 +32,11 @@ export async function CommissionTicketDetail({ id, admin }: { id: string; admin:
<Card className="h-fit lg:max-h-[78svh] lg:overflow-y-auto"><CardHeader><CardTitle>คำขอที่ชำระเงินแล้ว - ฿{data.checkout.amountBaht}</CardTitle></CardHeader>
<CardContent className="flex flex-col gap-3">{catalog && <CommissionRequestSummary request={data.checkout.request} catalog={catalog} amountBaht={data.checkout.amountBaht} />}
<p className="text-sm text-muted-foreground">การชำระเงิน: {data.payment.transRef} - {data.payment.transferredAt.toLocaleString("th-TH")}</p>
<CommissionSlipDialog ticketId={id} />
<CommissionSlipDialog src={publicMediaUrl(data.payment.slipObjectKey)} />
</CardContent></Card>
<CommissionTicketChat ticketId={id} userId={data.currentUserId} status={data.ticket.status} messages={data.messages} admin={admin} />
<CommissionTicketChat ticketId={id} userId={data.currentUserId} status={data.ticket.status}
messages={data.messages.map(({ imageObjectKey, ...message }) => ({ ...message,
imageUrl: imageObjectKey ? publicMediaUrl(imageObjectKey) : null }))} admin={admin} />
</div>
</div>;
}