Files
resolutionflow/docs/plans/2026-02-13-export-phase-a-frontend.md
2026-02-15 00:43:41 -05:00

11 KiB

Export Improvements Phase A — Frontend Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Add next_steps to types/UI, add "Copy for Ticket" to TreeNavigationPage, add step cutoff control to SessionDetailPage, display next_steps in session detail + completion modal.

Architecture: Update TypeScript types to match backend schema changes. Add next_steps textarea to SessionOutcomeModal. Display next_steps on SessionDetailPage. Add mid-session "Copy for Ticket" button to TreeNavigationPage. Add step cutoff dropdown to SessionDetailPage export controls.

Tech Stack: React 19, TypeScript, Tailwind CSS, Zustand, Vite

Backend dependency: Phase A backend is complete on feat/export-phase-a branch.


Task 1: Update TypeScript Types

Files:

  • Modify: frontend/src/types/session.ts

Step 1: Add next_steps to Session interface (line 59, after scratchpad)

  next_steps: string

Step 2: Add next_steps to SessionUpdate (line 68, after scratchpad)

  next_steps?: string

Step 3: Add next_steps to SessionComplete (line 83)

export interface SessionComplete {
  outcome: SessionOutcome
  outcome_notes?: string
  next_steps?: string
}

Step 4: Update SessionExport (line 77)

export interface SessionExport {
  format: 'text' | 'markdown' | 'html' | 'psa'
  include_timestamps?: boolean
  include_tree_info?: boolean
  include_outcome_notes?: boolean
  include_next_steps?: boolean
  max_step_index?: number
}

Step 5: Build to verify no type errors

Run: cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit

Step 6: Commit

git add frontend/src/types/session.ts
git commit -m "feat(frontend): add next_steps and export options to session types"

Task 2: Add Next Steps to Session Completion Modal

Files:

  • Modify: frontend/src/components/session/SessionOutcomeModal.tsx
  • Modify: frontend/src/pages/TreeNavigationPage.tsx (consumer callback)

Step 1: Update the onSubmit prop type (line 9)

Change the data shape to include next_steps:

  onSubmit: (data: { outcome: SessionOutcome; outcome_notes?: string; next_steps?: string }) => Promise<void>

Step 2: Update handleSubmit (line 28)

Add next_steps extraction from formData:

  const handleSubmit = async () => {
    if (!formRef.current) return
    const formData = new FormData(formRef.current)
    const outcome = (formData.get('session-outcome') as SessionOutcome | null) ?? 'resolved'
    const outcomeNotes = ((formData.get('outcome-notes') as string | null) ?? '').trim()
    const nextSteps = ((formData.get('next-steps') as string | null) ?? '').trim()

    await onSubmit({
      outcome,
      outcome_notes: outcomeNotes || undefined,
      next_steps: nextSteps || undefined,
    })
  }

Step 3: Add Next Steps textarea after the outcome notes textarea (after line 115)

Add a second textarea block right before the closing </form>:

        <div>
          <label className="block text-sm font-medium text-white">Next Steps / Follow-Up (optional)</label>
          <textarea
            name="next-steps"
            defaultValue=""
            rows={3}
            placeholder="Actions to take after this session..."
            className={cn(
              'mt-1 block w-full rounded-md border border-white/10 bg-black/50 px-3 py-2',
              'text-sm text-white placeholder:text-white/40',
              'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
            )}
          />
        </div>

Step 4: Update the consumer callback in TreeNavigationPage.tsx (line 341)

Change handleSubmitOutcome signature to match:

