From c2949e4bfeff1fbb09aa8a8395960a3a2b1b80f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Kr=C4=8Dek?= Date: Wed, 2 Jul 2025 23:23:15 +0200 Subject: [PATCH] Create event creation structuring and polishing --- .../private/events/event/new/+page.server.ts | 7 - .../private/events/event/new/+page.svelte | 524 +++--------------- .../new/components/EmailSettingsStep.svelte | 43 ++ .../new/components/EventDetailsStep.svelte | 44 ++ .../new/components/GoogleAuthStep.svelte | 144 +++++ .../new/components/GoogleSheetsStep.svelte | 213 +++++++ .../new/components/StepNavigation.svelte | 42 ++ .../event/new/components/StepNavigator.svelte | 28 + 8 files changed, 600 insertions(+), 445 deletions(-) delete mode 100644 src/routes/private/events/event/new/+page.server.ts create mode 100644 src/routes/private/events/event/new/components/EmailSettingsStep.svelte create mode 100644 src/routes/private/events/event/new/components/EventDetailsStep.svelte create mode 100644 src/routes/private/events/event/new/components/GoogleAuthStep.svelte create mode 100644 src/routes/private/events/event/new/components/GoogleSheetsStep.svelte create mode 100644 src/routes/private/events/event/new/components/StepNavigation.svelte create mode 100644 src/routes/private/events/event/new/components/StepNavigator.svelte diff --git a/src/routes/private/events/event/new/+page.server.ts b/src/routes/private/events/event/new/+page.server.ts deleted file mode 100644 index 036c394..0000000 --- a/src/routes/private/events/event/new/+page.server.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const load = async ({ locals }: { locals: any }) => { - const { session } = await locals.safeGetSession(); - - return { - session - }; -}; diff --git a/src/routes/private/events/event/new/+page.svelte b/src/routes/private/events/event/new/+page.svelte index bad1c12..5f20ee6 100644 --- a/src/routes/private/events/event/new/+page.svelte +++ b/src/routes/private/events/event/new/+page.svelte @@ -2,6 +2,15 @@ import { onMount } from 'svelte'; import type { GoogleSheet } from '$lib/sheets.js'; import { isTokenValid, refreshAccessToken } from '$lib/google.js'; + import { goto } from '$app/navigation'; + + // Import Components + import GoogleAuthStep from './components/GoogleAuthStep.svelte'; + import EventDetailsStep from './components/EventDetailsStep.svelte'; + import GoogleSheetsStep from './components/GoogleSheetsStep.svelte'; + import EmailSettingsStep from './components/EmailSettingsStep.svelte'; + import StepNavigator from './components/StepNavigator.svelte'; + import StepNavigation from './components/StepNavigation.svelte'; let { data } = $props(); @@ -16,7 +25,8 @@ connecting: false, showCancelOption: false, token: null as string | null, - error: null as string | null + error: null as string | null, + userEmail: null as string | null }); // Step 1: Event Details @@ -71,13 +81,20 @@ const isValid = await isTokenValid(accessToken); authData.isConnected = isValid; authData.token = accessToken; + + if (isValid) { + // Fetch user info + await fetchUserInfo(accessToken); + } } else { authData.isConnected = false; + authData.userEmail = null; } } catch (error) { console.error('Error checking Google auth:', error); authData.isConnected = false; authData.error = 'Error checking Google connection'; + authData.userEmail = null; } finally { authData.checking = false; } @@ -170,6 +187,59 @@ authData.connecting = false; authData.showCancelOption = false; } + + async function fetchUserInfo(accessToken: string) { + try { + const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { + headers: { + Authorization: `Bearer ${accessToken}` + } + }); + + if (response.ok) { + const userData = await response.json(); + authData.userEmail = userData.email; + } else { + console.error('Failed to fetch user info:', await response.text()); + authData.userEmail = null; + } + } catch (error) { + console.error('Error fetching user info:', error); + authData.userEmail = null; + } + } + + async function disconnectGoogle() { + try { + // First revoke the token at Google + const accessToken = localStorage.getItem('google_access_token'); + if (accessToken) { + await fetch(`https://accounts.google.com/o/oauth2/revoke?token=${accessToken}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + } + + // Remove tokens from local storage + localStorage.removeItem('google_access_token'); + localStorage.removeItem('google_refresh_token'); + + // Update auth state + authData.isConnected = false; + authData.token = null; + authData.userEmail = null; + + // Clear any selected sheets data + sheetsData.availableSheets = []; + sheetsData.selectedSheet = null; + sheetsData.sheetData = []; + } catch (error) { + console.error('Error disconnecting from Google:', error); + authData.error = 'Failed to disconnect from Google'; + } + } // Step navigation function nextStep() { @@ -300,7 +370,6 @@ loading = true; try { - // TODO: Replace with actual Supabase function call const { error } = await data.supabase.rpc('create_event', { p_name: eventData.name, p_date: eventData.date, @@ -316,7 +385,7 @@ if (error) throw error; // Redirect to events list or show success message - window.location.href = '/private/events'; + goto('/private/events'); } catch (error) { console.error('Error creating event:', error); errors.submit = 'Failed to create event. Please try again.'; @@ -336,423 +405,22 @@ if (currentStep === 3) return emailData.subject && emailData.body; return false; }); - - let stepTitle = $derived(() => { - if (currentStep === 0) return 'Connect Google Account'; - if (currentStep === 1) return 'Event Details'; - if (currentStep === 2) return 'Connect Google Sheets'; - if (currentStep === 3) return 'Email Settings'; - return ''; - });
-
-

