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 && (
+
+ )}
+
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() {
Major
-
+ className="select select-bordered w-full"
+ >
+
Select a major
+ {majorsList.map((major, index) => (
+
+ {major}
+
+ ))}
+
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)
}
/**