Skip to content

Question Management & Annotation Form: UI Specification

Detailed UI and interaction specification for the Question Management (QM) redesign and connected Annotation Form updates. Covers information architecture, component specifications, interaction patterns, state management, and accessibility. Implementation tracked via Epic #2488.


Table of Contents

  1. Design Decisions
  2. Information Architecture
  3. Design View
  4. Assign View
  5. Preview View
  6. Publish Wizard
  7. Annotation Form Updates
  8. Versioning UX
  9. Accessibility
  10. State Management & Performance
  11. Research References

1. Design Decisions

Agreed design decisions from the evaluation and discussion sessions. These are the foundation for the specification.

DD-01: Project Navigation Integration

Decision: Design, Assign, and Preview become sub-items under a "Questions" section in the project sidebar navigation, rather than tabs within the QM content area.

Rationale: - Eliminates nested tabs (previously: QM tabs > category tabs) - The horizontal bar is reserved exclusively for category navigation - Categories are always tabs at the top across all three views -- consistent with the annotation form's visual language - Design, Assign, Preview are distinct workflows that happen at different times, not parallel views of the same thing

Structure:

Project Sidebar Nav              Content Area
---------------------           ----------------------------------
  Dashboard                     [Study] [DMI] [Treatment] [OA] [Cohort] [Exp]
  Studies                       ... view-specific content ...
  Screening
  Data Extraction
v Questions
    Design       <-- active
    Assign
    Preview
  Stages
  Members
  Settings

Behaviour: Selected category persists when switching between Design/Assign/Preview via a route parameter or shared state.

DD-02: Workspace Layout -- WYSIWYG Tree + Properties Panel

Decision: The Design view uses a split-panel layout: a compact WYSIWYG question flow on the left, and a properties/edit panel on the right.

Rationale: - SyRF questions have ~10+ editable fields (text, description, type, control type, options, conditional settings, required, multiple, answer array, default checkbox status, etc.). Expanding all fields inline bloats tree nodes and pushes siblings out of view. - A properties panel keeps the tree compact and navigable at all times. - Clicking through questions to compare them is fast -- the tree doesn't change shape, only the panel updates. - Drag-and-drop works on compact nodes without awkwardness. - Version history and assignment info can live in the panel without bloating the tree. - This is the SurveyJS Creator / Figma / VS Code pattern -- familiar to users of any design tool.

The tree IS the lightweight preview: The compact hybrid view in the tree shows question text, type badge, option count, required indicator. It emulates the annotation form's question flow.

DD-03: Category Navigation via Horizontal Tabs

Decision: Categories (Study, DMI, Treatment, OA, Cohort, Experiment) are displayed as horizontal tabs spanning the full content width, above the view-specific content. This applies to all three views (Design, Assign, Preview).

Rationale: - Horizontal tabs are the visual language already established by the annotation form -- all SyRF users are familiar with this pattern. - Horizontal tabs use negligible vertical space (~40px) compared to a vertical MatNavList (~336px for 7 items). - Avoids the vertical space problem that would arise from putting category nav in a sidebar.

Component options (any of these work): - mat-tab-nav-bar (already used in codebase -- lowest change) - MatChipListbox horizontal (more compact, clear selected state) - MatButtonToggleGroup horizontal (clean selection semantics)

DD-04: Assign View -- Single Tree with Filter Toggle

Decision: The Assign view uses a single question tree with checkboxes per stage (one stage at a time via dropdown), replacing the dual-tree approach.

Rationale: - Single mental model: check to assign, uncheck to unassign - No "assignment mode" vs "unassignment mode" - System questions show a dash (auto-included, not user-controllable) - A "Show" filter toggle (All / On stage / Not on stage) provides the stage-specific tree view that the dual-tree offered

DD-05: Publishing Spectrum (Three Zones)

Decision: Question publishing follows a three-zone spectrum rather than a binary locked/unlocked model.

Zone State Constraints Reversible?
Drafting DraftAQs on project Everything mutable N/A
Published, no annotations AQs exist, stage active, no submissions yet Structure frozen, content versionable Use "Replace with new version" to create new editable drafts
Published, with annotations Annotations reference specific AQVersions Content versionable only (new version) Use "Replace with new version"; handling decisions made at publish time

Rationale: Matches REDCap's Development/Production model. The ceremony's intensity matches the stakes. Accidental publishing is recoverable if caught before annotators start working.

DD-06: Annotation Form -- Keep Tabs, Add Collapsible Unit Tiles

Decision: The annotation form keeps category tabs (familiar, unchanged) and adds collapsible unit tiles within categories that have units (DMI, Treatment, OA, Cohort, Experiment).

