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>
|
<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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue