feat(verification): pin evidence directly above each checklist item
The review dialog only showed data on the card; the checklist itself was just labels+checkboxes. Now each item shows its own evidence right above the checkbox (bio/species for authenticity, avatar+bio completeness, ban+warning status for standing, join date/posts/followers for presence) — double verification so a moderator can't check a box from memory instead of the actual data.
This commit is contained in:
1 parent
3a5b72b0f7
commit
4d34b4cd3d
1 file changed
+63
-15
@@ -102,6 +102,51 @@ export default function AdminVerificationPage() {
|
||||
|
||||
const allChecked = Object.values(checklist).every(Boolean);
|
||||
const missingLabels = CHECKLIST.filter((c) => !checklist[c.key]).map((c) => c.label);
|
||||
const activeRequest = requests.find((r) => r.id === reviewTarget);
|
||||
|
||||
// Repeats the same profile data already shown on the card, but pinned
|
||||
// directly above the matching checkbox — double verification so nothing
|
||||
// gets checked off from memory instead of the actual evidence.
|
||||
function renderEvidence(key: ChecklistKey) {
|
||||
if (!activeRequest) return null;
|
||||
const pet = activeRequest.pet;
|
||||
switch (key) {
|
||||
case "meetsAuthenticity":
|
||||
return (
|
||||
<>
|
||||
<span className="font-medium text-zinc-300">{pet.name}</span> ·{" "}
|
||||
{pet.breed?.name ?? pet.species.name}
|
||||
{pet.bio ? <> — “{pet.bio}”</> : <> — no bio set</>}
|
||||
</>
|
||||
);
|
||||
case "meetsCompleteProfile":
|
||||
return (
|
||||
<>
|
||||
Avatar {pet.avatarKey ? "✓ set" : "✗ missing"} · Bio {pet.bio ? "✓ set" : "✗ missing"}
|
||||
</>
|
||||
);
|
||||
case "meetsGoodStanding":
|
||||
return pet.owner.ban ? (
|
||||
<span className="text-red-400">
|
||||
Banned{pet.owner.ban.reason ? `: ${pet.owner.ban.reason}` : ""}
|
||||
</span>
|
||||
) : pet.owner._count.warnings > 0 ? (
|
||||
<span className="text-yellow-400">
|
||||
No ban, but {pet.owner._count.warnings} warning
|
||||
{pet.owner._count.warnings !== 1 ? "s" : ""}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-blue-400">No ban, no warnings</span>
|
||||
);
|
||||
case "meetsEstablishedPresence":
|
||||
return (
|
||||
<>
|
||||
Joined {new Date(pet.createdAt).toLocaleDateString()} · {pet._count.posts} posts ·{" "}
|
||||
{pet._count.followers} followers
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -265,22 +310,25 @@ export default function AdminVerificationPage() {
|
||||
<DialogTitle>Review request</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
{CHECKLIST.map((c) => (
|
||||
<label key={c.key} className="flex items-start gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checklist[c.key]}
|
||||
onChange={(e) =>
|
||||
setChecklist((prev) => ({ ...prev, [c.key]: e.target.checked }))
|
||||
}
|
||||
className="mt-0.5 h-4 w-4 shrink-0 accent-blue-500"
|
||||
/>
|
||||
<span>
|
||||
<span className="block text-sm text-zinc-100">{c.label}</span>
|
||||
<span className="block text-xs text-zinc-500">{c.hint}</span>
|
||||
</span>
|
||||
</label>
|
||||
<div key={c.key} className="rounded-lg border border-zinc-800 bg-zinc-950/40 p-2.5">
|
||||
<p className="text-xs text-zinc-400 mb-1.5">{renderEvidence(c.key)}</p>
|
||||
<label className="flex items-start gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checklist[c.key]}
|
||||
onChange={(e) =>
|
||||
setChecklist((prev) => ({ ...prev, [c.key]: e.target.checked }))
|
||||
}
|
||||
className="mt-0.5 h-4 w-4 shrink-0 accent-blue-500"
|
||||
/>
|
||||
<span>
|
||||
<span className="block text-sm text-zinc-100">{c.label}</span>
|
||||
<span className="block text-xs text-zinc-500">{c.hint}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user