Files
resolutionflow/docs/IMPLEMENTATION-PLAN-STEP-LIBRARY-FRONTEND.md
Michael Chihlas 4378ec4b20 docs: Refine implementation plan and document draft feature
- Updated IMPLEMENTATION-PLAN-STEP-LIBRARY-FRONTEND.md with design decisions:
  - Custom steps persistence: separate `custom_steps` field in sessions
  - Custom step navigation: full step type support (decision/action/solution)
  - Validation warnings: inline dismissible, no confirmation modal
- Added backend migration task (B.10) for custom_steps field
- Updated file count: 10 new, 8 modified, 1 migration
- Clarified acceptance criteria for validation behavior

- Created docs/plans/2026-02-03-draft-trees-feature.md:
  - Comprehensive design for draft trees and custom steps
  - Database schema, API changes, frontend UX patterns
  - Implementation phases and success metrics
  - Related to Issue #25 (planned for Phase 3)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 18:51:27 -05:00

24 KiB

Implementation Plan: Step Library Frontend + Tree Editor Validation

Date: February 3, 2026 (Updated after refinement session) Scope: Issues #1, #8, #9, #10 Estimated Components: 10 new files, 8 modified files, 1 migration Parallel Workstreams: 2 Related Planning: Issue #25 (Draft Trees - Phase 3)


Plan Refinements (Feb 3, 2026)

This plan was refined through collaborative design discussion. Key changes from initial draft:

  1. Custom Steps Persistence - Changed from storing in decisions array to separate custom_steps JSONB field

    • Added: Backend migration task (B.10)
    • Added: Session schema updates
    • Enables: Session resuming with custom steps
  2. Custom Step Navigation - Expanded to support all step types (decision/action/solution)

    • Added: Detailed rendering logic for each type in B.11
    • Enables: Custom branching decisions during troubleshooting
  3. Validation Warnings - Clarified as inline/dismissible (no confirmation modal)

    • Updated: Acceptance criteria to specify warning behavior
    • Deferred: "Save as Draft" feature to Issue #25 (Phase 3)

Overview

This plan covers two parallel workstreams:

Workstream Issues Description
A #1 Tree Editor Validation UI
B #10 → #9 → #8 Step Library Frontend (sequential)

Both workstreams are independent and can be developed simultaneously.


