Added new types for downline share breakdown and settlement period open hints to enhance the agent settlement API. Updated the admin console components to support these new features, improving the user experience with better data presentation and interaction. Additionally, refined the date range field to accommodate new calendar markers and hints, ensuring a more intuitive interface for managing settlement periods.
146 lines
4.6 KiB
TypeScript
146 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
|
|
import { postAdminCreateDraw } from "@/api/admin-draws";
|
|
import { AdminDateTimeField } from "@/components/admin/admin-datetime-field";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
adminLocalScheduleValueToTimezoneNaive,
|
|
getAdminBrowserTimeZoneLabel,
|
|
} from "@/lib/admin-datetime";
|
|
import { LOTTERY_SCHEDULE_TIMEZONE } from "@/lib/lottery-schedule-timezone";
|
|
import { LotteryApiBizError } from "@/types/api/errors";
|
|
|
|
type DrawCreateDialogProps = {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
scheduleTimezone?: string;
|
|
onCreated: () => void | Promise<void>;
|
|
};
|
|
|
|
function resetFormState(): {
|
|
drawTime: string;
|
|
closeTime: string;
|
|
startTime: string;
|
|
drawNo: string;
|
|
} {
|
|
return { drawTime: "", closeTime: "", startTime: "", drawNo: "" };
|
|
}
|
|
|
|
export function DrawCreateDialog({
|
|
open,
|
|
onOpenChange,
|
|
scheduleTimezone,
|
|
onCreated,
|
|
}: DrawCreateDialogProps) {
|
|
const { t } = useTranslation(["draws", "common"]);
|
|
const [form, setForm] = useState(resetFormState);
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
useEffect(() => {
|
|
queueMicrotask(() => {
|
|
if (!open) {
|
|
setForm(resetFormState());
|
|
}
|
|
});
|
|
}, [open]);
|
|
|
|
async function submit(): Promise<void> {
|
|
if (!form.drawTime.trim()) {
|
|
toast.error(t("createDraw.drawTimeRequired"));
|
|
return;
|
|
}
|
|
setSaving(true);
|
|
try {
|
|
const scheduleTz = scheduleTimezone ?? LOTTERY_SCHEDULE_TIMEZONE;
|
|
await postAdminCreateDraw({
|
|
draw_time: adminLocalScheduleValueToTimezoneNaive(form.drawTime.trim(), scheduleTz),
|
|
close_time: form.closeTime.trim()
|
|
? adminLocalScheduleValueToTimezoneNaive(form.closeTime.trim(), scheduleTz)
|
|
: undefined,
|
|
start_time: form.startTime.trim()
|
|
? adminLocalScheduleValueToTimezoneNaive(form.startTime.trim(), scheduleTz)
|
|
: undefined,
|
|
draw_no: form.drawNo.trim() || undefined,
|
|
});
|
|
toast.success(t("createDraw.success"));
|
|
setForm(resetFormState());
|
|
onOpenChange(false);
|
|
await onCreated();
|
|
} catch (e) {
|
|
toast.error(e instanceof LotteryApiBizError ? e.message : t("createDraw.failed"));
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent showCloseButton className="max-w-lg gap-4">
|
|
<DialogHeader>
|
|
<DialogTitle>{t("createDraw.title")}</DialogTitle>
|
|
<DialogDescription>
|
|
{t("createDraw.description", { tz: getAdminBrowserTimeZoneLabel() })}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="grid gap-3">
|
|
<AdminDateTimeField
|
|
id="draw-create-draw-time"
|
|
label={t("drawTime")}
|
|
value={form.drawTime}
|
|
onChange={(drawTime) => setForm((prev) => ({ ...prev, drawTime }))}
|
|
required
|
|
/>
|
|
<AdminDateTimeField
|
|
id="draw-create-close-time"
|
|
label={t("closeTime")}
|
|
value={form.closeTime}
|
|
onChange={(closeTime) => setForm((prev) => ({ ...prev, closeTime }))}
|
|
optional
|
|
/>
|
|
<AdminDateTimeField
|
|
id="draw-create-start-time"
|
|
label={t("startTime")}
|
|
value={form.startTime}
|
|
onChange={(startTime) => setForm((prev) => ({ ...prev, startTime }))}
|
|
optional
|
|
/>
|
|
<div className="grid gap-1.5">
|
|
<Label htmlFor="draw-create-draw-no">{t("drawNo")}</Label>
|
|
<Input
|
|
id="draw-create-draw-no"
|
|
placeholder={t("createDraw.drawNoPlaceholder")}
|
|
value={form.drawNo}
|
|
onChange={(e) => setForm((prev) => ({ ...prev, drawNo: e.target.value }))}
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">{t("createDraw.hint")}</p>
|
|
</div>
|
|
|
|
<DialogFooter className="gap-2 sm:gap-0">
|
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
|
{t("actions.cancel", { ns: "common" })}
|
|
</Button>
|
|
<Button type="button" disabled={saving} onClick={() => void submit()}>
|
|
{saving ? t("createDraw.saving") : t("createDraw.submit")}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|