From 4a33fc3ce4ba53fb635293c19cd738301811787b Mon Sep 17 00:00:00 2001 From: chark1es Date: Sun, 2 Mar 2025 01:21:42 -0800 Subject: [PATCH] update settings --- .../AccountSecuritySettings.tsx | 4 +- .../SettingsSection/DisplaySettings.tsx | 177 +++++++++++++++--- .../SettingsSection/UserProfileSettings.tsx | 18 +- src/schemas/pocketbase/schema.ts | 4 + 4 files changed, 168 insertions(+), 35 deletions(-) diff --git a/src/components/dashboard/SettingsSection/AccountSecuritySettings.tsx b/src/components/dashboard/SettingsSection/AccountSecuritySettings.tsx index e5ab5fd..d39d4a3 100644 --- a/src/components/dashboard/SettingsSection/AccountSecuritySettings.tsx +++ b/src/components/dashboard/SettingsSection/AccountSecuritySettings.tsx @@ -130,8 +130,8 @@ export default function AccountSecuritySettings() {

Authentication Options

- IEEE UCSD uses Single Sign-On (SSO) through UCSD for authentication. - Password management is handled through your UCSD account. + IEEE UCSD uses Single Sign-On (SSO) for authentication. + Password management is handled through your IEEEUCSD account.

diff --git a/src/components/dashboard/SettingsSection/DisplaySettings.tsx b/src/components/dashboard/SettingsSection/DisplaySettings.tsx index ce359ca..3ff9635 100644 --- a/src/components/dashboard/SettingsSection/DisplaySettings.tsx +++ b/src/components/dashboard/SettingsSection/DisplaySettings.tsx @@ -1,38 +1,110 @@ import { useState, useEffect } from 'react'; +import { Authentication } from '../../../scripts/pocketbase/Authentication'; +import { Update } from '../../../scripts/pocketbase/Update'; +import { Collections } from '../../../schemas/pocketbase/schema'; export default function DisplaySettings() { + const auth = Authentication.getInstance(); + const update = Update.getInstance(); const [theme, setTheme] = useState('dark'); const [fontSize, setFontSize] = useState('medium'); const [colorBlindMode, setColorBlindMode] = useState(false); const [reducedMotion, setReducedMotion] = useState(false); const [successMessage, setSuccessMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + const [saving, setSaving] = useState(false); - // Load saved preferences from localStorage on component mount + // Load saved preferences on component mount useEffect(() => { - const savedTheme = localStorage.getItem('theme') || 'dark'; - const savedFontSize = localStorage.getItem('fontSize') || 'medium'; - const savedColorBlindMode = localStorage.getItem('colorBlindMode') === 'true'; - const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true'; + const loadPreferences = async () => { + try { + // First check localStorage for immediate UI updates + const savedTheme = localStorage.getItem('theme') || 'dark'; + const savedFontSize = localStorage.getItem('fontSize') || 'medium'; + const savedColorBlindMode = localStorage.getItem('colorBlindMode') === 'true'; + const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true'; - setTheme(savedTheme); - setFontSize(savedFontSize); - setColorBlindMode(savedColorBlindMode); - setReducedMotion(savedReducedMotion); + setTheme(savedTheme); + setFontSize(savedFontSize); + setColorBlindMode(savedColorBlindMode); + setReducedMotion(savedReducedMotion); - // Apply theme to document - document.documentElement.setAttribute('data-theme', savedTheme); + // Apply theme to document + document.documentElement.setAttribute('data-theme', savedTheme); - // Apply font size - applyFontSize(savedFontSize); + // Apply font size + applyFontSize(savedFontSize); - // Apply accessibility settings - if (savedColorBlindMode) { - document.documentElement.classList.add('color-blind-mode'); - } + // Apply accessibility settings + if (savedColorBlindMode) { + document.documentElement.classList.add('color-blind-mode'); + } - if (savedReducedMotion) { - document.documentElement.classList.add('reduced-motion'); - } + if (savedReducedMotion) { + document.documentElement.classList.add('reduced-motion'); + } + + // Then check if user has saved preferences in their profile + const user = auth.getCurrentUser(); + if (user && user.display_preferences) { + try { + const userPrefs = JSON.parse(user.display_preferences); + + // Only update if values exist and are different from localStorage + if (userPrefs.theme && userPrefs.theme !== savedTheme) { + setTheme(userPrefs.theme); + localStorage.setItem('theme', userPrefs.theme); + document.documentElement.setAttribute('data-theme', userPrefs.theme); + } + + if (userPrefs.fontSize && userPrefs.fontSize !== savedFontSize) { + setFontSize(userPrefs.fontSize); + localStorage.setItem('fontSize', userPrefs.fontSize); + applyFontSize(userPrefs.fontSize); + } + } catch (e) { + console.error('Error parsing display preferences:', e); + } + } + + if (user && user.accessibility_settings) { + try { + const accessibilityPrefs = JSON.parse(user.accessibility_settings); + + if (typeof accessibilityPrefs.colorBlindMode === 'boolean' && + accessibilityPrefs.colorBlindMode !== savedColorBlindMode) { + setColorBlindMode(accessibilityPrefs.colorBlindMode); + localStorage.setItem('colorBlindMode', accessibilityPrefs.colorBlindMode.toString()); + + if (accessibilityPrefs.colorBlindMode) { + document.documentElement.classList.add('color-blind-mode'); + } else { + document.documentElement.classList.remove('color-blind-mode'); + } + } + + if (typeof accessibilityPrefs.reducedMotion === 'boolean' && + accessibilityPrefs.reducedMotion !== savedReducedMotion) { + setReducedMotion(accessibilityPrefs.reducedMotion); + localStorage.setItem('reducedMotion', accessibilityPrefs.reducedMotion.toString()); + + if (accessibilityPrefs.reducedMotion) { + document.documentElement.classList.add('reduced-motion'); + } else { + document.documentElement.classList.remove('reduced-motion'); + } + } + } catch (e) { + console.error('Error parsing accessibility settings:', e); + } + } + } catch (error) { + console.error('Error loading preferences:', error); + setErrorMessage('Failed to load display preferences'); + } + }; + + loadPreferences(); }, []); // Apply font size to document @@ -116,16 +188,51 @@ export default function DisplaySettings() { }; // Handle form submission - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + setSaving(true); + setSuccessMessage(''); + setErrorMessage(''); - // Show success message - setSuccessMessage('Display settings saved successfully!'); + try { + const user = auth.getCurrentUser(); + if (!user) throw new Error('User not authenticated'); - // Clear success message after 3 seconds - setTimeout(() => { - setSuccessMessage(''); - }, 3000); + // Save display preferences to user record + const displayPreferences = { + theme, + fontSize + }; + + // Save accessibility settings to user record + const accessibilitySettings = { + colorBlindMode, + reducedMotion + }; + + // Update user record + await update.updateFields( + Collections.USERS, + user.id, + { + display_preferences: JSON.stringify(displayPreferences), + accessibility_settings: JSON.stringify(accessibilitySettings) + } + ); + + // Show success message + setSuccessMessage('Display settings saved successfully!'); + + // Clear success message after 3 seconds + setTimeout(() => { + setSuccessMessage(''); + }, 3000); + } catch (error) { + console.error('Error saving display settings:', error); + setErrorMessage('Failed to save display settings to your profile'); + } finally { + setSaving(false); + } }; return ( @@ -138,6 +245,14 @@ export default function DisplaySettings() {
)} + {errorMessage && ( +
+
+ {errorMessage} +
+
+ )} +
{/* Theme Selection */}
@@ -260,8 +375,12 @@ export default function DisplaySettings() {
-
diff --git a/src/components/dashboard/SettingsSection/UserProfileSettings.tsx b/src/components/dashboard/SettingsSection/UserProfileSettings.tsx index c14a9fa..649bbd6 100644 --- a/src/components/dashboard/SettingsSection/UserProfileSettings.tsx +++ b/src/components/dashboard/SettingsSection/UserProfileSettings.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Update } from '../../../scripts/pocketbase/Update'; import { Collections, type User } from '../../../schemas/pocketbase/schema'; +import allMajors from '../../../data/allUCSDMajors.txt?raw'; export default function UserProfileSettings() { const auth = Authentication.getInstance(); @@ -19,6 +20,9 @@ export default function UserProfileSettings() { const [successMessage, setSuccessMessage] = useState(''); const [errorMessage, setErrorMessage] = useState(''); + // Parse the majors list from the text file + const majorsList = allMajors.split('\n').filter(major => major.trim() !== ''); + useEffect(() => { const loadUserData = async () => { try { @@ -164,13 +168,19 @@ export default function UserProfileSettings() { - + className="select select-bordered w-full" + > + + {majorsList.map((major, index) => ( + + ))} +
diff --git a/src/schemas/pocketbase/schema.ts b/src/schemas/pocketbase/schema.ts index 354916c..87cfeff 100644 --- a/src/schemas/pocketbase/schema.ts +++ b/src/schemas/pocketbase/schema.ts @@ -30,6 +30,10 @@ export interface User extends BaseRecord { graduation_year?: number; major?: string; zelle_information?: string; + last_login?: string; + notification_preferences?: string; // JSON string of notification settings + display_preferences?: string; // JSON string of display settings (theme, font size, etc.) + accessibility_settings?: string; // JSON string of accessibility settings (color blind mode, reduced motion) } /**