Rationale: - Category tabs are familiar to all SyRF users and support the focused, sequential data entry workflow. - Collapsible unit tiles (chip-like pills with status indicators) are new functionality -- units currently render all questions inline with no collapse/expand. - Tiles give quick navigation, progress visibility, and performance improvement (collapsed units don't render form fields). - Based on SurveyJS Dynamic Panel pattern.

DD-07: Completion Indicators -- Three-State, No Fixed Denominator

Decision: Unit completion uses three states (complete / in-progress / not started) without showing absolute counts like "⅗".

Rationale: The number of visible questions is dynamic due to conditional question logic. A parent answer can reveal or hide child questions, changing the total. A three-state indicator adapts automatically without confusing the user with changing numbers.

  • Complete (checkmark): All currently-visible questions answered
  • In progress (half-filled): Some answered, some remaining
  • Not started (empty): Nothing answered

Alternative: A progress ring (thin donut chart) that fills proportionally to visible-question completion. No numbers, just visual proportion.

DD-08: Option Description Field -- Ignore

Decision: The Description field on question options is a data artifact that is never displayed. The new UI should only expose Value (what annotators see and what gets stored).

Rationale: Confirmed via code analysis -- Description is defined in the data model but never rendered in any annotation form component, question editor, or data export.


2. Information Architecture

Project
  |-- Dashboard
  |-- Studies
  |-- Screening
  |-- Data Extraction
  |-- Questions                     <-- expandable section
  |     |-- Design                  <-- WYSIWYG tree + properties panel
  |     |-- Assign                  <-- single tree, checkboxes, filter toggle
  |     |-- Preview                 <-- interactive annotation form simulation
  |-- Stages
  |-- Members
  |-- Settings

Content Area Structure

All three Question views share a common horizontal category bar:

+--------------------------------------------------------------+
| [Study] [DMI] [Treatment] [OA] [Cohort] [Experiment]         |  <-- category tabs (shared)
+--------------------------------------------------------------+
|                                                                |
|   View-specific content (Design / Assign / Preview)           |
|                                                                |
+--------------------------------------------------------------+

Category Navigation Behaviour

  • Selecting a category filters the view to show only questions in that category
  • Selected category persists across Design/Assign/Preview navigation changes
  • Category badge shows question count: Treatment (4)
  • Category tab appearance matches the annotation form's category tabs for visual consistency

URL Structure

/project/:projectId/admin/questions/design?category=treatment&q=<questionId>
/project/:projectId/admin/questions/assign?category=treatment&stage=:stageId&q=<questionId>
/project/:projectId/admin/questions/preview?category=treatment&stage=:stageId

Category and stage selections are query parameters, not route segments, so they persist naturally across view transitions.

Selected question: ?q=<questionId> is appended as a query parameter for deep linking to a specific question. On load, the tree auto-expands ancestors of the selected question and scrolls it into view.

URL update strategy:

  • Category and view changes use pushState (create browser history entries -- intentional navigation)
  • Question selection uses replaceState (no history pollution -- UI refinement, not navigation)
  • queryParamsHandling: 'merge' preserves existing params when updating a single param
  • Stale links (deleted question) gracefully degrade to showing the category with no selection

What is NOT encoded in the URL: Panel width (stored in localStorage), expansion state (component state, auto-derived from ?q), scroll position (auto-derived from ?q).


3. Design View

Framing Line

A persistent, subtle line at the top of the Design workspace:

Changes here are drafts — annotators won't see them until you publish to a stage.

This establishes the sandbox mental model. The admin has permission to experiment freely because nothing in the Design view is live.

Layout

Split-panel workspace: WYSIWYG question tree (left) + properties panel (right).

+--------------------------------------------------------------+
| [Study] [DMI] [Treatment <-sel] [OA] [Cohort] [Experiment]   |
+-----------------------------+--------------------------------+
|                             |                                |
|  QUESTION FLOW              |  PROPERTIES                   |
|  (compact, hybrid view)     |  (edit form for selected Q)   |
|                             |                                |
|  +------------------------+ |  Question Text                |
|  | Drug Name              | |  [Drug Name             ]    |
|  | TextBox . Required     | |                               |
|  +------------------------+ |  Description                  |
|  +========================+ |  [                      ]     |
|  || Dose          <- sel  || |                               |
|  || TextBox . Required    || |  Type: [String v]             |
|  || "mg/kg"               || |  Control: [TextBox v]         |
|  +========================+ |  [x] Required                 |
|  +------------------------+ |  [ ] Multiple answers         |
|  | Route                  | |                               |
|  | Dropdown . Optional    | |  Conditional display:         |
|  | Oral, IV, IP, SC       | |  Parent: [Treatment Ctrl v]   |
|  +------------------------+ |  Show when: [Non-control v]   |
|                             |                                |
|  [+ Add Question]          |  All changes saved            |
|                             |                                |
+-----------------------------+--------------------------------+

Left Panel -- Question Flow (WYSIWYG Tree)

The left panel displays questions in their natural hierarchy using a compact "hybrid" view. Each question node shows:

  • Expand/collapse chevron (LEFT side, before all other content -- per GitHub Primer, Carbon, PatternFly conventions). Leaf nodes get an invisible spacer for alignment.
  • Drag handle for reordering
  • Control type icon using existing SyRF Material Icons: short_text (Input Box), arrow_drop_down_circle (Dropdown), check_box (Checkbox), checklist (Checklist), spellcheck (Autocomplete), radio_button_checked (Radio)
  • Question text -- full text, wrapping to multiple lines (NO truncation). Row uses align-items: flex-start so icons align to the first line.
  • Required indicator (small red dot)
  • Status indicator: edit_note Draft (never published), task_alt Published (live, clean), or published_with_changes Published with changes (has unpublished edits). NO version numbers on nodes (see PDD-17 for terminology).
  • Warning icon (amber triangle) if cross-question validation issues exist
  • Annotation count indicator for questions with answers
  • Selection highlight (border change) for the currently-selected question

Tree connectors: L-shaped branch lines showing parent-child relationships. Vertical lines connect siblings. Last child has no trailing vertical line. Same pattern as IDE file explorers.

Focus mode: When the tree exceeds 6 levels of indentation from the current visual root, focus mode activates automatically. The tree re-roots to show a subtree, displaying only the deeply nested portion. A breadcrumb bar appears above the tree showing the full path from root to the current focus point, with clickable links at each ancestor to navigate back up. A "Show full tree" exit button restores the complete tree view.

Focus mode specifics:

  • Trigger: Automatic when a question at depth 6+ is selected or expanded. Can also be triggered manually by right-clicking a node and selecting "Focus on subtree".
  • Breadcrumb bar: Fixed above the tree panel. Shows: tree icon > ancestor 1 > ancestor 2 > ... > current root (non-clickable). Each ancestor is a clickable link that re-focuses at that level.
  • Exit: "Show full tree" link at the right of the breadcrumb bar, or clicking the root-most breadcrumb.
  • Indentation cap: The tree never visually exceeds 6 levels of indentation regardless of actual depth, preventing horizontal overflow on narrow screens.
  • Persistence: Focus state is preserved when switching between Design/Assign/Preview views within the same category.
  • Keyboard: Escape exits focus mode when the tree panel has focus.

Edit mode toggle: Admin can switch between Side Panel mode (properties on the right) and Inline Expand mode (properties expand below the node). In inline mode, each node has a separate pencil icon for opening properties -- distinct from the tree chevron.

Tree structure: Matches the annotation form's question hierarchy. System questions appear as non-editable nodes (greyed out, no drag handle). Custom questions appear as interactive nodes.

Interactions on the left panel:

Action Trigger Result
Select question Click question node Properties panel updates with selected question's data
Expand/collapse Click chevron Show/hide child questions
Reorder Drag via handle Reposition within same parent (sibling constraint)
Add question Click [+ Add Question] New question created, selected, properties panel shows empty form
Add child Click [+ Add Related] on a question node (via overflow menu or hover action) New child question created under the selected parent

Node appearance per question state (see PDD-04 and PDD-17 for full icon specification):

State Icon Colour Visual Treatment
Draft (never published) edit_note Purple Icon in meta column. Question is fully mutable.
Published (clean) task_alt Green Icon in meta column. Positive confirmation that the question is live.
Published with changes (published with pending edits) published_with_changes Amber Icon in meta column. Publishing will create a new version.
Published, with annotations task_alt + annotation count Green "12 studies" count alongside published icon
Validation warning warning Amber Warning icon, amber row tint, persists until fixed
Selected Primary Highlighted background + primary-colour left border
System question lock + "System" badge Grey Greyed out / muted, no drag handle, read-only properties

Right Panel -- Properties Editor

The properties panel displays all editable fields for the selected question. It does NOT change the tree layout -- only the panel content updates when selection changes.

Resizable panel: The side panel width is adjustable via a drag handle on its left edge. Users can click and drag to resize the panel between 280px and 60% of viewport width. The drag handle shows a subtle grip indicator on hover. Panel width preference persists for the session.

Property layout order: Fields are organised in a deliberate order that prioritises context before content:

  1. Conditional display (top) -- parent relationship and visibility condition
  2. Question content -- text and help text
  3. Type and control -- data type, control type, options
  4. Behaviour toggles -- required, multiple answers, answer array (compact 2-column grid)
  5. Version history (bottom)

The rationale for placing conditionality first: when editing a child question, the first thing the user needs to understand is where it appears and under what conditions. This context informs all subsequent editing decisions (e.g., whether the question text should reference the parent, whether options should match parent option values).

Form fields (signal forms with schema validation):

Section Field Type Validation Notes
Conditionality Parent question Read-only display -- Shows parent name with tree icon. Reordering via drag-and-drop.
Conditionality Show when Select Conditional Boolean (checked/unchecked) or option matching
Content Question text Text input Required, max 80 chars Primary question wording
Content Help text Textarea Optional Guidance shown to annotators below the question
Type Control type Icon selector Required TextBox, Dropdown, CheckBox, Radio, Checklist, Autocomplete
Type Data type Icon selector Required String, Integer, Decimal, Boolean
Type Options Repeatable list Min 1 for Dropdown, unique values For Dropdown/CheckBox/Radio/Checklist/Autocomplete
Behaviour Required Toggle -- Whether answer is mandatory
Behaviour Multiple answers Toggle -- Allow multiple selections
Behaviour Answer array Toggle -- Store as array
Behaviour Group as single Toggle -- Group with parent (structural, frozen after publish)
Behaviour Default checkbox status Select Conditional (CheckBox only) Initial state

Conditional display section: Shown at the top of properties for all questions. - Root questions: Shows a subtle info line: "Root question -- always visible. Drag under another question to make it conditional." - Child questions: Shows a highlighted conditionality card with: - Parent question name (read-only, with tree icon for visual hierarchy) - Explanatory text: "This question is shown to annotators based on the parent question's answer." - Show When selector: Boolean (checked/unchecked) or option matching, depending on parent type - Context hint about reordering via drag-and-drop

Validation error highlighting: When a question has configuration errors (indicated by the warning icon in the tree), selecting it displays: - A validation summary banner at the top of the properties panel (amber background, error icon, description of each error) - The specific field(s) causing the error are highlighted with a red/amber border and an inline error message below the field - Error messages are actionable, explaining what needs to change (e.g., "Referenced parent option 'Non-control' no longer exists -- update the Show When condition") - This makes it immediately clear which property is invalid and why, without requiring the user to inspect each field manually

Option list editing: - Each option shows only the Value field (Description field is unused -- see DD-08) - Add/remove buttons per option - Drag to reorder options - Parent filter (advanced): Per-option conditional visibility based on parent question. Accessible via an overflow menu or "Advanced" expander on each option -- not prominent by default.

Autosave status indicator (bottom of properties panel): A subtle text line showing autosave state -- "All changes saved", "Saving...", or "Offline -- changes queued". No manual Save button exists. All draft changes are autosaved (debounced). There is no manual Save action. The only deliberate user action is Publish. See publishing-versioning-ux.md for the autosave model.

Annotation count display (panel header area, published questions only): Shows "Published · 47 annotations · 12 studies" as passive fact. No verb, no imperative. The admin draws their own conclusion.

Reassuring inline note (appears when editing a published question with annotations): Styled as info (blue/grey), NOT warning (amber). Appears below the field being edited:

23 annotations reference this option. Editing here updates your draft only — existing data is unaffected until you publish.

Follows the Mailchimp/NNg structure: what's relevant, what's safe, what stays the same, when it matters.

Impact & Mapping section (auto-expands when admin edits a published question with annotations, or when editing a replacement DraftQuestion with replacesAnnotationQuestionId pointing to an AQ with annotations):

The section shows annotation counts and answer distribution, and lets the admin configure preliminary decisions that will travel to the publish wizard as pre-populated suggestions.

This question has 47 annotations across 12 studies.
Editing here updates your draft only.

When published, how should existing answers be handled?

● Keep answers as they are
  Best for: wording improvements, adding help text,
  or changes that don't affect how answers are interpreted.

○ Map answers to updated options
  Best for: fixing a typo in an option name, merging
  duplicate options, or renaming for consistency.
  [Configure mapping →]

○ Ask annotators to re-answer
  Best for: significant changes to question meaning,
  new options that may better fit previous responses,
  or when the admin needs fresh judgements.

☐ Flag this change for review — annotators who have
  already answered may need to reconsider

[Change note: ________________________________]

Configuration is stored as DraftPublishDecision on AQ.draft.draftPublishDecision (for edits to existing published questions) or on the DraftQuestion.draftPublishDecision (for replacement drafts with lineage). Mutable until publish.

Action buttons (bottom of properties panel):

  • Delete: Opens confirmation dialog. Cascades to children.
  • Duplicate: Duplicates the question without children. Copies are always Draft with new GUIDs, regardless of the original's state.
  • Duplicate with children: Duplicates the question and all its descendants. All copies are Draft with new GUIDs.
  • Replace with new version: Available for all published questions. Shows annotation counts if the question has annotations. Creates new DraftQuestions (new GUIDs, full copy of content) with replacesAnnotationQuestionId pointing to the original AQ. All published descendants are also redrafted (published children have parentQuestionId pointing to the old AQ, and changing parentQuestionId is a structural change forbidden on published AQs). Originals removed from draft question set but never deleted — annotation references remain intact.

Panel states: - No selection: Shows a prompt: "Select a question to edit its properties" or a summary of the current category (question count, assignment status). - Question selected: Shows the edit form with conditionality section at top. Changes are autosaved as the user edits (debounced). The autosave status indicator reflects the current save state. - Question with errors selected: Shows validation summary + highlighted invalid fields + edit form. Autosave still operates but validation errors are highlighted immediately. - System question selected: Shows read-only informative properties including: role description (e.g., "Unit Label", "Control/Non-control Classifier"), explanation of how the question works in the annotation form, data extraction relevance (why it's auto-assigned when extraction is enabled), and a read-only summary of key properties (control type, data type, required status, hierarchy position). Properties are displayed as text labels rather than editable form controls to reinforce that they are not configurable.

Drag-and-Drop

  • Handle: Drag icon on the left of each question node
  • Constraint: Sibling-only moves (same parent). Cross-parent moves are blocked with an explanatory message.
  • Visual feedback: Dropzone dividers between siblings. Active dropzone highlighted on hover.
  • Keyboard alternative: Shift+Arrow keys to move a focused question up/down within its siblings (accessibility requirement)
  • Auto-expand: Hovering over a collapsed node during drag for >800ms auto-expands it
  • Optimistic update: Tree reorders immediately, backend save follows. On error, tree reverts.

4. Assign View

Layout

Full-width (no split panel needed -- assignment is a bulk operation, not a per-question detail task).

+--------------------------------------------------------------+
| Stage: [Extraction v]          Show: [All v]                  |
+--------------------------------------------------------------+
| ℹ Data extraction enabled — system questions locked.          |
+--------------------------------------------------------------+
| [Study] [DMI] [Treatment <-sel] [OA] [Cohort] [Experiment]   |
+--------------------------------------------------------------+
|                                                                |
|  [x]    🔒 Treatment Label                          🔒        |
|  [x]    ☑  Treatment Control                        🔒        |
|  [x]       Drug Name                                          |
|  [x]       Dose                                               |
| ┃[x] +     Treatment Duration                                |
|  [x]       Has a vehicle been used?                           |
|  [x]       Specify the vehicle used                  ⚠        |
| ┃[ ] −     Frequency of Administration                        |
|                                                                |
+--------------------------------------------------------------+
| 6 of 7 assigned · 2 pending changes   [Discard] [Review & Publish 2 changes] |
+--------------------------------------------------------------+

Deep nesting: The Assign view is full-width (~1200px content area) and comfortably handles depth 6+ with standard 20px indent per level (120px indent at depth 6, leaving >1000px for question text). No focus mode or truncation is required. The overflow-x: auto property on the tree container provides a safety net for extreme depths.

System Questions and Data Extraction

An info banner at the top of the Assign view explains how system questions interact with data extraction:

  • When data extraction is enabled for the selected stage: system questions (Label and Control questions for each category) are automatically assigned and cannot be removed. System questions provide the structural framework for entity relationships (treatments, disease models, outcomes, cohorts, experiments) required for quantitative data extraction. Custom questions can be freely assigned or unassigned via checkboxes.
  • When data extraction is not enabled: system questions at the label and control level can be individually assigned or unassigned. However, assigning any child question will automatically include its parent questions (label and control) to maintain the tree hierarchy.

This distinction matters because system questions define the unit structure (e.g., "Treatment Label" creates treatment units, "Cohort Label" creates cohort units) that quantitative analysis depends on. The banner adapts its wording based on whether the selected stage has data extraction enabled.

Controls

Stage selector: Dropdown listing all project stages. Selecting a stage loads its current question assignments.

Show filter toggle: Dropdown with three options:

Filter Behaviour
All Shows every question in the category. Checked = assigned, unchecked = unassigned. This is the primary assignment interaction.
On stage Shows only questions in the last published SQS for the selected stage. This is the current published stage baseline that annotators see.
Not on stage Shows only questions not currently in the published SQS for the selected stage. Useful for reviewing gaps and pending additions.

Tree Behaviour

  • Single mat-tree component (not two trees)
  • Checkbox per question node
  • System questions: when data extraction is enabled, show a locked checkbox with a dash -- (auto-included, not user-controllable). When data extraction is not enabled, system questions show a regular checkbox and can be assigned/unassigned, but assigning any child question auto-includes the parent system questions.
  • Checking a child question auto-selects all ancestors (required by parent integrity constraint). Indeterminate checkbox state shown when a parent is checked but not all descendants are.
  • Unchecking a parent automatically unchecks all descendants.
  • Tree indentation shows hierarchy with hover row highlight.

Assign Row Design — Two Visual Dimensions

The Assign page is a configuration tool (check/uncheck to assign questions), not a changeset review. Visual noise is minimised to keep the focus on the checklist interaction. Full changeset detail is shown in the Publish Wizard (see Section 6).

Row elements (left to right): [indent] [checkbox] [+/− prefix] [type icon] [label] [change status icon] [lock/warning badge]

Rows communicate state through exactly two visual dimensions:

Dimension 1: Change status (icon, per-question)

Indicator Meaning
No indicator Identical to what's currently published for this stage
Small amber dot / edit_note Has changes that will be included in next publish — whether from admin's own edits OR from another stage's publish (merged concept, no distinction in tree)
Warning icon (amber warning) Validation error that blocks publishing

Dimension 2: Assignment changes (row treatment)

Treatment Meaning
Normal row Currently published on this stage, will remain
Left green border + "+" prefix NOT currently published, will be added
Left red border + "−" prefix Currently published, will be removed
Unchecked, no border, muted Not assigned, never has been

No row background colours (no green/red fills) and no legend bar. This keeps the tree visually clean for the primary task of checking and unchecking questions.

Only two badge types appear on rows (in addition to the change status icon):

  • lock (grey) — system question, auto-assigned
  • warning (amber) — validation error, needs fixing

Status bar (bottom): Simple count format: N of M assigned · N pending changes. No colour-coded breakdown.

Row readability

  • Hover highlight: Entire row highlights on mouseover to connect label to checkbox
  • Compact spacing: Checkboxes are close to the question text
  • Long questions: Text wraps within the node. Full text available on hover tooltip.

5. Preview View

Layout

Full-width interactive annotation form simulation.

+--------------------------------------------------------------+
| [Study] [DMI] [Treatment <-sel] [OA] [Cohort] [Experiment]   |
+--------------------------------------------------------------+
| Stage: [Extraction v]                                          |
+--------------------------------------------------------------+
| +----------------------------------------------------------+ |
| | SIMULATION -- data entered here is not saved              | |
| +----------------------------------------------------------+ |
|                                                                |
|  [Interactive annotation form for the selected stage/category] |
|  (uses the same AnnotationFormComponent as real annotation,    |
|   but in simulation mode with no data persistence)             |
|                                                                |
+--------------------------------------------------------------+

Behaviour

  • Renders the actual AnnotationFormComponent (or its v2 replacement) in simulation mode
  • All controls are interactive -- admins can fill in dummy data to test conditional logic
  • No data is saved -- form state is purely local
  • Banner at the top clearly states this is a simulation
  • Conditional questions appear/disappear based on dummy answers, proving the logic works
  • Stage selector controls which question set is previewed
  • Category tabs filter to the selected category (consistent with Design and Assign views)

6. Publish Wizard

Publishing is organised as a step-by-step wizard to prevent overwhelming the admin with simultaneous decisions. Accessed via "Review & Publish N changes" button on the Assign page action bar.

Wizard Steps

Step 1: Review Changes
  "Here's what changed since this stage was last published."
  Grouped: your edits, updates from other stages, new assignments, removals.
  First-publish info card shown here if applicable (see below).
  No decisions -- just orientation.
  [Next]

Step 2: Confirm Impact on Annotations
  For each changed question WITH annotations:
  Radio: "Does not affect existing answers" / "May affect"
  Pre-populated from Design-time DraftPublishDecisions.
  Questions with zero annotations: auto-confirmed.
  Objectively invalid annotations: pre-expanded with required decisions.
  [Next]  (disabled until all confirmed)

Step 3: Configure Answer Handling
  (CONDITIONAL -- only appears if Step 2 had "May affect" or objectively invalid items)
  Per-question decisions with answer distribution shown.
  Mapping configuration for option-based questions.
  Separate sections for in-progress and completed sessions.
  Pre-populated from Design-time configurations.
  [Next]  (disabled until all configured)

Step 4: Resolve Conflicts
  (CONDITIONAL -- only appears if multi-question conflicts detected)
  Sessions where per-question decisions conflict.
  Admin resolves each conflict.
  [Next]  (disabled until all resolved)

Step 5: Review & Publish
  Session impact summary: computed effective state.
  Final review of all decisions.
  Change notes summary.
  [Publish]

Step Indicator

A progress indicator at the top shows: Review > Confirm impact > [Configure handling] > [Resolve conflicts] > Publish

Conditional steps (3, 4) are greyed out or hidden when not needed. For a simple publish (no annotations or all non-breaking), the wizard is: Step 1 > Step 2 (quick confirmations) > Step 5.

First Publish Info Card

A visually distinct info card (not warning) appears in Step 1 (or Step 5) when any questions are being published for the first time:

First-time publish: 2 questions

These questions will become permanent entries in the project.
After publishing, the following properties cannot be changed:
  - Category  - Data type  - Parent relationship  - Group as single

You can always change: question text, options, help text,
control type, required/optional, and multiple answer settings.

> "Drug Name" -- Dropdown, Text, under Treatment
> "Dose Regimen" -- Input, Decimal, under Drug Name

Only shown for first-time publishes. Not shown for routine version bumps.

Publish Dialog Sections (Step 1 Detail)

  1. Content changes section (blue edit icon): Lists questions with draft content changes and their diffs. Changes grouped by source -- "Content changes (your edits)" vs "Updated from other stages."
  2. Questions being added section (green add icon): Lists questions new to this stage.
  3. Questions being removed section (red remove icon): Lists questions being unassigned.
  4. Update summary: Sequential label + timestamp (e.g., "Update 3 -- Mar 28, 14:32").
  5. Change reason: Optional text input.

7. Annotation Form Updates

Category Tabs (Unchanged)

Category tabs remain as horizontal tabs at the top of the annotation form. No change from current behaviour.

Unit Representation: Mini Cards with Select-to-Show Workspace (New)

For categories with units (DMI, Treatment, OA, Cohort, Experiment), unit instances are displayed as mini cards in a wrapping row at the top. Units are NOT currently collapsible -- all questions render inline with no expand/collapse. This is entirely new functionality.

Mini Card Selector Row

-- Treatment (3 units) --

+------------+  +==============+  +------------+
| Aspirin    |  || Saline     ||  | Ibuprofen  |  [+]
| (complete) |  || (2 of 4)   ||  | (not       |
|            |  ||            ||  |  started)  |
+------------+  +==============+  +------------+

[2 of 3 open in workspace]     [Open all] [Close all]
  • Each card shows: unit label + three-state completion indicator (checkmark / half-filled / empty circle)
  • Cards use rectangular styling with structured content (name + status on separate lines) -- NOT pill/chip styling, which implies "selectable tag" rather than "configurable entity"
  • Selected/open cards get a highlighted border
  • The [+] button is inline for adding new units
  • Cards are NOT accordions -- clicking a card opens/closes that unit in the workspace below

Workspace: Multi-Expand with Annotation Form Controls

Units open as expandable panels in a workspace below the card row. Multiple units can be open simultaneously for comparison.

Each unit panel shows the actual annotation form controls (not question design tree icons): - Text questions render as text inputs with labels - Dropdown questions render as <select> elements - Radio questions render as radio button groups - Checkbox questions render as checkboxes - Conditional nesting: When a parent answer reveals a child question, the child appears indented with a subtle left border. An italic label explains the condition (e.g., "Shown because 'Has a vehicle been used?' is checked").

+-- Saline (control) ----------------------- [expand] [more] [v] --+
|                                                                    |
|  Drug Name:  [Saline                    ]                         |
|  Dose:       [10 ml/kg                  ]                         |
|  Route:      [Intraperitoneal v]                                  |
|  Duration:   [                          ]                         |
|                                                                    |
|  Has a vehicle been used?  [x] Yes                                |
|    |  Shown because "Has a vehicle been used?" is checked         |
|    |  Specify the used vehicle:  [0.5% methylcellulose]           |
|                                                                    |
|  Number of animals per group?  [12]                               |
+-------------------------------------------------------------------+

Toolbar

[2 of 3 open in workspace]              [Open all] [Close all]
  • Shows count of open units
  • Open all / Close all convenience buttons

Unit Panel Actions

Each panel header has: - Expand/collapse chevron: Collapses the panel body (unit stays in workspace but takes minimal space) - Full-screen dialog button (expand icon): Opens the unit in a centered dialog overlay with full width. Shows the same annotation form controls. Useful for complex units with many questions. - Overflow menu (three-dot icon) with: - Hide from workspace: Removes the panel from the workspace. Unit still exists -- click the card to re-open. This is temporary. - Edit label: Rename the unit - Duplicate unit: Copy with "Copy of..." prefix - Delete unit (destructive, red): Shows inline confirmation: "Permanently delete [name] and all its annotations?" Removes both the panel AND the card.

The overflow menu is also available on the mini cards themselves (three-dot icon, top-right), so units can be managed without opening them in the workspace.

Close vs Delete Distinction

Action Trigger Effect Reversible?
Hide from workspace Overflow menu or X button on panel Panel removed from workspace view. Card deselected. Unit and data intact. Yes -- click card to re-open
Delete unit Overflow menu (red action) Unit permanently deleted. Card removed. All annotations for this unit deleted. No -- requires confirmation

Completion Indicators

Three-state (no fixed denominator due to conditional questions):

  • Checkmark (filled circle with check): All currently-visible questions answered
  • Half-filled (circle with dot): Some answered, some remaining
  • Empty circle: Nothing answered yet

No numeric counts (e.g., "⅗") because conditional questions change the total dynamically.

Pagination for Many Units

When a category has many units (20+), paginate the card row:

  • Show 10 cards per page
  • Page controls below the card row: Showing 1-10 of 23 [Prev] [Next]
  • Lazy-render: only the current page's cards and open panels are in the DOM
  • Performance improvement for studies with 20-30+ cohorts/treatments

8. Versioning UX

Version Indicators on Tree Nodes

Version numbers (v1, v2, v3) are not shown on tree nodes — this is progressive disclosure. The tree shows lifecycle state icons instead (see PDD-04, PDD-17):

  • Draft (edit_note purple): Never published, no version exists yet
  • Published (task_alt green): Published, matches the last-published version
  • Published with changes (published_with_changes amber): Published but has pending edits — publishing will create a new version

Version numbers are only visible in the Version History panel (properties panel, bottom section) and the Version History dialog. Most users don't need to think about version numbers — they just need to know "is this published or does it have changes?"

Version History Timeline

Accessible from the properties panel when a published question is selected. Shows a chronological list of all versions:

HISTORY
-------
v3  Mar 15, 2026  Chris    "Fixed typo in treatment question"
v2  Mar 10, 2026  Chris    "Added 'Subcutaneous' to route options"
v1  Feb 28, 2026  Chris    (initial publication)

Each entry shows: version number, date, author, and change reason.

Expandable diff: Clicking a version entry expands to show what changed:

v2  Mar 10, 2026  Chris    "Added 'Subcutaneous' to route options"
  Options: Oral, IV, IP  -->  Oral, IV, IP, SC
  (no other changes)

Simple before/after per field, only showing fields that actually changed. Not a code diff -- structured field comparison.

Edit Published Question Flow

When editing a published question (via the properties panel):

  1. Properties panel shows current version's content (editable). Changes autosave to the draft (AQ.draft).
  2. Status icon changes from Published (green task_alt) to Published with changes (amber published_with_changes).
  3. If the question has annotations, the Impact & Mapping section auto-expands in the properties panel (see Section 3, Properties Editor). The admin configures preliminary handling decisions at design time.
  4. At publish time, the publish wizard (Section 6) loads the DraftPublishDecision as a pre-populated suggestion. The admin reviews and confirms in context of all changes together.
  5. On publish: new AQVersion created, DraftPublishDecision promoted to immutable PublishDecision, draft cleared, version history entry added.

9. Accessibility

Keyboard Navigation

Global (all Question Management views)

Keys Action Condition
1-6 Switch to category tab 1-6 When focus is not in a text input
Ctrl+Shift+P Open publish dialog Assign view (when changes exist)
Escape Exit focus mode / close dialog / deselect Contextual
? or Ctrl+/ Show keyboard shortcuts cheat sheet Global
Arrow Left/Right Switch between categories When category tabs have focus

Design View -- Tree

Keys Action
Up/Down Move focus between questions
Right Expand focused node / move to first child
Left Collapse focused node / move to parent
Enter/Space Select focused question (updates properties panel)
Shift+Up/Shift+Down Reorder focused question within siblings
Home/End Jump to first/last visible question
Ctrl+N Add new question (inline placeholder)
Delete Delete selected question (confirmation dialog)
Ctrl+D Duplicate selected question
F2 Focus question text in properties panel

Design View -- Properties Panel

Keys Action
Tab/Shift+Tab Move between fields
Escape Return focus to tree
Ctrl+Z Undo last change

Assign View

Keys Action
Up/Down Move focus between questions
Space Toggle checkbox
Shift+Space Toggle checkbox + all descendants
Ctrl+A Select all questions
Ctrl+Shift+A Deselect all questions

Preview View

Keys Action
Tab/Shift+Tab Move between form fields
Alt+1/Alt+2 Toggle Published/With Unpublished Changes mode

Dialogs

Keys Action
Escape Close dialog
Enter Confirm primary action (when not in textarea)

ARIA Attributes

  • Tree container: role="tree"
  • Tree nodes: role="treeitem" with aria-expanded, aria-level, aria-setsize, aria-posinset
  • Group containers: role="group"
  • Selected node: aria-selected="true"
  • Properties panel: aria-label="Question properties" with aria-live="polite" for content updates
  • Form fields: aria-invalid="true" when validation fails, aria-describedby linking to error messages
  • Drag-and-drop: aria-grabbed, aria-dropeffect (deprecated but still used), plus keyboard alternative

Screen Reader Announcements

  • When a question is selected: "Selected: [question text], [type], [category], version [N]"
  • When a question is reordered: "Moved [question text] to position [N] of [total]"
  • When form is saving: aria-live region announces "Saving changes"
  • When save completes: aria-live region announces "All changes saved"
  • When validation errors occur: Error messages announced via aria-live="assertive"

Reduced Motion

  • All animations respect prefers-reduced-motion
  • Drag-and-drop dropzone highlighting uses border changes, not animations
  • Tree expand/collapse can be instant rather than animated

Colour Independence

  • All status indicators (completion, Draft/Published/Published-with-changes states) use both colour AND distinct icon shape — never colour alone
  • Assign page pending changes use colour (left border) AND a text character (+/) — readable without colour vision
  • Dropzone indicators use border style changes, not colour alone

10. State Management & Performance

Signal-Based Architecture

  • Question Management store: ngrx SignalStore at the Question Management route level
  • Shared state: questions, categories, selected category, selected question ID
  • Design-specific: question info map (per-question form state, dirty/valid flags, view state)
  • Assign-specific: stage assignments, filter mode, checkbox states

  • Properties panel: Signal forms with schema-based validation (required(), validate(), applyWhen())

  • No VEST dependency -- use Angular's built-in signal forms validation
  • Validation co-located with form definition
  • Immediate feedback via signal-derived error states

  • Annotation form: Signal forms replacing reactive forms

  • Per-unit form state (not one giant reactive form)
  • Independent validation per unit (isolated, no cross-unit recalculation)
  • Conditional question visibility via computed signals (replaces the current Observable + hidden property approach)

Performance Strategies

Strategy Where How
Category-level lazy loading Design, Assign Only build tree data for the selected category. Other categories' trees are not in the DOM.
Collapsed unit lazy rendering Annotation Form Collapsed unit tiles do not render form fields. Only the expanded unit's form is in the DOM.
Unit pagination Annotation Form For 20+ units, paginate the tile grid (10 per page). Only current page tiles rendered.
Debounced validation Properties panel Validation runs on blur or after 300ms of inactivity, not on every keystroke.
Optimistic updates Drag-and-drop, save Update UI immediately, sync with backend asynchronously. Revert on error.
Virtual scrolling Question tree (if needed) For categories with 50+ questions, use CDK virtual scroll. Most categories won't need this.

State Persistence

  • Selected category: URL query parameter (survives page refresh)
  • Selected stage: URL query parameter
  • Tree expansion state: Component-level signal (lost on navigation, which is acceptable)
  • Draft form changes: Autosaved to backend (debounced). SignalStore tracks autosave status per question, with visual indicator on tree node.

Security: HTML Sanitization

All user-provided text (question text, option values, help text, change reasons) must be rendered via Angular's template binding ({{ }} or [textContent]), never via innerHTML or string interpolation into HTML. Angular's built-in sanitization handles escaping. Direct DOM manipulation with user content is prohibited.


11. Research References

Patterns and precedents that informed these decisions:

Design Challenge Best Precedent Pattern
Hierarchical question organization EPPI-Reviewer code tree Parent-child tree with category grouping
Workspace layout SurveyJS Creator, Figma Sidebar tree + properties panel
Publishing ceremony REDCap Development/Production lifecycle Explicit publish with draft mode for post-publish changes
Version tracking Form.io revision system Per-publish revision with ID, author, timestamp, notes
Stage assignment REDCap "Designate Instruments for Events" Checkbox-based assignment, one stage at a time
Interactive preview Qualtrics Preview + Generate Test Responses Fill dummy data, test branching logic
Collapsible repeating units SurveyJS Dynamic Panel, Cognito Forms Collapsible instances with dynamic titles + status
Performance at scale Qualtrics blocks + Survey Flow Category-level rendering + sidebar navigation

Key finding: No existing tool does per-question versioning. SyRF's AQVersion model is novel. All surveyed tools version at the form/instrument level.


Open Questions

  1. Category tab component choiceRESOLVED: Use horizontal tabs with Font Awesome category icons matching the annotation form (fa-file-text Study, fa-eyedropper DMI, fa-medkit Treatment, fa-eye Outcome, fa-paw Cohort, fa-flask Experiment). Tabs appear below the view toolbar. Icon source: Production uses Font Awesome icons for continuity with the current annotation form. Material Symbol alternatives for future consideration: description/article (Study), science/biotech (DMI), medication/medical_services (Treatment), visibility/monitoring (Outcome), pets/groups (Cohort), science/experiment (Experiment).
  2. Accordion vs multi-expand for unit tilesRESOLVED: Multi-expand. Multiple units can be open simultaneously for comparison. "Open all" / "Close all" buttons provided.
  3. Properties panel widthRESOLVED: Resizable via drag handle on the panel's left edge. Default 360px, min 280px, max 60% viewport width. Session-persisted.
  4. Mobile/responsive behaviour: How does the split panel degrade on small screens? Does the properties panel become a bottom sheet or a full-screen overlay?
  5. Annotation form category tabsRESOLVED: Yes, same category tabs with icons across all views (Design, Assign, Preview, Review). Category selection persists across view switches.

12. Prototype Design Decisions Addendum

Decisions and specifications established during interactive prototyping that augment the original specification. These should be treated as authoritative alongside the sections above.

PDD-01: Navigation Elevation (Supersedes DD-01)

Decision: Question Management is elevated to a top-level sidebar section called "Questions", positioned between Stages/Create Stage and Data Export. It is NOT nested under Project Settings.

Rationale: QM is a core workflow (design → assign → preview → publish), not a configuration task like "General" or "Membership". Burying it 3 levels deep under Project Settings did not reflect its importance.

Updated structure:

Project Overview
─────────────
Studies ▸
Screening Info
─────────────
STAGES
  Completed Screening      [grey dot: no questions]
  Data Extraction ▸        [green dot: published, active]
    Overview
    Review
    Settings
  Create Stage
─────────────
Questions ▸                [orange dot: has draft changes]
  Design
  Assign · Extraction      [stage context shown inline]
  Preview · Extraction
─────────────
Data Export ▸
─────────────
Project Settings ▸
  General
  Membership

Navigation indicators:

  • Stage status dots: Green = published with active sessions. Orange = has draft changes. Grey = no questions assigned.
  • Dirty state dot on Questions section: Orange dot when any draft changes exist across the project question set.
  • Stage context labels: Assign and Preview nav items show the currently-selected stage inline (e.g., "· Extraction").
  • Invisible chevron spacers: Non-expandable items have an invisible chevron to align icons with expandable groups.
  • Section dividers: Horizontal lines between Project Overview, Studies/Screening, Stages, Questions, Data Export, and Project Settings.

PDD-02: Design View Layout Toggle

Decision: The Design view toolbar includes a layout toggle between "Side Panel" mode (tree + properties panel split) and "Inline" mode (full-width tree with properties expanding below each question node).

Side Panel mode (default):

  • Split layout: question tree on left, properties panel on right (1fr | 360px)
  • Clicking a question updates the properties panel
  • Properties panel shows "Select a question to edit its properties" when nothing is selected

Inline mode:

  • Full-width tree, properties panel hidden
  • Each question node shows an edit (pencil) icon
  • Clicking the edit icon or the question row expands a properties panel directly below the node
  • Clicking again collapses it (toggle behaviour)
  • Both modes show identical property fields (full parity)

PDD-03: Design View Toolbar

Decision: The Design view toolbar (above category tabs) contains:

  1. Layout toggle — Side Panel / Inline buttons (mat-button-toggle-group style)
  2. Project Question History button — Opens a dialog to browse and restore previous published versions of the project's questions

PDD-04: Status Icon System (Updated)

All status indicators use Material Symbols Outlined icons with tooltips (no worded badges) to reduce visual noise in question trees. Three-state terminology: Draft (never published), Published (clean), Published with changes (published with pending edits).

Question State Icons

State Icon Colour Tooltip Used in
Draft (never published) edit_note Purple (#7b1fa2) "Draft — not yet published" Design tree, Properties panel, Assign change status
Published (clean) task_alt Green (#4caf50) "Published" Design tree, Properties panel
Published with changes (published with pending edits) published_with_changes Amber (#e65100) "Published with unpublished changes" Design tree, Properties panel, Assign change status

Note: "Update available" is no longer a separate state. Updates from other stages are merged into the "Published with changes" indicator (amber dot in the Assign view). The distinction between "your edits" and "updates from other stages" is shown in the publish wizard (Step 1) and properties panel on-demand, not in the tree.

Other Indicators

Indicator Icon/Treatment Colour Notes
Required Red * inline after question text Danger red Mirrors annotation form label convention — NOT a separate dot in the meta column
Validation warning warning icon Amber (#e65100) Design tree: icon in meta + amber row tint. Assign tree: icon in badges column only.
System question lock Disabled grey Both Design and Assign trees
Adding to stage Bold + prefix + green left border Success green (#2e7d32) Assign tree only. Minimal treatment — no row background.
Removing from stage Bold prefix + red left border Danger red Assign tree only. Minimal treatment — no row background.

Icon Presentation on Tree Nodes

Status icons (right-side column) use a grey outline by default treatment:

  • At rest: filter: grayscale(1); opacity: .4 with font-variation-settings: 'FILL' 0 (outline). Icons are a subtle grey hint.
  • On hover: filter: none; opacity: .7 with FILL 1 (filled). Original colour restored.
  • Selected: filter: none; opacity: .8 with FILL 1. Full colour and fill.

This progressive reveal avoids visual noise when scanning the tree, while still making state information accessible on interaction.

Checkbox Type Icon

The Checkbox control type uses select_check_box (outlined empty checkbox) instead of check_box (filled checkbox), to avoid confusion with interactive checkboxes in the UI.

Control Type Icon Map

Control Icon
TextBox / Input short_text
Dropdown arrow_drop_down_circle
Checkbox select_check_box
Radio radio_button_checked
Checklist checklist
Autocomplete spellcheck

PDD-05: Properties Panel — Full Field Specification (Updated)

All form fields use Material form field outline appearance (mat-form-field appearance="outline") with floating labels that sit on the border.

Fields (in order of appearance — conditionality first, then content, type, behaviour, history, actions):

Section Field Component Notes
Conditionality Parent question Read-only display in highlighted card "Child of Drug Name" with account_tree icon. Root questions show: "Root question — always visible."
Conditionality Show when Multi-select chips (dropdown parents) or select (boolean parents) Boolean: Always / When checked / When unchecked. Dropdown: removable chip per selected parent option, with + Add chip.
Conditionality Option filtering Expandable "advanced" section Per parent option, checkboxes control which child options are visible. Validation: filter parent options must be subset of Show When options.
Content Question text mat-ff text input Required
Content Help text mat-ff textarea Hint: "Guidance shown to annotators below the question"
Type Control type Icon-based toggle selector Icons: short_text Input, arrow_drop_down_circle Dropdown, select_check_box Checkbox, radio_button_checked Radio, checklist Checklist, spellcheck Autocomplete
Type Data type Icon-based toggle selector Icons: text_fields Text, 123 Integer, decimal_increase Decimal, toggle_on Yes/No
Type Options Scrollable list (max 200px) Only for Dropdown/Radio/Checklist/Autocomplete
Behaviour Required, Multiple answers, Answer array mat-checkbox in flex-wrap row Single line when panel is wide enough, wraps when narrow
Behaviour Default checkbox status mat-ff select Only for CheckBox controls
History Version history Clickable entries Opens master-detail version dialog. Links to "Project question history"
Actions Duplicate / Duplicate with children / Replace with new version / Delete Ghost buttons Delete is red (danger). Copies always Draft with new GUIDs. Replace available for published questions. Same actions available in inline mode.

Conditionality multi-option support: When the parent question has dropdown/radio options, the "Show when" selector uses removable chip tags for selecting multiple parent options. Each selected option appears as a chip with an × remove button. An + Add chip allows adding more options. The question is shown to annotators when the parent answer matches any of the selected options.

Option filtering (advanced): When the child question itself has options AND depends on parent options, an expandable "Option filtering" section appears. For each selected parent option, a checkbox grid controls which of the child's options are visible. Validation rule: parent options used in option filters must be a subset of (or equal to) the parent options in the "Show when" condition.

System question properties: When a system question is selected, the properties panel shows a read-only informative display (not the editable form):

  • Role description (e.g., "Unit Label (Treatment)", "Entity Lookup (Disease Models)")
  • Quantitative data extraction relevance with bar_chart icon — why it's auto-assigned
  • Read-only property summary: control type, data type, required status, hierarchy position
  • Properties displayed as text labels, not editable form controls

PDD-06: Advanced Options Editor

Decision: Options for Dropdown/Radio/Checklist/Autocomplete questions can be managed in two ways:

  1. Inline list (default in properties panel): Scrollable list with max-height 200px, drag handles for reorder, inline editing, add/paste/sort actions.

  2. Advanced dialog (opened via "Advanced" button): Full spreadsheet-like grid with two columns:

  3. Option Value — free text, editable
  4. Parent Answer Mapping — dropdown select from parent question's answers (e.g., "Non-control", "Control")

Advanced dialog features:

  • Add row, Sort A-Z, Clear mappings buttons
  • Delete row (hover to reveal)
  • Bulk paste textarea supporting one-per-line or tab-separated format (Option Value[TAB]Parent Answer)
  • Tab navigation between cells

Production implementation: Use Handsontable v14.4.0 (already a project dependency) via the existing handsontable-loader.ts dynamic import pattern and timepoint-spreadsheet.component.ts reference implementation.

PDD-07: Assign View — Layout & Behaviour (Updated)

Design principle: The Assign page is a configuration tool (check/uncheck to assign), not a changeset review. Visual treatment is deliberately minimal. The full changeset with all state details is shown in the Review & Publish dialog (PDD-16).

Row structure (left to right, in document flow):

[indent] [checkbox] [+/− prefix] [type icon] [label] ............. [lock] [warning]
  • +/ prefix: Bold green + when adding, bold red when removing. Empty space when unchanged.
  • Left border: 3px coloured strip — green (adding), red (removing). Transparent when unchanged.
  • No row background colours: No green/red/blue fills. Keeps the tree visually clean.
  • Change status icons on rows: A small amber dot / edit_note icon appears for questions with changes (from own edits or cross-stage updates). No separate "Update Available" indicator — this is merged into the single "changed" state.
  • Only two badge types: lock (system question) and warning (validation error).

Hierarchical checkbox rules:

  1. Checking a child → all ancestors are automatically checked
  2. Unchecking a parent → all descendants are automatically unchecked
  3. Indeterminate state → a parent shows a dash when checked but NOT ALL descendants are checked
  4. System questions → always checked, disabled, not user-controllable
  5. Questions with validation errors → checkbox remains enabled (errors are a design concern, not an assignment blocker)

Subtree selection shortcut: Shift+Click on a checkbox selects/deselects the node AND all its descendants. A tooltip on first use hints: "Shift+click to include all children." This enables efficient bulk assignment of entire subtrees (e.g., Drug Name + Dose + Route + Duration in one click).

Filter toggle (toolbar): "Show" label with "All" / "On stage" / "Not on stage" buttons. These are based on the published stage assignment baseline, not the current draft checkbox state. No reference to "SQS" or "PQS" in the UI -- these are internal technical terms only (see developer notes below).

Warning rows: warning icon in badges column. No amber background tint — consistent with minimal treatment.

Cross-stage updates: Merged into the amber "changed" indicator on rows. The distinction between own edits and cross-stage updates is shown in the publish wizard (Step 1), not in the tree.

PDD-08: Assign View — Action Bar & Validation (Updated)

Action bar (bottom, sticky):

  • Status text: "N of M assigned · N pending changes" — simple count, no colour-coded breakdown. Changeset detail lives in the publish dialog.
  • Discard button: "Discard". Disabled (with reduced opacity) when no changes from published baseline.
  • Publish button: "Review & Publish N changes" with dynamic count. Disabled when no changes or validation errors exist. Single primary action (replaced separate "Preview & Publish" / "Publish Directly").

Validation panel: Appears above the action bar when configuration errors exist. Uses consistent amber colour (#e65100) throughout — warning icon (not error_outline), "configuration error" label (not "validation error"). Per-error description with "Fix in Design →" link.

Info banner (top of Assign view): Single concise line: "Quantitative data extraction is enabled for this stage — system questions are automatically assigned and locked." Adapts based on stage extraction status.

Cross-stage notice (quiet inline, below the info banner): When questions on this stage have been updated by another stage's publish, a subtle notice appears: "3 questions were updated when Screening was published on Mar 15." This is informational only — not a banner or blocking alert. It disappears after the next publish of this stage.

PDD-09: Preview View — Published vs With Unpublished Changes Toggle

Toggle (toolbar): "Published" / "With unpublished changes" buttons

Published mode:

  • Shows exactly what annotators currently see
  • Simulation banner: "Simulation mode — data entered here is not saved."

With unpublished changes mode:

  • Orange banner: "Previewing unpublished changes — N updated, N draft, N removed since last publish"
  • published with changes badge on questions with unpublished edits
  • new badge on draft questions being added for the first time
  • Removed questions shown with −removed badge, danger-coloured outline, reduced opacity

PDD-10: Category Tab Persistence

Decision: Selected category persists across Design/Assign/Preview view switches. Selecting "Treatment" in Design keeps it selected when navigating to Assign or Preview. Implemented via shared selectedCategory state variable that syncs all [data-cat-bar] elements.

PDD-11: Review Page Layout

Scope note: The annotation form section of this design (question layout, conditional rendering, unit panels) is within QM v2 scope. The surrounding review page layout (study card, screening sidebar, progress bars) is included for contextual placement and may inform a future Review Page redesign (separate feature brief).

Grid layout: Study card (left, scrollable) + Screening actions sidebar (right, fixed width ~140px)

Progress bars (above study card):

  • Screening bar (blue): Three segments — You (solid primary), Remaining (light primary), Unavailable (hatched pattern for studies sufficiently screened by others)
  • Annotation bar (green): Three segments — You (solid success), Remaining (light success), Unavailable (hatched)
  • Legends with colour keys and counts below each bar
  • Tooltips on each segment with exact numbers

Study card (scrollable, max-height 260px):

  • Inclusion criteria (green left border) and exclusion criteria (red left border) shown as side-by-side cards
  • Screening decision chip: "Included" (green) or "Excluded" (red) — clickable to toggle
  • Annotation status chip: "Annotation in progress"
  • Full study metadata: title, authors, journal, year, DOI
  • Collapsible abstract via <details> element (starts collapsed)
  • PDF link

Screening actions sidebar:

  • Include (green), Exclude (red), Skip buttons
  • Navigation: Previous/Next with study counter
  • Annotation: Save, Complete buttons
  • Grouped under labelled sections

PDD-12: Annotation Form Question Layout

Three-column grid per question row (grid-template-columns: auto 1fr 1fr):

  1. Branch number — see "Display numbering" below
  2. Question control — Material outlined form field (mat-ff) with floating label
  3. Optional Comments — Material outlined textarea with "Optional Comments" floating label

Display numbering: Flat sequential numbering per category (1, 2, 3, ...) with hierarchy communicated by indentation. Numbers reflow when conditional questions are hidden (no gaps). This avoids unwieldy compound labels at depth 4+ and ensures the same question gets a consistent visual position within a category.

Stable identifiers (future Phase 2 enhancement): Each question definition will get a human-readable short code (e.g., TRT.DOSE, COH.SPECIES) for cross-referencing in training materials, discussions, and data exports. These are stage-independent and never change.

Conditional questions: Indented with margin-left: 46px, small subdirectory_arrow_right icon with hover tooltip explaining the condition (not inline text).

Checkbox questions: Flat layout without outline (mat-ff-checkbox pattern).

PDD-13: Unit Panel Fullscreen Dialog

Decision: The expand icon (open_in_full) on unit panel headers opens a fullscreen modal dialog (90vw, max 900px) containing the complete annotation form for that unit — all question rows with Material form fields and Optional Comments column. Save and Close buttons in the footer.

PDD-14: Version History Dialogs (Updated)

Both version history dialogs use a master-detail layout (not expandable cards):

Individual question version history (opened from Properties panel or inline):

  • Left panel (220px): Scrollable list of versions. Each entry shows version ID, timestamp, author, and stage. Published versions use bold text + "Published" badge. Draft snapshots use muted text + "Snapshot" badge.
  • Right panel: Detail for the selected version — header with version label + badge, change reason, timestamp. Full properties table (text, help, type, control with icon, required, multiple, options, parent, show when). "Apply as draft" button.
  • Clicking a version in the left list instantly updates the right panel.

Project Question History (opened from Design toolbar button or Properties panel link):

  • Left panel (220px): Same version list style — published updates with stages and timestamps, plus auto-saved snapshots.
  • Right panel: Header with version summary. Below that, the question tree for that version split into a nested master-detail: left half shows the tree with clickable rows and checkboxes, right half shows the selected question's properties at that version.
  • Multi-select restore: Checkboxes on each non-system question. "Restore all as draft" button + "Restore N selected" button (enabled when checkboxes are checked, count updates live).
  • Individual questions also have an "Apply this question as draft" button in the detail view.

PDD-15: Question Tree Hierarchy (Treatment Category)

Corrected hierarchy (matches AnnotationQuestion.cs production code):

Treatment Label (root, system, dropdown)
  └── Treatment Control (child, system, checkbox — control/non-control)
        └── Drug Name (custom)
              └── Dose, Route, Duration
        └── Has a vehicle been used? (custom, checkbox)
              └── Specify the vehicle used
        └── Frequency of Administration (custom)

Treatment Label is the root question (Root = true, SubquestionIds = {TreatmentControlQuestionGuid}). Treatment Control is its child. The same pattern applies to Disease Model Induction (label is root, control is child).

PDD-16: Publish Dialog Structure

Dialog content (dynamically generated from current draft state):

  1. Content changes section (blue edit icon): Lists questions with draft content changes and their diffs
  2. Questions being added section (green add icon): Lists questions new to this stage
  3. Questions being removed section (red remove icon): Lists questions being unassigned
  4. Result card: Shows resulting update summary (e.g., "Update 4 — Mar 28, 14:32").
  5. Change reason: Optional text input
  6. Breaking changes: Checkbox "Changes may invalidate existing answers (breaking)"

Access: "Review & Publish N changes" button on Assign page action bar.

Dialog sections (updated terminology):

  1. Unpublished changes section (published_with_changes amber icon, ~N prefix): Lists questions with unpublished edits and their field-level diffs. Contextual note: "Publishing creates a permanent version. Annotators on this stage will see the updated questions immediately."
  2. New questions section (add_circle green icon, +N prefix): Lists questions being published for the first time.
  3. Removing section (remove_circle red icon, −N prefix): Lists questions being unassigned from this stage.
  4. Updates from other stages subsection (within "Unpublished changes" section): Changes grouped by source -- "Your unpublished changes" vs "Updated from other stages." Cross-stage updates are shown with their source stage and publish date.
  5. Result card: Update summary with sequential label and timestamp.
  6. Change reason: Optional text input.
  7. Impact confirmation: Per-question confirmation for questions with annotations (feeds into wizard Step 2).
  8. Publish button: Proceeds to publish wizard Step 2 (or Step 5 if no annotations).

PDD-17: Terminology — Three-State Question Lifecycle

Decision: Consistent terminology across all UI: Draft, Published, and Published with changes for the question state. The delta itself is described as unpublished changes.

State Term Icon Colour Description
Never published Draft edit_note Purple (#7b1fa2) Question exists only in the project's draft workspace. Not visible to annotators.
Published, no pending edits Published task_alt Green (#4caf50) Live on at least one stage. Matches the last-published version exactly.
Published, with pending edits Published with changes published_with_changes Amber (#e65100) Published version is live, but unpublished changes exist on top of it. Publishing will create a new version.

Rationale: "Published with changes" keeps the live/published status explicit while still indicating that unpublished work exists on top of it. The change layer itself is described as "unpublished changes". That avoids the ambiguity of terms like "Draft changes", "Changed", or "Not live", which can incorrectly suggest that the whole question is no longer published.

"Update available" (removed as separate state): Updates from other stages are now merged into the "Published with changes" indicator. The distinction between own edits and cross-stage updates is surfaced in the publish wizard (Step 1) and properties panel, not as a separate tree icon.

PDD-18: Quantitative Data Extraction Terminology

Decision: All references to "data extraction" in the UI renamed to "quantitative data extraction". All references to "data export" renamed to "quantitative data export".

Rationale: All annotations are a form of data extraction. The qualifier "quantitative" differentiates the specific workflow that requires system questions (entity relationships for meta-analysis) from general annotation data extraction. Aligns with the data export section naming.

Affected UI elements:

  • Sidebar navigation: "Quantitative Data Extraction" (stage name), "Quantitative Data Export" (section)
  • Assign page info banner: "Quantitative data extraction is enabled for this stage"
  • System question properties: bar_chart icon with "Quantitative data extraction" label
  • All tooltips and descriptions referencing the feature

PDD-19: Breadcrumb Bar — Always Visible

Decision: The breadcrumb bar above the question tree is always visible on the Design page, not only during focus mode.

Behaviour:

  • No selection: Shows just the category name (e.g., "Treatment") in muted text.
  • Question selected: Shows full ancestor path: Category > Ancestor 1 > Ancestor 2 > Selected Question. Every ancestor is clickable — clicking navigates to and selects that question.
  • Focus mode active: Same breadcrumb, plus a "Show full tree" button (styled as a subtle outlined button, not underlined text) at the right.
  • Category switch: Breadcrumb resets to the new category name.

Fixed height: 34px, single line, overflow: hidden. Never wraps or causes layout shift.

Truncation: Each crumb has max-width: 140px with text-overflow: ellipsis. Full text on hover tooltip.

Overflow: When 4+ ancestors, middle crumbs collapse to a clickable ••• that opens a dropdown showing the hidden path entries. Pattern: Category > Root > ••• > Parent > Selected.

PDD-20: Deep Nesting — Truncation + Focus View

Decision: Questions nested beyond depth 5 (0-indexed) are hidden by default behind a truncation link. The truncation link triggers a focus view that re-roots the tree.

Truncation link: Appears as the last child of the deepest visible parent. Styled distinctly from questions:

  • Smaller text (11px), italic, muted colour (text-secondary)
  • No underline — colour changes to primary blue on hover
  • Text: "N more nested questions..."
  • subdirectory_arrow_right icon (14px, smaller than question type icons)

Focus view (activated by clicking the truncation link):

  1. Hidden deep children become visible
  2. Tree re-roots: ancestors above the focus root are hidden (rows + non-path siblings)
  3. Visual indentation collapses so the focused subtree appears as the root
  4. Breadcrumb updates to show the full path
  5. "Show full tree" button appears

Re-root depth calculation: The focus root is chosen so that MAX_VISIBLE_LEVELS (5) levels are shown. If the deep subtree has fewer than 5 levels, the view climbs up ancestors until 5 are displayed.

PDD-21: Adding Questions — Constraint-Aware Mechanisms

Decision: Three layered mechanisms for adding questions, respecting the constraint that all custom questions in unit categories must be descendants of the label question.

Layer 1 — Persistent inline "Add question" link: A subtle dashed-border row at the bottom of each valid parent's children list. For Treatment, it appears at the end of Treatment Control's children. For Cohort, at the end of Cohort Label's children. Always visible, in-context.

Layer 2 — Hover-revealed + and ⋮⋮ icons (Notion pattern):

  • Appear between the chevron and type icon on hover
  • + (leftmost): Single action — creates a child question via inline placeholder
  • ⋮⋮ (rightmost, closest to text): Drag to reorder, click to open actions menu (add child, add above/below, duplicate, duplicate with children, delete)
  • System questions: neither icon shown (not draggable, adding handled by the inline link)
  • Custom questions: both icons present, space reserved via visibility: hidden at rest so layout doesn't shift on hover

Layer 3 — Inline placeholder: When any "add" action is triggered, an auto-focused text input appears in the tree at the exact insertion point. Press Enter to create (question immediately selected with properties panel ready). Press Escape to cancel.

No root-level "Add question" button for unit categories (Treatment, DMI, Outcome, Cohort, Experiment) — all custom questions must be descendants of the label. Study category may have a root-level button since it has no label/unit structure.

No browser right-click override: The native browser context menu is preserved. Actions are accessed via the ⋮⋮ grip click.

PDD-22: Unit Panel Header Actions

Decision: Each expanded unit panel (on Preview and Review pages) shows four action icons in the header bar:

  1. open_in_full — Open in fullscreen dialog
  2. content_copy — Duplicate unit
  3. delete (muted/disabled colour) — Delete unit (opens confirmation dialog)
  4. more_vert — Overflow menu with: Open in dialog, Edit label, Duplicate, Delete unit

Unit overflow menu is a real popup menu (not alert() placeholders). Delete action shows a confirmation dialog: "Permanently delete [name] and all its annotations? This action cannot be undone."

PDD-23: Annotation Form — Child Row Indentation

Decision: Child question rows (1.a., 1.b., 1.c.) in the annotation form are indented 32px from their parent (question 1.). Conditional rows (2.a.) use 46px indent with a left border. This makes the parent-child hierarchy visually clear in the form layout.

PDD-24: Cohort Category — Real System Questions

Decision: The Cohort category uses the actual system questions from the codebase (AnnotationQuestion.cs). Unlike Treatment and DMI, Cohort has no Control question — only a Label question with entity lookups.

Question Type System Purpose
Cohort Label TextBox Yes (root) Identifies each cohort group
Disease Models Dropdown (lookup) Yes Links cohort to DMI procedures
Treatments Dropdown (lookup) Yes Links cohort to treatment procedures
Outcomes Dropdown (lookup) Yes Links cohort to outcome assessments
Number of Animals TextBox (integer) Yes Cohort size for quantitative analysis

Custom questions (Species, Strain, Sex, Age at Procedure, Weight) are children of Cohort Label.

PDD-25: Design Tree Node Layout

Decision: The exact element order within each question tree node row, left to right:

[warn-indicator*] [chevron] [+*] [⋮⋮*] [type-icon] [question text] [required *] ... [status-icon]

Elements marked with * are conditionally visible:

  • warn-indicator: Absolutely positioned in left margin (outside flow), only on questions with configuration errors
  • + and ⋮⋮: In flow but visibility: hidden at rest, visible on hover. Not present on system questions.
  • required *: Red asterisk inline after question text, only on required questions
  • status-icon: Right-aligned, grey outline at rest, coloured + filled on hover/selected

Row selection: Selected row has background: rgba(32,52,87,.10) + primary-colour left border. Hover has lighter rgba(32,52,87,.03). Clear visual hierarchy: hover (lightest) < normal < selected (darkest).