Issue Resolution with Mandatory Comments & Attachments

Table of Contents


Overview

This feature enables organizations to enforce documentation requirements when resolving issues. Based on configurable settings, users can be required to provide:

  • Comments explaining the resolution
  • Supporting attachments (photos, videos, or documents)
  • Or both

Benefits

  • Accountability: Ensures proper documentation of issue resolutions
  • Knowledge Base: Creates a historical record of how issues were resolved
  • Compliance: Meets organizational requirements for audit trails
  • Quality Assurance: Reduces rushed resolutions without proper documentation

Settings Configuration

Data Model

Settings are stored in the IssueTrackerSettingsExtended interface and retrieved via the organization’s issue tracker settings:

File: packages/app/shared/type/issue-tracker-settings-extended.ts

export interface IssueTrackerSettingsExtended extends BaseIssueTrackerSettings {
  /** Whether a comment is required when resolving issues */
  requireCommentOnResolve?: boolean
  /** Whether at least one attachment is required when resolving issues */
  requireAttachmentOnResolve?: boolean
}

Three Configuration Modes

Mode 1: No Requirements (Default)

Settings:

{
  "requireCommentOnResolve": false,
  "requireAttachmentOnResolve": false
}

Behavior:

  • No modal is shown when resolving issues
  • Users can resolve directly without comments or attachments
  • Traditional quick-resolve flow

Mode 2: Comment Required (Optional Attachments)

Settings:

{
  "requireCommentOnResolve": true,
  "requireAttachmentOnResolve": false
}

Behavior:

  • Modal is displayed when attempting to resolve
  • Comment is required (minimum 1 character)
  • Attachments are optional (max 10)
  • Submit button is disabled until comment is provided

User Message:

“Add maximum 10 attachments (optional)“


Mode 3: Comment + At Least 1 Attachment Required

Settings:

{
  "requireCommentOnResolve": true, // Can be false, still enforced
  "requireAttachmentOnResolve": true
}

Behavior:

  • Modal is displayed when attempting to resolve
  • Comment is required (minimum 1 character)
  • At least 1 attachment is required
  • Submit button is disabled until both requirements are met

User Messages:

“At least one attachment is required” “Add at least 1 attachment (max. 10)”

Important: When requireAttachmentOnResolve is true, the comment becomes automatically required regardless of the requireCommentOnResolve setting.

Validation Logic

File: packages/app/features/issues/hooks/use-issue-resolution-requirements.ts:31-45

const requireCommentOnResolve = issueTrackerSettings.requireCommentOnResolve || false
const requireAttachmentOnResolve = issueTrackerSettings.requireAttachmentOnResolve || false
 
// Logic:
// - If requireAttachmentOnResolve is true, BOTH comment and attachment are required
// - If only requireCommentOnResolve is true, only comment is required
// - If both are false, no modal is shown
const requireComment = requireCommentOnResolve || requireAttachmentOnResolve
const requireAttachment = requireAttachmentOnResolve
 
return {
  showCommentModal: requireCommentOnResolve || requireAttachmentOnResolve,
  requireComment,
  requireAttachment,
}

Implementation Locations

The feature is implemented in 4 primary locations where users can resolve issues:

1. Issue Details - Quick Resolve Button

Location: Issue Details Screen → “Resolve This Issue” Button Component: QuickResolveButton with QuickResolveComment modal Path: /issues/details/:issueID

Trigger Flow:

  1. User clicks “Resolve This Issue” button
  2. Alert dialog appears: “Are you sure you want to resolve this issue?”
  3. User clicks “Resolve” in the alert
  4. If showCommentModal is true: Comment modal opens
  5. If showCommentModal is false: Issue resolves immediately

Code Reference:

// https://github.com/Nimbly-Technologies/audit-lite/blob/master/packages/app/features/issues/details/components/quick-resolve-issue.tsx#L32-L38
const handleResolveIssue = async () => {
  if (selectedIssue.status !== IssueStatusType.RESOLVED) {
    if (showCommentModal) {
      setQuickResolveMessageSheet(true)
      setOpen(false)
      return
    }
    // ... direct resolve
  }
}

