- Add event-status-changed trigger for SmoothSchedule Activepieces piece - Add comprehensive test coverage for payments, tickets, messaging, mobile - Add test coverage for core services, signals, consumers, and views - Improve Activepieces UI: templates, billing hooks, project hooks - Update marketing automation showcase and workflow visual components - Add public API endpoints for availability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
172 lines
6.2 KiB
TypeScript
172 lines
6.2 KiB
TypeScript
import React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
Calendar,
|
|
Mail,
|
|
MessageSquare,
|
|
Clock,
|
|
Search,
|
|
FileText,
|
|
Sparkles,
|
|
ChevronRight,
|
|
} from 'lucide-react';
|
|
import type { LucideIcon } from 'lucide-react';
|
|
|
|
interface WorkflowBlock {
|
|
icon: LucideIcon;
|
|
label: string;
|
|
type: 'trigger' | 'action';
|
|
}
|
|
|
|
interface WorkflowVisualProps {
|
|
trigger: string;
|
|
actions: string[];
|
|
variant?: 'winback' | 'noshow' | 'report';
|
|
}
|
|
|
|
const getWorkflowConfig = (
|
|
variant: WorkflowVisualProps['variant']
|
|
): WorkflowBlock[] => {
|
|
switch (variant) {
|
|
case 'winback':
|
|
return [
|
|
{ icon: Clock, label: 'Schedule: Weekly', type: 'trigger' },
|
|
{ icon: Search, label: 'Find Inactive Customers', type: 'action' },
|
|
{ icon: Mail, label: 'Send Email', type: 'action' },
|
|
];
|
|
case 'noshow':
|
|
return [
|
|
{ icon: Calendar, label: 'Event Created', type: 'trigger' },
|
|
{ icon: Clock, label: 'Wait 2 Hours Before', type: 'action' },
|
|
{ icon: MessageSquare, label: 'Send SMS', type: 'action' },
|
|
];
|
|
case 'report':
|
|
return [
|
|
{ icon: Clock, label: 'Daily at 6 PM', type: 'trigger' },
|
|
{ icon: FileText, label: "Get Tomorrow's Schedule", type: 'action' },
|
|
{ icon: Mail, label: 'Send Summary', type: 'action' },
|
|
];
|
|
default:
|
|
return [
|
|
{ icon: Calendar, label: 'Event Created', type: 'trigger' },
|
|
{ icon: Clock, label: 'Wait', type: 'action' },
|
|
{ icon: Mail, label: 'Send Notification', type: 'action' },
|
|
];
|
|
}
|
|
};
|
|
|
|
const WorkflowVisual: React.FC<WorkflowVisualProps> = ({
|
|
variant = 'noshow',
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
const blocks = getWorkflowConfig(variant);
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-xl overflow-hidden">
|
|
{/* AI Copilot Input */}
|
|
<div className="p-4 bg-gradient-to-r from-purple-50 to-brand-50 dark:from-purple-900/20 dark:to-brand-900/20 border-b border-gray-200 dark:border-gray-700">
|
|
<div className="flex items-center gap-3 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-600 px-4 py-3 shadow-sm">
|
|
<Sparkles className="w-5 h-5 text-purple-500" />
|
|
<span className="text-gray-400 dark:text-gray-500 text-sm flex-1">
|
|
{t('marketing.plugins.aiCopilot.placeholder')}
|
|
</span>
|
|
<motion.div
|
|
animate={{ opacity: [0.5, 1, 0.5] }}
|
|
transition={{ duration: 1.5, repeat: Infinity }}
|
|
className="w-2 h-5 bg-purple-500 rounded-sm"
|
|
/>
|
|
</div>
|
|
<p className="text-xs text-gray-500 dark:text-gray-400 mt-2 ml-1">
|
|
{t('marketing.plugins.aiCopilot.examples')}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Workflow Visualization */}
|
|
<div className="p-6">
|
|
<div className="flex flex-col gap-3">
|
|
{blocks.map((block, index) => (
|
|
<React.Fragment key={index}>
|
|
{/* Block */}
|
|
<motion.div
|
|
initial={{ opacity: 0, x: -20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
transition={{ delay: index * 0.15 }}
|
|
className={`flex items-center gap-3 p-3 rounded-lg border ${
|
|
block.type === 'trigger'
|
|
? 'bg-gradient-to-r from-brand-50 to-purple-50 dark:from-brand-900/30 dark:to-purple-900/30 border-brand-200 dark:border-brand-800'
|
|
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700'
|
|
}`}
|
|
>
|
|
<div
|
|
className={`p-2 rounded-lg ${
|
|
block.type === 'trigger'
|
|
? 'bg-brand-100 dark:bg-brand-900/50 text-brand-600 dark:text-brand-400'
|
|
: 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
<block.icon className="w-5 h-5" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<span
|
|
className={`text-xs font-medium uppercase tracking-wide ${
|
|
block.type === 'trigger'
|
|
? 'text-brand-600 dark:text-brand-400'
|
|
: 'text-gray-500 dark:text-gray-400'
|
|
}`}
|
|
>
|
|
{block.type === 'trigger' ? 'When' : 'Then'}
|
|
</span>
|
|
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
|
{block.label}
|
|
</p>
|
|
</div>
|
|
<ChevronRight className="w-4 h-4 text-gray-400" />
|
|
</motion.div>
|
|
|
|
{/* Connector */}
|
|
{index < blocks.length - 1 && (
|
|
<div className="flex items-center justify-center h-4">
|
|
<div className="relative w-0.5 h-full bg-gray-200 dark:bg-gray-700">
|
|
<motion.div
|
|
className="absolute w-2 h-2 bg-brand-500 rounded-full left-1/2 -translate-x-1/2"
|
|
animate={{ y: [0, 12, 0] }}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
delay: index * 0.3,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
|
|
{/* Integration badges */}
|
|
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
|
<p className="text-xs text-gray-500 dark:text-gray-400 mb-2">
|
|
{t('marketing.plugins.integrations.description')}
|
|
</p>
|
|
<div className="flex gap-2 flex-wrap">
|
|
{['Gmail', 'Slack', 'Sheets', 'Twilio'].map((app) => (
|
|
<span
|
|
key={app}
|
|
className="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 rounded-md"
|
|
>
|
|
{app}
|
|
</span>
|
|
))}
|
|
<span className="px-2 py-1 text-xs text-gray-400 dark:text-gray-500">
|
|
+1000 more
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default WorkflowVisual;
|