Skip to content

Commit 4de3629

Browse files
prazgaitisclaude
andauthored
Redesign flagged activity review as inbox-style two-column layout (#218)
* Improve flagged activity review with full context Add media gallery, flagger info, metrics, notes, and activity details to the admin flagged activity review page. Convert to real-time client component for live updates during review. Backend: getFlaggedActivityDetail now returns media URLs, cloudinary IDs, individual flag records with flagger user info, comment count, triggered bonuses, location/time context, and scoring config. Frontend: New client component shows flag alert banner with reporter details, full activity card with all context, and dedicated sections for admin comments, actions, and history. https://claude.ai/code/session_016USSPskfc25tKX1qmCsJv5 * Redesign flagged activity review as inbox-style two-column layout Replaces the old list→detail page navigation with a persistent sidebar + detail panel layout optimized for fast admin triage: - Two-column inbox layout: sidebar list (filterable, real-time) on the left, activity detail on the right — no more back-and-forth navigation - Activity detail mirrors the user-facing activity page layout (user header, media, stats, caption) instead of admin-style card grids - Admin actions promoted above the fold with collapsible comment/edit forms behind toggle buttons - Keyboard shortcuts: arrow keys to navigate, R to resolve, C to comment, E to edit, ESC to close forms, Cmd/Ctrl+Enter to submit - Shared context between sidebar and detail panel so arrow navigation always matches the visible filtered list - Removed router.refresh() calls — Convex real-time queries handle updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5bbae6b commit 4de3629

12 files changed

Lines changed: 1386 additions & 508 deletions

File tree

apps/web/app/challenges/[id]/admin/flagged-activities/[activityId]/flagged-activity-comments.tsx

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -36,69 +36,59 @@ export function FlaggedActivityComments({ activityId }: { activityId: string })
3636

3737
if (!comments || comments.length === 0) {
3838
return (
39-
<div>
40-
<h3 className="text-sm font-medium text-muted-foreground">
41-
Admin Comments
42-
</h3>
43-
<p className="text-base text-muted-foreground">
44-
No admin comments yet.
45-
</p>
46-
</div>
39+
<p className="text-sm text-muted-foreground">
40+
No admin comments yet.
41+
</p>
4742
);
4843
}
4944

5045
return (
51-
<div>
52-
<h3 className="text-sm font-medium text-muted-foreground mb-2">
53-
Admin Comments
54-
</h3>
55-
<div className="space-y-2">
56-
{comments.map((entry) => (
57-
<div
58-
key={entry.comment.id}
59-
className="rounded-md border p-3 bg-muted/30"
60-
>
61-
<div className="flex items-center gap-2 text-xs text-muted-foreground">
62-
<UserAvatar
63-
user={entry.author}
64-
size="sm"
65-
showName
66-
disableLink
67-
className="text-xs"
46+
<div className="space-y-2">
47+
{comments.map((entry) => (
48+
<div
49+
key={entry.comment.id}
50+
className="rounded-md border p-3 bg-muted/30"
51+
>
52+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
53+
<UserAvatar
54+
user={entry.author}
55+
size="sm"
56+
showName
57+
disableLink
58+
className="text-xs"
59+
/>
60+
<span>
61+
{formatDistanceToNow(new Date(entry.comment.createdAt), {
62+
addSuffix: true,
63+
})}
64+
</span>
65+
{entry.comment.visibility && (
66+
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
67+
{entry.comment.visibility}
68+
</Badge>
69+
)}
70+
</div>
71+
<p className="mt-1 whitespace-pre-wrap text-sm">
72+
{entry.comment.content}
73+
</p>
74+
<div className="mt-1 flex items-center gap-1">
75+
<button
76+
type="button"
77+
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
78+
onClick={() =>
79+
toggleLike({
80+
commentId: entry.comment.id as Id<"comments">,
81+
})
82+
}
83+
>
84+
<Heart
85+
className={`h-3 w-3 ${entry.likedByMe ? "fill-red-500 text-red-500" : ""}`}
6886
/>
69-
<span>
70-
{formatDistanceToNow(new Date(entry.comment.createdAt), {
71-
addSuffix: true,
72-
})}
73-
</span>
74-
{entry.comment.visibility && (
75-
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
76-
{entry.comment.visibility}
77-
</Badge>
78-
)}
79-
</div>
80-
<p className="mt-1 whitespace-pre-wrap text-sm">
81-
{entry.comment.content}
82-
</p>
83-
<div className="mt-1 flex items-center gap-1">
84-
<button
85-
type="button"
86-
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
87-
onClick={() =>
88-
toggleLike({
89-
commentId: entry.comment.id as Id<"comments">,
90-
})
91-
}
92-
>
93-
<Heart
94-
className={`h-3 w-3 ${entry.likedByMe ? "fill-red-500 text-red-500" : ""}`}
95-
/>
96-
{entry.likeCount > 0 && <span>{entry.likeCount}</span>}
97-
</button>
98-
</div>
87+
{entry.likeCount > 0 && <span>{entry.likeCount}</span>}
88+
</button>
9989
</div>
100-
))}
101-
</div>
90+
</div>
91+
))}
10292
</div>
10393
);
10494
}

0 commit comments

Comments
 (0)