update settings

This commit is contained in:
chark1es 2025-03-02 01:21:42 -08:00
parent b7881213ce
commit 4a33fc3ce4
4 changed files with 168 additions and 35 deletions

View file

@ -130,8 +130,8 @@ export default function AccountSecuritySettings() {
<div> <div>
<h4 className="font-semibold text-lg mb-2">Authentication Options</h4> <h4 className="font-semibold text-lg mb-2">Authentication Options</h4>
<p className="text-sm opacity-70 mb-4"> <p className="text-sm opacity-70 mb-4">
IEEE UCSD uses Single Sign-On (SSO) through UCSD for authentication. IEEE UCSD uses Single Sign-On (SSO) for authentication.
Password management is handled through your UCSD account. Password management is handled through your IEEEUCSD account.
</p> </p>
<div className="alert alert-info"> <div className="alert alert-info">

View file

@ -1,38 +1,110 @@
import { useState, useEffect } from 'react'; 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() { export default function DisplaySettings() {
const auth = Authentication.getInstance();
const update = Update.getInstance();
const [theme, setTheme] = useState('dark'); const [theme, setTheme] = useState('dark');
const [fontSize, setFontSize] = useState('medium'); const [fontSize, setFontSize] = useState('medium');
const [colorBlindMode, setColorBlindMode] = useState(false); const [colorBlindMode, setColorBlindMode] = useState(false);
const [reducedMotion, setReducedMotion] = useState(false); const [reducedMotion, setReducedMotion] = useState(false);
const [successMessage, setSuccessMessage] = useState(''); 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(() => { useEffect(() => {
const savedTheme = localStorage.getItem('theme') || 'dark'; const loadPreferences = async () => {
const savedFontSize = localStorage.getItem('fontSize') || 'medium'; try {
const savedColorBlindMode = localStorage.getItem('colorBlindMode') === 'true'; // First check localStorage for immediate UI updates
const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true'; 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); setTheme(savedTheme);
setFontSize(savedFontSize); setFontSize(savedFontSize);
setColorBlindMode(savedColorBlindMode); setColorBlindMode(savedColorBlindMode);
setReducedMotion(savedReducedMotion); setReducedMotion(savedReducedMotion);
// Apply theme to document // Apply theme to document
document.documentElement.setAttribute('data-theme', savedTheme); document.documentElement.setAttribute('data-theme', savedTheme);
// Apply font size // Apply font size
applyFontSize(savedFontSize); applyFontSize(savedFontSize);
// Apply accessibility settings // Apply accessibility settings
if (savedColorBlindMode) { if (savedColorBlindMode) {
document.documentElement.classList.add('color-blind-mode'); document.documentElement.classList.add('color-blind-mode');
} }
if (savedReducedMotion) { if (savedReducedMotion) {
document.documentElement.classList.add('reduced-motion'); 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 // Apply font size to document
@ -116,16 +188,51 @@ export default function DisplaySettings() {
}; };
// Handle form submission // Handle form submission
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
setSaving(true);
setSuccessMessage('');
setErrorMessage('');
// Show success message try {
setSuccessMessage('Display settings saved successfully!'); const user = auth.getCurrentUser();
if (!user) throw new Error('User not authenticated');
// Clear success message after 3 seconds // Save display preferences to user record
setTimeout(() => { const displayPreferences = {
setSuccessMessage(''); theme,
}, 3000); 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 ( return (
@ -138,6 +245,14 @@ export default function DisplaySettings() {
</div> </div>
)} )}
{errorMessage && (
<div className="alert alert-error mb-4">
<div>
<span>{errorMessage}</span>
</div>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6"> <form onSubmit={handleSubmit} className="space-y-6">
{/* Theme Selection */} {/* Theme Selection */}
<div className="form-control"> <div className="form-control">
@ -260,8 +375,12 @@ export default function DisplaySettings() {
</div> </div>
<div className="form-control mt-6"> <div className="form-control mt-6">
<button type="submit" className="btn btn-primary"> <button
Save Settings type="submit"
className={`btn btn-primary ${saving ? 'loading' : ''}`}
disabled={saving}
>
{saving ? 'Saving...' : 'Save Settings'}
</button> </button>
</div> </div>
</form> </form>

View file

@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { Update } from '../../../scripts/pocketbase/Update'; import { Update } from '../../../scripts/pocketbase/Update';
import { Collections, type User } from '../../../schemas/pocketbase/schema'; import { Collections, type User } from '../../../schemas/pocketbase/schema';
import allMajors from '../../../data/allUCSDMajors.txt?raw';
export default function UserProfileSettings() { export default function UserProfileSettings() {
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
@ -19,6 +20,9 @@ export default function UserProfileSettings() {
const [successMessage, setSuccessMessage] = useState(''); const [successMessage, setSuccessMessage] = useState('');
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
// Parse the majors list from the text file
const majorsList = allMajors.split('\n').filter(major => major.trim() !== '');
useEffect(() => { useEffect(() => {
const loadUserData = async () => { const loadUserData = async () => {
try { try {
@ -164,13 +168,19 @@ export default function UserProfileSettings() {
<label className="label"> <label className="label">
<span className="label-text">Major</span> <span className="label-text">Major</span>
</label> </label>
<input <select
type="text"
name="major" name="major"
value={formData.major} value={formData.major}
onChange={handleInputChange} onChange={handleInputChange}
className="input input-bordered w-full" className="select select-bordered w-full"
/> >
<option value="">Select a major</option>
{majorsList.map((major, index) => (
<option key={index} value={major}>
{major}
</option>
))}
</select>
</div> </div>
<div className="form-control"> <div className="form-control">

View file

@ -30,6 +30,10 @@ export interface User extends BaseRecord {
graduation_year?: number; graduation_year?: number;
major?: string; major?: string;
zelle_information?: 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)
} }
/** /**