update settings
This commit is contained in:
parent
b7881213ce
commit
4a33fc3ce4
4 changed files with 168 additions and 35 deletions
|
@ -130,8 +130,8 @@ export default function AccountSecuritySettings() {
|
|||
<div>
|
||||
<h4 className="font-semibold text-lg mb-2">Authentication Options</h4>
|
||||
<p className="text-sm opacity-70 mb-4">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<div className="alert alert-info">
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
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 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';
|
||||
|
@ -33,6 +43,68 @@ export default function DisplaySettings() {
|
|||
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,8 +188,37 @@ export default function DisplaySettings() {
|
|||
};
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
setSuccessMessage('');
|
||||
setErrorMessage('');
|
||||
|
||||
try {
|
||||
const user = auth.getCurrentUser();
|
||||
if (!user) throw new Error('User not authenticated');
|
||||
|
||||
// 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!');
|
||||
|
@ -126,6 +227,12 @@ export default function DisplaySettings() {
|
|||
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() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{errorMessage && (
|
||||
<div className="alert alert-error mb-4">
|
||||
<div>
|
||||
<span>{errorMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Theme Selection */}
|
||||
<div className="form-control">
|
||||
|
@ -260,8 +375,12 @@ export default function DisplaySettings() {
|
|||
</div>
|
||||
|
||||
<div className="form-control mt-6">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Save Settings
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn btn-primary ${saving ? 'loading' : ''}`}
|
||||
disabled={saving}
|
||||
>
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -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() {
|
|||
<label className="label">
|
||||
<span className="label-text">Major</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
<select
|
||||
name="major"
|
||||
value={formData.major}
|
||||
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 className="form-control">
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue