fix(commission) : replace repeated notification buttons with entry prompt
CI / Verify (push) Successful in 1m36s
CI / Build immutable images and deploy (push) Successful in 2m17s

This commit is contained in:
2026-10-02 12:07:41 +07:00 Unverified
parent 3473b55768
commit 96c27f8fc3
11 changed files with 122 additions and 107 deletions
-2
View File
@@ -6,7 +6,6 @@ import { getDb } from "@/db";
import { commissionCheckouts, commissionTickets, users } from "@/db/schema";
import { AdminHeader } from "@/components/admin/admin-header";
import { CommissionLiveRefresh } from "@/components/commission/live-refresh";
import { CommissionPushPermission } from "@/components/commission/push-permission";
import { AdminCommissionAvailabilityControl } from "@/components/commission/admin-availability-control";
import { Badge } from "@/components/ui/badge";
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -31,7 +30,6 @@ export default async function AdminCommissionPage({ searchParams }: PageProps<"/
<main className="mx-auto flex max-w-5xl flex-col gap-5 p-4 py-10 sm:p-8">
<div className="flex flex-wrap items-center justify-between gap-3">
<h1 className="font-heading text-3xl font-semibold">Commission tickets</h1>
<CommissionPushPermission scope="admin" />
</div>
<AdminCommissionAvailabilityControl key={`${availability.isOpen}:${availability.closureMessage}`}
{...availability} />
@@ -14,7 +14,7 @@ vi.mock("@/lib/commission/push", () => ({
validPushEndpoint: (value: string) => value.startsWith("https://fcm.googleapis.com/"),
}));
const { POST } = await import("./route");
const { GET, POST } = await import("./route");
const subscription = { endpoint: "https://fcm.googleapis.com/fcm/send/token",
keys: { p256dh: "a".repeat(87), auth: "b".repeat(22) } };
@@ -40,6 +40,14 @@ describe("commission push subscriptions", () => {
expect(insert).not.toHaveBeenCalled();
});
it("only offers admin push scope to verified admins", async () => {
expect(await (await GET()).json()).toEqual({ publicKey: "public-key", canAdmin: false });
requireCommissionUser.mockResolvedValue({ id: "admin-1", role: "admin", emailVerified: false });
expect(await (await GET()).json()).toEqual({ publicKey: "public-key", canAdmin: false });
requireCommissionUser.mockResolvedValue({ id: "admin-1", role: "admin", emailVerified: true });
expect(await (await GET()).json()).toEqual({ publicKey: "public-key", canAdmin: true });
});
it("rejects a push endpoint outside browser push services", async () => {
expect((await POST(request("customer", "https://internal.example/push"))).status).toBe(400);
expect(insert).not.toHaveBeenCalled();
@@ -16,10 +16,11 @@ const subscriptionSchema = z.object({
export async function GET() {
try {
await requireCommissionUser();
const user = await requireCommissionUser();
const publicKey = pushPublicKey();
if (!publicKey) throw new HttpError(503, "push-not-configured");
return Response.json({ publicKey }, { headers: { "Cache-Control": "no-store" } });
return Response.json({ publicKey, canAdmin: user.role === "admin" && user.emailVerified },
{ headers: { "Cache-Control": "no-store" } });
} catch (cause) { return errorResponse(cause); }
}
-2
View File
@@ -5,7 +5,6 @@ import { connection } from "next/server";
import { getDb } from "@/db";
import { commissionCheckouts, commissionTickets } from "@/db/schema";
import { CommissionLiveRefresh } from "@/components/commission/live-refresh";
import { CommissionPushPermission } from "@/components/commission/push-permission";
import { CommissionSignOut } from "@/components/commission/sign-out";
import { SiteHeader } from "@/components/public/site-header";
import { Badge } from "@/components/ui/badge";
@@ -36,7 +35,6 @@ export default async function CommissionTicketsPage() {
<CommissionSignOut />
</div>
<Link href="/commission" className="text-sm underline">สร้างคำขอเพิ่ม</Link>
<CommissionPushPermission scope="customer" />
{pending.length > 0 && <h2 className="text-xl font-semibold">รอชำระเงิน</h2>}
{pending.map((checkout) => <Link key={checkout.id} href={`/commission/pay/${checkout.id}`}>
<Card><CardHeader><CardTitle>รอชำระเงิน - ฿{checkout.amount}</CardTitle></CardHeader>