development #18

Merged
erman merged 3 commits from development into main 2025-07-12 22:01:27 +02:00
3 changed files with 52 additions and 9 deletions
Showing only changes of commit a8f1b973e6 - Show all commits

View File

@@ -16,6 +16,16 @@ interface EmailResult {
error?: string; error?: string;
} }
/**
* Replaces template variables in a string with participant data
* Currently supports {name} and {surname} placeholders
*/
function replaceTemplateVariables(template: string, participant: Participant): string {
return template
.replace(/{name}/gi, participant.name || '')
.replace(/{surname}/gi, participant.surname || '');
}
async function generateQRCode(participantId: string): Promise<string> { async function generateQRCode(participantId: string): Promise<string> {
const qrCodeBase64 = await QRCode.toDataURL(participantId, { const qrCodeBase64 = await QRCode.toDataURL(participantId, {
type: 'image/png', type: 'image/png',
@@ -38,11 +48,15 @@ async function sendEmailToParticipant(
try { try {
const qrCodeBase64Data = await generateQRCode(participant.id); const qrCodeBase64Data = await generateQRCode(participant.id);
// Replace template variables in subject and body
const personalizedSubject = replaceTemplateVariables(subject, participant);
const personalizedText = replaceTemplateVariables(text, participant);
// Send email with QR code // Send email with QR code
await sendGmail(refreshToken, { await sendGmail(refreshToken, {
to: participant.email, to: participant.email,
subject: subject, subject: personalizedSubject,
text: text, text: personalizedText,
qr_code: qrCodeBase64Data qr_code: qrCodeBase64Data
}); });

View File

@@ -6,38 +6,69 @@
}; };
errors: Record<string, string>; errors: Record<string, string>;
}>(); }>();
const templateVariables = [
{ name: '{name}', description: "Participant's first name" },
{ name: '{surname}', description: "Participant's last name" }
];
const subjectTemplatesDetected = $derived(
templateVariables.filter((v) => emailData.subject && emailData.subject.includes(v.name))
);
const bodyTemplatesDetected = $derived(
templateVariables.filter((v) => emailData.body && emailData.body.includes(v.name))
);
</script> </script>
<div class="space-y-6"> <div class="space-y-6">
<div> <div>
<label for="emailSubject" class="block text-sm font-medium text-gray-700 mb-2"> <label for="emailSubject" class="mb-2 block text-sm font-medium text-gray-700">
Email Subject * Email Subject *
</label> </label>
<input <input
id="emailSubject" id="emailSubject"
type="text" type="text"
bind:value={emailData.subject} bind:value={emailData.subject}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" class="w-full rounded border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
placeholder="Event invitation subject" placeholder="Event invitation subject"
/> />
{#if subjectTemplatesDetected.length > 0}
<p class="mt-1 text-xs text-gray-500">
Detected templates: {subjectTemplatesDetected.map((v) => v.name).join(', ')}
</p>
{/if}
{#if errors.subject} {#if errors.subject}
<p class="mt-1 text-sm text-red-600">{errors.subject}</p> <p class="text-sm text-red-600">{errors.subject}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="emailBody" class="block text-sm font-medium text-gray-700 mb-2"> <label for="emailBody" class="mb-2 block text-sm font-medium text-gray-700">
Email Body * Email Body *
</label> </label>
<textarea <textarea
id="emailBody" id="emailBody"
bind:value={emailData.body} bind:value={emailData.body}
rows="8" rows="8"
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" class="w-full rounded border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
placeholder="Email message content..." placeholder="Email message content..."
></textarea> ></textarea>
{#if bodyTemplatesDetected.length > 0}
<p class="text-xs text-gray-500">
Detected templates: {bodyTemplatesDetected.map((v) => v.name).join(', ')}
</p>
{/if}
{#if errors.body} {#if errors.body}
<p class="mt-1 text-sm text-red-600">{errors.body}</p> <p class="mt-1 text-sm text-red-600">{errors.body}</p>
{/if} {/if}
</div> </div>
<div>
<p class="mt-2 mb-2 block text-sm font-medium text-gray-700">Tip:</p>
<p class="text-xs text-gray-500">
Use <code class="rounded bg-gray-100 px-1 py-0.5 text-xs">&#123;name&#125;</code> and
<code class="rounded bg-gray-100 px-1 py-0.5 text-xs">&#123;surname&#125;</code> to personalize
your message. Works for both subject and body. (e.g., "Hello &#123;name&#125;, welcome to our event!")
</p>
</div>
</div> </div>

View File

@@ -16,8 +16,6 @@ export interface Event {
export async function getEvents(supabase: SupabaseClient, searchTerm: string = '') { export async function getEvents(supabase: SupabaseClient, searchTerm: string = '') {
try { try {
const searchPattern = searchTerm.trim() ? `%${searchTerm}%` : null; const searchPattern = searchTerm.trim() ? `%${searchTerm}%` : null;
console.log('Actually fetching!');
// Build regular events query // Build regular events query
let regularQuery = supabase let regularQuery = supabase