nextjstailwindCSSstrapi CMSGraphQL/ApolloCloudinaryi18nAPI Integration

Agency Website with AI-Powered Show Concept Generation

By Georgos Gakis
Picture of the author
Published on
Duration
6 Months
Role
Front- & Back-End Developer, UI-Designer, Content Manager
Dancing Bear Productions Website
Dancing Bear Productions Website
Dancing Bear Ai Show Production Generator
Dancing Bear Ai Show Production Generator
Dancing Bear website strapi CMS
Dancing Bear website strapi CMS
Dancing Bear website strapi CMS
Dancing Bear website strapi CMS

AI-POWERED SHOW CONCEPT GENERATOR

Project Overview

This is a project I worked on for Dancing Bear Productions, a creative agency specializing in show and media production who combines live performance with cutting-edge technology.

How It Works

To achieve this, we will integrate the OpenAI and Anthropic Claude APIs with:

  • A specially designed prompt to ensure high-quality, structured outputs
  • Curated content from top-tier productions for inspiration and relevance
  • A seamless user experience that delivers customized, engaging concepts

This feature will help clients quickly create compelling and well-structured show ideas, making the creative process more efficient. 🚀

Try it out and create your production :o)

Key Technical Features

  • Multi-Model AI Integration: Implements both OpenAI and Claude models with seamless switching between them
  • Token-Efficient Processing: Custom optimization for handling large context windows and reducing token usage
  • State Persistence: Comprehensive localStorage implementation for maintaining application state during navigation
  • Dynamic Content Generation: Real-time AI-generated content with streaming responses
  • Responsive Design: Tailwind CSS implementation with mobile-first approach and custom breakpoints

Implementation Highlights

Frontend Architecture
  • React component architecture with modular design patterns
  • Custom hooks for state management (useConceptState, useConceptActions)
  • Dynamic loading states with CSS-based animations
API Integration
  • Server-side API routing with Next.js
  • Fetch interceptors for handling timeouts and errors
  • Streaming response handling
  • Model-specific prompt engineering
User Experience Features
  • Real-time progress indicators with custom CSS animations
  • Modal-based UI for generating and adapting concepts
  • Client-side caching for improved performance
  • Error handling with user-friendly feedback

Technical Stack

  • Framework: Next.js with React
  • Styling: Tailwind CSS
  • Animation: CSS animations and Framer Motion
  • State Management: React Hooks with custom persistence layer
  • APIs: OpenAI GPT-4, Anthropic Claude
  • Internationalization: next-i18next

Development Challenges

  • Handling API timeout issues for complex generation tasks
  • Building a responsive UI that works across all device sizes
  • Creating an optimized token management system to reduce API costs
  • Implementing smooth transitions between different application states

This project demonstrates advanced front-end development techniques, API integration strategies, and modern React patterns within a production-grade application.


CODE SNIPPETS

AI Model Selection Implementation
// Model selection UI component with context-aware options
<div className='w-full md:w-auto gap-1 px-3 py-2 text-xs text-white border border-white/20 text-items-center xl:gap-2 sm:flex'>
  <StyledSwitch
    checked={useClaudeModel}
    onChange={(e) => setUseClaudeModel(e.target.checked)}
    name='use-claude'
    label='Use Enhanced AI'
  />
  <div className='text-xs mt-1 text-white/60'>
    Better for more detailed content avg. durration 30 s
  </div>
</div>
Advanced Prompt Engineering Examples
/**
 * System message for show concept generation with anti-hallucination guardrails
 */
const showConceptSystemPrompt = `You are an expert show concept generator with deep knowledge of live entertainment, event production, and performance arts.

KEY INSTRUCTIONS:
1. ONLY use factual data provided - never invent or hallucinate performers, acts, or capabilities.
2. Reference only show acts and technical capabilities explicitly listed in the provided CMS data.
3. For each act in your concept:
   - Include exact title, media URLs, and video URL if available
   - Provide detailed staging and positioning information
   - Specify lighting requirements and effects
   - Detail sound design requirements
   - List any special equipment or technical needs
4. Create a coherent narrative that:
   - Has a clear beginning, middle, and end
   - Builds dramatic tension throughout
   - Creates memorable highlight moments
   - Maintains thematic consistency
5. Consider technical feasibility by:
   - Accounting for setup and transition times
   - Planning equipment placement and movement
   - Ensuring safety requirements are met`;