const handleSubmitOutcome = async (data: { outcome: SessionOutcome; outcome_notes?: string; next_steps?: string }) => {

The body already passes data directly to sessionsApi.complete(session.id, data) so the next_steps field will propagate automatically.

Step 5: Build to verify

Run: cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit

Step 6: Commit

git add frontend/src/components/session/SessionOutcomeModal.tsx frontend/src/pages/TreeNavigationPage.tsx
git commit -m "feat(frontend): add next_steps field to session completion modal"

Task 3: Display Next Steps on SessionDetailPage

Files:

  • Modify: frontend/src/pages/SessionDetailPage.tsx

Step 1: Display next_steps after outcome_notes (after line 334)

Find the existing outcome_notes display:

{session.outcome_notes && (
  <p className="mt-2 text-sm text-white/60">Outcome Notes: {session.outcome_notes}</p>
)}

Add next_steps display right after it. Use whitespace-pre-wrap to preserve line breaks (engineers often enter bullet lists):

{session.next_steps && (
  <div className="mt-2">
    <span className="text-sm text-white/40">Next Steps:</span>
    <p className="mt-0.5 text-sm text-white/60 whitespace-pre-wrap">{session.next_steps}</p>
  </div>
)}

Step 2: Build to verify

Run: cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit

Step 3: Commit

git add frontend/src/pages/SessionDetailPage.tsx
git commit -m "feat(frontend): display next_steps on session detail page"

Task 4: Add "Copy for Ticket" Button to TreeNavigationPage

Files:

  • Modify: frontend/src/pages/TreeNavigationPage.tsx

This adds a mid-session export button. It should be secondary/outline style — accessible but not prominent (engineers shouldn't think it ends their session).

Step 1: Import necessary icons and API

Check what's already imported. You'll need Copy and Check from lucide-react, and sessionsApi from @/api. Also need toast if not already imported.

Step 2: Add state for copy feedback and loading (near other state declarations)

const [copiedForTicket, setCopiedForTicket] = useState(false)
const [isCopyingForTicket, setIsCopyingForTicket] = useState(false)

Step 3: Add the copy handler (with loading guard to prevent double-clicks)

const handleCopyForTicket = async () => {
  if (!session || isCopyingForTicket) return
  setIsCopyingForTicket(true)
  try {
    const content = await sessionsApi.export(session.id, {
      format: 'psa',
      include_timestamps: true,
      include_tree_info: true,
    })
    if (content) {
      await navigator.clipboard.writeText(content)
      setCopiedForTicket(true)
      setTimeout(() => setCopiedForTicket(false), 2000)
      toast.success('Copied progress notes to clipboard')
    }
  } catch (err) {
    console.error('Copy for ticket failed:', err)
    toast.error('Failed to copy notes')
  } finally {
    setIsCopyingForTicket(false)
  }
}

Step 4: Add the button to the page toolbar

Find the toolbar/header area of TreeNavigationPage. Look for where other action buttons are (like scratchpad toggle, session timer, etc.). Add a secondary-style button with disabled state:

<button
  onClick={handleCopyForTicket}
  disabled={isCopyingForTicket}
  title="Copy progress notes for ticket"
  className={cn(
    'flex items-center gap-1.5 rounded-md border border-white/10 px-3 py-1.5 text-xs font-medium text-white/60',
    'hover:bg-white/10 hover:text-white transition-colors disabled:opacity-50'
  )}
>
  {copiedForTicket ? <Check className="h-3.5 w-3.5 text-emerald-400" /> : <Copy className="h-3.5 w-3.5" />}
  {copiedForTicket ? 'Copied!' : 'Copy for Ticket'}
</button>

Place it near other toolbar actions but not in a position where it could be confused with session completion buttons.

Step 5: Build to verify

Run: cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit

Step 6: Commit

git add frontend/src/pages/TreeNavigationPage.tsx
git commit -m "feat(frontend): add mid-session Copy for Ticket button to navigation page"

Task 5: Add Step Cutoff Control to SessionDetailPage Export

Files:

  • Modify: frontend/src/pages/SessionDetailPage.tsx

Step 1: Add state for step cutoff (near other export state)

const [maxStepIndex, setMaxStepIndex] = useState<number | null>(null)

Step 2: Update fetchExportContent to include new options (line 91)

const fetchExportContent = async () => {
  if (!session) return null
  const options: SessionExport = {
    format: exportFormat,
    include_timestamps: true,
    include_tree_info: true,
    ...(maxStepIndex !== null && { max_step_index: maxStepIndex }),
  }
  return await sessionsApi.export(session.id, options)
}

Step 3: Update handleCopyForTicket similarly (line 135)

Add max_step_index to PSA export options too:

const options: SessionExport = {
  format: 'psa',
  include_timestamps: true,
  include_tree_info: true,
  ...(maxStepIndex !== null && { max_step_index: maxStepIndex }),
}

Step 4: Add step cutoff dropdown in the export controls area

Find the export format <select> element (line 368). Add a step cutoff dropdown right after it, but only show it when the session has decisions:

{session.decisions.length > 1 && (
  <select
    value={maxStepIndex ?? ''}
    onChange={(e) => setMaxStepIndex(e.target.value ? Number(e.target.value) : null)}
    aria-label="Export through step"
    className={cn(
      'rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white',
      'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
    )}
  >
    <option value="">All steps</option>
    {session.decisions.map((_, idx) => (
      <option key={idx + 1} value={idx + 1}>
        Through step {idx + 1}
      </option>
    ))}
  </select>
)}

Step 5: Build to verify

Run: cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit

Step 6: Full build

Run: cd /c/Dev/Projects/patherly/frontend && npm run build

Step 7: Commit

git add frontend/src/pages/SessionDetailPage.tsx
git commit -m "feat(frontend): add step cutoff control to export options"

Task 6: Final Verification

Step 1: Full build

Run: cd /c/Dev/Projects/patherly/frontend && npm run build

Expected: Build succeeds with no errors.

Step 2: Verify git status is clean

git status
git log --oneline feat/export-phase-a --not main

Frontend Acceptance Checklist (Manual QA)

After all tasks are complete, verify these scenarios with the running app:

  1. Completion with next_steps: Complete a session with both outcome_notes and next_steps filled in. Verify both appear on SessionDetailPage with line breaks preserved.
  2. Completion without next_steps: Complete a session with only outcome. Verify no empty "Next Steps" section appears on detail page or in exports.
  3. Mid-session copy: While navigating a tree, click "Copy for Ticket". Paste the result — it should show "In progress" duration and steps completed so far. Verify the session is NOT marked as exported.
  4. Step cutoff: On a completed session with 5+ steps, use the "Through step 3" dropdown, then Preview. Verify only steps 1-3 appear. Verify "Copy for Ticket" also respects the cutoff.
  5. Double-click protection: Click "Copy for Ticket" rapidly on TreeNavigationPage. Verify only one API call fires (button should disable during loading).