feat(portal-shell): 学生域全页面迁移与规范合规修复
- 学生域 32 页全量迁移(含作答/自动保存/提交/诊断) - 补齐 4 个 MSW mock 缺口,修 diagnostic case 名 - 修 4 处 Tailwind 任意值;新增共享组件与路由
This commit is contained in:
@@ -26,8 +26,26 @@ import {
|
||||
type ClassInvitationCode,
|
||||
} from "@/lib/api";
|
||||
import { notify } from "@/shared/lib/notify";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/shared/components/ui/alert-dialog";
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/components/ui/dialog";
|
||||
import { Input } from "@/shared/components/ui/input";
|
||||
import { FormField } from "@/features/admin/school/schools-client";
|
||||
|
||||
@@ -97,23 +115,25 @@ export function ClassInvitationManagerDialog({
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
||||
onClick={onClose}
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(o) => {
|
||||
if (!o) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex max-h-[90vh] w-full max-w-3xl flex-col rounded-xl border bg-background p-6 shadow-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">{t("title")}</h2>
|
||||
{className ? (
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{t("classLabel")}: {className}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<DialogContent className="flex max-h-[90vh] max-w-3xl flex-col p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("title")}</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
{t("title")}
|
||||
</DialogDescription>
|
||||
{className ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("classLabel")}: {className}
|
||||
</p>
|
||||
) : null}
|
||||
</DialogHeader>
|
||||
<div className="flex justify-end">
|
||||
<Button size="sm" onClick={() => setGenerateOpen(true)}>
|
||||
<Plus className="mr-1.5 size-4" />
|
||||
{t("generate")}
|
||||
@@ -174,7 +194,7 @@ export function ClassInvitationManagerDialog({
|
||||
? new Date(record.expiresAt).toLocaleString()
|
||||
: t("neverExpires")}
|
||||
</td>
|
||||
<td className="max-w-[200px] truncate p-3 text-sm text-muted-foreground">
|
||||
<td className="max-w-xs truncate p-3 text-sm text-muted-foreground">
|
||||
{record.note ?? "-"}
|
||||
</td>
|
||||
<td className="p-3 text-right">
|
||||
@@ -211,11 +231,11 @@ export function ClassInvitationManagerDialog({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<DialogFooter className="mt-4">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
{tCommon("form.close")}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
|
||||
<GenerateCodeDialog
|
||||
open={generateOpen}
|
||||
@@ -225,43 +245,40 @@ export function ClassInvitationManagerDialog({
|
||||
/>
|
||||
|
||||
{revokeTarget ? (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
||||
onClick={() => setRevokeTarget(null)}
|
||||
<AlertDialog
|
||||
open={true}
|
||||
onOpenChange={(o) => {
|
||||
if (!o && !revokeMutation.loading) setRevokeTarget(null);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-full max-w-sm rounded-xl border bg-background p-6 shadow-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h2 className="mb-2 text-lg font-semibold">{t("revoke")}</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
{t("revokeConfirm")}
|
||||
</p>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setRevokeTarget(null)}
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{t("revoke")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("revokeConfirm")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel
|
||||
disabled={revokeMutation.loading}
|
||||
onClick={() => setRevokeTarget(null)}
|
||||
>
|
||||
{tCommon("form.cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
disabled={revokeMutation.loading}
|
||||
onClick={() => void handleRevoke()}
|
||||
>
|
||||
{revokeMutation.loading
|
||||
? tCommon("form.processing")
|
||||
: t("revoke")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -344,20 +361,19 @@ function GenerateCodeDialog({
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
||||
onClick={onClose}
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(o) => {
|
||||
if (!o) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-full max-w-md rounded-xl border bg-background p-6 shadow-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h2 className="mb-1 text-lg font-semibold">
|
||||
{t("generateWithCustom")}
|
||||
</h2>
|
||||
<p className="mb-4 text-xs text-muted-foreground">
|
||||
{t("defaultDuration")} · {t("defaultMaxUses")}
|
||||
</p>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("generateWithCustom")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t("defaultDuration")} · {t("defaultMaxUses")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<FormField label={t("expiresInHours")}>
|
||||
<Input
|
||||
@@ -386,7 +402,7 @@ function GenerateCodeDialog({
|
||||
maxLength={255}
|
||||
/>
|
||||
</FormField>
|
||||
<div className="flex justify-end gap-2">
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -400,9 +416,9 @@ function GenerateCodeDialog({
|
||||
? tCommon("form.processing")
|
||||
: t("generate")}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user