- 9 minutes to read
Comments and Annotations
This spoke covers the comment data model, @mention functionality, notifications, and thread structure for Mapify's collaborative annotation system. For the full overview, see the Multi-User Collaboration hub.
Why Use Comments?
Comments transform Mapify from a visualization tool into a collaborative workspace:
- Decisions made in context – Questions answered directly on entities; no meeting scheduling needed
- @mention notifications – Tag team members to direct their attention to specific entities
- Approval workflows – Track "Pending Review" → "Approved" → "Closed" lifecycle
- Permanent documentation – Decision rationale preserved in audit trail
- Knowledge transfer – New team members read historical discussions to understand "why"
- Compliance evidence – Demonstrate review and approval for SOX/GDPR audits
Typical use cases:
- Compliance approval: "Legal has reviewed this GDPR integration – approved ✓"
- Incident root-cause analysis: "Why did this integration fail last Tuesday?"
- Architecture questions: "Should we use REST or SOAP for this System?"
- Change coordination: "Don't modify this until Friday's deployment window"
- Knowledge capture: "This Service requires special firewall rules – see ticket #1234"
Comment Data Structure
| Field Name | Type | Example Value | Purpose |
|---|---|---|---|
CommentId |
GUID | 7c9e6679-7425-40de-944b-e07fc1f90ae7 |
Unique identifier |
EntityId |
GUID | 3fa85f64-5717-4562-b3fc-2c963f66afa6 |
Entity this comment is attached to |
EntityType |
String | Integration |
Type (denormalized for reporting) |
EntityName |
String | SAP to Salesforce |
Name (denormalized for email notifications) |
UserId |
GUID | 8d2f3a4b-1c5e-6f7d-8a9b-0c1d2e3f4a5b |
User who created the comment |
UserName |
String | Alice Johnson |
Display name (denormalized) |
UserEmail |
String | alice.johnson@contoso.com |
Email for notifications (denormalized) |
CommentText |
String (max 4,000 chars) | @bob.taylor Can you review the error handling? |
Content (supports Markdown and @mentions) |
ParentCommentId |
GUID (nullable) | null (top-level) or GUID (reply) |
Parent comment for threading; null for top-level |
CreatedDate |
DateTime (UTC) | 2026-01-19T14:23:45Z |
When created |
ModifiedDate |
DateTime (UTC, nullable) | 2026-01-19T15:10:00Z |
When last edited; null if never edited |
Status |
Enum | Open, Resolved, Closed |
Approval workflow state |
MentionedUsers |
JSON Array | ["bob.taylor@contoso.com"] |
Users mentioned via @mention (for notifications) |
IsEdited |
Boolean | false |
Shows "(edited)" label in UI |
IsDeleted |
Boolean | false |
Soft delete flag — hides comment without removing audit record |
Database Schema
CREATE TABLE [dbo].[EntityComments]
(
[CommentId] UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),
[EntityId] UNIQUEIDENTIFIER NOT NULL,
[EntityType] NVARCHAR(50) NOT NULL,
[EntityName] NVARCHAR(255) NULL, -- Denormalized
[UserId] UNIQUEIDENTIFIER NOT NULL,
[UserName] NVARCHAR(255) NOT NULL, -- Denormalized
[UserEmail] NVARCHAR(255) NOT NULL, -- Denormalized
[CommentText] NVARCHAR(4000) NOT NULL,
[ParentCommentId] UNIQUEIDENTIFIER NULL REFERENCES [dbo].[EntityComments]([CommentId]),
[Status] NVARCHAR(20) NOT NULL DEFAULT 'Open', -- 'Open','Resolved','Closed'
[MentionedUsers] NVARCHAR(MAX) NULL, -- JSON array of email addresses
[CreatedDate] DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
[ModifiedDate] DATETIME2 NULL,
[IsEdited] BIT NOT NULL DEFAULT 0,
[IsDeleted] BIT NOT NULL DEFAULT 0,
INDEX [IX_Comments_EntityId] ([EntityId], [CreatedDate] DESC),
INDEX [IX_Comments_ParentCommentId] ([ParentCommentId]),
INDEX [IX_Comments_UserId] ([UserId]),
INDEX [IX_Comments_Status] ([Status], [EntityId])
);
API Response (JSON with Threading)
{
"commentId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entityType": "Integration",
"entityName": "SAP to Salesforce",
"userName": "Alice Johnson",
"userEmail": "alice.johnson@contoso.com",
"commentText": "@bob.taylor Can you review the error handling logic before we deploy?",
"parentCommentId": null,
"status": "Open",
"mentionedUsers": ["bob.taylor@contoso.com"],
"createdDate": "2026-01-19T14:23:45Z",
"isEdited": false,
"replies": [
{
"commentId": "9d4e7891-...",
"userName": "Bob Taylor",
"commentText": "Looks good! Suggest adding logging for the fallback path.",
"status": "Open",
"createdDate": "2026-01-19T15:10:00Z",
"replies": []
}
]
}
@Mention Functionality
Mention Syntax
- By username:
@alice.johnson - By email:
@alice.johnson@contoso.com - By display name:
@Alice Johnson(fuzzy matched if unique) - Multiple:
@bob.taylor @charlie.davis(space-separated)
Autocomplete While Typing
When users type @, an autocomplete dropdown appears:
<div class="mention-autocomplete" role="listbox" aria-label="Mention user suggestions">
<div class="mention-option" role="option" aria-selected="false" tabindex="0">
<img src="avatar-alice.png" alt="" class="mention-avatar" aria-hidden="true">
<div class="mention-details">
<strong>Alice Johnson</strong>
<small>alice.johnson@contoso.com</small>
</div>
</div>
<div class="mention-option" role="option" aria-selected="false" tabindex="0">
<img src="avatar-bob.png" alt="" class="mention-avatar" aria-hidden="true">
<div class="mention-details">
<strong>Bob Taylor</strong>
<small>bob.taylor@contoso.com</small>
</div>
</div>
</div>
Autocomplete behavior:
- Triggers immediately when
@is typed - Filters as user continues typing:
@ali→ "Alice Johnson" - Keyboard: Arrow keys to select, Enter to insert, Escape to cancel
- Fuzzy matching:
@alice→ "Alice Johnson" and "alice.johnson@contoso.com" - Max 10 suggestions (sorted by recent collaboration frequency)
Notification Mechanism
When a comment with @mentions is saved:
- Parse mentions from
CommentTextusing regex:/@(\S+@\S+\.\S+|[\w.]+)/g - Resolve user emails (match username to user account)
- Store in
MentionedUsersJSON array field - Send email notifications to each mentioned user
- Create in-app notifications (bell icon in Mapify header)
Email Notification Template
Subject: Alice Johnson mentioned you in a comment on "SAP to Salesforce"
Hi Bob,
Alice Johnson mentioned you in a comment on the Integration "SAP to Salesforce":
@bob.taylor Can you review the error handling logic before we deploy?
Also cc @charlie.davis for security review.
Click here to view and reply:
https://mapify.contoso.com/integration/3fa85f64-5717-4562-b3fc-2c963f66afa6#comment-7c9e6679
---
This is an automated notification from Nodinite Mapify.
Update your notification preferences: https://mapify.contoso.com/settings/notifications
In-App Notification
<div class="notification-item unread" role="alert">
<img src="avatar-alice.png" alt="Alice Johnson avatar" class="notification-avatar">
<div class="notification-content">
<strong>Alice Johnson</strong> mentioned you in a comment on
<a href="/integration/3fa85f64#comment-7c9e6679">SAP to Salesforce</a>
<small class="notification-time">2 minutes ago</small>
</div>
<button class="btn-mark-read" aria-label="Mark notification as read">
<i class="fas fa-check" aria-hidden="true"></i>
</button>
</div>
User Notification Preferences
| Setting | Options | Default |
|---|---|---|
| Email notifications | On / Off / Digest (daily summary) | On |
| In-app notifications | On / Off | On |
| Mobile push notifications | On / Off | Off |
| Quiet hours | Disable outside business hours by timezone | Off |
Comment Threading (Nested Replies)
Threading Structure
- Top-level comments:
ParentCommentId = NULL - Replies:
ParentCommentId= GUID of parent comment - Max nesting depth: 3 levels (top → reply → reply-to-reply)
- Indentation: 20px per nesting level
Comment #1 (Alice, depth 0)
├── Reply #2 (Bob, depth 1)
│ └── Reply #3 (Alice, depth 2)
│ └── Reply #4 (Charlie, depth 3) ← Max depth
└── Reply #5 (Charlie, depth 1)
Thread HTML/CSS
<div class="comment-thread">
<!-- Top-level comment (depth 0) -->
<div class="comment" data-comment-id="comment-1" data-depth="0">
<img src="avatar-alice.png" alt="Alice Johnson avatar" class="comment-avatar">
<div class="comment-content">
<div class="comment-header">
<strong>Alice Johnson</strong>
<time class="comment-time" datetime="2026-01-19T14:23:45Z">2 hours ago</time>
<span class="comment-status status-open">
<i class="fas fa-circle-dot" aria-hidden="true"></i> Open
</span>
</div>
<div class="comment-body">
@bob.taylor Can you review the error handling logic before we deploy?
</div>
<div class="comment-actions">
<button class="btn-reply"
aria-label="Reply to Alice Johnson's comment">
<i class="fas fa-reply" aria-hidden="true"></i> Reply
</button>
<button class="btn-resolve"
aria-label="Mark comment as resolved">
<i class="fas fa-check" aria-hidden="true"></i> Resolve
</button>
</div>
</div>
</div>
<!-- Nested reply (depth 1) -->
<div class="comment reply" data-comment-id="comment-2" data-depth="1"
style="margin-left: 20px;">
<img src="avatar-bob.png" alt="Bob Taylor avatar" class="comment-avatar">
<div class="comment-content">
<div class="comment-header">
<strong>Bob Taylor</strong>
<time class="comment-time" datetime="2026-01-19T15:10:00Z">1 hour ago</time>
</div>
<div class="comment-body">
Looks good! One suggestion: add logging for the fallback path.
</div>
<div class="comment-actions">
<button class="btn-reply"
aria-label="Reply to Bob Taylor's comment">
<i class="fas fa-reply" aria-hidden="true"></i> Reply
</button>
</div>
</div>
</div>
</div>
.comment-thread { position: relative; }
.comment {
display: flex;
gap: 12px;
margin-bottom: 16px;
}
.comment.reply {
border-left: 2px solid #e0e0e0;
padding-left: 12px;
}
.comment-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
border: 2px solid #fff;
box-shadow: 0 2px 4px rgba(0,0,0,.2);
}
.comment-content {
flex: 1;
background: #f8f9fa;
border-radius: 8px;
padding: 12px;
}
.comment-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-size: 13px;
color: #6c757d;
}
.comment-header strong { color: #212529; }
.comment-body { font-size: 14px; line-height: 1.5; color: #212529; }
.comment-actions {
display: flex;
gap: 8px;
margin-top: 8px;
}
.btn-reply, .btn-resolve {
background: none;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 4px 10px;
font-size: 13px;
cursor: pointer;
color: #495057;
}
.btn-reply:hover, .btn-resolve:hover { background-color: #f0f0f0; }
.btn-resolve:hover { background-color: #e8f5e9; border-color: #28a745; color: #155724; }
.comment-status {
padding: 2px 8px;
border-radius: 100px;
font-size: 12px;
font-weight: 600;
}
.status-open { background: #e3f2fd; color: #0056b3; }
.status-resolved { background: #e8f5e9; color: #155724; }
.status-closed { background: #f8f9fa; color: #6c757d; }
@media (prefers-reduced-motion: reduce) {
.comment, .mention-autocomplete { transition: none; animation: none; }
}
Related Topics
- Multi-User Collaboration Hub — full feature overview
- Comment Status and UI — status lifecycle, UI options, use cases, accessibility
- Change Tracking and Audit Trail — comments logged in audit trail
- Audit Reports and Retention — compliance reports include comment activity
- Architecture and Presence — real-time comment broadcasting
- Scalability and Offline Editing — message queue for email delivery