Files
smoothschedule/smoothschedule/schedule/management/commands/seed_platform_plugins.py
poduck 9b106bf129 feat: Add plugin configuration editing with template variable parsing
## Backend Changes:
- Enhanced PluginTemplate.save() to auto-parse template variables from plugin code
- Updated PluginInstallationSerializer to expose template metadata (description, category, version, author, logo, template_variables)
- Fixed template variable parser to handle nested {{ }} braces in default values
- Added brace-counting algorithm to properly extract variables with insertion codes
- Fixed explicit type parameter detection (textarea, text, email, etc.)
- Made scheduled_task optional on PluginInstallation model
- Added EventPlugin through model for event-plugin relationships
- Added Event.execute_plugins() method for plugin automation

## Frontend Changes:
- Created Tasks.tsx page for managing scheduled tasks
- Enhanced MyPlugins page with clickable plugin cards
- Added edit configuration modal with dynamic form generation
- Implemented escape sequence handling (convert \n, \', etc. for display)
- Added plugin logos to My Plugins page
- Updated type definitions for PluginInstallation interface
- Added insertion code documentation to Plugin Docs

## Plugin System:
- All platform plugins now have editable email templates with textarea support
- Template variables properly parsed with full default values
- Insertion codes ({{CUSTOMER_NAME}}, {{BUSINESS_NAME}}, etc.) documented
- Plugin logos displayed in marketplace and My Plugins

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:45:55 -05:00

354 lines
13 KiB
Python

from django.core.management.base import BaseCommand
from django.utils import timezone
from schedule.models import PluginTemplate
class Command(BaseCommand):
help = 'Seed platform-owned plugins into the database'
def handle(self, *args, **options):
plugins_data = [
{
'name': 'Daily Appointment Summary Email',
'slug': 'daily-appointment-summary',
'category': PluginTemplate.Category.EMAIL,
'short_description': 'Send daily email summary of appointments',
'description': '''Stay on top of your schedule with automated daily appointment summaries.
This plugin sends a comprehensive email digest every morning with:
- List of all appointments for the day
- Customer names and contact information
- Service details and duration
- Staff/resource assignments
- Any special notes or requirements
Perfect for managers and staff who want to start their day informed and prepared.''',
'plugin_code': '''from datetime import datetime, timedelta
# Get today's appointments
today = datetime.now().date()
appointments = api.get_appointments(
start_date=today.isoformat(),
end_date=today.isoformat()
)
# Format the appointment list
summary = f"Daily Appointment Summary - {today}\\n\\n"
summary += f"Total Appointments: {len(appointments)}\\n\\n"
for apt in appointments:
summary += f"- {apt['title']} at {apt['start_time']}\\n"
summary += f" Status: {apt['status']}\\n\\n"
# Get customizable email settings
staff_email = '{{PROMPT:staff_email|Staff Email}}'
email_subject = '{{PROMPT:email_subject|Email Subject|Daily Appointment Summary - {{TODAY}}}}'
# Send email
api.send_email(
to=staff_email,
subject=email_subject,
body=summary
)
''',
'logo_url': '/plugin-logos/daily-appointment-summary.png',
},
{
'name': 'No-Show Customer Tracker',
'slug': 'no-show-tracker',
'category': PluginTemplate.Category.REPORTS,
'short_description': 'Track customers who miss appointments',
'description': '''Identify patterns of missed appointments and reduce no-shows.
This plugin automatically tracks and reports on:
- Customers who didn\'t show up for scheduled appointments
- Frequency of no-shows per customer
- Total revenue lost due to missed appointments
- Trends over time
Helps you identify customers who may need reminder calls or deposits, improving your booking efficiency and revenue.''',
'plugin_code': '''from datetime import datetime, timedelta
# Get configuration
days_back = int('{{PROMPT:days_back|Days to Look Back|7}}')
week_ago = (datetime.now() - timedelta(days=days_back)).date()
today = datetime.now().date()
# Get appointments with NOSHOW status
appointments = api.get_appointments(
start_date=week_ago.isoformat(),
end_date=today.isoformat(),
status='NOSHOW'
)
# Count no-shows per customer
customer_noshows = {}
for apt in appointments:
customer_id = apt.get('customer_id')
if customer_id:
customer_noshows[customer_id] = customer_noshows.get(customer_id, 0) + 1
# Generate report
report = f"No-Show Report ({week_ago} to {today})\\n\\n"
report += f"Total No-Shows: {len(appointments)}\\n"
report += f"Unique Customers: {len(customer_noshows)}\\n\\n"
report += "Top Offenders:\\n"
for customer_id, count in sorted(customer_noshows.items(), key=lambda x: x[1], reverse=True)[:10]:
report += f"- Customer {customer_id}: {count} no-shows\\n"
# Get customizable email settings
manager_email = '{{PROMPT:manager_email|Manager Email}}'
email_subject = '{{PROMPT:email_subject|Email Subject|No-Show Report}}'
# Send report
api.send_email(
to=manager_email,
subject=email_subject,
body=report
)
''',
'logo_url': '/plugin-logos/no-show-tracker.png',
},
{
'name': 'Birthday Greeting Campaign',
'slug': 'birthday-greetings',
'category': PluginTemplate.Category.CUSTOMER,
'short_description': 'Send birthday emails with offers',
'description': '''Delight your customers with personalized birthday greetings and special offers.
This plugin automatically:
- Identifies customers with birthdays today
- Sends personalized birthday emails
- Includes custom discount codes or special offers
- Helps drive repeat bookings and customer loyalty
A simple way to show customers you care while encouraging them to book their next appointment.''',
'plugin_code': '''# Get all customers with email addresses
customers = api.get_customers(has_email=True, limit=1000)
# Get customizable email template
discount_code = '{{PROMPT:discount_code|Discount Code}}'
email_subject = '{{PROMPT:email_subject|Email Subject|Happy Birthday!}}'
email_body = '{{PROMPT:email_body|Email Message|Happy Birthday {{CUSTOMER_NAME}}!\n\nWe hope you have a wonderful day! As a special birthday gift, we\'d like to offer you {discount_code} on your next appointment.\n\nBook now and treat yourself!\n\nBest wishes,\n{{BUSINESS_NAME}}||textarea}}'
# Filter for birthdays today (would need birthday field in customer data)
# For now, send to all customers as example
for customer in customers:
# Format email body with discount code
formatted_body = email_body.format(discount_code=discount_code)
api.send_email(
to=customer['email'],
subject=email_subject,
body=formatted_body
)
api.log(f"Sent {len(customers)} birthday greetings")
''',
'logo_url': '/plugin-logos/birthday-greetings.png',
},
{
'name': 'Monthly Revenue Report',
'slug': 'monthly-revenue-report',
'category': PluginTemplate.Category.REPORTS,
'short_description': 'Monthly business statistics',
'description': '''Get comprehensive monthly insights into your business performance.
This plugin generates detailed reports including:
- Total revenue and number of appointments
- Revenue breakdown by service type
- Busiest days and times
- Most popular services
- Customer retention metrics
- Year-over-year comparisons
Perfect for owners and managers who want to track business growth and identify opportunities.''',
'plugin_code': '''from datetime import datetime, timedelta
# Get last month's date range
today = datetime.now()
first_of_this_month = today.replace(day=1)
last_month_end = first_of_this_month - timedelta(days=1)
last_month_start = last_month_end.replace(day=1)
# Get all appointments from last month
appointments = api.get_appointments(
start_date=last_month_start.isoformat(),
end_date=last_month_end.isoformat()
)
# Calculate statistics
total_appointments = len(appointments)
completed = len([a for a in appointments if a['status'] == 'COMPLETED'])
canceled = len([a for a in appointments if a['status'] == 'CANCELED'])
# Generate report
month_name = last_month_start.strftime('%B %Y')
report = f"""Monthly Revenue Report - {month_name}
SUMMARY
-------
Total Appointments: {total_appointments}
Completed: {completed}
Canceled: {canceled}
Completion Rate: {(completed/total_appointments*100):.1f}%
DETAILS
-------
"""
for apt in appointments[:10]: # Show first 10
report += f"- {apt['title']} ({apt['status']})\\n"
# Get customizable email settings
owner_email = '{{PROMPT:owner_email|Owner Email}}'
email_subject = '{{PROMPT:email_subject|Email Subject|Monthly Revenue Report}}'
# Send report
api.send_email(
to=owner_email,
subject=email_subject,
body=report
)
''',
'logo_url': '/plugin-logos/monthly-revenue-report.png',
},
{
'name': 'Appointment Reminder (24hr)',
'slug': 'appointment-reminder-24hr',
'category': PluginTemplate.Category.BOOKING,
'short_description': 'Remind customers 24hrs before appointments',
'description': '''Reduce no-shows with automated appointment reminders.
This plugin sends friendly reminder emails to customers 24 hours before their scheduled appointments, including:
- Appointment date and time
- Service details
- Location/directions
- Custom message or instructions
- Cancellation policy reminder
Studies show that appointment reminders can reduce no-shows by up to 90%.''',
'plugin_code': '''from datetime import datetime, timedelta
# Get appointments 24 hours from now
tomorrow = (datetime.now() + timedelta(days=1)).date()
appointments = api.get_appointments(
start_date=tomorrow.isoformat(),
end_date=tomorrow.isoformat(),
status='SCHEDULED'
)
# Get customizable email template
email_subject = '{{PROMPT:email_subject|Email Subject|Reminder: Your Appointment Tomorrow}}'
email_body = '{{PROMPT:email_body|Email Message|Hi {{CUSTOMER_NAME}},\n\nThis is a friendly reminder about your appointment:\n\nDate/Time: {{APPOINTMENT_TIME}}\nService: {{APPOINTMENT_SERVICE}}\n\nPlease arrive 10 minutes early.\n\nIf you need to cancel or reschedule, please let us know as soon as possible.\n\nBest regards,\n{{BUSINESS_NAME}}||textarea}}'
# Send reminders
for apt in appointments:
customer_id = apt.get('customer_id')
if customer_id:
api.send_email(
to=customer_id,
subject=email_subject,
body=email_body
)
api.log(f"Sent {len(appointments)} appointment reminders")
''',
'logo_url': '/plugin-logos/appointment-reminder-24hr.png',
},
{
'name': 'Inactive Customer Re-engagement',
'slug': 'inactive-customer-reengagement',
'category': PluginTemplate.Category.CUSTOMER,
'short_description': 'Email inactive customers with offers',
'description': '''Win back customers who haven\'t booked in a while.
This plugin automatically identifies customers who haven\'t made an appointment recently and sends them:
- Personalized "we miss you" messages
- Special comeback offers or discounts
- Reminders of services they previously enjoyed
- Easy booking links
Configurable inactivity period (default: 60 days). A proven strategy for increasing customer lifetime value and reducing churn.''',
'plugin_code': '''from datetime import datetime, timedelta
# Get configuration
inactive_days = int('{{PROMPT:inactive_days|Days Inactive|60}}')
discount_code = '{{PROMPT:discount_code|Discount Code}}'
email_subject = '{{PROMPT:email_subject|Email Subject|We Miss You! Come Back Soon}}'
email_body = '{{PROMPT:email_body|Email Message|Hi {{CUSTOMER_NAME}},\n\nWe noticed it\'s been a while since your last visit, and we wanted to reach out.\n\nWe\'d love to see you again! As a special welcome back offer, use code {discount_code} on your next appointment.\n\nBook now and let us take care of you!\n\nBest regards,\n{{BUSINESS_NAME}}||textarea}}'
# Get recent appointments to find active customers
cutoff_date = (datetime.now() - timedelta(days=inactive_days)).date()
recent_appointments = api.get_appointments(
start_date=cutoff_date.isoformat(),
end_date=datetime.now().date().isoformat()
)
# Get active customer IDs
active_customer_ids = set(apt.get('customer_id') for apt in recent_appointments if apt.get('customer_id'))
# Get all customers
all_customers = api.get_customers(has_email=True, limit=1000)
# Find inactive customers and send re-engagement emails
inactive_count = 0
for customer in all_customers:
if customer['id'] not in active_customer_ids:
# Format email body with discount code
formatted_body = email_body.format(discount_code=discount_code)
api.send_email(
to=customer['email'],
subject=email_subject,
body=formatted_body
)
inactive_count += 1
api.log(f"Sent re-engagement emails to {inactive_count} inactive customers")
''',
'logo_url': '/plugin-logos/inactive-customer-reengagement.png',
},
]
created_count = 0
skipped_count = 0
for plugin_data in plugins_data:
# Check if plugin already exists by slug
if PluginTemplate.objects.filter(slug=plugin_data['slug']).exists():
self.stdout.write(
self.style.WARNING(f"Skipping '{plugin_data['name']}' - already exists")
)
skipped_count += 1
continue
# Create the plugin
plugin = PluginTemplate.objects.create(
name=plugin_data['name'],
slug=plugin_data['slug'],
category=plugin_data['category'],
short_description=plugin_data['short_description'],
description=plugin_data['description'],
plugin_code=plugin_data['plugin_code'],
logo_url=plugin_data.get('logo_url', ''),
visibility=PluginTemplate.Visibility.PLATFORM,
is_approved=True,
approved_at=timezone.now(),
author_name='Smooth Schedule',
license_type='PLATFORM',
)
self.stdout.write(
self.style.SUCCESS(f"Created plugin: '{plugin.name}'")
)
created_count += 1
# Summary
self.stdout.write(
self.style.SUCCESS(
f'\nSuccessfully created {created_count} plugin(s), {skipped_count} already existed.'
)
)