1/** @jsxImportSource https://esm.sh/react@18.2.0 */
2import { Calendar, Heart, Image, Mail, Music } from "https://esm.sh/lucide-react@latest";
3import { nanoid } from "https://esm.sh/nanoid@4.0.0";
4import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
11 name: "Romantic Classic",
12 description: "Elegant design with beautiful animations and love counter",
13 previewImage: "https://maxm-imggenurl.web.val.run/romantic-classic",
14 features: ["Love Counter", "Music Player", "Photo Gallery", "Love Letter"],
15 },
18 name: "Modern Love Story",
19 description: "Contemporary design with timeline and story features",
20 previewImage: "https://maxm-imggenurl.web.val.run/modern-story",
21 features: ["Timeline View", "Story Cards", "Photo Grid", "Music Integration"],
22 },
25 name: "Vintage Romance",
26 description: "Nostalgic design with romantic elements and memories",
27 previewImage: "https://maxm-imggenurl.web.val.run/vintage-romance",
28 features: ["Memory Wall", "Love Journal", "Classic Design", "Photo Album"],
29 },
191 musicUrl: "",
192 musicThumbnail: "",
193 images: [],
194 });
195 const [generatedUrl, setGeneratedUrl] = useState(null);
224 }
225 };
226 const handleImageUpload = async (e) => {
227 const files = Array.from(e.target.files);
228
229 if (files.length > 6) {
230 alert("Maximum 6 images allowed");
231 return;
232 }
233
234 try {
235 const base64Images = await Promise.all(files.map(fileToBase64));
236
237 setFormData(prev => ({
238 ...prev,
239 images: base64Images,
240 }));
241 } catch (error) {
242 console.error("Image upload error", error);
243 alert("Failed to upload images");
244 }
245 };
310 >
311 <img
312 src={template.previewImage}
313 alt={template.name}
314 className="w-full h-48 object-cover rounded-lg mb-4"
401 type="file"
402 multiple
403 accept="image/*"
404 onChange={handleImageUpload}
405 className="w-full px-3 py-2 border rounded-md focus:outline-pink-500"
406 />
407 <div className="flex flex-wrap gap-2 mt-2">
408 {formData.images.map((img, index) => (
409 <img
410 key={index}
493 music_thumbnail TEXT,
494 template_type TEXT NOT NULL,
495 images TEXT NOT NULL,
496 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
497 )
510 musicUrl,
511 musicThumbnail,
512 images,
513 shareableId,
514 template,
526 `
527 INSERT INTO ${KEY}_love_templates_${SCHEMA_VERSION}
528 (shareable_id, partner_name, start_date, love_letter, music_url, music_thumbnail, template_type, images)
529 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
530 `,
537 musicThumbnail || "",
538 template,
539 JSON.stringify(images),
540 ],
541 );
582
583 const websiteData = result.rows[0];
584 const parsedImages = JSON.parse(websiteData.images);
585
586 return new Response(
673 <!-- Memory Gallery -->
674 ${
675 parsedImages.length > 0
676 ? `
677 <div class="mt-6 fade-in">
679 <div class="grid grid-cols-3 gap-4 mt-4">
680 ${
681 parsedImages.map(img => `
682 <div class="overflow-hidden rounded-lg shadow-md">
683 <img src="${img}" class="w-full h-32 object-cover transition-transform hover:scale-110" />
731 // Fetch paginated records
732 const result = await sqlite.execute(`
733 SELECT shareable_id, partner_name, start_date, love_letter, music_url, music_thumbnail, template_type, images, created_at
734 FROM ${KEY}_love_templates_${SCHEMA_VERSION}
735 ORDER BY created_at DESC
767 <th>Music Thumbnail</th>
768 <th>Template Type</th>
769 <th>Images</th>
770 <th>Created At</th>
771 </tr>
783 <td><img src="${row.music_thumbnail}" alt="Thumbnail" width="50"></td>
784 <td>${row.template_type}</td>
785 <td>${row.images ? `<img src="${row.images}" width="50">` : "No Image"}</td>
786 <td>${row.created_at}</td>
787 </tr>`;
835 }
836 </style>
837 <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text y='28' font-size='28'>πΉ</text></svg>">
838 </head>
839 <body>