From 308587d10b7d47363c1f797731c04a9872d337ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brayan=20Steven=20Mar=C3=ADn=20Quir=C3=B3s?= <49928451+BrayanMQ@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:08:53 -0600 Subject: [PATCH 1/5] Add language column to user_settings table Add language column to user_settings table - Add language TEXT column with default 'en' - Add check constraint for valid language codes (en, es, pt, fr, de) - Backfill existing records with default language --- ...31120000_add_language_to_user_settings.sql | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 supabase/migrations/20260131120000_add_language_to_user_settings.sql diff --git a/supabase/migrations/20260131120000_add_language_to_user_settings.sql b/supabase/migrations/20260131120000_add_language_to_user_settings.sql new file mode 100644 index 0000000..f86292b --- /dev/null +++ b/supabase/migrations/20260131120000_add_language_to_user_settings.sql @@ -0,0 +1,20 @@ +-- ============================================================================ +-- Migration: Add language column to user_settings table +-- ============================================================================ +-- Adds language preference column to store user's selected language +-- Includes check constraint for valid language codes +-- ============================================================================ + +-- Add language column with default value +ALTER TABLE public.user_settings +ADD COLUMN language TEXT NOT NULL DEFAULT 'en'; + +-- Add check constraint to validate language codes +ALTER TABLE public.user_settings +ADD CONSTRAINT user_settings_language_check +CHECK (language IN ('en', 'es', 'pt', 'fr', 'de')); + +-- Backfill existing records with default language (already done by DEFAULT, but explicit for clarity) +UPDATE public.user_settings +SET language = 'en' +WHERE language IS NULL; From 06b4057b0be75f9d02ae4ebb00ed49b1371de990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brayan=20Steven=20Mar=C3=ADn=20Quir=C3=B3s?= <49928451+BrayanMQ@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:09:07 -0600 Subject: [PATCH 2/5] Update UserSettings type and add language field Update UserSettings type and add language persistence - Add language field to UserSettings type - Include language in default settings creation - Create updateLanguage mutation for database persistence - Add language sync effect with user authentication check - Export updateLanguage, isUpdatingLanguage, and updateLanguageError --- hooks/useUserSettings.ts | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/hooks/useUserSettings.ts b/hooks/useUserSettings.ts index 252d745..4f0b16b 100644 --- a/hooks/useUserSettings.ts +++ b/hooks/useUserSettings.ts @@ -14,6 +14,7 @@ export type UserSettings = { id: string; user_id: string; theme: 'light' | 'dark' | 'system'; + language: string; created_at: string; updated_at: string; }; @@ -85,6 +86,7 @@ export function useUserSettings() { .insert({ user_id: user.id, theme: 'system', + language: 'en', }) .select() .single(); @@ -141,6 +143,35 @@ export function useUserSettings() { }, }); + // Mutation to update language + const updateLanguageMutation = useMutation({ + mutationFn: async (language: string) => { + if (!user?.id) { + throw new Error('User must be authenticated'); + } + + const supabase = createBrowserClient(); + + // Update language in database + const { data, error: updateError } = await supabase + .from('user_settings') + .update({ language }) + .eq('user_id', user.id) + .select() + .single(); + + if (updateError) { + throw updateError; + } + + return data as UserSettings; + }, + onSuccess: (data) => { + // Update cache with new settings + queryClient.setQueryData(userSettingsKeys.user(user?.id ?? null), data); + }, + }); + // Sync theme from database to next-themes when settings load // This effect ensures next-themes is synchronized with the database theme useEffect(() => { @@ -157,6 +188,32 @@ export function useUserSettings() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [settings?.theme, currentTheme]); // Include currentTheme to detect when it changes + // Sync language from database to i18next when settings load + // This effect ensures i18next is synchronized with the database language + // Runs on login and whenever settings change + useEffect(() => { + if (!settings?.language || !user?.id) { + // Don't sync if settings aren't loaded or user isn't authenticated + return; + } + + // Import i18n dynamically to avoid circular dependencies + import('@/i18n').then((i18nModule) => { + const i18n = i18nModule.default; + + // Always sync language from database when settings load + // This ensures language is restored on login + const currentLang = (i18n.language || 'en').split('-')[0]; + + if (settings.language !== currentLang) { + console.log(`[useUserSettings] Syncing language from DB: ${settings.language}`); + i18n.changeLanguage(settings.language); + } + }).catch((error) => { + console.error('[useUserSettings] Failed to sync language:', error); + }); + }, [settings?.language, user?.id]); + // Invalidate settings query when auth state changes useEffect(() => { if (!user?.id) { @@ -175,6 +232,9 @@ export function useUserSettings() { updateTheme: updateThemeMutation.mutateAsync, isUpdating: updateThemeMutation.isPending, updateError: updateThemeMutation.error as PostgrestError | null, + updateLanguage: updateLanguageMutation.mutateAsync, + isUpdatingLanguage: updateLanguageMutation.isPending, + updateLanguageError: updateLanguageMutation.error as PostgrestError | null, }; } From bda0b49f6883116858036cc4bf81e662aa0b9e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brayan=20Steven=20Mar=C3=ADn=20Quir=C3=B3s?= <49928451+BrayanMQ@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:10:37 -0600 Subject: [PATCH 3/5] Implement language persistence in settings UI Implement language persistence in settings page - Update handleLanguageChange to persist language to database - Add optimistic UI update for immediate feedback - Add loading states to language selector buttons - Disable buttons during language update operation --- app/dashboard/settings/page.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/dashboard/settings/page.tsx b/app/dashboard/settings/page.tsx index eb8cde3..a74bb9b 100644 --- a/app/dashboard/settings/page.tsx +++ b/app/dashboard/settings/page.tsx @@ -25,7 +25,7 @@ export default function SettingsPage() { const { t } = useTranslation() const router = useRouter() const { user, signOut, signOutPending } = useAuth() - const { settings, updateTheme, isUpdating: isUpdatingTheme } = useUserSettings() + const { settings, updateTheme, isUpdating: isUpdatingTheme, updateLanguage, isUpdatingLanguage } = useUserSettings() const { isInstallable, isInstalled, install: installPWA } = usePWAInstall() // Modal states @@ -50,8 +50,16 @@ export default function SettingsPage() { } } - const handleLanguageChange = (lang: string) => { + const handleLanguageChange = async (lang: string) => { + // Optimistically update UI immediately i18n.changeLanguage(lang) + + // Persist to database + try { + await updateLanguage(lang) + } catch (error) { + console.error("Error updating language:", error) + } } const currentTheme = settings?.theme || "system" @@ -232,7 +240,7 @@ export default function SettingsPage() { - @@ -242,7 +250,7 @@ export default function SettingsPage() { {languages.map((lang) => (