Skip to content

Comment Responder – Comments

  • Path: /account/comments
  • Shell nav section: Comment Responder
  • Shell nav label: Comments
  • Parent: README.md

Monitor, moderate, and act on inbound social comments across connected profiles. Enable operators to:

  • identify comments requiring moderation or business action
  • distinguish engagement, risk, and conversion opportunities
  • review AI actions already taken on each comment
  • query, filter, sort, and inspect large volumes of comments efficiently

  • Manager, Agent
  • Permissions: comment_responder.view, comment_responder.reply, comment_responder.moderate (TBD)

Reference: Roles & Permissions Model


Two-panel list + detail, consistent with the Conversations page pattern:

Zone 1 — Page header (fixed, never scrolls)

Section titled “Zone 1 — Page header (fixed, never scrolls)”
  • Page title: Comments + total count chip
  • Row 1: primary filter controls
    • Platform filter (dropdown: All platforms, Facebook, Instagram, LinkedIn, Threads)
    • Filters toggle button (shows active filter count badge)
  • Row 2: expanded filter panel (shown when Filters is toggled)
    • Date range (from / to)
    • Moderation state (All, Visible, Hidden, Deleted)
    • Response state (All, Unreplied, Replied)
    • Attention state (All, Requires attention, No attention)
    • Opportunity state (All, Opportunity, Not opportunity)
    • Sentiment (All, Very positive, Positive, Neutral, Negative, Very negative)
    • Reset button

Zone 2 — Metrics bar (fixed, never scrolls)

Section titled “Zone 2 — Metrics bar (fixed, never scrolls)”

Clickable chips that act as quick filters. Each chip shows a count and label. Active chip is visually highlighted.

Chips (in order):

  1. totalcomments.length
  2. requires attention — amber highlight, only shown when count > 0
  3. opportunities — only shown when count > 0
  4. favorites — only shown when count > 0
  5. hidden — count of moderation_state === 'hidden'
  6. replied — count of response_state === 'replied'
  7. deleted — count of moderation_state === 'deleted'

Sentiment breakdown (display-only, not clickable filters — too granular for chips):

  • very positive / positive / neutral / negative / very negative counts shown as a compact inline row

Zone 3 — Two-panel body (fills remaining height)

Section titled “Zone 3 — Two-panel body (fills remaining height)”

Left panel — comment list (fixed width ~380px, scrollable)

Section titled “Left panel — comment list (fixed width ~380px, scrollable)”
  • List header: comment count + sort toggle (newest / oldest, by created_at)
  • Search input (debounced, searches: content, author name, profile name)
  • Scrollable comment list
    • Each row: platform icon, author avatar/initials, author name, relative time, comment content (truncated), status badges (moderation state, sentiment, opportunity, favorite)
    • Attention indicator: left border accent (amber) when attention_state === 'requires_attention'
    • Selected state: background highlight
    • Empty state when no comments match filters

Right panel — comment detail (fills remaining width)

Section titled “Right panel — comment detail (fills remaining width)”
  • No selection state: prompt to select a comment
  • Active selection:
    • Detail header:
      • Author identity (avatar, name, platform icon, profile name)
      • Source post context (post caption, media type)
      • Meta row: platform, profile, created at, sentiment badge, moderation state badge
    • AI response section (if ai_response present): shows generated_by, text, timestamp; read-only
    • Action toolbar: favorite, hide/unhide, mark opportunity/remove, flag, delete, reply (AI), reply (manual)
    • Reply composer (shown when reply action is triggered): textarea + send button
    • Assignment control: assign to reviewer dropdown

type Comment = {
id: string
external_id?: string
platform: 'facebook' | 'instagram' | 'linkedin' | 'threads' | 'unknown'
profile: {
id: string
name: string
handle?: string
avatar_url?: string
}
author: {
id?: string
name?: string
handle?: string
avatar_url?: string
} | null
source_post: {
id: string
caption?: string
media_type?: 'image' | 'video' | 'carousel' | 'text' | 'unknown'
thumbnail_url?: string
published_at?: string
} | null
content: string
created_at: string
updated_at: string
moderation_state: 'visible' | 'hidden' | 'deleted'
response_state: 'unreplied' | 'replied'
attention_state: 'requires_attention' | 'no_attention'
opportunity_state: 'opportunity' | 'not_opportunity'
sentiment: 'very_positive' | 'positive' | 'neutral' | 'negative' | 'very_negative' | 'unknown'
is_favorite: boolean
assigned_to?: string | null
last_action: {
type: 'replied_by_ai' | 'replied_by_agent' | 'hidden' | 'deleted' | 'flagged' | 'favorited' | 'none'
actor?: 'ai' | 'agent' | 'system'
timestamp?: string
}
ai_response?: {
generated_by: 'Chatti™' | 'agent' | 'system'
text?: string
timestamp?: string
} | null
}
type SocialProfile = {
id: string
platform: 'facebook' | 'instagram' | 'linkedin' | 'threads' | 'unknown'
name: string
handle?: string
avatar_url?: string
status?: 'connected' | 'disconnected'
}
type SourcePost = {
id: string
profile_id: string
platform: 'facebook' | 'instagram' | 'linkedin' | 'threads' | 'unknown'
caption?: string
media_type?: 'image' | 'video' | 'carousel' | 'text' | 'unknown'
thumbnail_url?: string
published_at?: string
}

Each comment carries independent state across six dimensions. These must never be collapsed into a single status field.

