Skip to main content
All Docs
FeaturesPurple PepperUpdated April 8, 2026

Comments System

Comments System

The platform includes a generic, polymorphic comments system that lets users add notes and discussion threads to any supported entity. Comments are attributed to the authoring user and written to the audit log.

Supported Entity Types

Entity TypeDescription
offerComments on a property offer record
tenancyTermComments on a tenancy term record
todoComments on a to-do item
complianceComments on a compliance document

Using the CommentThread Component

The CommentThread component can be embedded in any page that corresponds to a supported entity type.

import { CommentThread } from "@/components/comment-thread";

<CommentThread
  entityType="offer"
  entityId={offerId}
  placeholder="Add a comment about this offer..."
  title="Offer Comments"
/>

Props

PropTypeRequiredDefaultDescription
entityType"offer" | "tenancyTerm" | "todo" | "compliance"The type of entity being commented on
entityIdstringThe ID of the entity
placeholderstring"Type a comment or note..."Placeholder text for the comment input
titlestring"Comments"Section header label for the comments list

Behaviour

  • Comments are scoped to the active organisation (orgId) automatically.
  • The textarea enforces a 2,000 character limit with a live counter.
  • On successful post, the input clears and the comment list and count badge refresh automatically.
  • If posting fails, an inline error message is displayed beneath the form.
  • The list shows a loading skeleton while fetching and an empty-state message when no comments exist.
  • Each comment displays the author's name initial as an avatar, their full name, the comment body, and a relative timestamp (e.g. "3 minutes ago").

Displaying a Comment Count Badge

To show a comment count outside the CommentThread component (e.g. in a page header), use the comment.countByEntity tRPC query:

const { data: commentCount } = trpc.comment.countByEntity.useQuery(
  { entityType: "offer", entityId: offerId },
  { enabled: !!orgId }
);

{(commentCount?.count ?? 0) > 0 && (
  <span>{commentCount?.count}</span>
)}

tRPC API Reference

comment.add

Creates a new comment on an entity. Verifies the entity exists and belongs to the caller's organisation, then writes to the audit log.

Input

{
  entityType: "offer" | "tenancyTerm" | "todo" | "compliance";
  entityId: string;
  body: string; // max 2,000 characters
}

comment.listByEntity

Returns a list of comments for a given entity, ordered by creation date (newest first), with author names resolved.

Input

{
  entityType: "offer" | "tenancyTerm" | "todo" | "compliance";
  entityId: string;
  limit?: number; // default 50
}

Response

{
  items: Array<{
    id: string;
    body: string;
    authorName: string;
    createdAt: string; // ISO timestamp
  }>;
}

comment.countByEntity

Returns the total number of comments for a given entity. Useful for rendering count badges without loading the full comment list.

Input

{
  entityType: "offer" | "tenancyTerm" | "todo" | "compliance";
  entityId: string;
}

Response

{ count: number }

Database Schema

The entity_comments table stores all comments:

CREATE TABLE entity_comments (
  id            TEXT PRIMARY KEY,
  org_id        TEXT NOT NULL,
  entity_type   TEXT NOT NULL,  -- 'offer' | 'tenancyTerm' | 'todo' | 'compliance'
  entity_id     TEXT NOT NULL,
  author_user_id TEXT NOT NULL,
  body          TEXT NOT NULL,
  created_at    TIMESTAMP DEFAULT NOW() NOT NULL
);

Indexes:

  • (org_id) — organisation-level scoping
  • (org_id, entity_type, entity_id) — primary lookup for per-entity comment lists
  • (org_id, author_user_id) — per-author lookups
  • (org_id, entity_type, entity_id, created_at) — ordered fetches

Notes

  • The legacy tenancyComments table and tenancy.addComment / tenancy.listComments procedures are not affected by this release. They continue to function as before. The new generic system runs alongside the existing tenancy-specific comments.
  • Entity ownership is verified server-side before a comment is accepted — posting a comment to an entity that doesn't belong to your organisation will be rejected.