feat: Add SMTP settings and collapsible email configuration UI
- Add SMTP fields to TicketEmailSettings model (host, port, TLS/SSL, credentials, from email/name) - Update serializers with SMTP fields and is_smtp_configured flag - Add TicketEmailTestSmtpView for testing SMTP connections - Update frontend API types and hooks for SMTP settings - Add collapsible IMAP and SMTP configuration sections with "Configured" badges - Fix TypeScript errors in mockData.ts (missing required fields, type mismatches) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
143
frontend/public/plugin-logos/generate_icons.py
Normal file
143
frontend/public/plugin-logos/generate_icons.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate plugin icons using Gemini 2.5 Flash Image API.
|
||||
Based on: https://ai.google.dev/gemini-api/docs/image-generation
|
||||
"""
|
||||
from google import genai
|
||||
from PIL import Image
|
||||
import os
|
||||
import time
|
||||
|
||||
# API Key
|
||||
client = genai.Client(api_key="AIzaSyBF3_KnttSerq2BK2CaRKSTJHN8BoXp6Hw")
|
||||
|
||||
OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Plugin configurations with improved prompts
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background transitioning from deep indigo (#4338ca) to vibrant blue (#3b82f6).
|
||||
|
||||
Central element: A stylized white clipboard or document with 3 horizontal lines representing a list, and a small clock or sun symbol in the top right corner indicating "daily".
|
||||
|
||||
Design principles: Minimalist, clean lines, no text, suitable for a scheduling/calendar application. The icon should be instantly recognizable as "daily summary" or "daily report" at small sizes like 32x32.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background from deep red (#b91c1c) to coral red (#ef4444).
|
||||
|
||||
Central element: A white calendar or appointment card with a prominent empty circle or "absent" symbol - perhaps a chair silhouette with a question mark, or a clock with a slash through it indicating a missed appointment.
|
||||
|
||||
Design principles: Minimalist, clean lines, no text. The icon should clearly communicate "missed" or "no-show" at a glance. Professional warning aesthetic without being alarming.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background from magenta (#c026d3) to pink (#ec4899).
|
||||
|
||||
Central element: A white birthday cake with 2-3 simple candles, or a wrapped gift box with a ribbon bow. Add subtle confetti dots or small stars around it for a celebratory feel.
|
||||
|
||||
Design principles: Minimalist, cheerful but professional, clean lines, no text. Should feel warm and celebratory while still fitting a business application aesthetic.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background from emerald green (#059669) to teal (#14b8a6).
|
||||
|
||||
Central element: A white line chart showing an upward trend with 3-4 data points connected by lines, or ascending bar chart with dollar sign integrated subtly. The graph should clearly show growth/success.
|
||||
|
||||
Design principles: Minimalist, professional finance/analytics aesthetic, clean lines, no text. Should instantly communicate "revenue" and "growth" at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background from amber (#d97706) to orange (#f97316).
|
||||
|
||||
Central element: A white notification bell with a small circular badge, combined with a "24" numeral or a clock face showing time. The bell should be the primary focus with the time element secondary.
|
||||
|
||||
Design principles: Minimalist, attention-grabbing but professional, clean lines, no text except possibly "24". Should clearly indicate "reminder" and "advance notice" at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''Generate a professional app icon, 512x512 pixels, square with rounded corners.
|
||||
|
||||
Style: Modern gradient background from violet (#7c3aed) to purple (#a855f7).
|
||||
|
||||
Central element: A white person silhouette or user icon with a curved "return" arrow circling back to them, or a magnet symbol attracting a small person icon. Should convey "bringing customers back".
|
||||
|
||||
Design principles: Minimalist, warm and welcoming professional aesthetic, clean lines, no text. Should communicate "re-engagement" or "win back" at a glance.'''
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def generate_logo(prompt: str, filename: str) -> bool:
|
||||
"""Generate a logo using Gemini 2.5 Flash Image (per official docs)"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
try:
|
||||
# Using the exact format from Google's documentation
|
||||
response = client.models.generate_content(
|
||||
model='gemini-2.5-flash-image',
|
||||
contents=[prompt],
|
||||
)
|
||||
|
||||
# Process response per docs
|
||||
for part in response.parts:
|
||||
if part.inline_data is not None:
|
||||
# Get the raw image data and convert to PIL
|
||||
from io import BytesIO
|
||||
image_data = part.inline_data.data
|
||||
pil_image = Image.open(BytesIO(image_data))
|
||||
|
||||
# Resize to 256x256 for consistency
|
||||
if pil_image.size != (256, 256):
|
||||
pil_image = pil_image.resize((256, 256), Image.Resampling.LANCZOS)
|
||||
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
pil_image.save(output_path, 'PNG')
|
||||
print(f" ✓ Saved: {output_path}")
|
||||
return True
|
||||
elif part.text is not None:
|
||||
print(f" Model text: {part.text[:100]}...")
|
||||
|
||||
print(" ✗ No image in response")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "RESOURCE_EXHAUSTED" in error_msg or "quota" in error_msg.lower():
|
||||
print(f" ✗ Quota exceeded - try again later")
|
||||
else:
|
||||
print(f" ✗ Error: {type(e).__name__}: {error_msg[:300]}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("Generating Plugin Icons with Gemini 2.5 Flash Image")
|
||||
print("=" * 60)
|
||||
|
||||
success_count = 0
|
||||
for i, plugin in enumerate(plugins):
|
||||
if i > 0:
|
||||
print("\nWaiting 5 seconds...")
|
||||
time.sleep(5)
|
||||
|
||||
if generate_logo(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Complete: {success_count}/{len(plugins)} icons generated")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user