From 11bb83a85db94360090b054baeeba7c241fff0c4 Mon Sep 17 00:00:00 2001 From: poduck Date: Wed, 3 Dec 2025 02:18:05 -0500 Subject: [PATCH] feat: Add comprehensive help documentation system and plugin creation page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CreatePlugin.tsx page for custom plugin creation with code editor - Add HelpButton component for contextual help links - Create 21 new help pages covering all dashboard features: - Core: Dashboard, Scheduler, Tasks - Manage: Customers, Services, Resources, Staff - Communicate: Messages (Ticketing already existed) - Money: Payments - Extend: Plugins overview and creation guide - Settings: General, Resource Types, Booking, Appearance, Email, Domains, API, Auth, Billing, Quota - Update HelpGuide.tsx as main documentation hub with quick start guide - Add routes for all help pages in App.tsx - Add HelpButton to Dashboard, Customers, Services, and Tasks pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- frontend/src/App.tsx | 58 +- frontend/src/components/HelpButton.tsx | 33 + frontend/src/pages/CreatePlugin.tsx | 568 ++++++++++++++++++ frontend/src/pages/Customers.tsx | 10 +- frontend/src/pages/Dashboard.tsx | 19 +- frontend/src/pages/HelpGuide.tsx | 199 +++++- frontend/src/pages/Services.tsx | 18 +- frontend/src/pages/Tasks.tsx | 18 +- frontend/src/pages/help/HelpCreatePlugin.tsx | 189 ++++++ frontend/src/pages/help/HelpCustomers.tsx | 175 ++++++ frontend/src/pages/help/HelpDashboard.tsx | 228 +++++++ frontend/src/pages/help/HelpMessages.tsx | 162 +++++ frontend/src/pages/help/HelpPayments.tsx | 167 +++++ frontend/src/pages/help/HelpPlugins.tsx | 175 ++++++ frontend/src/pages/help/HelpResources.tsx | 165 +++++ frontend/src/pages/help/HelpScheduler.tsx | 300 +++++++++ frontend/src/pages/help/HelpServices.tsx | 134 +++++ frontend/src/pages/help/HelpSettingsApi.tsx | 160 +++++ .../src/pages/help/HelpSettingsAppearance.tsx | 154 +++++ frontend/src/pages/help/HelpSettingsAuth.tsx | 165 +++++ .../src/pages/help/HelpSettingsBilling.tsx | 155 +++++ .../src/pages/help/HelpSettingsBooking.tsx | 168 ++++++ .../src/pages/help/HelpSettingsDomains.tsx | 167 +++++ frontend/src/pages/help/HelpSettingsEmail.tsx | 158 +++++ .../src/pages/help/HelpSettingsGeneral.tsx | 130 ++++ frontend/src/pages/help/HelpSettingsQuota.tsx | 155 +++++ .../pages/help/HelpSettingsResourceTypes.tsx | 164 +++++ frontend/src/pages/help/HelpStaff.tsx | 168 ++++++ frontend/src/pages/help/HelpTasks.tsx | 352 +++++++++++ 29 files changed, 4673 insertions(+), 41 deletions(-) create mode 100644 frontend/src/components/HelpButton.tsx create mode 100644 frontend/src/pages/CreatePlugin.tsx create mode 100644 frontend/src/pages/help/HelpCreatePlugin.tsx create mode 100644 frontend/src/pages/help/HelpCustomers.tsx create mode 100644 frontend/src/pages/help/HelpDashboard.tsx create mode 100644 frontend/src/pages/help/HelpMessages.tsx create mode 100644 frontend/src/pages/help/HelpPayments.tsx create mode 100644 frontend/src/pages/help/HelpPlugins.tsx create mode 100644 frontend/src/pages/help/HelpResources.tsx create mode 100644 frontend/src/pages/help/HelpScheduler.tsx create mode 100644 frontend/src/pages/help/HelpServices.tsx create mode 100644 frontend/src/pages/help/HelpSettingsApi.tsx create mode 100644 frontend/src/pages/help/HelpSettingsAppearance.tsx create mode 100644 frontend/src/pages/help/HelpSettingsAuth.tsx create mode 100644 frontend/src/pages/help/HelpSettingsBilling.tsx create mode 100644 frontend/src/pages/help/HelpSettingsBooking.tsx create mode 100644 frontend/src/pages/help/HelpSettingsDomains.tsx create mode 100644 frontend/src/pages/help/HelpSettingsEmail.tsx create mode 100644 frontend/src/pages/help/HelpSettingsGeneral.tsx create mode 100644 frontend/src/pages/help/HelpSettingsQuota.tsx create mode 100644 frontend/src/pages/help/HelpSettingsResourceTypes.tsx create mode 100644 frontend/src/pages/help/HelpStaff.tsx create mode 100644 frontend/src/pages/help/HelpTasks.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5435406..7ca6eea 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -67,9 +67,33 @@ const HelpTicketing = React.lazy(() => import('./pages/HelpTicketing')); // Impo const HelpApiDocs = React.lazy(() => import('./pages/HelpApiDocs')); // Import API documentation page const HelpPluginDocs = React.lazy(() => import('./pages/HelpPluginDocs')); // Import Plugin documentation page const HelpEmailSettings = React.lazy(() => import('./pages/HelpEmailSettings')); // Import Email settings help page + +// Import new help pages +const HelpDashboard = React.lazy(() => import('./pages/help/HelpDashboard')); +const HelpScheduler = React.lazy(() => import('./pages/help/HelpScheduler')); +const HelpTasks = React.lazy(() => import('./pages/help/HelpTasks')); +const HelpCustomers = React.lazy(() => import('./pages/help/HelpCustomers')); +const HelpServices = React.lazy(() => import('./pages/help/HelpServices')); +const HelpResources = React.lazy(() => import('./pages/help/HelpResources')); +const HelpStaff = React.lazy(() => import('./pages/help/HelpStaff')); +const HelpMessages = React.lazy(() => import('./pages/help/HelpMessages')); +const HelpPayments = React.lazy(() => import('./pages/help/HelpPayments')); +const HelpPlugins = React.lazy(() => import('./pages/help/HelpPlugins')); +const HelpCreatePlugin = React.lazy(() => import('./pages/help/HelpCreatePlugin')); +const HelpSettingsGeneral = React.lazy(() => import('./pages/help/HelpSettingsGeneral')); +const HelpSettingsResourceTypes = React.lazy(() => import('./pages/help/HelpSettingsResourceTypes')); +const HelpSettingsBooking = React.lazy(() => import('./pages/help/HelpSettingsBooking')); +const HelpSettingsAppearance = React.lazy(() => import('./pages/help/HelpSettingsAppearance')); +const HelpSettingsEmail = React.lazy(() => import('./pages/help/HelpSettingsEmail')); +const HelpSettingsDomains = React.lazy(() => import('./pages/help/HelpSettingsDomains')); +const HelpSettingsApi = React.lazy(() => import('./pages/help/HelpSettingsApi')); +const HelpSettingsAuth = React.lazy(() => import('./pages/help/HelpSettingsAuth')); +const HelpSettingsBilling = React.lazy(() => import('./pages/help/HelpSettingsBilling')); +const HelpSettingsQuota = React.lazy(() => import('./pages/help/HelpSettingsQuota')); const PlatformSupport = React.lazy(() => import('./pages/PlatformSupport')); // Import Platform Support page (for businesses to contact SmoothSchedule) const PluginMarketplace = React.lazy(() => import('./pages/PluginMarketplace')); // Import Plugin Marketplace page const MyPlugins = React.lazy(() => import('./pages/MyPlugins')); // Import My Plugins page +const CreatePlugin = React.lazy(() => import('./pages/CreatePlugin')); // Import Create Plugin page const Tasks = React.lazy(() => import('./pages/Tasks')); // Import Tasks page for scheduled plugin executions const EmailTemplates = React.lazy(() => import('./pages/EmailTemplates')); // Import Email Templates page @@ -592,8 +616,30 @@ const AppContent: React.FC = () => { } /> } /> } /> - } /> + } /> } /> + {/* New help pages */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> { ) } /> + + ) : ( + + ) + } + /> = ({ helpPath, className = '' }) => { + const { t } = useTranslation(); + + return ( + + + {t('common.help', 'Help')} + + ); +}; + +export default HelpButton; diff --git a/frontend/src/pages/CreatePlugin.tsx b/frontend/src/pages/CreatePlugin.tsx new file mode 100644 index 0000000..e4991b2 --- /dev/null +++ b/frontend/src/pages/CreatePlugin.tsx @@ -0,0 +1,568 @@ +/** + * Create Plugin Page + * + * Allows businesses to create custom plugins with code editor, + * category selection, and visibility options. + */ + +import React, { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + Code, + Save, + Eye, + EyeOff, + ArrowLeft, + Info, + CheckCircle, + AlertTriangle, + Mail, + BarChart3, + Users, + Calendar, + Link as LinkIcon, + Bot, + Package, + Image, + HelpCircle, +} from 'lucide-react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import api from '../api/client'; +import { PluginCategory } from '../types'; +import { usePlanFeatures } from '../hooks/usePlanFeatures'; + +// Category icon mapping +const categoryIcons: Record = { + EMAIL: , + REPORTS: , + CUSTOMER: , + BOOKING: , + INTEGRATION: , + AUTOMATION: , + OTHER: , +}; + +// Category descriptions +const categoryDescriptions: Record = { + EMAIL: 'Email notifications and automated messaging', + REPORTS: 'Analytics, reports, and data exports', + CUSTOMER: 'Customer engagement and retention', + BOOKING: 'Scheduling and booking automation', + INTEGRATION: 'Third-party service integrations', + AUTOMATION: 'General business automation', + OTHER: 'Miscellaneous plugins', +}; + +// Default plugin code template +const DEFAULT_PLUGIN_CODE = `# My Custom Plugin +# +# This plugin runs on a schedule and can interact with your business data. +# Use template variables to make your plugin configurable. +# +# Available template variables: +# {{PROMPT:variable_name:default_value:description}} +# {{CONTEXT:context_type}} - Access business context (CUSTOMERS, EVENTS, etc.) +# {{DATE:format}} - Current date in specified format + +# Example: Get all customers who haven't booked in 30 days +inactive_days = int("{{PROMPT:inactive_days:30:Days of inactivity}}") + +# Access customer data +customers = {{CONTEXT:CUSTOMERS}} + +# Filter inactive customers +from datetime import datetime, timedelta +cutoff_date = datetime.now() - timedelta(days=inactive_days) + +inactive_customers = [ + c for c in customers + if not c.get('last_booking') or + datetime.fromisoformat(c['last_booking']) < cutoff_date +] + +# Return results (will be logged) +result = { + 'inactive_count': len(inactive_customers), + 'customers': inactive_customers[:10], # First 10 for preview + 'message': f"Found {len(inactive_customers)} inactive customers" +} +`; + +interface FormData { + name: string; + shortDescription: string; + description: string; + category: PluginCategory; + pluginCode: string; + version: string; + logoUrl: string; + visibility: 'PRIVATE' | 'PUBLIC'; +} + +const CreatePlugin: React.FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const { canUse } = usePlanFeatures(); + + const [formData, setFormData] = useState({ + name: '', + shortDescription: '', + description: '', + category: 'AUTOMATION', + pluginCode: DEFAULT_PLUGIN_CODE, + version: '1.0.0', + logoUrl: '', + visibility: 'PRIVATE', + }); + + const [showPreview, setShowPreview] = useState(false); + const [extractedVariables, setExtractedVariables] = useState([]); + + // Extract template variables from code + const extractVariables = useCallback((code: string) => { + const promptPattern = /\{\{PROMPT:([^:}]+):([^:}]*):([^}]*)\}\}/g; + const contextPattern = /\{\{CONTEXT:([^}]+)\}\}/g; + const datePattern = /\{\{DATE:([^}]+)\}\}/g; + + const variables: any[] = []; + let match; + + while ((match = promptPattern.exec(code)) !== null) { + variables.push({ + type: 'PROMPT', + name: match[1], + default: match[2], + description: match[3], + }); + } + + while ((match = contextPattern.exec(code)) !== null) { + variables.push({ + type: 'CONTEXT', + name: match[1], + }); + } + + while ((match = datePattern.exec(code)) !== null) { + variables.push({ + type: 'DATE', + format: match[1], + }); + } + + return variables; + }, []); + + // Update extracted variables when code changes + const handleCodeChange = (e: React.ChangeEvent) => { + const newCode = e.target.value; + setFormData(prev => ({ ...prev, pluginCode: newCode })); + setExtractedVariables(extractVariables(newCode)); + }; + + // Create plugin mutation + const createMutation = useMutation({ + mutationFn: async (data: FormData) => { + const payload = { + name: data.name, + short_description: data.shortDescription, + description: data.description, + category: data.category, + plugin_code: data.pluginCode, + version: data.version, + logo_url: data.logoUrl || undefined, + visibility: data.visibility, + }; + const response = await api.post('/plugin-templates/', payload); + return response.data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ['plugin-templates'] }); + navigate('/plugins/my-plugins'); + }, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + createMutation.mutate(formData); + }; + + // Check if user can create plugins + const canCreatePlugins = canUse('can_create_plugins'); + + if (!canCreatePlugins) { + return ( +
+
+ +

+ {t('plugins.upgradeRequired', 'Upgrade Required')} +

+

+ {t('plugins.upgradeToCreate', 'Plugin creation is available on higher-tier plans. Upgrade your subscription to create custom plugins.')} +

+ +
+
+ ); + } + + return ( +
+ {/* Header */} +
+ +
+
+

+ + {t('plugins.createPlugin', 'Create Custom Plugin')} +

+

+ {t('plugins.createPluginDescription', 'Build a custom automation for your business')} +

+
+ + + {t('plugins.viewDocs', 'View Documentation')} + +
+
+ +
+
+ {/* Left Column - Basic Info */} +
+ {/* Plugin Name */} +
+

+ {t('plugins.basicInfo', 'Basic Information')} +

+ +
+
+ + setFormData(prev => ({ ...prev, name: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500" + placeholder="e.g., Win Back Inactive Customers" + required + /> +
+ +
+ + setFormData(prev => ({ ...prev, shortDescription: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-brand-500" + placeholder="Brief summary for marketplace listing" + maxLength={200} + required + /> +

{formData.shortDescription.length}/200

+
+ +
+ +