Workstream A: Tree Editor Validation (Issue #1)

Current State

The validation logic already exists in treeEditorStore.ts:519-658. It checks:

  • Tree name required
  • Decision nodes need question + options
  • Option labels required
  • Action/Solution nodes need titles
  • At least one solution node
  • Orphan node detection
  • Invalid node reference detection

What's Missing

  1. UI to display validation errors - No visual feedback shown to user
  2. Prevent save on errors - Save button should be disabled or show confirmation
  3. Circular reference detection - Not implemented in current validation
  4. Real-time validation - Currently only validates on explicit call

Tasks

A.1: Add Circular Reference Detection

File: frontend/src/store/treeEditorStore.ts

Add to the validate() function after line 624:

// Check for circular references in next_node_id chains
const detectCircularRefs = (startId: string, visited: Set<string> = new Set()): boolean => {
  if (visited.has(startId)) return true
  visited.add(startId)

  const node = findNodeInTree(startId, state.treeStructure)
  if (!node) return false

  // Check options
  if (node.options) {
    for (const opt of node.options) {
      if (opt.next_node_id && detectCircularRefs(opt.next_node_id, new Set(visited))) {
        errors.push({
          nodeId: node.id,
          message: `Circular reference detected: "${opt.label}" creates a loop`,
          severity: 'error'
        })
        return true
      }
    }
  }

  // Check next_node_id
  if (node.next_node_id && detectCircularRefs(node.next_node_id, new Set(visited))) {
    errors.push({
      nodeId: node.id,
      message: `Circular reference detected in node "${node.title || node.id}"`,
      severity: 'error'
    })
    return true
  }

  return false
}

// Run from root
detectCircularRefs('root')

A.2: Create Validation Summary Component

File: frontend/src/components/tree-editor/ValidationSummary.tsx (NEW)

interface ValidationSummaryProps {
  errors: ValidationError[]
  onSelectNode: (nodeId: string) => void
}

Features:

  • Collapsible panel showing error/warning count
  • Click error to select the problematic node
  • Color-coded: red for errors, yellow for warnings
  • Icon indicators (AlertCircle, AlertTriangle from lucide-react)

A.3: Integrate Validation UI into TreeEditorPage

File: frontend/src/pages/TreeEditorPage.tsx

Modifications:

  1. Call validate() before save attempt
  2. Show ValidationSummary when errors or warnings exist
  3. Block save if any severity='error' exists (disable save button)
  4. Warnings are informational only - do not block save, no confirmation modal
  5. Add "Validate" button in toolbar for manual check
  6. Auto-validate on blur from form fields (debounced)

A.4: Visual Node Error Indicators

File: frontend/src/components/tree-editor/NodeList.tsx

Modifications:

  • Add red border/highlight to nodes with validation errors
  • Show error icon badge on problem nodes
  • Tooltip on hover showing the specific error

Acceptance Criteria - Workstream A

  • Cannot save tree without a name (error blocks save)
  • Cannot save tree without at least one solution node (error blocks save)
  • Validation errors display in a clear, clickable list
  • Clicking an error selects the problematic node
  • Circular references are detected and blocked (error)
  • Orphan nodes show as warnings (informational, do not block save)
  • Save button disabled when errors exist
  • Warnings are dismissible/ignorable - user can save with warnings present

Workstream B: Step Library Frontend (Issues #10, #9, #8)

Dependencies

  • Backend API complete (/api/v1/steps/*, /api/v1/step-categories/*)
  • No frontend API client exists yet

Execution Order

#10 Step Library Browser Component
         ↓
#9 Custom Step Creation Modal (embeds browser)
         ↓
#8 Add Custom Step Button (triggers modal)

Issue #10: Step Library Browser Component

B.1: Create Steps API Client

File: frontend/src/api/steps.ts (NEW)

// API client for step library endpoints
export const stepsApi = {
  list: (params?: StepListParams) => Promise<StepListItem[]>
  get: (id: string) => Promise<Step>
  create: (data: StepCreate) => Promise<Step>
  update: (id: string, data: StepUpdate) => Promise<Step>
  delete: (id: string) => Promise<void>
  search: (query: string) => Promise<StepListItem[]>
  getPopularTags: () => Promise<PopularTag[]>
  rate: (id: string, data: RatingCreate) => Promise<Rating>
  updateRating: (id: string, data: RatingUpdate) => Promise<Rating>
  deleteRating: (id: string) => Promise<void>
  getReviews: (id: string) => Promise<Review[]>
}

B.2: Create Step Categories API Client

File: frontend/src/api/stepCategories.ts (NEW)

export const stepCategoriesApi = {
  list: () => Promise<StepCategory[]>
  get: (id: string) => Promise<StepCategory>
}

B.3: Add TypeScript Types

File: frontend/src/types/step.ts (NEW)

export interface StepCommand {
  label: string
  command: string
  command_type?: string
}

export interface StepContent {
  instructions: string
  help_text?: string
  commands?: StepCommand[]
}

export interface Step {
  id: string
  title: string
  step_type: 'decision' | 'action' | 'solution'
  content: StepContent
  visibility: 'private' | 'team' | 'public'
  category_id?: string
  category_name?: string
  tags: string[]
  usage_count: number
  rating_average: number
  rating_count: number
  helpful_yes: number
  helpful_no: number
  is_featured: boolean
  is_verified: boolean
  created_by: string
  author_name?: string
  created_at: string
  updated_at: string
}

export interface StepListItem {
  id: string
  title: string
  step_type: string
  visibility: string
  category_id?: string
  category_name?: string
  tags: string[]
  usage_count: number
  rating_average: number
  rating_count: number
  is_featured: boolean
  created_by: string
  author_name?: string
  created_at: string
}

export interface StepCategory {
  id: string
  name: string
  description?: string
  display_order: number
  team_id?: string
  is_active: boolean
}

export interface StepListParams {
  visibility?: 'private' | 'team' | 'public'
  category_id?: string
  tags?: string[]
  min_rating?: number
  step_type?: 'decision' | 'action' | 'solution'
  sort_by?: 'recent' | 'popular' | 'highest_rated' | 'most_used'
  limit?: number
  offset?: number
}

export interface PopularTag {
  tag: string
  count: number
}

B.4: Create Step Card Component

File: frontend/src/components/step-library/StepCard.tsx (NEW)

A card displaying:

  • Step title and type badge (decision/action/solution)
  • Category name
  • Star rating (if rated) or "Not rated" placeholder
  • Tags as chips (max 3 visible, "+N more" overflow)
  • Author name and created date
  • Usage count
  • [Preview] and [Insert] buttons

B.5: Create Step Detail Modal

File: frontend/src/components/step-library/StepDetailModal.tsx (NEW)

Full step preview showing:

  • Title, type, category, tags
  • Full rating breakdown (star distribution chart)
  • Instructions (markdown rendered)
  • Commands (copyable code blocks)
  • Help text
  • Top reviews (2-3 shown, "See all" link)
  • [Cancel] and [Insert Into Session] buttons

B.6: Create Step Library Browser Component

File: frontend/src/components/step-library/StepLibraryBrowser.tsx (NEW)

Main browser component with:

Header:

  • Search input (full-text, debounced 300ms)
  • Filter dropdowns: Category, Type, Min Rating, Sort By
  • Popular tags as clickable chips

Body:

  • Grouped sections: "My Steps", "Team Steps", "Community"
  • Each section collapsible
  • Virtualized list for performance (if >50 items)
  • Loading skeletons while fetching
  • Empty state: "No steps found. Create your first step!"

Footer:

  • [+ Create New Step] button (optional, for standalone use)

Props:

interface StepLibraryBrowserProps {
  onInsert: (step: Step) => void
  onCreateNew?: () => void
  showCreateButton?: boolean
}

B.7: Update API Index

File: frontend/src/api/index.ts

Add exports for stepsApi and stepCategoriesApi.

Acceptance Criteria - Issue #10

  • Can search steps with full-text query
  • Can filter by category, type, minimum rating
  • Can sort by recent, popular, highest rated, most used
  • Steps grouped by visibility (My Steps, Team, Community)
  • Can click step to see full preview modal
  • Can copy commands from preview
  • Can click "Insert" to select a step
  • Popular tags shown and clickable as quick filters

Issue #9: Custom Step Creation Modal

B.8: Create Step Form Component

File: frontend/src/components/step-library/StepForm.tsx (NEW)

Form fields:

  • Step Type: Radio group (Decision / Action / Solution)
  • Title: Text input (required)
  • Instructions: Textarea with markdown support (required)
  • Help Text: Textarea (optional)
  • Commands: Dynamic array field (optional)
    • Each command: label + command + type dropdown
  • Category: Dropdown (optional)
  • Tags: Tag input with autocomplete (optional)
  • Visibility: Dropdown (Private / Team / Public)
  • Checkbox: "Save to My Step Library for reuse"

B.9: Create Custom Step Modal

File: frontend/src/components/step-library/CustomStepModal.tsx (NEW)

Tabbed modal with:

  • Tab 1: "Type My Own" - StepForm component
  • Tab 2: "Browse Library" - StepLibraryBrowser component

Props:

interface CustomStepModalProps {
  isOpen: boolean
  onClose: () => void
  onInsertStep: (step: Step | CustomStepDraft) => void
}

Behavior:

  • Tab 1: User fills form, clicks [Insert Step]
    • If "Save to library" checked, POST to /api/v1/steps first
    • Returns step data to parent
  • Tab 2: User browses, clicks Insert on a step
    • Returns selected step to parent

Acceptance Criteria - Issue #9

  • Modal has two tabs: "Type My Own" and "Browse Library"
  • Can create custom step with type, title, instructions
  • Can optionally add commands, help text, category, tags
  • Can optionally save step to personal library
  • Can switch to Browse tab and select existing step
  • Insert button returns step data to parent component

Issue #8: Add Custom Step Button in Tree Navigation

B.10: Add Custom Steps Backend Support

Files:

  • backend/alembic/versions/XXXX_add_custom_steps_to_sessions.py (NEW - migration)
  • backend/app/schemas/session.py (MODIFIED)
  • backend/app/api/endpoints/sessions.py (MODIFIED)

Migration:

def upgrade():
    # Add custom_steps JSONB column to sessions table
    op.add_column('sessions',
        sa.Column('custom_steps', JSONB, nullable=False, server_default='[]')
    )

def downgrade():
    op.drop_column('sessions', 'custom_steps')

Schema Updates:

# In DecisionRecord - no changes needed (still uses node_id)

# In SessionResponse - add custom_steps field
class SessionResponse(BaseModel):
    # ... existing fields
    custom_steps: list[dict[str, Any]] = Field(default_factory=list)  # NEW

# In SessionUpdate - add custom_steps field
class SessionUpdate(BaseModel):
    # ... existing fields
    custom_steps: Optional[list[dict[str, Any]]] = None  # NEW

Custom Step Structure in Database:

# Each item in custom_steps array:
{
    "id": "uuid-string",
    "inserted_after_node_id": "parent-node-id",
    "step_data": {
        "title": "Check Additional Logs",
        "step_type": "action",  # decision | action | solution
        "content": {
            "instructions": "Check /var/log/...",
            "help_text": "Optional help",
            "commands": [...]  # optional
        },
        "category_id": "optional-uuid",
        "tags": ["optional"]
    },
    "timestamp": "2026-02-03T10:30:00Z"
}

API Logic:

  • PUT /api/v1/sessions/{id} - Accept custom_steps in request body
  • Validation: Ensure step_data.step_type is valid enum value
  • No cascade delete needed - custom steps are session-scoped

B.11: Modify TreeNavigationPage

File: frontend/src/pages/TreeNavigationPage.tsx

Add state:

const [showCustomStepModal, setShowCustomStepModal] = useState(false)
const [customSteps, setCustomSteps] = useState<CustomStep[]>([])

UI Changes:

  1. After each decision node's options, add:
<button
  onClick={() => setShowCustomStepModal(true)}
  className="mt-2 text-sm text-primary hover:underline"
>
  + Add Custom Step
</button>
  1. Add CustomStepModal at bottom of component

  2. Handle insert:

const handleInsertCustomStep = (step: Step | CustomStepDraft) => {
  // Create custom step object
  const customStep: CustomStep = {
    id: crypto.randomUUID(),
    inserted_after_node_id: currentNodeId,
    step_data: step,
    timestamp: new Date().toISOString()
  }

  // Add to local state
  const newCustomSteps = [...customSteps, customStep]
  setCustomSteps(newCustomSteps)

  // Navigate to custom step (becomes current)
  setCurrentNodeId(customStep.id)
  setShowCustomStepModal(false)

  // Save to backend
  if (session) {
    sessionsApi.update(session.id, {
      custom_steps: newCustomSteps
    }).catch(err => console.error('Failed to save custom step:', err))
  }
}
  1. Render custom steps based on type:
{/* Render custom step in navigation flow */}
{currentNode?.id.startsWith('custom-') && (
  <div className="rounded-lg border border-purple-200 bg-purple-50 p-4 dark:border-purple-800 dark:bg-purple-900/20">
    {/* Custom Step Badge */}
    <span className="mb-2 inline-block rounded-full bg-purple-100 px-2 py-1 text-xs font-medium text-purple-800 dark:bg-purple-900 dark:text-purple-100">
      Custom Step
    </span>

    {/* Render based on step type */}
    {customStepNode.step_data.step_type === 'decision' && (
      <>
        <h2>{customStepNode.step_data.title}</h2>
        <MarkdownContent content={customStepNode.step_data.content.instructions} />
        {/* Render decision options - user creates them inline or uses predefined */}
        <div className="mt-4 space-y-2">
          <button onClick={() => handleCustomDecisionOption('yes')}>Yes</button>
          <button onClick={() => handleCustomDecisionOption('no')}>No</button>
        </div>
      </>
    )}

    {customStepNode.step_data.step_type === 'action' && (
      <>
        <h2>{customStepNode.step_data.title}</h2>
        <MarkdownContent content={customStepNode.step_data.content.instructions} />
        {customStepNode.step_data.content.commands?.map(cmd => (
          <pre key={cmd.label}>{cmd.command}</pre>
        ))}
        <button onClick={handleContinueFromCustomAction}>Continue</button>
      </>
    )}

    {customStepNode.step_data.step_type === 'solution' && (
      <>
        <h2>{customStepNode.step_data.title}</h2>
        <MarkdownContent content={customStepNode.step_data.content.instructions} />
        <button onClick={handleComplete}>Complete Session</button>
      </>
    )}
  </div>
)}

Navigation Logic:

  • Custom decision: User selects option → continues to next tree node or another custom step
  • Custom action: User clicks Continue → returns to tree flow (next_node_id of original tree node)
  • Custom solution: User clicks Complete → ends session

B.12: Update Session Export

File: frontend/src/components/session/ExportPreviewModal.tsx or backend/app/api/endpoints/sessions.py

Ensure custom steps are included in export with clear marking:

## Step 5: [CUSTOM] Check Additional Logs
*Custom step added by user*

Instructions: ...

B.13: Update Session Types

File: frontend/src/types/index.ts

Add:

export interface CustomStep {
  id: string
  inserted_after_node_id: string
  step_data: Step | CustomStepDraft
  timestamp: string
}

export interface CustomStepDraft {
  title: string
  step_type: 'decision' | 'action' | 'solution'
  content: StepContent
  category_id?: string
  tags?: string[]
}

Acceptance Criteria - Issue #8

  • "+ Add Custom Step" button visible at each decision point
  • Button opens CustomStepModal
  • Can insert step from "Type My Own" tab
  • Can insert step from "Browse Library" tab
  • Custom steps appear in session flow with visual indicator
  • Custom steps included in session export
  • Session continues after custom step

File Summary

New Files (10)

File Issue Description
frontend/components/tree-editor/ValidationSummary.tsx #1 Error/warning display
frontend/api/steps.ts #10 Steps API client
frontend/api/stepCategories.ts #10 Categories API client
frontend/types/step.ts #10 Step TypeScript types
frontend/components/step-library/StepCard.tsx #10 Step list item card
frontend/components/step-library/StepDetailModal.tsx #10 Step preview modal
frontend/components/step-library/StepLibraryBrowser.tsx #10 Main browser component
frontend/components/step-library/StepForm.tsx #9 Step creation form
frontend/components/step-library/CustomStepModal.tsx #9 Tabbed modal wrapper
backend/alembic/versions/XXXX_add_custom_steps.py #8 Add custom_steps to sessions

Modified Files (6)

File Issue Changes
frontend/store/treeEditorStore.ts #1 Add circular reference detection
frontend/pages/TreeEditorPage.tsx #1 Integrate validation UI
frontend/components/tree-editor/NodeList.tsx #1 Node error indicators
frontend/pages/TreeNavigationPage.tsx #8 Add custom step button + rendering
frontend/api/index.ts #10 Export new API clients
frontend/types/index.ts #8 Add CustomStep types
backend/app/schemas/session.py #8 Add custom_steps field
backend/app/api/endpoints/sessions.py #8 Handle custom_steps in update

Execution Plan

Phase 1: Foundation (Do First)

Workstream A                    Workstream B
─────────────                   ─────────────
A.1 Circular ref detection      B.1 Steps API client
                                B.2 Categories API client
                                B.3 TypeScript types

Phase 2: Core Components

Workstream A                    Workstream B
─────────────                   ─────────────
A.2 ValidationSummary           B.4 StepCard
A.3 TreeEditorPage integration  B.5 StepDetailModal
A.4 NodeList indicators         B.6 StepLibraryBrowser

Phase 3: Integration

Workstream A                    Workstream B
─────────────                   ─────────────
Testing & polish                B.8 StepForm
                                B.9 CustomStepModal
                                B.10 Backend custom steps support (migration + schemas)
                                B.11 TreeNavigationPage integration
                                B.12-13 Export & types updates

Testing Checklist

Workstream A - Validation

  • Create tree without name → Error shown, save blocked
  • Create tree without solution → Error shown, save blocked
  • Create decision without options → Error shown
  • Create circular reference (A → B → A) → Error detected
  • Create orphan node → Warning shown (can still save)
  • Click error → Node selected
  • Fix all errors → Save enabled

Workstream B - Step Library

  • Open browser → Steps load, grouped by visibility
  • Search "citrix" → Filtered results appear
  • Filter by category → Results filtered
  • Click step → Preview modal opens
  • Click copy command → Copied to clipboard
  • Click Insert → Modal closes, step returned
  • Create custom step → Form validates, step inserted
  • Save to library checked → Step saved to API
  • Add custom step in navigation → Step appears with badge
  • Complete session → Export includes custom steps

Notes for Implementation

  1. Use existing patterns: Follow Modal.tsx, TagInput.tsx, and existing page structures
  2. Dark mode: All components must support light/dark themes via Tailwind classes
  3. Keyboard navigation: Support Escape to close modals, Enter to submit
  4. Loading states: Show skeletons or spinners during API calls
  5. Error handling: Display user-friendly error messages, log details to console
  6. Accessibility: Use proper ARIA labels, maintain focus management in modals

Design Decisions (Resolved)

1. Custom Steps Persistence

Decision: Separate custom_steps JSONB field in sessions table

Rationale:

  • Clean separation of concerns (decisions track actions taken, custom_steps store step content)
  • Enables session resuming with custom steps intact
  • Easier to query and export custom steps separately
  • Follows normalized data structure patterns

Implementation: Task B.10 adds migration + schema updates

2. Custom Step Navigation Flow

Decision: Full step type support (decision/action/solution)

Rationale:

  • Real-world troubleshooting requires branching after custom actions
  • Example: "Try clearing cache" (action) → "Did it work?" (decision)
  • Consistent with tree structure mental model
  • Maximum flexibility for engineers

Implementation: Task B.11 renders custom steps based on step_type

3. Validation Warning UX

Decision: Inline dismissible warnings, no confirmation modal

Rationale:

  • Warnings are informational, not critical
  • Orphan nodes might be intentional (work in progress)
  • Trust users to manage their own tree quality
  • Simpler UX, fewer clicks
  • Draft feature (Issue #25) handles "save incomplete work" use case

Implementation: Task A.2-A.3 show warnings in ValidationSummary, don't block save


Future Enhancements (Out of Scope)

  1. Draft Trees Feature - Documented in Issue #25

    • Save incomplete trees without validation errors blocking
    • Planned for Phase 3 after Step Library is complete
  2. Standalone Step Library Page - /steps route

    • Browse/manage personal step library outside of active sessions
    • Add after Issue #10 is complete and usage patterns are understood
  3. Rate Limiting - Backend concern

    • Prevent step library spam
    • Add if abuse is observed in production