2. Issue Details - Status Dropdown

Location: Issue Details Screen → Status Field Dropdown Component: IssueStatus with IssueResolvedComment modal Path: /issues/details/:issueID (Details Tab)

Trigger Flow:

  1. User clicks on Status dropdown in issue details
  2. User selects “Resolved” from the list
  3. If showCommentModal is true: Comment modal opens
  4. If showCommentModal is false: Status updates immediately

Code Reference:

// https://github.com/Nimbly-Technologies/audit-lite/blob/master/packages/app/features/issues/details/components/issue-status.tsx#L39-L48
const handleUpdate = (item: SelectorItemType) => {
  if (item.id === IssueStatusType.BLOCKED) {
    setIssueBlockMessageSheet(true)
    return
  }
 
  if (item.id === IssueStatusType.RESOLVED && showCommentModal) {
    setIssueResolvedMessageSheet(true)
    return
  }
  // ... direct update
}

3. Bulk Edit - Resolving Multiple Issues

Location: Issue List → Select Multiple Issues → Bulk Edit Component: BulkEditList with BulkResolvedComment modal Path: /issues/bulkEdit?ids=...

Trigger Flow:

  1. User selects multiple issues from the issue list
  2. User navigates to Bulk Edit screen
  3. User changes Status to “Resolved”
  4. User clicks “Update Issues” button
  5. If showCommentModal is true: Bulk comment modal opens
  6. Comment and attachments apply to ALL selected issues
  7. If showCommentModal is false: All issues update immediately

Code Reference:

// https://github.com/Nimbly-Technologies/audit-lite/blob/master/packages/app/features/issues/components/bulkEdit.tsx#L53-L59
const handleUpdateIssue = handleSubmit(async (data: UpdateIssueRequest) => {
  // If status is resolved AND comment/attachment is required, show comment sheet
  if (data.status === "resolved" && showCommentModal) {
    setPendingUpdate(data)
    setShowCommentSheet(true)
    return
  }
  // ... direct update
})

Special Note: The bulk comment modal shows a subtitle indicating how many issues will be affected:

“Please provide details about the resolution. (3 Issues)“


4. Issue Blocking (Bonus Feature)

While not part of the resolution requirements, the same infrastructure handles blocking issues:

Component: IssueBlockComment Trigger: When changing status to “Blocked”

This always requires a comment explaining why the issue is blocked.


User Experience

Comment Modal Features

All comment modals share these features:

1. Comment Input

  • Character Limit: 250 characters (with live counter)
  • Mentions: Support for @mentions of team members
    • Type @ to trigger user suggestions
    • Suggestions filter as you type
    • Selected users receive notifications
  • Multiline: Supports multiple lines of text
  • Placeholder: “Enter resolution details”

2. Attachment Support

Attachment Types:

  • 📷 Photos: JPEG, JPG, PNG, GIF
  • 🎥 Videos: MP4, AVI, MKV, MOV
  • 📄 Documents: PDF and other document types

Attachment Sources:

  • Camera (with watermark option)
  • Photo Gallery
  • Document Picker
  • Video Recorder

Attachment Limits:

  • Minimum: 0 (optional) or 1 (required based on settings)
  • Maximum: 10 attachments
  • File size: Individual file size limits enforced

File Information Display:

  • Supported file size and formats shown in info box
  • Max size details
  • Supported image types
  • Supported document types
  • Supported video types

3. Validation & Feedback

Submit Button States:

The submit button is disabled when:

// Disabled if:
!(
  (!requireComment || comment?.length > 0) && // Comment check
  (!requireAttachment || refercenceKeys.length > 0) && // Attachment check
  !issueAttachMentUploading && // Upload in progress
  !isLoading // Request in progress
)

