contents(admin): tidy the review queue + add gallery screenshots
- Review queue: past verdicts (e.g. the 13 hidden labels from an Aug/Sep cleanup
sweep) are correct but noisy — they buried the items that still need a
decision. They are now COLLAPSED behind a 'Show actioned (N)' toggle, with the
verdicts untouched (still hidden) and 'Undo all N' still available. Verified:
the Spots tab renders 0 hidden cards by default with the toggle + Undo all.
- public/images/screenshots/{map,spot,marketplace}.png (1280x800, quantised) for
the app-listing gallery.
This commit is contained in:
parent
48a27d552d
commit
3051858de7
4 changed files with 32 additions and 3 deletions
BIN
public/images/screenshots/map.png
Normal file
BIN
public/images/screenshots/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
public/images/screenshots/marketplace.png
Normal file
BIN
public/images/screenshots/marketplace.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
public/images/screenshots/spot.png
Normal file
BIN
public/images/screenshots/spot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 319 KiB |
|
|
@ -125,6 +125,9 @@ function QueueList({
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const [bulkBusy, setBulkBusy] = useState(false);
|
const [bulkBusy, setBulkBusy] = useState(false);
|
||||||
|
// Old verdicts (e.g. a cleanup sweep from weeks ago) are correct but noisy —
|
||||||
|
// they must not bury the items that still need a decision.
|
||||||
|
const [showActioned, setShowActioned] = useState(false);
|
||||||
const mappers = useMemo(() => [...new Set(rows.map((r) => r.pubkey))], [rows]);
|
const mappers = useMemo(() => [...new Set(rows.map((r) => r.pubkey))], [rows]);
|
||||||
const { data: trustScores } = useBatchTrustScores(mappers);
|
const { data: trustScores } = useBatchTrustScores(mappers);
|
||||||
const { getCounts } = useSpotReports(rows);
|
const { getCounts } = useSpotReports(rows);
|
||||||
|
|
@ -214,6 +217,8 @@ function QueueList({
|
||||||
}
|
}
|
||||||
|
|
||||||
const actioned = prepared.filter((r) => r.actioned);
|
const actioned = prepared.filter((r) => r.actioned);
|
||||||
|
const open = prepared.filter((r) => !r.actioned);
|
||||||
|
const visible = showActioned ? prepared : open;
|
||||||
|
|
||||||
const undoAll = async () => {
|
const undoAll = async () => {
|
||||||
setBulkBusy(true);
|
setBulkBusy(true);
|
||||||
|
|
@ -235,11 +240,22 @@ function QueueList({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{actioned.length > 1 && (
|
{actioned.length > 0 && (
|
||||||
<div className="flex flex-wrap items-center justify-between gap-2 rounded-2xl border border-white/10 bg-white/[0.02] px-4 py-3">
|
<div className="flex flex-wrap items-center justify-between gap-2 rounded-2xl border border-white/10 bg-white/[0.02] px-4 py-3">
|
||||||
<p className="text-xs text-gray-400">
|
<p className="text-xs text-gray-400">
|
||||||
{actioned.length} item{actioned.length === 1 ? '' : 's'} hidden or confirmed in this tab.
|
{actioned.length} already actioned in this tab (hidden or confirmed).
|
||||||
</p>
|
</p>
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="gap-1.5 text-gray-400 hover:text-white"
|
||||||
|
onClick={() => setShowActioned((v) => !v)}
|
||||||
|
>
|
||||||
|
<EyeOff className="h-3.5 w-3.5" />
|
||||||
|
{showActioned ? 'Hide actioned' : `Show actioned (${actioned.length})`}
|
||||||
|
</Button>
|
||||||
|
{actioned.length > 1 && (
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button size="sm" variant="outline" className="gap-1.5 border-white/20" disabled={bulkBusy}>
|
<Button size="sm" variant="outline" className="gap-1.5 border-white/20" disabled={bulkBusy}>
|
||||||
|
|
@ -263,9 +279,22 @@ function QueueList({
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{prepared.map(({ row, coord, hidden, verified, trust, reports, flags }) => (
|
{visible.length === 0 && (
|
||||||
|
<Card className="border-dashed border-white/10">
|
||||||
|
<CardContent className="space-y-3 py-12 text-center">
|
||||||
|
<CheckCircle2 className="mx-auto h-8 w-8 text-emerald-500" />
|
||||||
|
<p className="text-sm text-gray-400">
|
||||||
|
Nothing waiting on you. {actioned.length} earlier verdict
|
||||||
|
{actioned.length === 1 ? '' : 's'} {actioned.length === 1 ? 'is' : 'are'} hidden above.
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
{visible.map(({ row, coord, hidden, verified, trust, reports, flags }) => (
|
||||||
<Card
|
<Card
|
||||||
key={row.id}
|
key={row.id}
|
||||||
className={
|
className={
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue