Skip to content

One-Click 'Create Draft from This Idea' Buttons #5

@jjroelofs

Description

@jjroelofs

Problem Statement

Currently, users receive AI-generated content recommendations but cannot act on them directly. The interface is a dead-end: users must manually copy ideas, navigate to content creation forms, and recreate context. This creates friction between strategy and execution.

Proposed Solution

Add a "📝 Create Draft" button next to each content idea in all recommendation cards (Content Gaps, Authority Topics, Expertise Demonstrations, Trust Signals) that:

  1. Opens Drupal's node creation form with pre-filled content
  2. Auto-selects the appropriate content type
  3. Pre-populates title and body fields with AI recommendation context
  4. Reduces time-to-draft from ~5 minutes to ~10 seconds

Content Type Selection Logic

Option 1: Configuration-Based Mapping (Recommended for MVP)

Create admin configuration form at /admin/config/ai/content-strategy/settings:

content_type_mapping:
  content_gaps:
    default: 'article'
    keywords:
      'guide': 'page'
      'tutorial': 'tutorial'
      'faq': 'faq'
  authority_topics:
    default: 'article'
  expertise_demonstrations:
    default: 'article'
    keywords:
      'webinar': 'event'
      'case study': 'case_study'
      'video': 'video'
  trust_signals:
    default: 'page'
    keywords:
      'testimonial': 'testimonial'
      'certification': 'certification'

Logic:

  1. Check recommendation section (content_gaps, authority_topics, etc.)
  2. Scan content idea text for keyword matches
  3. Use mapped content type if keyword found
  4. Fall back to section default
  5. Fall back to site default content type (from config)

Benefits:

  • Simple to implement
  • No AI calls needed
  • Users can customize mappings
  • Predictable behavior

Option 2: AI-Powered Content Type Detection (Future Enhancement)

Send lightweight prompt to AI:

Given this content idea: "{idea_text}"
And these available content types: {json_list_of_content_types}
Return only the machine name of the most appropriate content type.

Benefits:

  • More intelligent matching
  • Adapts to any content type

Drawbacks:

  • Adds latency (200-500ms)
  • Costs per API call
  • Less predictable

Recommendation: Start with Option 1, add Option 2 as checkbox "Use AI to suggest content type"


Option 3: User Choice Dropdown (Hybrid Approach)

Show mini-form before creating draft:

┌─────────────────────────────────────┐
│ Create draft from this idea?        │
├─────────────────────────────────────┤
│ Content Type: [Article ▼]           │
│   - Article (suggested)              │
│   - Page                             │
│   - Tutorial                         │
│   - Case Study                       │
│                                      │
│ [Cancel]  [Create Draft →]          │
└─────────────────────────────────────┘

Benefits:

  • User maintains control
  • Can override AI suggestion
  • Clear and transparent

Recommendation: Combine with Option 1 - show suggested type as default, allow override

Technical Implementation

1. Add Button to Template

File: templates/ai-content-strategy-recommendations-items.html.twig

{% for idea in item.content_ideas %}
  <tr>
    <td>{{ idea }}</td>
    <td class="actions">
      <button class="create-draft-button button button--small" 
              data-section="{{ section }}" 
              data-recommendation="{{ item[section_config.item_key] }}"
              data-idea="{{ idea }}">
        📝 Create Draft
      </button>
    </td>
  </tr>
{% endfor %}

2. JavaScript Handler

File: js/content-ideas.js (or new js/create-draft.js)

Drupal.behaviors.aiContentStrategyCreateDraft = {
  attach: function (context, settings) {
    $('.create-draft-button', context).once('create-draft').on('click', function(e) {
      e.preventDefault();
      
      const $button = $(this);
      const section = $button.data('section');
      const recommendation = $button.data('recommendation');
      const idea = $button.data('idea');
      
      // Get content type via AJAX
      $.ajax({
        url: '/admin/reports/ai/content-strategy/suggest-content-type',
        method: 'POST',
        data: {
          section: section,
          idea: idea
        },
        success: function(response) {
          // Build node/add URL with query parameters
          const params = new URLSearchParams({
            ai_title: idea,
            ai_context: recommendation,
            ai_section: section
          });
          
          window.location.href = `/node/add/${response.content_type}?${params.toString()}`;
        }
      });
    });
  }
};

3. New Route for Content Type Suggestion

File: ai_content_strategy.routing.yml

ai_content_strategy.suggest_content_type:
  path: '/admin/reports/ai/content-strategy/suggest-content-type'
  defaults:
    _controller: '\Drupal\ai_content_strategy\Controller\ContentStrategyController::suggestContentType'
    _title: 'Suggest Content Type'
  requirements:
    _permission: 'access ai content strategy'
  methods: [POST]

