Compare commits

...

3 Commits

Author SHA1 Message Date
Roman Krček
c063c1c5c4 Improved UX for the creation flow 2025-06-24 17:46:35 +02:00
Roman Krček
297641ee2d Disable button if not all conditions are satisfied 2025-06-24 17:30:34 +02:00
Roman Krček
2c2ee18fa3 Remove debugging 2025-06-24 17:21:30 +02:00
5 changed files with 69 additions and 66 deletions

View File

@@ -4,8 +4,6 @@ import Papa from 'papaparse';
import { fail } from '@sveltejs/kit';
export async function load({ locals }) {
console.log("▶️ fetching events…");
const { data: events, error } = await locals.supabase
.from('events')
.select('*')
@@ -23,8 +21,6 @@ export async function load({ locals }) {
export const actions = {
create: async (event) => {
const formData = await event.request.formData();
console.log(Array.from(formData.entries()));
console.log('create_event date', formData.get("date"), formData.get("name"), formData.get("description"));
let { data: new_event, error } = await event.locals.supabase.rpc("create_event",
{
"p_name": formData.get('name'),
@@ -51,8 +47,6 @@ export const actions = {
parsedRows.shift();
}
console.log('Parsed rows:', parsedRows);
// Map each row to an object with keys: name, surname, email
const participants = parsedRows.map((row: string[]) => ({
name: row[0],
@@ -60,8 +54,6 @@ export const actions = {
email: row[2]
}));
console.log('Mapped participants:', participants);
return {
participants,
}

View File

@@ -11,8 +11,8 @@
let participants = $state([]);
let subject = $state('');
let body = $state('');
let authorized = $state(false);
// Update events and participants from the form data
$effect(() => {
if (form && form.new_event) {
new_event = form.new_event;
@@ -31,17 +31,20 @@
StepOverview
];
// State variable for current step
let step = $state(0);
let step: number = $state(0);
let stepConditions = $derived([
authorized,
!!new_event?.name,
!!participants?.length,
!!subject && !!body
]);
// Helper to go to next/previous step (optional)
function nextStep() {
if (step < steps.length - 1) step += 1;
console.log(step);
}
function prevStep() {
if (step > 0) step -= 1;
console.log(step);
}
</script>
@@ -58,16 +61,15 @@
</span>
<button
onclick={nextStep}
disabled={step === steps.length - 1}
disabled={step === steps.length - 1 || !stepConditions[step]}
class="min-w-[100px] py-2 px-4 bg-white border border-gray-300 text-gray-700 rounded hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
Next
</button>
</div>
<!-- Render the current step component -->
{#if step == 0}
<StepConnectGoogle />
<StepConnectGoogle bind:authorized />
{:else if step == 1}
<StepCreateEvent events={data.events} {new_event} />
{:else if step == 2}
@@ -75,5 +77,11 @@
{:else if step == 3}
<StepCraftEmail bind:subject bind:body />
{:else if step == 4}
<StepOverview {new_event} {participants} {subject} {body} />
<StepOverview
{new_event}
{participants}
{subject}
{body}
{stepConditions}
/>
{/if}

View File

@@ -2,8 +2,9 @@
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
export let authorized = false;
let refreshToken = '';
let authorized = false;
let loading = true;
let to = '';
@@ -32,22 +33,6 @@
/* ⇢ redirects straight to Google via server 302 */
const connect = () => goto('/private/api/gmail?action=auth');
async function sendEmail() {
const r = await fetch('/private/api/gmail', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'send',
to,
subject,
text: body,
refreshToken
})
});
r.ok ? alert('Sent!') : alert(await r.text());
to = subject = body = '';
}
async function disconnect() {
if (!confirm('Disconnect Google account?')) return;
await fetch('/private/api/gmail', {

View File

@@ -1,10 +1,10 @@
<script lang="ts">
let { new_event, participants, subject, body } = $props();
let { new_event, participants, subject, body, stepConditions } = $props();
</script>
<!-- New Event Overview -->
<div class="bg-white border border-gray-300 rounded p-4 mb-4">
<h2 class="text-xl font-bold mb-2">Event Overview</h2>
<div class="mb-4 rounded border border-gray-300 bg-white p-4">
<h2 class="mb-2 text-xl font-bold">Event Overview</h2>
<ul class="space-y-1">
<li><span class="font-semibold">Name:</span> {new_event.name}</li>
<li><span class="font-semibold">Date:</span> {new_event.date}</li>
@@ -13,31 +13,50 @@
</div>
<!-- Email Overview -->
<div class="bg-white border border-gray-300 rounded p-4 mb-4">
<h2 class="text-xl font-bold mb-2">Email Preview</h2>
<div class="mb-4 rounded border border-gray-300 bg-white p-4">
<h2 class="mb-2 text-xl font-bold">Email Preview</h2>
<div class="mb-2"><span class="font-semibold">Subject:</span> {subject}</div>
<div class="whitespace-pre-line border rounded p-2 bg-gray-50 text-gray-700"><span class="font-semibold"></span>
<div class="rounded border bg-gray-50 p-2 whitespace-pre-line text-gray-700">
<span class="font-semibold"></span>
<div>{body}</div>
</div>
</div>
<!-- Participants Overview -->
<div class="bg-white border border-gray-300 rounded p-4">
<h2 class="text-xl font-bold mb-2">Participants ({participants.length})</h2>
<div class="rounded border border-gray-300 bg-white p-4">
<h2 class="mb-2 text-xl font-bold">Participants ({participants.length})</h2>
<ul class="space-y-1">
{#each participants.slice(0, 10) as p}
<li class="flex items-center gap-2 border-b last:border-b-0 pb-1">
<li class="flex items-center gap-2 border-b pb-1 last:border-b-0">
<span class="font-semibold">{p.name} {p.surname}</span>
<span class="flex-1"></span>
<span class="font-mono text-xs text-gray-600 text-right">{p.email}</span>
<span class="text-right font-mono text-xs text-gray-600">{p.email}</span>
</li>
{/each}
</ul>
<p class="text-sm text-gray-500 mt-2">Note: Only the first 10 participants are shown.</p>
<p class="mt-2 text-sm text-gray-500">Note: Only the first 10 participants are shown.</p>
</div>
<button
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded mt-4 transition-colors duration-200"
class="mt-4 w-full rounded bg-blue-600 px-4 py-3 font-bold text-white
transition-colors duration-200 hover:bg-blue-700
disabled:cursor-not-allowed disabled:bg-gray-300 disabled:text-gray-500"
disabled={!stepConditions.every(Boolean)}
>
Generate QR codes and send
</button>
<div class="mt-2 space-y-1">
{#if !stepConditions[0]}
<p class="text-sm text-red-500">Please provide an event name before proceeding.</p>
{/if}
{#if !stepConditions[1]}
<p class="text-sm text-red-500">Please add at least one participant before proceeding.</p>
{/if}
{#if !stepConditions[2]}
<p class="text-sm text-red-500">Please provide an email subject before proceeding.</p>
{/if}
{#if !stepConditions[3]}
<p class="text-sm text-red-500">Please provide an email body before proceeding.</p>
{/if}
</div>

View File

@@ -12,7 +12,6 @@
$effect(() => {
if (scanned_id === "") return;
console.log('New QR code found:', scanned_id);
scan_state = ScanState.scanning;
data.supabase