Skip to content

feat(campaigns): Redesign Campaign Step Editor UI #151

Merged
anujeet98 merged 5 commits into
mainfrom
feat/campaign-step-editor-ui
Jun 5, 2026
Merged

feat(campaigns): Redesign Campaign Step Editor UI #151
anujeet98 merged 5 commits into
mainfrom
feat/campaign-step-editor-ui

Conversation

@Rishavraaj

@Rishavraaj Rishavraaj commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Overview

Redesigns the campaign step editor (StepCard) to match Apollo's native email step UI — a split-panel layout with a rich text editor on the left
and a live email preview on the right.


Changes

Layout & Structure

  • Split-panel layout: editor occupies the left (~60%) and a "Generate preview for contact" panel occupies the right (~40%)
  • Collapsible step card: header now includes a toggle switch, breadcrumb (Step N: Automatic email / Test A), timing label (Send email in immediately), and a collapse/expand button
  • Variant tabs: "Test A" tab with an Active badge and an "+ Add test" placeholder tab
  • Subject + Type row: subject input and a "New thread / Reply to thread" type dropdown rendered side-by-side in the same row

Rich Text Editor

  • Replaced the plain <textarea> body with the existing EditorJS component (@editorjs/editorjs)
  • Supports all configured EditorJS tools: paragraphs, headers, lists, code blocks, quotes, delimiters, etc.
  • Used a callback ref pattern (onEditorChangeRef) to fix EditorJS's stale closure issue — the captured onChange/onReady callbacks always
    call through to the latest handler, ensuring previewData state is always up to date
  • onReady seeds the preview immediately on load (EditorJS doesn't fire onChange on init)

Live Preview Panel

  • Renders EditorJS OutputData blocks directly with a PreviewBody component that handles paragraphs, headers, ordered/unordered lists,
    checklists, quotes, code, and delimiters
  • Substitutes {{variables}} with example values (e.g. {{first_name}}Example)
  • Desktop / mobile width toggle
  • "Choose a contact" dropdown and Refresh button (UI placeholder for future contact-based preview)

Data Model

  • Added touchType: 'new_thread' | 'reply_to_thread' to StepFormState (was previously hardcoded in stepsToPayload)
  • outputDataToText serialiser handles both EditorJS list v1 (plain strings) and v2 ({ content: string } objects) item formats

Removed

  • AI-related UI: "Assisted" / "Prompt" content mode tabs, "Write with AI" button, "AI personalised opener" toggle
  • Custom static toolbar (T, link, image, attachment, code, document, emoji, {}, *, calendar icons)
  • Footer stats labels row (- Scheduled - Delivered - Bounce …) that was overflowing

Files Changed

File Change
apps/web/src/components/Campaigns/campaign-shared.tsx Full redesign of StepCard, new PreviewBody component, updated StepFormState,
outputDataToText serialiser

Testing

  • Create a new campaign — step editor renders correctly with subject, type, body, and preview
  • Edit an existing campaign — existing body_html loads into EditorJS; preview seeds on load
  • Type in the body — preview updates in real time with variable substitution
  • Collapse/expand a step card
  • Toggle "Include signature"
  • Change "Type" dropdown (New thread / Reply to thread) — saved correctly on update
  • Verify stepsToPayload produces correct API payload with touchType

Screenshot

image

…S body

- Redesign StepCard to match Apollo-style layout: header with toggle/breadcrumb/collapse, variant tabs (Test A), and a split panel (editor left, preview right)
- Replace plain textarea with EditorJS rich text editor
- Add live preview panel with contact selector, desktop/mobile toggle, and block-aware PreviewBody renderer
- Add touchType field (new_thread / reply_to_thread) to StepFormState
- Use callback ref pattern to fix EditorJS stale closure issue with onChange/onReady
- Remove AI-related UI (Assisted/Prompt tabs, Write with AI button, AI opener toggle)
- Remove custom toolbar; EditorJS native toolbar handles formatting
- Fix footer overflow by removing stats labels row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Rishavraaj Rishavraaj force-pushed the feat/campaign-step-editor-ui branch from 4d160c4 to 0c86b5f Compare June 5, 2026 06:42
@Rishavraaj Rishavraaj changed the title eat(campaigns): Redesign Campaign Step Editor UI feat(campaigns): Redesign Campaign Step Editor UI Jun 5, 2026
Rishavraaj and others added 3 commits June 5, 2026 12:19
- Remove stale DOMParser declaration left from merge conflict
- Fix no-array-index-key: use block.id and item text as stable keys
- Fix label-has-associated-control: add htmlFor/id pairs for Subject and Type fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace static wait label with editable number input + unit dropdown
  (minutes / hours / days) so each step's delay can be configured
- Hide the Subject field when touchType is reply_to_thread, matching
  Apollo's behaviour where replies inherit the thread subject

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
waitMode: step.wait_mode as 'day' | 'hour' | 'minute',
apolloTouchId: touch?.id,
apolloTemplateId: touch?.emailer_template_id,
touchType: 'new_thread',

@anujeet98 anujeet98 Jun 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TouchType should load the value from api response.
touchType: (touch?.type as 'new_thread' | 'reply_to_thread') ?? 'new_thread',


// EditorJS list v2 stores items as { content: string; items: [] },
// while v1 stored plain strings. Handle both.
function extractListItemText(

@anujeet98 anujeet98 Jun 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is doing same as listItemText (duplicate)
can remove this method

return html.replace(/<[^>]*>/g, '');
}

function outputDataToText(data: OutputData): string {

@anujeet98 anujeet98 Jun 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outputDataToText strips all HTML tags and joins blocks with \n, storing plain text in step.bodyHtml. But the backend passes bodyHtml directly to Apollo as body_html, which expects HTML. This means formatting is lost and the email doesn't not render correctly.

- Load touchType from API response instead of hardcoding 'new_thread'
- Remove extractListItemText (duplicate of listItemText)
- Replace outputDataToText with outputDataToHtml so bodyHtml stores
  proper HTML for Apollo's body_html field, preserving formatting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@anujeet98 anujeet98 merged commit 83bb38a into main Jun 5, 2026
2 checks passed
@anujeet98 anujeet98 deleted the feat/campaign-step-editor-ui branch June 5, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants