Files
smoothschedule/frontend/public/plugin-logos/generate_plugin_logos.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

156 lines
4.8 KiB
Python

import os
import sys
try:
from PIL import Image, ImageDraw, ImageFont
except ImportError:
print("Error: Pillow library not found.")
print("Please install it using: pip install Pillow")
sys.exit(1)
# Configuration
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
SIZE = (144, 144)
RADIUS = 30
def create_base_logo(bg_color):
"""Creates the base image with rounded corners and background color."""
img = Image.new('RGBA', SIZE, (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
rect_bounds = [0, 0, SIZE[0], SIZE[1]]
draw.rounded_rectangle(rect_bounds, radius=RADIUS, fill=bg_color)
return img, draw
def save_logo(img, filename):
"""Saves the image to the output directory."""
path = os.path.join(OUTPUT_DIR, filename)
os.makedirs(os.path.dirname(path), exist_ok=True)
img.save(path)
print(f"Saved: {path}")
# 1. No-Show Customer Tracker (Red, Person with X)
def create_no_show_logo():
img, draw = create_base_logo("#dc2626") # Red
# Person Silhouette
cx, cy = 72, 72
# Head
draw.ellipse([cx-15, 35, cx+15, 65], fill="white")
# Shoulders/Body
draw.rounded_rectangle([cx-30, 70, cx+30, 130], radius=10, fill="white")
# Cancel Symbol (Red X on white circle or just overlaid)
# Let's use a thick Red X overlay
x_center_y = 85
half = 20
width = 8
# Draw X
draw.line([cx - half, x_center_y - half, cx + half, x_center_y + half], fill="#dc2626", width=width)
draw.line([cx + half, x_center_y - half, cx - half, x_center_y + half], fill="#dc2626", width=width)
save_logo(img, "no-show-tracker.png")
# 2. Birthday Greeting Campaign (Pink, Gift)
def create_birthday_logo():
img, draw = create_base_logo("#ec4899") # Pink
# Gift Box
box_w, box_h = 70, 60
box_x = (SIZE[0] - box_w) // 2
box_y = (SIZE[1] - box_h) // 2 + 10
# Box body
draw.rectangle([box_x, box_y, box_x + box_w, box_y + box_h], fill="white")
# Ribbons (Lighter Pink)
r_w = 12
ribbon_color = "#fbcfe8"
# Vertical
draw.rectangle([box_x + (box_w - r_w)//2, box_y, box_x + (box_w + r_w)//2, box_y + box_h], fill=ribbon_color)
# Horizontal
draw.rectangle([box_x, box_y + (box_h - r_w)//2, box_x + box_w, box_y + (box_h + r_w)//2], fill=ribbon_color)
# Bow
bow_w = 20
draw.ellipse([72 - bow_w, box_y - 15, 72, box_y], fill="white")
draw.ellipse([72, box_y - 15, 72 + bow_w, box_y], fill="white")
save_logo(img, "birthday-greetings.png")
# 3. Monthly Revenue Report (Green, Chart)
def create_revenue_logo():
img, draw = create_base_logo("#10b981") # Green
# Bar Chart
margin_bottom = 110
bar_w = 20
gap = 10
start_x = (144 - (3 * bar_w + 2 * gap)) // 2
# Bars
heights = [30, 50, 75]
for i, h in enumerate(heights):
x = start_x + i * (bar_w + gap)
draw.rectangle([x, margin_bottom - h, x + bar_w, margin_bottom], fill="white")
# Trend Arrow (Rising)
arrow_start = (start_x - 5, margin_bottom - 10)
arrow_end = (start_x + 3 * bar_w + 2 * gap + 5, margin_bottom - 90)
# Simple line for trend
# draw.line([arrow_start, arrow_end], fill="white", width=4)
save_logo(img, "monthly-revenue-report.png")
# 4. Appointment Reminder (24hr) (Amber, Bell)
def create_reminder_logo():
img, draw = create_base_logo("#f59e0b") # Amber
cx, cy = 72, 70
r = 30
# Bell Dome
draw.chord([cx - r, cy - r, cx + r, cy + r], 180, 0, fill="white")
draw.rectangle([cx - r, cy, cx + r, cy + 20], fill="white")
# Bell Flare
draw.polygon([(cx - r, cy + 20), (cx + r, cy + 20), (cx + r + 5, cy + 30), (cx - r - 5, cy + 30)], fill="white")
# Clapper
draw.ellipse([cx - 8, cy + 28, cx + 8, cy + 42], fill="white")
# Notification Dot
draw.ellipse([cx + 15, cy - 25, cx + 35, cy - 5], fill="#dc2626")
save_logo(img, "appointment-reminder-24hr.png")
# 5. Inactive Customer Re-engagement (Purple, Heart)
def create_inactive_logo():
img, draw = create_base_logo("#8b5cf6") # Purple
hx, hy = 72, 75
size = 18
# Heart Shape
# Left circle
draw.ellipse([hx - size * 2, hy - size, hx, hy + size], fill="white")
# Right circle
draw.ellipse([hx, hy - size, hx + size * 2, hy + size], fill="white")
# Bottom triangle
draw.polygon([(hx - size * 2 + 1, hy + 4), (hx + size * 2 - 1, hy + 4), (hx, hy + size * 2 + 8)], fill="white")
# Refresh/Loop Arrow (Subtle arc above)
bbox = [25, 25, 119, 119]
draw.arc(bbox, start=180, end=0, fill="#ddd6fe", width=5)
draw.polygon([(119, 72), (110, 60), (128, 60)], fill="#ddd6fe")
save_logo(img, "inactive-customer-reengagement.png")
if __name__ == "__main__":
create_no_show_logo()
create_birthday_logo()
create_revenue_logo()
create_reminder_logo()
create_inactive_logo()