development #19

Merged
erman merged 8 commits from development into main 2025-07-14 21:40:16 +02:00
4 changed files with 76 additions and 87 deletions
Showing only changes of commit b9db3d22a3 - Show all commits

View File

@@ -4,7 +4,7 @@
import type { GoogleSheet } from '$lib/google/sheets/types.ts'; import type { GoogleSheet } from '$lib/google/sheets/types.ts';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { toast } from '$lib/stores/toast.js'; import { toast } from '$lib/stores/toast.js';
// Import Components // Import Components
import GoogleAuthStep from './components/GoogleAuthStep.svelte'; import GoogleAuthStep from './components/GoogleAuthStep.svelte';
import EventDetailsStep from './components/EventDetailsStep.svelte'; import EventDetailsStep from './components/EventDetailsStep.svelte';
@@ -42,7 +42,7 @@
selectedSheet: null as GoogleSheet | null, selectedSheet: null as GoogleSheet | null,
sheetData: [] as string[][], sheetData: [] as string[][],
columnMapping: { columnMapping: {
name: 0, // Initialize to 0 (no column selected) name: 0, // Initialize to 0 (no column selected)
surname: 0, surname: 0,
email: 0, email: 0,
confirmation: 0 confirmation: 0
@@ -64,7 +64,7 @@
onMount(async () => { onMount(async () => {
// Check Google auth status on mount // Check Google auth status on mount
await checkGoogleAuth(); await checkGoogleAuth();
if (currentStep === 2) { if (currentStep === 2) {
await loadRecentSheets(); await loadRecentSheets();
} }
@@ -76,13 +76,13 @@
try { try {
const accessToken = localStorage.getItem('google_access_token'); const accessToken = localStorage.getItem('google_access_token');
const refreshToken = localStorage.getItem('google_refresh_token'); const refreshToken = localStorage.getItem('google_refresh_token');
if (accessToken && refreshToken) { if (accessToken && refreshToken) {
// Check if token is still valid // Check if token is still valid
const isValid = await isTokenValid(accessToken); const isValid = await isTokenValid(accessToken);
authData.isConnected = isValid; authData.isConnected = isValid;
authData.token = accessToken; authData.token = accessToken;
if (isValid) { if (isValid) {
// Fetch user info // Fetch user info
await fetchUserInfo(accessToken); await fetchUserInfo(accessToken);
@@ -104,15 +104,16 @@
async function connectToGoogle() { async function connectToGoogle() {
authData.error = ''; authData.error = '';
authData.connecting = true; authData.connecting = true;
try { try {
// Open popup window for OAuth // Open popup window for OAuth
const popup = window.open( const popup = window.open(
'/auth/google', '/auth/google',
'google-auth', 'google-auth',
'width=500,height=600,scrollbars=yes,resizable=yes,left=' + 'width=500,height=600,scrollbars=yes,resizable=yes,left=' +
Math.round(window.screen.width / 2 - 250) + ',top=' + Math.round(window.screen.width / 2 - 250) +
Math.round(window.screen.height / 2 - 300) ',top=' +
Math.round(window.screen.height / 2 - 300)
); );
if (!popup) { if (!popup) {
@@ -127,12 +128,12 @@
// Store current timestamp to detect changes in localStorage // Store current timestamp to detect changes in localStorage
const startTimestamp = localStorage.getItem('google_auth_timestamp') || '0'; const startTimestamp = localStorage.getItem('google_auth_timestamp') || '0';
// Poll localStorage for auth completion // Poll localStorage for auth completion
const pollInterval = setInterval(() => { const pollInterval = setInterval(() => {
try { try {
const currentTimestamp = localStorage.getItem('google_auth_timestamp'); const currentTimestamp = localStorage.getItem('google_auth_timestamp');
// If timestamp has changed, auth is complete // If timestamp has changed, auth is complete
if (currentTimestamp && currentTimestamp !== startTimestamp) { if (currentTimestamp && currentTimestamp !== startTimestamp) {
handleAuthSuccess(); handleAuthSuccess();
@@ -141,24 +142,24 @@
console.error('Error checking auth timestamp:', e); console.error('Error checking auth timestamp:', e);
} }
}, 500); // Poll every 500ms }, 500); // Poll every 500ms
// Common handler for authentication success // Common handler for authentication success
function handleAuthSuccess() { function handleAuthSuccess() {
if (authCompleted) return; // Prevent duplicate handling if (authCompleted) return; // Prevent duplicate handling
authCompleted = true; authCompleted = true;
authData.connecting = false; authData.connecting = false;
authData.showCancelOption = false; authData.showCancelOption = false;
// Clean up timers // Clean up timers
clearInterval(pollInterval); clearInterval(pollInterval);
if (popupTimer) clearTimeout(popupTimer); if (popupTimer) clearTimeout(popupTimer);
if (cancelTimeout) clearTimeout(cancelTimeout); if (cancelTimeout) clearTimeout(cancelTimeout);
// Update auth state // Update auth state
setTimeout(checkGoogleAuth, 100); setTimeout(checkGoogleAuth, 100);
} }
// Clean up function to handle all cleanup in one place // Clean up function to handle all cleanup in one place
const cleanUp = () => { const cleanUp = () => {
clearInterval(pollInterval); clearInterval(pollInterval);
@@ -190,19 +191,18 @@
cleanUp(); cleanUp();
} }
}, 60 * 1000); // Reduced from 3min to 1min }, 60 * 1000); // Reduced from 3min to 1min
} catch (error) { } catch (error) {
console.error('Error connecting to Google:', error); console.error('Error connecting to Google:', error);
authData.error = 'Failed to connect to Google'; authData.error = 'Failed to connect to Google';
authData.connecting = false; authData.connecting = false;
} }
} }
function cancelGoogleAuth() { function cancelGoogleAuth() {
authData.connecting = false; authData.connecting = false;
authData.showCancelOption = false; authData.showCancelOption = false;
} }
async function fetchUserInfo(accessToken: string) { async function fetchUserInfo(accessToken: string) {
try { try {
// Use the new getUserInfo function from our lib // Use the new getUserInfo function from our lib
@@ -217,7 +217,7 @@
authData.userEmail = null; authData.userEmail = null;
} }
} }
async function disconnectGoogle() { async function disconnectGoogle() {
try { try {
// First revoke the token at Google using our API // First revoke the token at Google using our API
@@ -225,16 +225,16 @@
if (accessToken) { if (accessToken) {
await revokeToken(accessToken); await revokeToken(accessToken);
} }
// Remove tokens from local storage // Remove tokens from local storage
localStorage.removeItem('google_access_token'); localStorage.removeItem('google_access_token');
localStorage.removeItem('google_refresh_token'); localStorage.removeItem('google_refresh_token');
// Update auth state // Update auth state
authData.isConnected = false; authData.isConnected = false;
authData.token = null; authData.token = null;
authData.userEmail = null; authData.userEmail = null;
// Clear any selected sheets data // Clear any selected sheets data
sheetsData.availableSheets = []; sheetsData.availableSheets = [];
sheetsData.selectedSheet = null; sheetsData.selectedSheet = null;
@@ -263,7 +263,7 @@
// Clear previous errors // Clear previous errors
errors = {}; errors = {};
let isValid = true; let isValid = true;
if (currentStep === 0) { if (currentStep === 0) {
if (!authData.isConnected) { if (!authData.isConnected) {
toast.error('Please connect your Google account to continue'); toast.error('Please connect your Google account to continue');
@@ -287,17 +287,17 @@
errors.sheet = 'Please select a Google Sheet'; errors.sheet = 'Please select a Google Sheet';
isValid = false; isValid = false;
} }
if (sheetsData.selectedSheet) { if (sheetsData.selectedSheet) {
// Validate column mappings // Validate column mappings
const { name, surname, email, confirmation } = sheetsData.columnMapping; const { name, surname, email, confirmation } = sheetsData.columnMapping;
const missingColumns = []; const missingColumns = [];
if (!name) missingColumns.push('Name'); if (!name) missingColumns.push('Name');
if (!surname) missingColumns.push('Surname'); if (!surname) missingColumns.push('Surname');
if (!email) missingColumns.push('Email'); if (!email) missingColumns.push('Email');
if (!confirmation) missingColumns.push('Confirmation'); if (!confirmation) missingColumns.push('Confirmation');
if (missingColumns.length > 0) { if (missingColumns.length > 0) {
const errorMsg = `Please map the following columns: ${missingColumns.join(', ')}`; const errorMsg = `Please map the following columns: ${missingColumns.join(', ')}`;
toast.error(errorMsg); toast.error(errorMsg);
@@ -326,16 +326,16 @@
sheetsData.loading = true; sheetsData.loading = true;
// Always expand the sheet list when loading new sheets // Always expand the sheet list when loading new sheets
sheetsData.expandedSheetList = true; sheetsData.expandedSheetList = true;
try { try {
// Use the new unified API endpoint // Use the new unified API endpoint
const response = await fetch('/private/api/google/sheets/recent', { const response = await fetch('/private/api/google/sheets/recent', {
method: 'GET', method: 'GET',
headers: { headers: {
'Authorization': `Bearer ${localStorage.getItem('google_refresh_token')}` Authorization: `Bearer ${localStorage.getItem('google_refresh_token')}`
} }
}); });
if (response.ok) { if (response.ok) {
sheetsData.availableSheets = await response.json(); sheetsData.availableSheets = await response.json();
} }
@@ -349,24 +349,24 @@
async function selectSheet(sheet: GoogleSheet) { async function selectSheet(sheet: GoogleSheet) {
const sameSheet = sheetsData.selectedSheet?.id === sheet.id; const sameSheet = sheetsData.selectedSheet?.id === sheet.id;
sheetsData.selectedSheet = sheet; sheetsData.selectedSheet = sheet;
sheetsData.loading = true; sheetsData.loading = true;
// Collapse sheet list when selecting a new sheet // Collapse sheet list when selecting a new sheet
if (!sameSheet) { if (!sameSheet) {
sheetsData.expandedSheetList = false; sheetsData.expandedSheetList = false;
} }
try { try {
// Use the new unified API endpoint // Use the new unified API endpoint
const response = await fetch(`/private/api/google/sheets/${sheet.id}/data`, { const response = await fetch(`/private/api/google/sheets/${sheet.id}/data`, {
method: 'GET', method: 'GET',
headers: { headers: {
'Authorization': `Bearer ${localStorage.getItem('google_refresh_token')}` Authorization: `Bearer ${localStorage.getItem('google_refresh_token')}`
} }
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
sheetsData.sheetData = data.values || []; sheetsData.sheetData = data.values || [];
@@ -378,7 +378,7 @@
sheetsData.loading = false; sheetsData.loading = false;
} }
} }
// Toggle the sheet list expansion // Toggle the sheet list expansion
function toggleSheetList() { function toggleSheetList() {
sheetsData.expandedSheetList = !sheetsData.expandedSheetList; sheetsData.expandedSheetList = !sheetsData.expandedSheetList;
@@ -427,49 +427,45 @@
}); });
</script> </script>
<div class="max-w-4xl mx-auto p-6"> <!-- Header -->
<!-- Header --> <StepNavigator {currentStep} {totalSteps} />
<StepNavigator {currentStep} {totalSteps} />
<!-- Step Content --> <!-- Step Content -->
<div class="rounded-lg border border-gray-300 bg-white p-6 mb-4"> <div class="mb-4 rounded border border-gray-300 bg-white p-6">
{#if currentStep === 0} {#if currentStep === 0}
<GoogleAuthStep <GoogleAuthStep
bind:errors onSuccess={(token) => {
onSuccess={(token) => { authData.error = null;
authData.error = null; authData.token = token;
authData.token = token; authData.isConnected = true;
authData.isConnected = true; setTimeout(checkGoogleAuth, 100);
setTimeout(checkGoogleAuth, 100); }}
}} onError={(error) => {
onError={(error) => { authData.error = error;
authData.error = error; authData.isConnected = false;
authData.isConnected = false; }}
}} />
/> {:else if currentStep === 1}
{:else if currentStep === 1} <EventDetailsStep bind:eventData />
<EventDetailsStep bind:eventData /> {:else if currentStep === 2}
{:else if currentStep === 2} <GoogleSheetsStep
<GoogleSheetsStep bind:sheetsData bind:errors {loadRecentSheets} {selectSheet} {toggleSheetList} /> bind:sheetsData
{:else if currentStep === 3} {loadRecentSheets}
<EmailSettingsStep bind:emailData bind:errors /> {selectSheet}
{/if} {toggleSheetList}
/>
{#if errors.submit} {:else if currentStep === 3}
<div class="mt-4 p-3 bg-red-50 border border-red-200 rounded"> <EmailSettingsStep bind:emailData />
<p class="text-sm text-red-600">{errors.submit}</p> {/if}
</div>
{/if}
</div>
<!-- Navigation -->
<StepNavigation
{currentStep}
{totalSteps}
{canProceed}
{loading}
{prevStep}
{nextStep}
{createEvent}
/>
</div> </div>
<!-- Navigation -->
<StepNavigation
{currentStep}
{totalSteps}
{canProceed}
{loading}
{prevStep}
{nextStep}
{createEvent}
/>

View File

@@ -54,7 +54,6 @@
Detected templates: {bodyTemplatesDetected.map((v) => v.name).join(', ')} Detected templates: {bodyTemplatesDetected.map((v) => v.name).join(', ')}
</p> </p>
{/if} {/if}
<!-- Errors now shown as toast notifications -->
</div> </div>
<div> <div>

View File

@@ -21,7 +21,5 @@
onSuccess={onSuccess} onSuccess={onSuccess}
onError={onError} onError={onError}
/> />
<!-- Error messages are now shown as toast notifications -->
</div> </div>
</div> </div>

View File

@@ -255,8 +255,6 @@
{/if} {/if}
</div> </div>
{/if} {/if}
<!-- Error messages are now shown as toast notifications -->
</div> </div>
{#if sheetsData.selectedSheet && sheetsData.sheetData.length > 0} {#if sheetsData.selectedSheet && sheetsData.sheetData.length > 0}
@@ -370,8 +368,6 @@
<div class="text-gray-600">Loading sheet data...</div> <div class="text-gray-600">Loading sheet data...</div>
</div> </div>
{/if} {/if}
<!-- Error messages are now shown as toast notifications -->
</div> </div>