feat : updae
This commit is contained in:
@@ -7,7 +7,7 @@ import { Separator } from "@/components/ui/separator";
|
||||
const steps = [
|
||||
{ title: "1. เลือกรายการ", description: "เลือกทีม อาวุธ หรือกลุ่มดาวที่ต้องการคำนวณ" },
|
||||
{ title: "2. ชำระเงิน", description: "ตรวจสอบยอด สแกน PromptPay และส่งสลิป" },
|
||||
{ title: "3. รับผลใน Ticket", description: "ติดตามคำขอและพูดคุยเรื่องผลลัพธ์ใน Ticket ของคุณ" },
|
||||
{ title: "3. รับผลในแชตคำขอ", description: "ติดตามคำขอและพูดคุยเรื่องผลลัพธ์ในแชตของคำขอ (Ticket)" },
|
||||
];
|
||||
|
||||
const sampleMembers = [
|
||||
@@ -21,7 +21,7 @@ function CommissionTeamPreview() {
|
||||
return <Card>
|
||||
<CardHeader>
|
||||
<CardTitle>ตัวอย่างทีม 4 ตัวละคร</CardTitle>
|
||||
<CardDescription>โรเทชั่น 20 วิ · ข้อมูลสมมติสำหรับดูรูปแบบผลลัพธ์</CardDescription>
|
||||
<CardDescription>โรเทชั่น 20 วิ - ข้อมูลสมมติสำหรับดูรูปแบบผลลัพธ์</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-4 gap-1 sm:gap-3">
|
||||
@@ -54,7 +54,7 @@ export function CommissionIntro() {
|
||||
<div>
|
||||
<h2 id="commission-how-it-works" className="font-heading text-2xl font-semibold">ขอคำนวณแบบที่ต้องการ</h2>
|
||||
<p className="mt-2 text-muted-foreground">เลือกได้ทั้งจัดทีม เปรียบเทียบอาวุธ และเปรียบเทียบกลุ่มดาว โดยเห็นยอดก่อนชำระเงิน</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">ระยะเวลาจัดทำ 1–7 วันหลังยืนยันการชำระเงิน ขึ้นอยู่กับขนาดคำขอ งานเล็กอาจเสร็จภายในประมาณ 4 ชั่วโมง ติดตามความคืบหน้าและสอบถามได้ใน Ticket</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">ระยะเวลาจัดทำ 1–7 วันหลังยืนยันการชำระเงิน ขึ้นอยู่กับขนาดคำขอ งานเล็กอาจเสร็จภายในประมาณ 4 ชั่วโมง ติดตามความคืบหน้าและสอบถามได้ในแชตของคำขอ (Ticket)</p>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
{steps.map((step) => <Card key={step.title} size="sm">
|
||||
|
||||
@@ -15,7 +15,7 @@ const date = new Intl.DateTimeFormat("th-TH", { timeZone: "Asia/Bangkok", day: "
|
||||
|
||||
function Totals({ amount }: { amount: number }) {
|
||||
const shares = commissionIncomeShares(amount);
|
||||
return <span>รวม {money(amount * 100)} · Buzz {money(shares.buzzSatang)} · Gunshin {money(shares.gunshinSatang)}</span>;
|
||||
return <span>รวม {money(amount * 100)} - Buzz {money(shares.buzzSatang)} - Gunshin {money(shares.gunshinSatang)}</span>;
|
||||
}
|
||||
|
||||
export function CommissionIncomeCalendar({ period, rows }: { period: Period; rows: IncomeRow[] }) {
|
||||
|
||||
@@ -49,7 +49,7 @@ export function MobileSlipForm() {
|
||||
{ready && <><p>ยอดชำระ ฿{amount}</p><form onSubmit={submit} className="flex flex-col gap-4">
|
||||
<Field><FieldLabel htmlFor="mobile-slip">เลือกรูปสลิปจากแอปธนาคาร</FieldLabel>
|
||||
<Input id="mobile-slip" name="file" type="file" accept="image/png,image/jpeg,image/webp" required disabled={busy} /></Field>
|
||||
<Button type="submit" disabled={busy}>{busy ? "กำลังตรวจสอบ..." : "ตรวจสอบสลิป"}</Button>
|
||||
<Button type="submit" className="min-h-11" disabled={busy}>{busy ? "กำลังตรวจสอบ..." : "ตรวจสอบสลิป"}</Button>
|
||||
</form></>}
|
||||
{error && <Alert variant="destructive"><AlertDescription>{error}</AlertDescription></Alert>}
|
||||
</div>;
|
||||
|
||||
@@ -70,15 +70,15 @@ export function MobileSlipHandoff({ checkoutId }: { checkoutId: string }) {
|
||||
}, [checkoutId, link, router]);
|
||||
|
||||
return <div className="flex w-full flex-col items-start gap-3">
|
||||
<Button type="button" variant="link" className="h-auto px-0 text-foreground underline" disabled={busy}
|
||||
<Button type="button" variant="link" className="min-h-11 px-0 text-foreground underline" disabled={busy}
|
||||
onClick={() => { if (link && !expired && secondsLeft > 0) setOpen(true); else void createLink(); }}>
|
||||
{busy ? "กำลังสร้าง QR..." : "อัปโหลดจากมือถือ"}
|
||||
{busy ? "กำลังสร้าง QR..." : "ใช้มือถืออีกเครื่องอัปโหลด"}
|
||||
</Button>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>อัปโหลดสลิปจากมือถือ</DialogTitle>
|
||||
<DialogDescription>สแกน QR ด้วยมือถือ แล้วเลือกรูปสลิปจากแอปธนาคาร</DialogDescription>
|
||||
<DialogTitle>ใช้มือถืออีกเครื่องอัปโหลดสลิป</DialogTitle>
|
||||
<DialogDescription>สแกน QR นี้ด้วยมือถืออีกเครื่อง แล้วเลือกรูปสลิปจากแอปธนาคาร หากใช้มือถือเครื่องนี้ ให้ปิดหน้าต่างแล้วเลือกไฟล์สลิปได้โดยตรง</DialogDescription>
|
||||
</DialogHeader>
|
||||
{link && <div className="flex flex-col items-center gap-3">
|
||||
{!expired && <>
|
||||
@@ -86,7 +86,7 @@ export function MobileSlipHandoff({ checkoutId }: { checkoutId: string }) {
|
||||
<img src={link.qr} alt="QR สำหรับเปิดหน้าอัปโหลดสลิปบนมือถือ" width={240} height={240} />
|
||||
<p className="text-center text-sm text-muted-foreground">เวลาที่เหลือ {timeLeft}</p>
|
||||
</>}
|
||||
{expired && <p className="text-center text-sm text-muted-foreground">ลิงก์หมดอายุแล้ว ปิดหน้าต่างนี้แล้วกดอัปโหลดจากมือถืออีกครั้ง</p>}
|
||||
{expired && <p className="text-center text-sm text-muted-foreground">ลิงก์หมดอายุแล้ว ปิดหน้าต่างนี้แล้วกดใช้มือถืออีกเครื่องอัปโหลดอีกครั้ง</p>}
|
||||
</div>}
|
||||
{error && <Alert variant="destructive"><AlertDescription>{error}</AlertDescription></Alert>}
|
||||
</DialogContent>
|
||||
|
||||
@@ -53,7 +53,9 @@ export function CommissionPaymentForm({ checkoutId, amountBaht, qr, expiresAt, s
|
||||
{/* Generated locally from the fixed checkout amount and configured recipient. */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={qr} alt={`PromptPay QR สำหรับชำระเงิน ${amountBaht} บาท`} width={300} height={300} />
|
||||
<Button nativeButton={false} className="min-h-11" render={<a href={qr} download="promptpay-qr.png" />}>บันทึก QR สำหรับชำระเงิน</Button>
|
||||
<strong>฿{amountBaht}</strong>
|
||||
<p className="text-center text-sm text-muted-foreground">ใช้มือถือเครื่องนี้ชำระเงิน: บันทึก QR แล้วเลือกรูปในแอปธนาคาร</p>
|
||||
<p className="text-sm text-muted-foreground">โอนเงินแล้วอัปโหลดรูปสลิปเพื่อยืนยัน</p>
|
||||
<p className="text-sm text-muted-foreground">เวลาชำระเงินที่เหลือ {String(Math.floor(secondsLeft / 60)).padStart(2, "0")}:{String(secondsLeft % 60).padStart(2, "0")}</p>
|
||||
</CardContent></Card>
|
||||
@@ -61,6 +63,6 @@ export function CommissionPaymentForm({ checkoutId, amountBaht, qr, expiresAt, s
|
||||
<Field><FieldLabel htmlFor="slip">อัปโหลดสลิปที่ชำระเงินแล้ว</FieldLabel>
|
||||
<Input id="slip" name="file" type="file" accept="image/png,image/jpeg,image/webp" required />
|
||||
<MobileSlipHandoff checkoutId={checkoutId} /></Field>
|
||||
<Button type="submit" disabled={busy}>{busy ? "กำลังตรวจสอบ..." : "ตรวจสอบสลิปและเปิด Ticket"}</Button>
|
||||
<Button type="submit" className="min-h-11" disabled={busy}>{busy ? "กำลังตรวจสอบ..." : "ตรวจสอบสลิปและเปิดแชตคำขอ"}</Button>
|
||||
</FieldGroup>{error && <Alert variant="destructive"><AlertDescription>{error}</AlertDescription></Alert>}</form></>;
|
||||
}
|
||||
|
||||
@@ -171,11 +171,11 @@ export function CommissionRequestForm({ catalog, userId }: { catalog: Catalog; u
|
||||
}} triggerProps={{ size: "sm", className: "h-6 min-w-0 flex-1 justify-center px-1 text-xs [&>svg]:hidden" }} />
|
||||
</div>
|
||||
<p className="mt-1 truncate text-center text-xs font-medium" title={character?.name}>{character?.name ?? "เลือกตัวละคร"}</p>
|
||||
{errorsAt(`teams.${teamIndex}.members.${memberIndex}`).length > 0 && <FieldError>{errorsAt(`teams.${teamIndex}.members.${memberIndex}`).join(" · ")}</FieldError>}
|
||||
{errorsAt(`teams.${teamIndex}.members.${memberIndex}`).length > 0 && <FieldError>{errorsAt(`teams.${teamIndex}.members.${memberIndex}`).join(" - ")}</FieldError>}
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
{issues.some((issue) => issue.path === `teams.${teamIndex}`) && <FieldError data-commission-errors tabIndex={-1}>{issues.filter((issue) => issue.path === `teams.${teamIndex}`).map((issue) => issue.message).join(" · ")}</FieldError>}
|
||||
{issues.some((issue) => issue.path === `teams.${teamIndex}`) && <FieldError data-commission-errors tabIndex={-1}>{issues.filter((issue) => issue.path === `teams.${teamIndex}`).map((issue) => issue.message).join(" - ")}</FieldError>}
|
||||
<Button variant="outline" onClick={() => patch({ teams: request.teams.filter((_, index) => index !== teamIndex) })}>ลบทีม</Button>
|
||||
</CardContent></Card>)}
|
||||
<Button variant="outline" onClick={() => patch({ teams: [...request.teams, blankTeam()] })}>เพิ่มทีม</Button>
|
||||
@@ -234,7 +234,7 @@ export function CommissionRequestForm({ catalog, userId }: { catalog: Catalog; u
|
||||
{catalog.characters.find((item) => item.key === set.characterKey)?.name ?? "เลือกตัวละคร"}</p>
|
||||
</div>
|
||||
</div>
|
||||
{errorsAt(`weapons.${index}`).length > 0 && <FieldError data-commission-errors tabIndex={-1}>{errorsAt(`weapons.${index}`).join(" · ")}</FieldError>}
|
||||
{errorsAt(`weapons.${index}`).length > 0 && <FieldError data-commission-errors tabIndex={-1}>{errorsAt(`weapons.${index}`).join(" - ")}</FieldError>}
|
||||
<Button variant="outline" disabled={set.weaponKeys.length >= 100} onClick={() => {
|
||||
const weapons = structuredClone(request.weapons); weapons[index].weaponKeys.push(""); patch({ weapons });
|
||||
}}>เพิ่มอาวุธ</Button>
|
||||
@@ -263,21 +263,22 @@ export function CommissionRequestForm({ catalog, userId }: { catalog: Catalog; u
|
||||
{catalog.characters.find((item) => item.key === set.characterKey)?.name ?? "เลือกตัวละคร"}</p>
|
||||
</div>
|
||||
</div>
|
||||
{errorsAt(`constellations.${index}`).length > 0 && <FieldError data-commission-errors tabIndex={-1}>{errorsAt(`constellations.${index}`).join(" · ")}</FieldError>}
|
||||
{errorsAt(`constellations.${index}`).length > 0 && <FieldError data-commission-errors tabIndex={-1}>{errorsAt(`constellations.${index}`).join(" - ")}</FieldError>}
|
||||
<Button variant="outline" onClick={() => patch({ constellations: request.constellations.filter((_, i) => i !== index) })}>ลบชุดนี้</Button>
|
||||
</FieldGroup>)}
|
||||
<Button variant="outline" onClick={() => patch({ constellations: [...request.constellations, { characterKey: "", levels: [] }] })}>เพิ่มชุดกลุ่มดาว</Button>
|
||||
</CardContent></Card>
|
||||
<Card className="sticky bottom-2 z-20 shadow-lg">
|
||||
<CardHeader className="hidden sm:grid"><CardTitle>สรุปคำขอ</CardTitle><CardDescription>
|
||||
{request.teams.length} ทีม · {request.weapons.reduce((sum, set) => sum + set.weaponKeys.filter(Boolean).length, 0)} อาวุธ · {request.constellations.reduce((sum, set) => sum + set.levels.length, 0)} ระดับกลุ่มดาว
|
||||
{request.teams.length} ทีม - {request.weapons.reduce((sum, set) => sum + set.weaponKeys.filter(Boolean).length, 0)} อาวุธ - {request.constellations.reduce((sum, set) => sum + set.levels.length, 0)} ระดับกลุ่มดาว
|
||||
</CardDescription></CardHeader>
|
||||
<CardFooter className="flex flex-wrap items-center justify-between gap-3">
|
||||
{error && <Alert variant="destructive" role="alert" className="w-full"><AlertDescription>{error}</AlertDescription></Alert>}
|
||||
<div><strong className="text-lg">รวม ฿{commissionPrice(request)}</strong>
|
||||
<p className="text-xs text-muted-foreground sm:hidden">{request.teams.length} ทีม - {request.weapons.reduce((sum, set) => sum + set.weaponKeys.filter(Boolean).length, 0)} อาวุธ - {request.constellations.reduce((sum, set) => sum + set.levels.length, 0)} ระดับกลุ่มดาว</p>
|
||||
{request.teams.length === 0 && request.weapons.length + request.constellations.length > 0 && <p className="text-sm text-muted-foreground">รวมค่าบริการพื้นฐาน ฿100</p>}
|
||||
</div>
|
||||
<Button disabled={!hydrated || busy || commissionPrice(request) === 0} onClick={() => void submit()}>{busy ? "กำลังสร้างคำขอ…" : "ยืนยันและชำระเงิน"}</Button>
|
||||
<Button className="min-h-11" disabled={!hydrated || busy || commissionPrice(request) === 0} onClick={() => void submit()}>{busy ? "กำลังสร้างคำขอ…" : "ยืนยันและชำระเงิน"}</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</FieldSet>;
|
||||
|
||||
@@ -645,10 +645,11 @@ export function CommissionTicketChat({
|
||||
<Card className="h-full min-h-0 min-w-0 gap-0 py-0">
|
||||
<CardHeader className="shrink-0 border-b py-3">
|
||||
<CardTitle>แชต{admin ? "กับลูกค้า" : "กับทีมงาน"}</CardTitle>
|
||||
{!admin && <CardDescription>สถานะนี้บอกว่าส่งข้อความได้หรือไม่ สอบถามความคืบหน้างานได้ในแชต</CardDescription>}
|
||||
<CardAction className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant={status === "open" ? "outline" : "destructive"}
|
||||
className={status === "open" ? "border-success/30 bg-success/10 text-success" : undefined}>
|
||||
{status === "open" ? "เปิดอยู่" : "ปิดแล้ว"}
|
||||
{admin ? (status === "open" ? "เปิดอยู่" : "ปิดแล้ว") : (status === "open" ? "แชตเปิดอยู่" : "แชตปิดแล้ว")}
|
||||
</Badge>
|
||||
{admin &&
|
||||
(status === "open" ? (
|
||||
@@ -1080,7 +1081,7 @@ export function CommissionTicketChat({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{images.length}/10 รูป · สูงสุด 10 MiB ต่อรูป</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{images.length}/10 รูป - สูงสุด 10 MiB ต่อรูป</p>
|
||||
</div>
|
||||
)}
|
||||
<Field>
|
||||
|
||||
Reference in New Issue
Block a user