; // item, unlocked
} = $props();
@@ -126,6 +130,8 @@
{emptyDescription}
{emptyActionLabel}
{emptyActionHref}
+ {fallbackColor}
+ {fallbackTheme}
>
{#snippet headerAction()}
@@ -150,6 +156,8 @@
itemCount={wishlist.items?.length || 0}
color={wishlist.color}
theme={wishlist.theme}
+ fallbackColor={fallbackColor}
+ fallbackTheme={fallbackTheme}
>
{@render actions(item, unlocked)}
diff --git a/src/lib/components/layout/DashboardHeader.svelte b/src/lib/components/layout/DashboardHeader.svelte
index 5bb5a9d..5852c7d 100644
--- a/src/lib/components/layout/DashboardHeader.svelte
+++ b/src/lib/components/layout/DashboardHeader.svelte
@@ -3,6 +3,7 @@
import { ThemeToggle } from '$lib/components/ui/theme-toggle';
import { LanguageToggle } from '$lib/components/ui/language-toggle';
import ThemePicker from '$lib/components/ui/theme-picker.svelte';
+ import ColorPicker from '$lib/components/ui/ColorPicker.svelte';
import { signOut } from '@auth/sveltekit/client';
import { languageStore } from '$lib/stores/language.svelte';
import { enhance } from '$app/forms';
@@ -11,25 +12,27 @@
userName,
userEmail,
dashboardTheme = 'none',
+ dashboardColor = null,
isAuthenticated = false,
- onThemeUpdate
+ onThemeUpdate,
+ onColorUpdate
}: {
userName?: string | null;
userEmail?: string | null;
dashboardTheme?: string;
+ dashboardColor?: string | null;
isAuthenticated?: boolean;
onThemeUpdate?: (theme: string | null) => void;
+ onColorUpdate?: (color: string | null) => void;
} = $props();
const t = $derived(languageStore.t);
async function handleThemeChange(theme: string) {
- // Update theme immediately for instant visual feedback
if (onThemeUpdate) {
onThemeUpdate(theme);
}
- // Only submit to database for authenticated users
if (isAuthenticated) {
const formData = new FormData();
formData.append('theme', theme);
@@ -40,6 +43,30 @@
});
}
}
+
+ let localColor = $state(dashboardColor);
+
+ $effect(() => {
+ localColor = dashboardColor;
+ });
+
+ async function handleColorChange() {
+ if (onColorUpdate) {
+ onColorUpdate(localColor);
+ }
+
+ if (isAuthenticated) {
+ const formData = new FormData();
+ if (localColor) {
+ formData.append('color', localColor);
+ }
+
+ await fetch('?/updateDashboardColor', {
+ method: 'POST',
+ body: formData
+ });
+ }
+ }
@@ -52,6 +79,7 @@
{/if}
+
diff --git a/src/lib/components/themes/ThemeCard.svelte b/src/lib/components/themes/ThemeCard.svelte
index 70fe3cc..535c490 100644
--- a/src/lib/components/themes/ThemeCard.svelte
+++ b/src/lib/components/themes/ThemeCard.svelte
@@ -7,8 +7,8 @@
themeName,
color
}: {
- themeName?: string;
- color?: string;
+ themeName?: string | null;
+ color?: string | null;
} = $props();
const theme = $derived(getTheme(themeName));
diff --git a/src/lib/server/schema.ts b/src/lib/server/schema.ts
index f5adc97..d5a796c 100644
--- a/src/lib/server/schema.ts
+++ b/src/lib/server/schema.ts
@@ -13,7 +13,8 @@ export const users = pgTable('user', {
image: text('image'),
password: text('password'),
username: text('username').unique(),
- dashboardTheme: text('dashboard_theme').default('none')
+ dashboardTheme: text('dashboard_theme').default('none'),
+ dashboardColor: text('dashboard_color')
});
export const accounts = pgTable(
diff --git a/src/routes/dashboard/+page.server.ts b/src/routes/dashboard/+page.server.ts
index ff67458..cc81900 100644
--- a/src/routes/dashboard/+page.server.ts
+++ b/src/routes/dashboard/+page.server.ts
@@ -174,6 +174,22 @@ export const actions: Actions = {
.set({ dashboardTheme: theme })
.where(eq(users.id, session.user.id));
+ return { success: true };
+ },
+
+ updateDashboardColor: async ({ request, locals }) => {
+ const session = await locals.auth();
+ if (!session?.user?.id) {
+ throw redirect(303, '/signin');
+ }
+
+ const formData = await request.formData();
+ const color = formData.get('color') as string | null;
+
+ await db.update(users)
+ .set({ dashboardColor: color })
+ .where(eq(users.id, session.user.id));
+
return { success: true };
}
};
diff --git a/src/routes/dashboard/+page.svelte b/src/routes/dashboard/+page.svelte
index e29f516..93a51ef 100644
--- a/src/routes/dashboard/+page.svelte
+++ b/src/routes/dashboard/+page.svelte
@@ -24,7 +24,21 @@
}
}
+ // For anonymous users, get color from localStorage
+ function getInitialColor() {
+ if (data.isAuthenticated) {
+ return data.user?.dashboardColor || null;
+ } else {
+ // Anonymous user - get from localStorage
+ if (typeof window !== 'undefined') {
+ return localStorage.getItem('dashboardColor') || null;
+ }
+ return null;
+ }
+ }
+
let currentTheme = $state(getInitialTheme());
+ let currentColor = $state(getInitialColor());
// Save to localStorage when theme changes for anonymous users
function handleThemeUpdate(theme: string | null) {
@@ -35,6 +49,19 @@
}
}
+ // Save to localStorage when color changes for anonymous users
+ function handleColorUpdate(color: string | null) {
+ currentColor = color;
+
+ if (!data.isAuthenticated && typeof window !== 'undefined') {
+ if (color) {
+ localStorage.setItem('dashboardColor', color);
+ } else {
+ localStorage.removeItem('dashboardColor');
+ }
+ }
+ }
+
const t = $derived(languageStore.t);
// Only owned wishlists for "My Wishlists"
@@ -58,17 +85,23 @@
});
-
+
-
+
{#if data.isAuthenticated}
@@ -80,6 +113,8 @@
emptyActionLabel={t.dashboard.emptyWishlistsAction}
emptyActionHref="/"
showCreateButton={true}
+ fallbackColor={currentColor}
+ fallbackTheme={currentTheme}
>
{#snippet actions(wishlist, unlocked)}
@@ -135,6 +170,8 @@
emptyMessage={t.dashboard.emptyClaimedWishlists}
emptyDescription={t.dashboard.emptyClaimedWishlistsDescription}
hideIfEmpty={true}
+ fallbackColor={currentColor}
+ fallbackTheme={currentTheme}
>
{#snippet actions(wishlist, unlocked)}
@@ -189,6 +226,8 @@
items={savedWishlists()}
emptyMessage={t.dashboard.emptySavedWishlists}
emptyDescription={t.dashboard.emptySavedWishlistsDescription}
+ fallbackColor={currentColor}
+ fallbackTheme={currentTheme}
>
{#snippet actions(saved, unlocked)}