/**
 * User prompt template with structured data integration
 */
function generateUserPrompt(userRequest, companyData) {
  return `I need a creative show concept based on this description: "${userRequest}"
    
=== IMPORTANT INSTRUCTIONS ===
Please ONLY use actual show acts, capabilities, and technical elements from our real data below.

=== OUR ACTUAL SHOW ACTS FROM CMS ===
${JSON.stringify(companyData.showActs.slice(0, 10), null, 2)}

=== RELEVANT TAGS FROM OUR SYSTEM ===
${JSON.stringify(companyData.tags, null, 2)}

=== OUR ACTUAL PERFORMANCE CAPABILITIES ===
${JSON.stringify(companyData.capabilities, null, 2)}

=== SUCCESSFUL PAST PROJECTS WITH THEIR CONCEPTS ===
${JSON.stringify(companyData.successfulProjects, null, 2)}
}

State Persistence with localStorage
// Save state to localStorage when changes occur
useEffect(() => {
  // Only save if there's actual content
  if (prompt.trim() || selectedTags.length > 0) {
    const stateToSave = {
      prompt,
      selectedTags,
      locale,
      shouldSaveToCms,
      useClaudeModel,
      useEnhancedModel,
    };
    localStorage.setItem('conceptGeneratorState', JSON.stringify(stateToSave));
  }
}, [
  prompt,
  selectedTags,
  locale,
  shouldSaveToCms,
  useClaudeModel,
  useEnhancedModel,
]);

// Load state from localStorage on component mount
useEffect(() => {
  const savedState = localStorage.getItem('conceptGeneratorState');
  if (savedState) {
    try {
      const parsedState = JSON.parse(savedState);
      setPrompt(parsedState.prompt || '');
      setSelectedTags(parsedState.selectedTags || []);
      // Additional state restoration
    } catch (e) {
      console.error('Error restoring concept generator state:', e);
    }
  }
}, []);
Multi-Model AI Integration Logic
// Determine which model to use
let selectedModel;
if (options.useClaudeModel) {
  // If Claude model is specifically requested
  selectedModel = options.useEnhancedModel
    ? AI_MODELS.claudeEnhanced
    : AI_MODELS.claude;

  console.log(`[TokenEfficientAI] Using Claude model: ${selectedModel}`);
} else {
  // Otherwise use OpenAI models
  selectedModel = options.useEnhancedModel
    ? AI_MODELS.enhanced
    : AI_MODELS.default;

  console.log(`[TokenEfficientAI] Using OpenAI model: ${selectedModel}`);
}

// Determine which provider to use based on the selected model
const provider =
  selectedModel && MODEL_PROVIDERS[selectedModel]
    ? MODEL_PROVIDERS[selectedModel]
    : AI_PROVIDERS.OPENAI;

// Use different processing paths based on the provider
if (provider === AI_PROVIDERS.ANTHROPIC) {
  result = await generateWithClaude(prompt, optimizedData, selectedModel);
} else {
  result = await generateWithOpenAI(prompt, optimizedData, selectedModel);
}
Dynamic Progress Animation with CSS Variables
/* Progress bar with dynamic duration based on model */
.progress-bar {
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, #f1851b 0%, #ff9f3d 50%, #f1851b 100%);
  background-size: 200% 100%;
  transform: translateX(-100%);
  animation: progress var(--duration) linear forwards, gradient 2s linear
      infinite;
}

@keyframes progress {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(0);
  }
}

@keyframes gradient {
  0% {
    background-position: 0% 50%;
  }
  100% {
    background-position: 200% 50%;
  }
}