4. Controller Method

File: src/Controller/ContentStrategyController.php

/**
 * Suggests a content type based on recommendation section and idea text.
 *
 * @param \Symfony\Component\HttpFoundation\Request $request
 *   The request object.
 *
 * @return \Symfony\Component\HttpFoundation\JsonResponse
 *   JSON response with suggested content_type.
 */
public function suggestContentType(Request $request) {
  $section = $request->request->get('section');
  $idea = $request->request->get('idea');
  
  // Get configuration
  $config = $this->config('ai_content_strategy.content_types');
  $mapping = $config->get('mapping') ?? [];
  
  // Default content type
  $content_type = 'article';
  
  // Check section-specific mapping
  if (isset($mapping[$section])) {
    $section_config = $mapping[$section];
    
    // Check for keyword matches
    if (isset($section_config['keywords'])) {
      foreach ($section_config['keywords'] as $keyword => $type) {
        if (stripos($idea, $keyword) !== FALSE) {
          $content_type = $type;
          break;
        }
      }
    }
    
    // Use section default if no keyword match
    if ($content_type === 'article' && isset($section_config['default'])) {
      $content_type = $section_config['default'];
    }
  }
  
  return new JsonResponse(['content_type' => $content_type]);
}

5. Hook into Node Form

File: ai_content_strategy.module

/**
 * Implements hook_form_BASE_FORM_ID_alter() for node_form.
 */
function ai_content_strategy_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Check if we have AI-generated content in query parameters
  $request = \Drupal::request();
  $ai_title = $request->query->get('ai_title');
  $ai_context = $request->query->get('ai_context');
  $ai_section = $request->query->get('ai_section');
  
  if ($ai_title) {
    // Pre-fill title
    $form['title']['widget'][0]['value']['#default_value'] = $ai_title;
    
    // Pre-fill body with context
    if (isset($form['body']) && $ai_context) {
      $body_text = "## Content Strategy Recommendation\n\n";
      $body_text .= "**Section:** " . ucwords(str_replace('_', ' ', $ai_section)) . "\n\n";
      $body_text .= "**Recommendation:** {$ai_context}\n\n";
      $body_text .= "**Idea:** {$ai_title}\n\n";
      $body_text .= "---\n\n";
      $body_text .= "[Start writing your content here...]";
      
      $form['body']['widget'][0]['#default_value'] = $body_text;
      $form['body']['widget'][0]['#format'] = 'basic_html';
    }
    
    // Add informational message
    \Drupal::messenger()->addStatus(t('This draft was created from an AI content strategy recommendation. Feel free to edit and customize.'));
  }
}

6. Configuration Schema

File: config/schema/ai_content_strategy.schema.yml

Add:

ai_content_strategy.content_types:
  type: config_object
  label: 'Content Type Mapping'
  mapping:
    mapping:
      type: sequence
      label: 'Section to Content Type Mapping'
      sequence:
        type: mapping
        mapping:
          default:
            type: string
            label: 'Default content type'
          keywords:
            type: sequence
            label: 'Keyword mappings'
            sequence:
              type: mapping
              mapping:
                keyword:
                  type: string
                content_type:
                  type: string

7. Default Configuration

File: config/install/ai_content_strategy.content_types.yml

mapping:
  content_gaps:
    default: 'article'
    keywords:
      guide: 'page'
      tutorial: 'tutorial'
      faq: 'faq'
  authority_topics:
    default: 'article'
  expertise_demonstrations:
    default: 'article'
    keywords:
      webinar: 'event'
      'case study': 'case_study'
      video: 'video'
  trust_signals:
    default: 'page'
    keywords:
      testimonial: 'testimonial'
      certification: 'page'

Acceptance Criteria

  • Each content idea row has a "Create Draft" button
  • Clicking button suggests appropriate content type based on configuration
  • Node creation form opens with pre-filled title from content idea
  • Body field contains context about the recommendation
  • User sees message indicating draft was created from AI recommendation
  • Configuration form allows admins to customize content type mappings
  • Works with any installed content types on the site
  • Gracefully handles missing content types (falls back to 'article')
  • Button shows loading state during AJAX request
  • Appropriate permissions checked before allowing draft creation

Future Enhancements

  1. Add "Create Multiple Drafts" checkbox to create nodes from all ideas at once
  2. AI-powered content type detection option
  3. Save as unpublished by default with option to configure
  4. Add to editorial calendar/workflow if modules installed
  5. Assign to specific users/teams

Value Proposition

Time Saved: 5 minutes per idea → 10 seconds per idea
User Impact: Transforms passive recommendations into active content pipeline
Development Time: 8-12 hours
ROI: ⭐⭐⭐⭐⭐

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions