shipdeck
Version:
Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.
1,152 lines (1,025 loc) • 37.1 kB
JavaScript
/**
* Growth Hacker Agent - Rapid User Acquisition & Viral Growth Strategies
*
* Specializes in finding and exploiting growth opportunities through
* marketing, product features, and analytics to drive viral user acquisition.
*
* @author Shipdeck Ultimate Framework
* @version 1.0.0
*/
const { BaseAgent } = require('./base-agent');
/**
* Growth Hacker Agent for viral user acquisition and retention strategies
*/
class GrowthHackerAgent extends BaseAgent {
constructor(options = {}) {
super({
name: 'GrowthHackerAgent',
description: 'Specialized agent for rapid user acquisition, viral growth loops, and conversion optimization',
version: '1.0.0',
...options
});
// Growth strategy templates
this.growthTemplates = {
viral: ['referral', 'social-sharing', 'invite-friends', 'network-effects'],
acquisition: ['waitlist', 'landing-pages', 'content-marketing', 'partnerships'],
retention: ['onboarding', 'gamification', 'notifications', 'engagement-loops'],
conversion: ['a-b-testing', 'funnel-optimization', 'social-proof', 'scarcity']
};
// Analytics tracking events
this.growthEvents = [
'user_signup', 'referral_sent', 'referral_converted', 'social_share',
'invite_sent', 'feature_adoption', 'subscription_upgrade', 'churn_risk'
];
}
/**
* Get agent capabilities
* @returns {Array<string>} Array of capability strings
*/
getCapabilities() {
return [
'viral-loops',
'user-acquisition',
'retention',
'a-b-testing',
'analytics'
];
}
/**
* Get system prompt for growth hacking tasks
* @returns {string} System prompt
*/
getSystemPrompt() {
return `You are a Growth Hacker Agent specialized in rapid user acquisition and viral growth strategies.
Your expertise includes:
- Viral loop mechanisms and referral systems
- User acquisition funnels and conversion optimization
- A/B testing frameworks and growth analytics
- Retention strategies and engagement loops
- Social proof and scarcity tactics
- Onboarding flows that convert and retain users
You generate production-ready React/Next.js code with:
- Complete error handling and loading states
- Analytics tracking integration
- Mobile-responsive designs
- Accessibility compliance
- Performance optimization
- TypeScript support when requested
Focus on measurable growth metrics and data-driven optimization.`;
}
/**
* Execute growth hacking task
* @param {Object} task - Task configuration
* @param {Object} context - Execution context
* @returns {Promise<Object>} Execution result
*/
async execute(task, context = {}) {
this.validateTask(task);
const prompt = this.createPrompt(task, context);
const response = await this.sendMessage(prompt, {
temperature: 0.7,
maxTokens: 4096
});
if (!response.success) {
throw new Error(`Growth hacker execution failed: ${response.error}`);
}
return this.parseResponse(response.content);
}
/**
* Create viral referral system
* @param {Object} config - Referral system configuration
* @returns {Promise<Object>} Generated referral system code
*/
async createReferralSystem(config = {}) {
const task = {
type: 'viral-loops',
description: 'Create a comprehensive viral referral system',
requirements: [
'Referral code generation and tracking',
'Reward system for referrers and referees',
'Social sharing integration',
'Analytics tracking for referral metrics',
'Email notifications for successful referrals',
'Dashboard for tracking referral performance'
],
input: {
rewards: config.rewards || { referrer: '10% credit', referee: '20% discount' },
channels: config.channels || ['email', 'social', 'link'],
analytics: config.analytics || true,
notifications: config.notifications || true
}
};
return await this.execute(task);
}
/**
* Build user acquisition funnel
* @param {Object} config - Funnel configuration
* @returns {Promise<Object>} Generated acquisition funnel
*/
async buildAcquisitionFunnel(config = {}) {
const task = {
type: 'user-acquisition',
description: 'Build complete user acquisition funnel with landing pages and conversion optimization',
requirements: [
'High-converting landing pages',
'Lead capture forms with validation',
'Email sequence automation',
'A/B testing framework for optimization',
'Analytics and conversion tracking',
'Social proof and testimonials integration'
],
input: {
pages: config.pages || ['landing', 'signup', 'onboarding'],
abTests: config.abTests || ['headline', 'cta', 'form-fields'],
socialProof: config.socialProof || true,
emailSequence: config.emailSequence || true
}
};
return await this.execute(task);
}
/**
* Create retention and engagement system
* @param {Object} config - Retention system configuration
* @returns {Promise<Object>} Generated retention system
*/
async createRetentionSystem(config = {}) {
const task = {
type: 'retention',
description: 'Create user retention and engagement system with gamification',
requirements: [
'User onboarding flow with progress tracking',
'Gamification elements (points, badges, levels)',
'Push notification system for re-engagement',
'Email drip campaigns for inactive users',
'In-app messaging and tooltips',
'Churn prediction and intervention'
],
input: {
onboarding: config.onboarding || { steps: 5, progress: true },
gamification: config.gamification || { points: true, badges: true, leaderboard: false },
notifications: config.notifications || ['push', 'email', 'in-app'],
churnPrevention: config.churnPrevention || true
}
};
return await this.execute(task);
}
/**
* Setup A/B testing framework
* @param {Object} config - A/B testing configuration
* @returns {Promise<Object>} Generated A/B testing system
*/
async setupABTesting(config = {}) {
const task = {
type: 'a-b-testing',
description: 'Setup comprehensive A/B testing framework for growth experiments',
requirements: [
'Feature flag system for experiment control',
'User segmentation and targeting',
'Statistical significance testing',
'Real-time results dashboard',
'Experiment management interface',
'Integration with analytics platforms'
],
input: {
experiments: config.experiments || ['landing-page', 'pricing', 'onboarding'],
segmentation: config.segmentation || ['new-users', 'returning-users', 'geographic'],
analytics: config.analytics || ['conversion', 'retention', 'revenue'],
dashboard: config.dashboard || true
}
};
return await this.execute(task);
}
/**
* Build growth analytics dashboard
* @param {Object} config - Analytics dashboard configuration
* @returns {Promise<Object>} Generated analytics dashboard
*/
async buildGrowthAnalytics(config = {}) {
const task = {
type: 'analytics',
description: 'Build comprehensive growth analytics dashboard with key metrics',
requirements: [
'Key growth metrics visualization (CAC, LTV, Churn)',
'Funnel analysis and conversion tracking',
'Cohort analysis for retention insights',
'Real-time user activity monitoring',
'Custom event tracking system',
'Automated reporting and alerts'
],
input: {
metrics: config.metrics || ['cac', 'ltv', 'churn', 'mrr', 'viral-coefficient'],
visualizations: config.visualizations || ['charts', 'funnels', 'cohorts', 'heatmaps'],
realtime: config.realtime || true,
alerts: config.alerts || ['goal-reached', 'anomaly-detected', 'experiment-complete']
}
};
return await this.execute(task);
}
/**
* Generate growth strategy templates
* @param {string} strategy - Strategy type (viral, acquisition, retention, conversion)
* @returns {Object} Strategy template configuration
*/
getGrowthTemplate(strategy) {
const templates = {
viral: {
name: 'Viral Growth Loop',
components: ['referral-system', 'social-sharing', 'invite-mechanism'],
metrics: ['viral-coefficient', 'referral-rate', 'sharing-rate'],
tactics: [
'Double-sided incentives (referrer + referee rewards)',
'Social proof in sharing messages',
'Gamified invitation process',
'Network effect amplification'
]
},
acquisition: {
name: 'User Acquisition Funnel',
components: ['landing-pages', 'lead-magnets', 'conversion-forms'],
metrics: ['conversion-rate', 'cac', 'traffic-sources'],
tactics: [
'High-value lead magnets',
'Social proof and testimonials',
'Scarcity and urgency tactics',
'Multi-step micro-conversions'
]
},
retention: {
name: 'User Retention System',
components: ['onboarding', 'engagement-loops', 'notification-system'],
metrics: ['retention-rate', 'dau-mau-ratio', 'feature-adoption'],
tactics: [
'Progressive onboarding with quick wins',
'Habit-forming engagement loops',
'Personalized re-engagement campaigns',
'Churn prediction and intervention'
]
},
conversion: {
name: 'Conversion Optimization',
components: ['a-b-tests', 'funnel-analysis', 'user-feedback'],
metrics: ['conversion-rate', 'ltv', 'mrr-growth'],
tactics: [
'Continuous A/B testing program',
'Friction analysis and removal',
'Social proof at conversion points',
'Value proposition optimization'
]
}
};
return templates[strategy] || null;
}
/**
* Create viral loop implementation
* @param {Object} config - Viral loop configuration
* @returns {string} React component code for viral loop
*/
generateViralLoopCode(config = {}) {
const { rewardType = 'credit', socialChannels = ['twitter', 'facebook', 'linkedin'] } = config;
return `import React, { useState, useEffect } from 'react';
import { useUser } from '@/hooks/useUser';
import { trackEvent } from '@/lib/analytics';
/**
* Viral Referral System Component
* Implements double-sided incentive referral system with social sharing
*/
export default function ViralReferralSystem() {
const { user } = useUser();
const [referralCode, setReferralCode] = useState('');
const [referralStats, setReferralStats] = useState({
invitesSent: 0,
conversions: 0,
totalRewards: 0
});
const [shareUrl, setShareUrl] = useState('');
useEffect(() => {
// Generate unique referral code
const generateReferralCode = () => {
return user?.id ? \`\${user.username || 'user'}\${Date.now().toString(36)}\` : '';
};
if (user) {
const code = generateReferralCode();
setReferralCode(code);
setShareUrl(\`\${window.location.origin}?ref=\${code}\`);
// Track viral loop view
trackEvent('viral_loop_viewed', { userId: user.id });
}
}, [user]);
const handleSocialShare = async (platform) => {
const shareMessage = \`🚀 Join me on this amazing platform! Use my link to get ${config.refereeReward || '20% off'}: \${shareUrl}\`;
const shareUrls = {
twitter: \`https://twitter.com/intent/tweet?text=\${encodeURIComponent(shareMessage)}\`,
facebook: \`https://www.facebook.com/sharer/sharer.php?u=\${encodeURIComponent(shareUrl)}\`,
linkedin: \`https://www.linkedin.com/sharing/share-offsite/?url=\${encodeURIComponent(shareUrl)}\`
};
// Track social share
trackEvent('referral_shared', {
userId: user.id,
platform,
referralCode,
shareUrl
});
// Open share window
window.open(shareUrls[platform], '_blank', 'width=600,height=400');
};
const copyReferralLink = async () => {
try {
await navigator.clipboard.writeText(shareUrl);
trackEvent('referral_link_copied', { userId: user.id, referralCode });
// Show success toast
} catch (err) {
console.error('Failed to copy:', err);
}
};
const sendEmailInvite = async (email) => {
try {
const response = await fetch('/api/referrals/invite', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
referrerCode: referralCode,
email,
message: \`I'd love for you to try this platform! Use my referral link: \${shareUrl}\`
})
});
if (response.ok) {
trackEvent('email_invite_sent', { userId: user.id, referralCode, email });
// Show success message
}
} catch (error) {
console.error('Email invite failed:', error);
}
};
return (
<div className="max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-lg">
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-2">
Earn ${config.referrerReward || '10% credit'} for Every Friend!
</h2>
<p className="text-gray-600">
Share your unique referral link and both you and your friends get rewards
</p>
</div>
{/* Referral Stats */}
<div className="grid grid-cols-3 gap-4 mb-8">
<div className="text-center p-4 bg-blue-50 rounded-lg">
<div className="text-2xl font-bold text-blue-600">{referralStats.invitesSent}</div>
<div className="text-sm text-gray-600">Invites Sent</div>
</div>
<div className="text-center p-4 bg-green-50 rounded-lg">
<div className="text-2xl font-bold text-green-600">{referralStats.conversions}</div>
<div className="text-sm text-gray-600">Friends Joined</div>
</div>
<div className="text-center p-4 bg-purple-50 rounded-lg">
<div className="text-2xl font-bold text-purple-600">\${referralStats.totalRewards}</div>
<div className="text-sm text-gray-600">Total Earned</div>
</div>
</div>
{/* Referral Link */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
Your Referral Link
</label>
<div className="flex">
<input
type="text"
value={shareUrl}
readOnly
className="flex-1 px-3 py-2 border border-gray-300 rounded-l-md bg-gray-50"
/>
<button
onClick={copyReferralLink}
className="px-4 py-2 bg-blue-600 text-white rounded-r-md hover:bg-blue-700 transition-colors"
>
Copy
</button>
</div>
</div>
{/* Social Sharing */}
<div className="mb-6">
<h3 className="text-lg font-medium text-gray-900 mb-3">Share on Social Media</h3>
<div className="flex gap-3">
{socialChannels.map(platform => (
<button
key={platform}
onClick={() => handleSocialShare(platform)}
className={\`px-4 py-2 rounded-md text-white transition-colors \${
platform === 'twitter' ? 'bg-blue-400 hover:bg-blue-500' :
platform === 'facebook' ? 'bg-blue-600 hover:bg-blue-700' :
'bg-blue-800 hover:bg-blue-900'
}\`}
>
Share on {platform.charAt(0).toUpperCase() + platform.slice(1)}
</button>
))}
</div>
</div>
{/* Email Invite */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-3">Invite by Email</h3>
<EmailInviteForm onSend={sendEmailInvite} />
</div>
{/* How It Works */}
<div className="mt-8 p-4 bg-gray-50 rounded-lg">
<h4 className="font-medium text-gray-900 mb-2">How It Works:</h4>
<ul className="text-sm text-gray-600 space-y-1">
<li>1. Share your referral link with friends</li>
<li>2. They sign up using your link and get ${config.refereeReward || '20% off'}</li>
<li>3. You earn ${config.referrerReward || '10% credit'} for each successful referral</li>
<li>4. There's no limit to how much you can earn!</li>
</ul>
</div>
</div>
);
}
/**
* Email Invite Form Component
*/
function EmailInviteForm({ onSend }) {
const [email, setEmail] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
if (!email) return;
setIsLoading(true);
await onSend(email);
setEmail('');
setIsLoading(false);
};
return (
<form onSubmit={handleSubmit} className="flex gap-2">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="friend@example.com"
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
/>
<button
type="submit"
disabled={isLoading}
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 disabled:opacity-50 transition-colors"
>
{isLoading ? 'Sending...' : 'Send Invite'}
</button>
</form>
);
}`;
}
/**
* Generate A/B testing framework code
* @param {Object} config - A/B test configuration
* @returns {string} A/B testing hook and component code
*/
generateABTestingCode(config = {}) {
return `import React, { useState, useEffect, createContext, useContext } from 'react';
import { trackEvent } from '@/lib/analytics';
/**
* A/B Testing Context and Provider
* Manages experiment variants and user assignments
*/
const ABTestContext = createContext();
export function ABTestProvider({ children }) {
const [experiments, setExperiments] = useState({});
const [userId, setUserId] = useState(null);
useEffect(() => {
// Initialize user ID and load experiments
const initializeABTests = async () => {
const user = await getCurrentUser();
setUserId(user?.id);
// Load active experiments
const activeExperiments = await fetchActiveExperiments();
const userExperiments = {};
for (const experiment of activeExperiments) {
const variant = await assignUserToVariant(user?.id, experiment);
userExperiments[experiment.name] = variant;
// Track experiment assignment
trackEvent('experiment_assigned', {
userId: user?.id,
experimentName: experiment.name,
variant: variant.name,
timestamp: new Date().toISOString()
});
}
setExperiments(userExperiments);
};
initializeABTests();
}, []);
const getVariant = (experimentName, defaultVariant = 'control') => {
return experiments[experimentName]?.name || defaultVariant;
};
const trackConversion = (experimentName, goalName, value = 1) => {
const variant = experiments[experimentName];
if (variant) {
trackEvent('experiment_conversion', {
userId,
experimentName,
variant: variant.name,
goalName,
value,
timestamp: new Date().toISOString()
});
}
};
return (
<ABTestContext.Provider value={{
experiments,
getVariant,
trackConversion
}}>
{children}
</ABTestContext.Provider>
);
}
/**
* Hook for using A/B tests in components
* @param {string} experimentName - Name of the experiment
* @param {string} defaultVariant - Default variant if experiment not found
* @returns {Object} Experiment utilities
*/
export function useABTest(experimentName, defaultVariant = 'control') {
const context = useContext(ABTestContext);
if (!context) {
throw new Error('useABTest must be used within ABTestProvider');
}
const { getVariant, trackConversion } = context;
const variant = getVariant(experimentName, defaultVariant);
return {
variant,
isVariant: (variantName) => variant === variantName,
trackConversion: (goalName, value) => trackConversion(experimentName, goalName, value)
};
}
/**
* A/B Test Component for declarative testing
*/
export function ABTest({ name, variants, defaultVariant = 'control', children }) {
const { variant } = useABTest(name, defaultVariant);
const renderVariant = variants[variant] || variants[defaultVariant];
return renderVariant ? renderVariant() : children;
}
/**
* Utility functions for A/B testing
*/
async function getCurrentUser() {
try {
const response = await fetch('/api/user/me');
return response.ok ? await response.json() : null;
} catch (error) {
console.error('Failed to get current user:', error);
return null;
}
}
async function fetchActiveExperiments() {
try {
const response = await fetch('/api/experiments/active');
return response.ok ? await response.json() : [];
} catch (error) {
console.error('Failed to fetch experiments:', error);
return [];
}
}
async function assignUserToVariant(userId, experiment) {
try {
const response = await fetch('/api/experiments/assign', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, experimentName: experiment.name })
});
if (response.ok) {
return await response.json();
}
// Fallback to client-side assignment
return clientSideAssignment(userId, experiment);
} catch (error) {
console.error('Failed to assign variant:', error);
return clientSideAssignment(userId, experiment);
}
}
function clientSideAssignment(userId, experiment) {
// Deterministic assignment based on user ID and experiment name
const hash = simpleHash(\`\${userId}-\${experiment.name}\`);
const bucketSize = 1 / experiment.variants.length;
const bucket = (hash % 1000) / 1000;
for (let i = 0; i < experiment.variants.length; i++) {
if (bucket < bucketSize * (i + 1)) {
return experiment.variants[i];
}
}
return experiment.variants[0]; // Fallback
}
function simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash);
}
// Example usage components
export function ExampleLandingPageTest() {
const { variant, trackConversion } = useABTest('landing-page-headline');
const handleSignup = () => {
trackConversion('signup', 1);
// Handle actual signup logic
};
return (
<div className="text-center py-16">
{variant === 'control' && (
<h1 className="text-4xl font-bold mb-4">
Welcome to Our Amazing Product
</h1>
)}
{variant === 'benefits' && (
<h1 className="text-4xl font-bold mb-4">
Save 50% Time with Our Solution
</h1>
)}
{variant === 'social-proof' && (
<h1 className="text-4xl font-bold mb-4">
Join 10,000+ Happy Customers
</h1>
)}
<button
onClick={handleSignup}
className="px-8 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Get Started Free
</button>
</div>
);
}
export function ExamplePricingTest() {
return (
<ABTest
name="pricing-display"
variants={{
control: () => <StandardPricing />,
discount: () => <DiscountPricing />,
social: () => <SocialProofPricing />
}}
/>
);
}`;
}
/**
* Create growth analytics dashboard code
* @param {Object} config - Analytics configuration
* @returns {string} Analytics dashboard component code
*/
generateGrowthAnalyticsCode(config = {}) {
return `import React, { useState, useEffect } from 'react';
import { trackEvent } from '@/lib/analytics';
/**
* Growth Analytics Dashboard
* Displays key growth metrics, funnels, and cohort analysis
*/
export default function GrowthAnalyticsDashboard() {
const [metrics, setMetrics] = useState({
cac: 0,
ltv: 0,
churnRate: 0,
mrr: 0,
viralCoefficient: 0,
conversionRate: 0
});
const [funnelData, setFunnelData] = useState([]);
const [cohortData, setCohortData] = useState([]);
const [realTimeUsers, setRealTimeUsers] = useState(0);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
loadGrowthMetrics();
const interval = setInterval(loadRealTimeData, 30000); // Update every 30 seconds
return () => clearInterval(interval);
}, []);
const loadGrowthMetrics = async () => {
try {
setIsLoading(true);
const [metricsRes, funnelRes, cohortRes] = await Promise.all([
fetch('/api/analytics/growth-metrics'),
fetch('/api/analytics/funnel'),
fetch('/api/analytics/cohort')
]);
if (metricsRes.ok) {
const data = await metricsRes.json();
setMetrics(data);
}
if (funnelRes.ok) {
const data = await funnelRes.json();
setFunnelData(data);
}
if (cohortRes.ok) {
const data = await cohortRes.json();
setCohortData(data);
}
trackEvent('growth_dashboard_loaded', { timestamp: new Date().toISOString() });
} catch (error) {
console.error('Failed to load growth metrics:', error);
} finally {
setIsLoading(false);
}
};
const loadRealTimeData = async () => {
try {
const response = await fetch('/api/analytics/realtime');
if (response.ok) {
const data = await response.json();
setRealTimeUsers(data.activeUsers);
}
} catch (error) {
console.error('Failed to load real-time data:', error);
}
};
if (isLoading) {
return <AnalyticsSkeleton />;
}
return (
<div className="p-6 space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-3xl font-bold text-gray-900">Growth Analytics</h1>
<div className="flex items-center space-x-2">
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<span className="text-sm text-gray-600">{realTimeUsers} users online</span>
</div>
</div>
{/* Key Metrics Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<MetricCard
title="Customer Acquisition Cost"
value={\`$\${metrics.cac.toFixed(2)}\`}
change={metrics.cacChange}
trend={metrics.cacTrend}
/>
<MetricCard
title="Lifetime Value"
value={\`$\${metrics.ltv.toFixed(2)}\`}
change={metrics.ltvChange}
trend={metrics.ltvTrend}
/>
<MetricCard
title="Churn Rate"
value={\`\${(metrics.churnRate * 100).toFixed(1)}%\`}
change={metrics.churnChange}
trend={metrics.churnTrend}
inverse={true}
/>
<MetricCard
title="Monthly Recurring Revenue"
value={\`$\${(metrics.mrr / 1000).toFixed(1)}k\`}
change={metrics.mrrChange}
trend={metrics.mrrTrend}
/>
<MetricCard
title="Viral Coefficient"
value={metrics.viralCoefficient.toFixed(2)}
change={metrics.viralChange}
trend={metrics.viralTrend}
/>
<MetricCard
title="Conversion Rate"
value={\`\${(metrics.conversionRate * 100).toFixed(1)}%\`}
change={metrics.conversionChange}
trend={metrics.conversionTrend}
/>
</div>
{/* Funnel Analysis */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4">Conversion Funnel</h2>
<ConversionFunnel data={funnelData} />
</div>
{/* Cohort Analysis */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4">Cohort Analysis</h2>
<CohortTable data={cohortData} />
</div>
{/* Real-time Activity */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4">Real-time Activity</h2>
<RealTimeActivity />
</div>
</div>
);
}
/**
* Metric Card Component
*/
function MetricCard({ title, value, change, trend, inverse = false }) {
const isPositive = inverse ? change < 0 : change > 0;
const changeColor = isPositive ? 'text-green-600' : 'text-red-600';
const changeIcon = isPositive ? '↗' : '↘';
return (
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">{title}</p>
<p className="text-2xl font-bold text-gray-900">{value}</p>
</div>
<div className="flex items-center space-x-1">
<span className={\`text-sm font-medium \${changeColor}\`}>
{changeIcon} {Math.abs(change).toFixed(1)}%
</span>
</div>
</div>
<div className="mt-4 h-2 bg-gray-200 rounded">
<div
className={\`h-2 rounded \${isPositive ? 'bg-green-400' : 'bg-red-400'}\`}
style={{ width: \`\${Math.min(Math.abs(change), 100)}%\` }}
></div>
</div>
</div>
);
}
/**
* Conversion Funnel Component
*/
function ConversionFunnel({ data }) {
if (!data || data.length === 0) {
return <div className="text-gray-500 text-center py-8">No funnel data available</div>;
}
const maxUsers = data[0]?.users || 1;
return (
<div className="space-y-4">
{data.map((step, index) => {
const percentage = (step.users / maxUsers) * 100;
const conversionRate = index > 0 ? ((step.users / data[index - 1].users) * 100) : 100;
return (
<div key={step.name} className="flex items-center space-x-4">
<div className="w-32 text-sm font-medium text-gray-700">{step.name}</div>
<div className="flex-1">
<div className="flex items-center justify-between mb-1">
<span className="text-sm text-gray-600">{step.users.toLocaleString()} users</span>
<span className="text-sm text-gray-600">{conversionRate.toFixed(1)}% conversion</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: \`\${percentage}%\` }}
></div>
</div>
</div>
</div>
);
})}
</div>
);
}
/**
* Cohort Analysis Table
*/
function CohortTable({ data }) {
if (!data || data.length === 0) {
return <div className="text-gray-500 text-center py-8">No cohort data available</div>;
}
const weeks = Array.from({ length: 12 }, (_, i) => \`Week \${i + 1}\`);
return (
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Cohort
</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Users
</th>
{weeks.map(week => (
<th key={week} className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{week}
</th>
))}
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{data.map((cohort, index) => (
<tr key={index}>
<td className="px-3 py-2 whitespace-nowrap text-sm font-medium text-gray-900">
{cohort.period}
</td>
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-500">
{cohort.users.toLocaleString()}
</td>
{cohort.retention.map((rate, weekIndex) => (
<td key={weekIndex} className="px-3 py-2 whitespace-nowrap text-sm text-gray-900">
<div
className={\`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium \${
rate > 0.5 ? 'bg-green-100 text-green-800' :
rate > 0.2 ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}\`}
>
{(rate * 100).toFixed(0)}%
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
/**
* Real-time Activity Component
*/
function RealTimeActivity() {
const [activities, setActivities] = useState([]);
useEffect(() => {
const loadActivities = async () => {
try {
const response = await fetch('/api/analytics/activity');
if (response.ok) {
const data = await response.json();
setActivities(data);
}
} catch (error) {
console.error('Failed to load activities:', error);
}
};
loadActivities();
const interval = setInterval(loadActivities, 5000); // Update every 5 seconds
return () => clearInterval(interval);
}, []);
return (
<div className="space-y-3">
{activities.map((activity, index) => (
<div key={index} className="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
<div className="flex-shrink-0">
<div className={\`w-2 h-2 rounded-full \${
activity.type === 'signup' ? 'bg-green-400' :
activity.type === 'referral' ? 'bg-blue-400' :
activity.type === 'purchase' ? 'bg-purple-400' :
'bg-gray-400'
}\`}></div>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm text-gray-900">{activity.description}</p>
<p className="text-xs text-gray-500">{activity.timestamp}</p>
</div>
</div>
))}
</div>
);
}
/**
* Loading Skeleton Component
*/
function AnalyticsSkeleton() {
return (
<div className="p-6 space-y-6">
<div className="h-8 bg-gray-200 rounded w-1/3 animate-pulse"></div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="bg-white rounded-lg shadow p-6">
<div className="h-4 bg-gray-200 rounded w-1/2 mb-2 animate-pulse"></div>
<div className="h-8 bg-gray-200 rounded w-1/3 animate-pulse"></div>
</div>
))}
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="h-6 bg-gray-200 rounded w-1/4 mb-4 animate-pulse"></div>
<div className="space-y-3">
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className="h-8 bg-gray-200 rounded animate-pulse"></div>
))}
</div>
</div>
</div>
);
}`;
}
/**
* Format task prompt with growth-specific context
* @param {Object} task - Task configuration
* @returns {string} Formatted task prompt
*/
formatTaskPrompt(task) {
const basePrompt = super.formatTaskPrompt(task);
// Add growth-specific context
const growthContext = `
Growth Strategy Context:
- Focus on measurable metrics and data-driven optimization
- Implement viral mechanics and network effects
- Create frictionless user acquisition flows
- Build retention and engagement loops
- Include comprehensive analytics tracking
- Ensure mobile-responsive and accessible design
- Provide production-ready error handling`;
return `${basePrompt}\n\n${growthContext}`;
}
/**
* Validate growth-specific task requirements
* @param {Object} task - Task to validate
* @returns {boolean} True if valid
*/
validateTask(task) {
super.validateTask(task);
const validTypes = ['viral-loops', 'user-acquisition', 'retention', 'a-b-testing', 'analytics'];
if (!validTypes.includes(task.type)) {
throw new Error(`Invalid task type: ${task.type}. Must be one of: ${validTypes.join(', ')}`);
}
return true;
}
}
module.exports = { GrowthHackerAgent };