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>
This commit is contained in:
194
frontend/public/plugin-logos/generate_with_gemini.py
Normal file
194
frontend/public/plugin-logos/generate_with_gemini.py
Normal file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
# Gemini API configuration
|
||||
API_KEY = "AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw"
|
||||
API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent"
|
||||
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
|
||||
# Plugin configurations
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Daily Appointment Summary Email".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Indigo/blue (#4f46e5)
|
||||
- Icon should combine email and calendar/scheduling concepts
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional and trustworthy appearance
|
||||
|
||||
The icon represents a plugin that sends daily email summaries of appointments to staff members.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "No-Show Customer Tracker".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Red (#dc2626)
|
||||
- Icon should represent missed appointments or absent customers
|
||||
- Could show a person silhouette with an X, slash, or cancel symbol
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional appearance
|
||||
|
||||
The icon represents a plugin that tracks customers who miss their appointments.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Birthday Greeting Campaign".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Pink (#ec4899)
|
||||
- Icon should represent birthdays and celebrations
|
||||
- Could show a birthday cake, gift, party hat, or balloon
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Friendly and celebratory appearance
|
||||
|
||||
The icon represents a plugin that sends birthday emails with special offers to customers.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Monthly Revenue Report".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Green (#10b981)
|
||||
- Icon should represent business growth, analytics, and financial reporting
|
||||
- Could show an upward trending chart, graph, or money symbol
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional and successful appearance
|
||||
|
||||
The icon represents a plugin that generates comprehensive monthly business statistics and revenue reports.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Appointment Reminder (24hr)".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Amber/Orange (#f59e0b)
|
||||
- Icon should represent notifications, alerts, and reminders
|
||||
- Could show a bell with a notification badge, clock, or alarm
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Attention-grabbing but professional appearance
|
||||
|
||||
The icon represents a plugin that sends reminder emails to customers 24 hours before their appointments.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Inactive Customer Re-engagement".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Purple (#8b5cf6)
|
||||
- Icon should represent customer retention, returning customers, or re-engagement
|
||||
- Could show a heart, person with return arrow, refresh symbol, or comeback concept
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Warm and welcoming appearance
|
||||
|
||||
The icon represents a plugin that wins back customers who haven't booked appointments recently.'''
|
||||
}
|
||||
]
|
||||
|
||||
def generate_image(prompt, filename):
|
||||
"""Generate an image using Gemini API"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"contents": [{
|
||||
"parts": [{
|
||||
"text": prompt
|
||||
}]
|
||||
}],
|
||||
"generationConfig": {
|
||||
"temperature": 1,
|
||||
"topK": 40,
|
||||
"topP": 0.95,
|
||||
"maxOutputTokens": 8192,
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{API_URL}?key={API_KEY}",
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
|
||||
# Check if there's an image in the response
|
||||
if 'candidates' in result and len(result['candidates']) > 0:
|
||||
candidate = result['candidates'][0]
|
||||
if 'content' in candidate and 'parts' in candidate['content']:
|
||||
for part in candidate['content']['parts']:
|
||||
if 'inlineData' in part:
|
||||
# Extract and save the image
|
||||
image_data = base64.b64decode(part['inlineData']['data'])
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
print(f"✓ Saved: {output_path}")
|
||||
return True
|
||||
elif 'text' in part:
|
||||
print(f"Response: {part['text'][:200]}...")
|
||||
|
||||
print(f"✗ No image generated. Response: {result}")
|
||||
return False
|
||||
else:
|
||||
print(f"✗ API Error ({response.status_code}): {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
# Create output directory
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print("Starting logo generation with Gemini API...")
|
||||
print("=" * 60)
|
||||
|
||||
success_count = 0
|
||||
for plugin in plugins:
|
||||
if generate_image(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Generation complete: {success_count}/{len(plugins)} successful")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user