DimensionValues
moderation_statevisible | hidden | deleted
response_stateunreplied | replied
attention_staterequires_attention | no_attention
opportunity_stateopportunity | not_opportunity
sentimentvery_positive | positive | neutral | negative | very_negative | unknown
is_favoriteboolean

All metrics computed dynamically from the current (unfiltered) comment dataset.

total_comments = comments.length
requires_attention = comments.filter(c => c.attention_state === 'requires_attention').length
hidden = comments.filter(c => c.moderation_state === 'hidden').length
replied = comments.filter(c => c.response_state === 'replied').length
deleted = comments.filter(c => c.moderation_state === 'deleted').length
very_positive = comments.filter(c => c.sentiment === 'very_positive').length
positive = comments.filter(c => c.sentiment === 'positive').length
neutral = comments.filter(c => c.sentiment === 'neutral').length
negative = comments.filter(c => c.sentiment === 'negative').length
very_negative = comments.filter(c => c.sentiment === 'very_negative').length
opportunities = comments.filter(c => c.opportunity_state === 'opportunity').length
favorites = comments.filter(c => c.is_favorite).length

  • Debounced (200ms), case-insensitive, partial match
  • Searchable fields: content, author.name, profile.name, source_post.caption

Primary (always visible):

  • Platform: facebook | instagram | linkedin | threads | all

Expandable panel:

  • Date range: created_at from / to
  • moderation_state
  • response_state
  • attention_state
  • opportunity_state
  • sentiment

Metrics bar quick filters (single-select, toggleable):

  • requires attention
  • opportunities
  • favorites
  • hidden
  • replied
  • deleted
  • created_at newest first (default) / oldest first
  • Toggle button in list header

selectedCommentId: string | null
  • Only one comment selected at a time
  • Selecting a comment loads full detail in the right panel
  • When filters change and the selected comment is no longer in the filtered set, auto-select the first result (or clear if empty)

  • Favorite / unfavorite — toggles is_favorite
  • Hide — sets moderation_state to hidden; button label changes to “Unhide” when already hidden
  • Delete — sets moderation_state to deleted; requires confirmation
  • Flag for review — sets last_action.type to flagged
  • Reply with AI — triggers AI reply generation (demo: shows placeholder response); sets response_state to replied, last_action.type to replied_by_ai
  • Reply manually — opens reply composer; on send sets response_state to replied, last_action.type to replied_by_agent
  • Mark as opportunity / remove flag — toggles opportunity_state
  • Assign to reviewer — sets assigned_to (dropdown of available reviewers)

StateCondition
Empty datasetcomments.length === 0
No selectionselectedCommentId == null
Active selectionselectedCommentId != null
No results from querycomments.length > 0 && filteredComments.length === 0
Loadingdata fetch in progress
Errorfetch failed / permission denied

  • Purchase intent, booking interest, pricing intent, request for contact, strong positive response to offer
  • Negative sentiment, trust concerns, escalation risk, policy/brand risk, unanswered direct question
  • Hide or delete when: abusive, misleading, brand-damaging, spam, or policy-violating

~20 comments across Facebook, Instagram, LinkedIn, and Threads. Multiple profiles and source posts. Distribution covers all sentiment values, moderation states, attention states, and opportunity states. Includes both AI-replied and unreplied comments, and a mix of favorites.

See implementation: src/app/account/comments/_lib/demo-comments.ts


  • Business logic isolated from UI layer
  • State dimensions must not be collapsed into a single status field
  • Source platform and source post context must remain attached to each comment
  • System must remain extensible for additional platforms and classifiers
  • UI consumes the system through clean interfaces; no business logic embedded in view components
  • Demo data module follows the same pattern as src/app/account/conversations/_lib/demo-conversations.ts

  • GET /comment-responder/comments
  • GET /comment-responder/comments/{id}
  • POST /comment-responder/comments/{id}/hide
  • POST /comment-responder/comments/{id}/delete
  • POST /comment-responder/comments/{id}/favorite
  • POST /comment-responder/comments/{id}/flag
  • POST /comment-responder/comments/{id}/opportunity
  • POST /comment-responder/comments/{id}/assign
  • POST /comment-responder/comments/{id}/generate-reply
  • POST /comment-responder/comments/{id}/send-reply

  • Support high-volume comment streams
  • Minimize unnecessary re-rendering (memoized filtering and metrics)
  • Support lazy loading / pagination / windowing (future)
  • Remain responsive under large datasets

  • Comment deleted on platform mid-session (show stale indicator)
  • Author data missing (show “Unknown” fallback)
  • ai_response.text may be null even when generated_by is set (show “Response pending” fallback)
  • All comments filtered out (show empty-query state, not empty-dataset state)
  • Attention chip hidden when count is 0 (avoid visual noise)
  • Moderation action on already-deleted comment (disable actions gracefully)

  • Pre-send compliance checks before reply actions
  • Audit trail: generate, approve, send
  • No PII in analytics events

Reference: Security & Compliance


  • comment_responder.comment.viewed
  • comment_responder.comment.hidden
  • comment_responder.comment.deleted
  • comment_responder.comment.favorited
  • comment_responder.comment.flagged
  • comment_responder.comment.opportunity_marked
  • comment_responder.reply.generated
  • comment_responder.reply.sent

Reference: Analytics Events (MVP)


  • Confidence scores for opportunity classification
  • Moderation rule builder
  • Suggested response quality scoring
  • Duplicate / spam cluster detection
  • Escalation queues
  • Reviewer workload balancing
  • Per-profile moderation policies
  • Post-level performance overlays
  • Conversion attribution from comment to lead
  • Inspect source post (link out to platform or post detail view)