mirror of
https://github.com/massbug/judge4c.git
synced 2025-05-17 23:12:23 +00:00
refactor(problems): consolidate judge status toast
- Migrate status toast from shared hooks to problems feature - Remove deprecated show-status-toast.tsx and status.ts - Implement self-contained JudgeToast component with built-in status mapping
This commit is contained in:
parent
9003e4b5a4
commit
4ca6840bbd
95
src/features/problems/components/judge-toast.tsx
Normal file
95
src/features/problems/components/judge-toast.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
AlertTriangleIcon,
|
||||||
|
BanIcon,
|
||||||
|
CircleCheckIcon,
|
||||||
|
XIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { Status } from "@/generated/client";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
interface JudgeToastProps {
|
||||||
|
t: number | string;
|
||||||
|
status: Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIconForStatus = (status: Status) => {
|
||||||
|
switch (status) {
|
||||||
|
case Status.PD:
|
||||||
|
case Status.QD:
|
||||||
|
case Status.CP:
|
||||||
|
case Status.CE:
|
||||||
|
case Status.RU:
|
||||||
|
case Status.TLE:
|
||||||
|
case Status.MLE:
|
||||||
|
case Status.RE:
|
||||||
|
case Status.WA:
|
||||||
|
return AlertTriangleIcon;
|
||||||
|
case Status.CS:
|
||||||
|
case Status.AC:
|
||||||
|
return CircleCheckIcon;
|
||||||
|
case Status.SE:
|
||||||
|
return BanIcon;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getColorClassForStatus = (status: Status) => {
|
||||||
|
switch (status) {
|
||||||
|
case Status.PD:
|
||||||
|
case Status.QD:
|
||||||
|
return "text-gray-500";
|
||||||
|
case Status.CP:
|
||||||
|
case Status.CE:
|
||||||
|
return "text-yellow-500";
|
||||||
|
case Status.CS:
|
||||||
|
case Status.AC:
|
||||||
|
return "text-green-500";
|
||||||
|
case Status.RU:
|
||||||
|
case Status.TLE:
|
||||||
|
return "text-blue-500";
|
||||||
|
case Status.MLE:
|
||||||
|
return "text-purple-500";
|
||||||
|
case Status.RE:
|
||||||
|
return "text-orange-500";
|
||||||
|
case Status.WA:
|
||||||
|
case Status.SE:
|
||||||
|
return "text-red-500";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const JudgeToast = ({ t, status }: JudgeToastProps) => {
|
||||||
|
const s = useTranslations("StatusMessage");
|
||||||
|
const Icon = getIconForStatus(status);
|
||||||
|
const colorClass = getColorClassForStatus(status);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-background text-foreground w-full rounded-md border px-4 py-1 shadow-lg h-10 flex items-center">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="flex grow gap-3">
|
||||||
|
<Icon
|
||||||
|
className={cn("mt-0.5 shrink-0", colorClass)}
|
||||||
|
size={16}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<div className="flex grow justify-between gap-12">
|
||||||
|
<p className="text-sm">{s(status)}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="group -my-1.5 -me-2 size-8 shrink-0 p-0 hover:bg-transparent"
|
||||||
|
onClick={() => toast.dismiss(t)}
|
||||||
|
aria-label="Close toast"
|
||||||
|
>
|
||||||
|
<XIcon
|
||||||
|
size={16}
|
||||||
|
className="opacity-60 transition-opacity group-hover:opacity-100"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,75 +0,0 @@
|
|||||||
import {
|
|
||||||
LucideIcon,
|
|
||||||
XIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import type { Status } from "@/generated/client";
|
|
||||||
import { getStatusColorClass, statusMap } from "@/lib/status";
|
|
||||||
|
|
||||||
const StatusToast = ({
|
|
||||||
t,
|
|
||||||
Icon,
|
|
||||||
message,
|
|
||||||
colorClass,
|
|
||||||
}: {
|
|
||||||
t: string | number;
|
|
||||||
Icon: LucideIcon;
|
|
||||||
message: string;
|
|
||||||
colorClass: string;
|
|
||||||
}) => {
|
|
||||||
const s = useTranslations("StatusMessage");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-background text-foreground w-full rounded-md border px-4 py-1 shadow-lg h-10 flex items-center">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<div className="flex grow gap-3">
|
|
||||||
<Icon
|
|
||||||
className={`mt-0.5 shrink-0 ${colorClass}`}
|
|
||||||
size={16}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<div className="flex grow justify-between gap-12">
|
|
||||||
<p className="text-sm">{s(`${message}`)}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="group -my-1.5 -me-2 size-8 shrink-0 p-0 hover:bg-transparent"
|
|
||||||
onClick={() => toast.dismiss(t)}
|
|
||||||
aria-label="Close sonner"
|
|
||||||
>
|
|
||||||
<XIcon
|
|
||||||
size={16}
|
|
||||||
className="opacity-60 transition-opacity group-hover:opacity-100"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ShowStatusToastProps {
|
|
||||||
status: Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showStatusToast({
|
|
||||||
status,
|
|
||||||
}: ShowStatusToastProps) {
|
|
||||||
const { icon: Icon, message } = statusMap.get(status) || {
|
|
||||||
icon: XIcon,
|
|
||||||
message: "Unknown Error",
|
|
||||||
};
|
|
||||||
const colorClass = getStatusColorClass(status);
|
|
||||||
|
|
||||||
toast.custom((t) => (
|
|
||||||
<StatusToast
|
|
||||||
t={t}
|
|
||||||
Icon={Icon}
|
|
||||||
message={message}
|
|
||||||
colorClass={colorClass}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
import {
|
|
||||||
AlertTriangleIcon,
|
|
||||||
BanIcon,
|
|
||||||
CircleCheckIcon,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { LucideIcon } from "lucide-react";
|
|
||||||
import { Status } from "@/generated/client";
|
|
||||||
|
|
||||||
export const getStatusColorClass = (status: Status) => {
|
|
||||||
const colorMap: Record<Status, string> = {
|
|
||||||
PD: "text-gray-500",
|
|
||||||
QD: "text-gray-500",
|
|
||||||
CP: "text-yellow-500",
|
|
||||||
CE: "text-yellow-500",
|
|
||||||
CS: "text-green-500",
|
|
||||||
RU: "text-blue-500",
|
|
||||||
TLE: "text-blue-500",
|
|
||||||
MLE: "text-purple-500",
|
|
||||||
RE: "text-orange-500",
|
|
||||||
AC: "text-green-500",
|
|
||||||
WA: "text-red-500",
|
|
||||||
SE: "text-red-500",
|
|
||||||
};
|
|
||||||
return colorMap[status] || "text-gray-500";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const statusMap = new Map<Status, { icon: LucideIcon; message: string }>([
|
|
||||||
["PD", { icon: AlertTriangleIcon, message: "PD" }],
|
|
||||||
["QD", { icon: AlertTriangleIcon, message: "QD" }],
|
|
||||||
["CP", { icon: AlertTriangleIcon, message: "CP" }],
|
|
||||||
["CE", { icon: AlertTriangleIcon, message: "CE" }],
|
|
||||||
["CS", { icon: CircleCheckIcon, message: "CS" }],
|
|
||||||
["RU", { icon: AlertTriangleIcon, message: "RU" }],
|
|
||||||
["TLE", { icon: AlertTriangleIcon, message: "TLE" }],
|
|
||||||
["MLE", { icon: AlertTriangleIcon, message: "MLE" }],
|
|
||||||
["RE", { icon: AlertTriangleIcon, message: "RE" }],
|
|
||||||
["AC", { icon: CircleCheckIcon, message: "AC" }],
|
|
||||||
["WA", { icon: AlertTriangleIcon, message: "WA" }],
|
|
||||||
["SE", { icon: BanIcon, message: "SE" }],
|
|
||||||
]);
|
|
Loading…
Reference in New Issue
Block a user