feat(i18n) : add personal languages and compact weapon exports
CI / Verify (push) Successful in 1m18s
CI / Build immutable images and deploy (push) Successful in 1m57s

This commit is contained in:
2026-09-14 16:44:44 +07:00 Unverified
parent f214d330f0
commit e0794822b1
72 changed files with 4199 additions and 1011 deletions
+21 -20
View File
@@ -1,5 +1,6 @@
"use client";
import { useTranslation } from "@/components/i18n/locale-provider";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { closestCenter, DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent } from "@dnd-kit/core";
@@ -42,22 +43,22 @@ import { cn } from "@/lib/utils";
type ExtraLink = { id: string; slug: string; title: string };
function SortableExtraCard({ extra, index, characterKey, disabled }: { extra: ExtraLink; index: number; characterKey: string; disabled: boolean }) {
const { t } = useTranslation();
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: extra.id, disabled });
return (
<Card ref={setNodeRef} style={{ transform: CSS.Transform.toString(transform), transition }} className={cn(isDragging && "relative z-10 opacity-70 shadow-lg")}>
<CardHeader>
<CardTitle>{extra.title}</CardTitle>
<CardDescription>Extra section {index + 1}</CardDescription>
<CardDescription>{t("Extra section")}{" "}{index + 1}</CardDescription>
<CardAction>
<Button type="button" variant="ghost" size="icon-sm" disabled={disabled} className="touch-none cursor-grab active:cursor-grabbing" aria-label={`จัดลำดับ ${extra.title}`} {...attributes} {...listeners}>
<Button type="button" variant="ghost" size="icon-sm" disabled={disabled} className="touch-none cursor-grab active:cursor-grabbing" aria-label={t("จัดลำดับ {name}", { name: extra.title })} {...attributes} {...listeners}>
<GripVerticalIcon />
</Button>
</CardAction>
</CardHeader>
<CardContent>
<Button variant="outline" render={<Link href={`/admin/${characterKey}/${extra.slug}`} />} nativeButton={false}>
แก้ไข Extra
<ArrowUpRightIcon data-icon="inline-end" />
{t("แก้ไข Extra")}<ArrowUpRightIcon data-icon="inline-end" />
</Button>
</CardContent>
</Card>
@@ -77,6 +78,7 @@ export function AddExtraButton({
size?: ComponentProps<typeof Button>["size"];
label?: string;
}) {
const { t } = useTranslation();
const router = useRouter();
const [open, setOpen] = useState(false);
const [title, setTitle] = useState("");
@@ -88,7 +90,7 @@ export function AddExtraButton({
const browserWindow = window as Window & { __guideHasUnsavedChanges?: boolean };
if (
browserWindow.__guideHasUnsavedChanges &&
!window.confirm("ข้อมูลยังบันทึกไม่เสร็จ ต้องการสร้าง Extra ใหม่หรือไม่?")
!window.confirm(t("ข้อมูลยังบันทึกไม่เสร็จ ต้องการสร้าง Extra ใหม่หรือไม่?"))
) {
return;
}
@@ -97,12 +99,12 @@ export function AddExtraButton({
const result = await createGuideExtra(guideId, { title: trimmedTitle });
setPending(false);
if (result.status === "error") {
toast.error(result.message);
toast.error(t(result.message));
return;
}
setOpen(false);
setTitle("");
toast.success("สร้าง Extra แล้ว");
toast.success(t("สร้าง Extra แล้ว"));
router.push(`/admin/${characterKey}/${result.section.slug}`);
router.refresh();
};
@@ -113,19 +115,18 @@ export function AddExtraButton({
type="button"
variant={variant}
size={size}
aria-label={label || "เพิ่ม Extra"}
aria-label={label || t("เพิ่ม Extra")}
onClick={() => setOpen(true)}
>
<PlusIcon data-icon="inline-start" />
{label}
{t(label)}
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>เพิ่ม Extra section</DialogTitle>
<DialogTitle>{t("เพิ่ม Extra section")}</DialogTitle>
<DialogDescription>
ตั้งชื่อส่วนเสริม ระบบจะสร้างและเปิดหน้าแก้ไขให้ทันที
</DialogDescription>
{t("ตั้งชื่อส่วนเสริม ระบบจะสร้างและเปิดหน้าแก้ไขให้ทันที")}</DialogDescription>
</DialogHeader>
<form
onSubmit={(event) => {
@@ -135,7 +136,7 @@ export function AddExtraButton({
>
<FieldGroup>
<Field>
<FieldLabel htmlFor="new-extra-title">ชื่อ Extra</FieldLabel>
<FieldLabel htmlFor="new-extra-title">{t("ชื่อ Extra")}</FieldLabel>
<Input
id="new-extra-title"
value={title}
@@ -148,8 +149,7 @@ export function AddExtraButton({
<DialogFooter className="mt-4">
<Button type="submit" disabled={!title.trim() || pending}>
{pending ? <Spinner data-icon="inline-start" /> : <PlusIcon data-icon="inline-start" />}
สร้าง Extra
</Button>
{t("สร้าง Extra")}</Button>
</DialogFooter>
</form>
</DialogContent>
@@ -169,6 +169,7 @@ export function ExtraOverview({
initialVersion: number;
initialExtras: ExtraLink[];
}) {
const { t } = useTranslation();
const router = useRouter();
const [extras, setExtras] = useState(initialExtras);
const [version, setVersion] = useState(initialVersion);
@@ -191,7 +192,7 @@ export function ExtraOverview({
setPending(false);
if (result.status !== "saved") {
setExtras(previous);
toast.error(result.message);
toast.error(t(result.message));
if (result.status === "conflict") window.location.reload();
return;
}
@@ -203,8 +204,8 @@ export function ExtraOverview({
<section className="flex flex-col gap-4">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 className="font-heading text-xl font-semibold">Extra sections</h2>
<p className="text-sm text-muted-foreground">แต่ละ Extra มีหน้าแก้ไขและบันทึกแยกจากกัน</p>
<h2 className="font-heading text-xl font-semibold">{t("Extra sections")}</h2>
<p className="text-sm text-muted-foreground">{t("แต่ละ Extra มีหน้าแก้ไขและบันทึกแยกจากกัน")}</p>
</div>
<AddExtraButton
guideId={guideId}
@@ -223,8 +224,8 @@ export function ExtraOverview({
) : (
<Card>
<CardHeader>
<CardTitle>ยังไม่มี Extra</CardTitle>
<CardDescription>กดเพิ่ม Extra เพื่อสร้างส่วนเสริมแรกของ Guide นี้</CardDescription>
<CardTitle>{t("ยังไม่มี Extra")}</CardTitle>
<CardDescription>{t("กดเพิ่ม Extra เพื่อสร้างส่วนเสริมแรกของ Guide นี้")}</CardDescription>
</CardHeader>
</Card>
)}