Create New Event

-
- {#each Array(totalSteps) as _, index} -
-
- {index + 1} -
- {#if index < totalSteps - 1} -
- {/if} -
- {/each} -
-

Step {currentStep + 1} of {totalSteps}: {stepTitle}

-
+
{#if currentStep === 0} - -
-
-

Connect Your Google Account

-

- To create events and import participants from Google Sheets, you need to connect your Google account. -

- - {#if authData.checking} -
-
- Checking connection... -
- {:else if authData.isConnected} -
-
-
- - - -
-
-

- Google account connected successfully! -

-

- You can now access Google Sheets and Gmail features. -

-
-
-
- {:else} -
-
-
- - - -
-
-

- Google account not connected -

-

- Please connect your Google account to continue with event creation. -

-
-
-
- -
- - - {#if authData.connecting && authData.showCancelOption} - -

- Taking too long? You can cancel and try again. -

- {/if} -
- {/if} - - {#if authData.error} -
-
-
- - - -
-
-

- Connection Error -

-

- {authData.error} -

-
-
-
- {/if} - - {#if errors.auth} -

{errors.auth}

- {/if} -
-
- + {:else if currentStep === 1} - -
-
- - - {#if errors.name} -

{errors.name}

- {/if} -
- -
- - - {#if errors.date} -

{errors.date}

- {/if} -
-
- + {:else if currentStep === 2} - -
-
-

Select Google Sheet

- - {#if sheetsData.loading && sheetsData.availableSheets.length === 0} -
- {#each Array(5) as _} -
-
-
-
- {/each} -
- {:else if sheetsData.availableSheets.length === 0} -
-

No Google Sheets found.

- -
- {:else} -
- {#if !sheetsData.expandedSheetList && sheetsData.selectedSheet} - -
-
-
{sheetsData.selectedSheet.name}
-
- Modified: {new Date(sheetsData.selectedSheet.modifiedTime).toLocaleDateString()} -
-
- -
- {:else} - -
-

Available Sheets

- {#if sheetsData.selectedSheet} - - {/if} -
-
- {#each sheetsData.availableSheets as sheet} - - {/each} -
- {/if} -
- {/if} - - {#if errors.sheet} -

{errors.sheet}

- {/if} -
- - {#if sheetsData.selectedSheet && sheetsData.sheetData.length > 0} -
-

Column Mapping

- - -
-

Column Mapping Instructions:

-

- Select what each column represents by using the dropdown in each column header. - Make sure to assign Name, Surname, Email, and Confirmation columns. -

-
- -
- - - - {#each sheetsData.sheetData[0] || [] as header, index} - - {/each} - - - - {#each sheetsData.sheetData.slice(0, 10) as row, rowIndex} - - {#each row as cell, cellIndex} - - {/each} - - {/each} - -
-
-
- Column {index + 1} - ({header || 'Empty'}) -
- - {#if sheetsData.columnMapping.name === index + 1} - Name Column - {:else if sheetsData.columnMapping.surname === index + 1} - Surname Column - {:else if sheetsData.columnMapping.email === index + 1} - Email Column - {:else if sheetsData.columnMapping.confirmation === index + 1} - Confirmation Column - {/if} -
-
- - {cell || ''} - -
-
-

Showing first 10 rows

-
- {/if} - - {#if sheetsData.loading && sheetsData.selectedSheet} -
-
Loading sheet data...
-
- {/if} - - {#if errors.sheetData} -

{errors.sheetData}

- {/if} -
- + {:else if currentStep === 3} - -
-
- - - {#if errors.subject} -

{errors.subject}

- {/if} -
- -
- - - {#if errors.body} -

{errors.body}

- {/if} -
- - -
-

Preview

-
-
Subject: {emailData.subject || 'No subject'}
-
{emailData.body || 'No content'}
-
-
-
+ {/if} {#if errors.submit} @@ -763,33 +431,13 @@
-
- - -
- {#if currentStep < totalSteps - 1} - - {:else} - - {/if} -
-
+
diff --git a/src/routes/private/events/event/new/components/EmailSettingsStep.svelte b/src/routes/private/events/event/new/components/EmailSettingsStep.svelte new file mode 100644 index 0000000..560d9a5 --- /dev/null +++ b/src/routes/private/events/event/new/components/EmailSettingsStep.svelte @@ -0,0 +1,43 @@ + + +
+
+ + + {#if errors.subject} +

{errors.subject}

+ {/if} +
+ +
+ + + {#if errors.body} +

{errors.body}

+ {/if} +
+
diff --git a/src/routes/private/events/event/new/components/EventDetailsStep.svelte b/src/routes/private/events/event/new/components/EventDetailsStep.svelte new file mode 100644 index 0000000..3868340 --- /dev/null +++ b/src/routes/private/events/event/new/components/EventDetailsStep.svelte @@ -0,0 +1,44 @@ + + +
+
+

Event details

+ + + + {#if errors.name} +

{errors.name}

+ {/if} +
+ +
+ + + {#if errors.date} +

{errors.date}

+ {/if} +
+
diff --git a/src/routes/private/events/event/new/components/GoogleAuthStep.svelte b/src/routes/private/events/event/new/components/GoogleAuthStep.svelte new file mode 100644 index 0000000..a3f469f --- /dev/null +++ b/src/routes/private/events/event/new/components/GoogleAuthStep.svelte @@ -0,0 +1,144 @@ + + +
+
+

Connect Your Google Account

+

+ To create events and import participants from Google Sheets, you need to connect your Google account. +

+ + {#if authData.checking} +
+
+ Checking connection... +
+ {:else if authData.isConnected} +
+
+
+

+ Google account connected successfully! +

+ {#if authData.userEmail} +
+

+ {authData.userEmail} +

+
+ {/if} +

+ You can now access Google Sheets and Gmail features. +

+
+
+ +
+ +
+
+ {:else} +
+
+
+ + + +
+
+

+ Google account not connected +

+

+ Please connect your Google account to continue with event creation. +

+
+
+
+ +
+ + + {#if authData.connecting && authData.showCancelOption} + +

+ Taking too long? You can cancel and try again. +

+ {/if} +
+ {/if} + + {#if authData.error} +
+
+
+ + + +
+
+

+ Connection Error +

+

+ {authData.error} +

+
+
+
+ {/if} + + {#if errors.auth} +

{errors.auth}

+ {/if} +
+
diff --git a/src/routes/private/events/event/new/components/GoogleSheetsStep.svelte b/src/routes/private/events/event/new/components/GoogleSheetsStep.svelte new file mode 100644 index 0000000..2dac6ec --- /dev/null +++ b/src/routes/private/events/event/new/components/GoogleSheetsStep.svelte @@ -0,0 +1,213 @@ + + +
+
+

Select Google Sheet

+ + {#if sheetsData.loading && sheetsData.availableSheets.length === 0} +
+ {#each Array(5) as _} +
+
+
+
+ {/each} +
+ {:else if sheetsData.availableSheets.length === 0} +
+

No Google Sheets found.

+ +
+ {:else} +
+ {#if !sheetsData.expandedSheetList && sheetsData.selectedSheet} + +
+
+
{sheetsData.selectedSheet.name}
+
+ Modified: {new Date(sheetsData.selectedSheet.modifiedTime).toLocaleDateString()} +
+
+ +
+ {:else} + +
+

Available Sheets

+ {#if sheetsData.selectedSheet} + + {/if} +
+
+ {#each sheetsData.availableSheets as sheet} + + {/each} +
+ {/if} +
+ {/if} + + {#if errors.sheet} +

{errors.sheet}

+ {/if} +
+ + {#if sheetsData.selectedSheet && sheetsData.sheetData.length > 0} +
+

Column Mapping

+ + +
+

Column Mapping Instructions:

+

+ Select what each column represents by using the dropdown in each column header. + Make sure to assign Name, Surname, Email, and Confirmation columns. +

+
+ +
+ + + + {#each sheetsData.sheetData[0] || [] as header, index} + + {/each} + + + + {#each sheetsData.sheetData.slice(0, 10) as row, rowIndex} + + {#each row as cell, cellIndex} + + {/each} + + {/each} + +
+
+
+ {header || `Empty Column ${index + 1}`} +
+ +
+ {#if sheetsData.columnMapping.name === index + 1} + Name Column + {:else if sheetsData.columnMapping.surname === index + 1} + Surname Column + {:else if sheetsData.columnMapping.email === index + 1} + Email Column + {:else if sheetsData.columnMapping.confirmation === index + 1} + Confirmation Column + {:else} + Not Mapped + {/if} +
+
+
+ + {cell || ''} + +
+
+

Showing first 10 rows

+
+ {/if} + + {#if sheetsData.loading && sheetsData.selectedSheet} +
+
Loading sheet data...
+
+ {/if} + + {#if errors.sheetData} +

{errors.sheetData}

+ {/if} +
diff --git a/src/routes/private/events/event/new/components/StepNavigation.svelte b/src/routes/private/events/event/new/components/StepNavigation.svelte new file mode 100644 index 0000000..207caba --- /dev/null +++ b/src/routes/private/events/event/new/components/StepNavigation.svelte @@ -0,0 +1,42 @@ + + +
+ + +
+ {#if currentStep < totalSteps - 1} + + {:else} + + {/if} +
+
diff --git a/src/routes/private/events/event/new/components/StepNavigator.svelte b/src/routes/private/events/event/new/components/StepNavigator.svelte new file mode 100644 index 0000000..f26fb87 --- /dev/null +++ b/src/routes/private/events/event/new/components/StepNavigator.svelte @@ -0,0 +1,28 @@ + + +
+
+ {#each Array(totalSteps) as _, index} +
+
+ {index + 1} +
+ {#if index < totalSteps - 1} +
+ {/if} +
+ {/each} +
+