Error Messages:

  • If attachment required but missing: “At least one attachment is required” (shown in red)
  • Upload failures: Error toast notification

Loading States:

  • Attachment upload: Individual file upload progress
  • Form submission: “Submitting…” button text

4. Workflow Example

Scenario: Organization requires comment + 1 attachment

  1. User clicks “Resolve This Issue”
  2. Alert confirms: “Are you sure you want to resolve this issue?”
  3. User clicks “Resolve”
  4. Modal opens: “Resolve Issue”
    • Subtitle: “Please provide details about the resolution.”
  5. User types comment: “Fixed by replacing faulty component”
  6. Submit button still disabled (no attachment)
  7. Error message: “At least one attachment is required”
  8. User clicks “Add Photo” → Takes photo from camera
  9. Photo uploads and thumbnail appears
  10. Submit button becomes enabled
  11. User clicks “Submit”
  12. Comment is created with attachment
  13. Issue status changes to “Resolved”
  14. NPS survey triggers (if configured)
  15. Modal closes, success confirmation

Technical Architecture

Core Hook: useIssueResolutionRequirements

File: packages/app/features/issues/hooks/use-issue-resolution-requirements.ts

This hook centralizes the logic for determining resolution requirements:

export interface IssueResolutionRequirements {
  /** Whether to show comment modal when resolving issues */
  showCommentModal: boolean
  /** Whether comment is required when resolving issues */
  requireComment: boolean
  /** Whether at least one attachment is required when resolving issues */
  requireAttachment: boolean
}

Usage Example:

const { showCommentModal, requireComment, requireAttachment } = useIssueResolutionRequirements()
 
// Check if modal should be shown
if (showCommentModal) {
  setShowCommentSheet(true)
}
 
// Validate form
const canSubmit =
  (!requireComment || comment.length > 0) && (!requireAttachment || attachments.length > 0)

Component Architecture

Quick Resolve Components

  • Modal: QuickResolveComment - Main comment modal for quick resolve
  • Attachments: QuickResolveCommentAttachments - Attachment picker/manager
  • State Atoms:
    • quickResolveMessageSheetAtom - Modal open state
    • quickResolveCommentAtom - Comment text
    • quickResolveCommentAttachment - Attachment data
    • quickResolveCommentAttachmentUpload - Upload status
    • quickResolveCommentAttachmentReferenceKeys - Storage references

Issue Resolved Components

  • Modal: IssueResolvedComment - Comment modal for status change
  • Attachments: IssueResolvedCommentAttachments
  • State Atoms:
    • issueResolvedMessageSheetAtom
    • issueResolvedCommentAtom
    • issueResolvedCommentAttachment
    • issueResolvedCommentAttachmentUpload
    • issueResolvedCommentAttachmentReferenceKeys

Bulk Resolve Components

  • Modal: BulkResolvedComment - Comment modal for bulk operations
  • Attachments: BulkResolvedCommentAttachments
  • State Atoms:
    • bulkResolvedCommentSheetAtom
    • bulkResolvedCommentAtom
    • bulkResolvedCommentAttachmentAtom
    • bulkResolvedCommentAttachmentUploadAtom
    • bulkResolvedCommentAttachmentReferenceKeysAtom
    • bulkEditIssueIdsAtom - List of issue IDs

State Management

Atoms Location: packages/app/features/issues/details/controllers/details-screen-controller.ts and packages/app/features/issues/controllers/bulk-edit-controller.ts

All modals use Jotai atoms for state management, ensuring:

  • Component isolation
  • State persistence during navigation
  • Clean reset on modal close

API Integration

1. Create Message API

Mutation: useCreateIssueMessage / useBulkCreateIssueMessage

Creates a comment message with attachments:

await createMessage({
  issueId: selectedIssue?.issueID,
  createdAt: dayjs().format(),
  createdBy: user?.data?.userID || "",
  createdByName: user?.data?.displayName || "",
  message: comment,
  messageType: "comment",
  attachments: refercenceKeys || [],
  // ... other fields
})

2. Resolve Issue API

Mutation: useQuickResolveIssue / useUpdateIssueMutation

Updates issue status to resolved:

await quickResolveIssue({
  issueID: selectedIssue.issueID,
  resolvedBy: authedUserId || "",
  triggerNotification: true,
})

3. Attachment Upload

Hook: useUploadFileAttachment

Handles file uploads with:

  • Retry logic on failure
  • Upload progress tracking
  • File validation
  • Reference key generation

Upload Status:

enum UPLOAD_STATUS {
  PENDING = "pending",
  UPLOADING = "uploading",
  COMPLETED = "completed",
  FAILED = "failed",
}

Attachment Upload Flow

  1. User selects file (camera/gallery/document)
  2. File is converted to UploadFileI object
  3. Added to attachments state via addAttachments([file])
  4. Upload begins automatically
  5. Status tracked: PENDING → UPLOADING → COMPLETED
  6. On completion: Reference key generated and stored
  7. Submit button enables when all uploads complete
  8. On submit: Reference keys sent to API

Localization Support

The feature supports 7 languages with full translations:

Supported Languages

Key Translation Strings

Namespace: LL.issue.issues.issueResolvedComment

issueResolvedComment: {
    title: 'Resolve Issue',
    subtitle: 'Please provide details about the resolution.',
    placeholder: 'Enter resolution details',
    submit: 'Submit',
    submitting: 'Submitting',
    attachmentRequired: 'At least one attachment is required',
    attachmentOptional: 'Attachments (optional)',
    attachmentRequiredLabel: 'Attachments (min. 1 required)',
    attachmentInfo: 'Add at least 1 attachment (max. 10)',
    attachmentInfoOptional: 'Add maximum 10 attachments (optional)',
}

Example Translations

English:

“At least one attachment is required”

Spanish:

“Se requiere al menos un archivo adjunto”

Korean:

“최소 하나의 첨부 파일이 필요합니다”

Thai:

“ต้องมีไฟล์แนบอย่างน้อย 1 ไฟล์”


Code References

Primary Files

FilePurpose
use-issue-resolution-requirements.tsCore hook for settings logic
quick-resolve-issue.tsxQuick resolve button trigger
quick-resolve-comment.tsxQuick resolve modal
quick-resolve-comment-attachment.tsxQuick resolve attachments
issue-status.tsxStatus dropdown trigger
issue-resolved-comment.tsxStatus change modal
issue-resolved-comment-attachment.tsxStatus change attachments
bulkEdit.tsxBulk edit trigger
bulk-resolved-comment.tsxBulk resolve modal
bulk-resolved-comment-attachment.tsxBulk resolve attachments
issue-tracker-settings-extended.tsSettings type definitions

Testing Files

FileCoverage
issue-status.test.tsxStatus component tests
issue-block-comment.test.tsxBlock comment tests
issue-comment-attachments.test.tsxAttachment component tests
  • NPS Triggering: After successful resolution, NPS survey is triggered
  • Notifications: Team members mentioned in comments receive notifications
  • Issue History: Comments and attachments appear in issue activity timeline
  • Offline Support: Comments and attachments queued when offline

Summary

This feature provides a comprehensive solution for enforcing documentation standards when resolving issues. With flexible configuration options, organizations can:

✅ Require explanatory comments ✅ Mandate supporting documentation via attachments ✅ Ensure accountability and traceability ✅ Build a knowledge base of resolutions

The implementation is:

  • Consistent: Same experience across 4 different trigger points
  • Flexible: Three configuration modes to fit organizational needs
  • User-Friendly: Clear validation messages and intuitive UI
  • Localized: Support for 7 languages
  • Robust: Retry logic, upload tracking, and error handling

Last Updated: October 6, 2025 Version: 2.11.17 Related PR: #982 - Add mandatory comment with attachments when resolving issues