add default settings
This commit is contained in:
parent
4a33fc3ce4
commit
03b0e677ed
5 changed files with 265 additions and 258 deletions
|
@ -4,6 +4,7 @@ import UserProfileSettings from "./SettingsSection/UserProfileSettings";
|
|||
import AccountSecuritySettings from "./SettingsSection/AccountSecuritySettings";
|
||||
import NotificationSettings from "./SettingsSection/NotificationSettings";
|
||||
import DisplaySettings from "./SettingsSection/DisplaySettings";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
---
|
||||
|
||||
<div id="settings-section" class="">
|
||||
|
@ -12,6 +13,8 @@ import DisplaySettings from "./SettingsSection/DisplaySettings";
|
|||
<p class="opacity-70">Manage your account settings and preferences</p>
|
||||
</div>
|
||||
|
||||
<Toaster position="top-right" client:load />
|
||||
|
||||
<!-- Profile Settings Card -->
|
||||
<div
|
||||
class="card bg-base-100 shadow-xl border border-base-200 hover:border-primary transition-all duration-300 hover:-translate-y-1 transform mb-6"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import { SendLog } from '../../../scripts/pocketbase/SendLog';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
export default function AccountSecuritySettings() {
|
||||
const auth = Authentication.getInstance();
|
||||
|
@ -50,6 +51,7 @@ export default function AccountSecuritySettings() {
|
|||
window.location.href = '/';
|
||||
} catch (error) {
|
||||
console.error('Error during logout:', error);
|
||||
toast.error('Failed to log out. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -96,11 +98,9 @@ export default function AccountSecuritySettings() {
|
|||
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<div className="alert alert-error">
|
||||
<div>
|
||||
<div className="p-4 text-error bg-error bg-opacity-10 rounded-lg">
|
||||
<span>You must be logged in to access this page.</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,11 +134,9 @@ export default function AccountSecuritySettings() {
|
|||
Password management is handled through your IEEEUCSD account.
|
||||
</p>
|
||||
|
||||
<div className="alert alert-info">
|
||||
<div>
|
||||
<span>To change your password, please visit the UCSD SSO portal.</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-info p-3 bg-info bg-opacity-10 rounded-lg">
|
||||
To change your password, please use the "Forgot Password" option on the login page.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Account Actions */}
|
||||
|
@ -153,14 +151,10 @@ export default function AccountSecuritySettings() {
|
|||
Sign Out
|
||||
</button>
|
||||
|
||||
<div className="alert alert-warning">
|
||||
<div>
|
||||
<span>
|
||||
<p className="text-sm text-warning p-3 bg-warning bg-opacity-10 rounded-lg">
|
||||
If you need to delete your account or have other account-related issues,
|
||||
please contact an IEEE UCSD administrator.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,16 +2,27 @@ import { useState, useEffect } from 'react';
|
|||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import { Update } from '../../../scripts/pocketbase/Update';
|
||||
import { Collections } from '../../../schemas/pocketbase/schema';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
// Default display preferences
|
||||
const DEFAULT_DISPLAY_PREFERENCES = {
|
||||
theme: 'dark',
|
||||
fontSize: 'medium'
|
||||
};
|
||||
|
||||
// Default accessibility settings
|
||||
const DEFAULT_ACCESSIBILITY_SETTINGS = {
|
||||
colorBlindMode: false,
|
||||
reducedMotion: false
|
||||
};
|
||||
|
||||
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 [theme, setTheme] = useState(DEFAULT_DISPLAY_PREFERENCES.theme);
|
||||
const [fontSize, setFontSize] = useState(DEFAULT_DISPLAY_PREFERENCES.fontSize);
|
||||
const [colorBlindMode, setColorBlindMode] = useState(DEFAULT_ACCESSIBILITY_SETTINGS.colorBlindMode);
|
||||
const [reducedMotion, setReducedMotion] = useState(DEFAULT_ACCESSIBILITY_SETTINGS.reducedMotion);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
// Load saved preferences on component mount
|
||||
|
@ -19,18 +30,20 @@ export default function DisplaySettings() {
|
|||
const loadPreferences = async () => {
|
||||
try {
|
||||
// First check localStorage for immediate UI updates
|
||||
const savedTheme = localStorage.getItem('theme') || 'dark';
|
||||
const savedFontSize = localStorage.getItem('fontSize') || 'medium';
|
||||
const savedTheme = localStorage.getItem('theme') || DEFAULT_DISPLAY_PREFERENCES.theme;
|
||||
// Ensure theme is either light or dark
|
||||
const validTheme = ['light', 'dark'].includes(savedTheme) ? savedTheme : DEFAULT_DISPLAY_PREFERENCES.theme;
|
||||
const savedFontSize = localStorage.getItem('fontSize') || DEFAULT_DISPLAY_PREFERENCES.fontSize;
|
||||
const savedColorBlindMode = localStorage.getItem('colorBlindMode') === 'true';
|
||||
const savedReducedMotion = localStorage.getItem('reducedMotion') === 'true';
|
||||
|
||||
setTheme(savedTheme);
|
||||
setTheme(validTheme);
|
||||
setFontSize(savedFontSize);
|
||||
setColorBlindMode(savedColorBlindMode);
|
||||
setReducedMotion(savedReducedMotion);
|
||||
|
||||
// Apply theme to document
|
||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||
document.documentElement.setAttribute('data-theme', validTheme);
|
||||
|
||||
// Apply font size
|
||||
applyFontSize(savedFontSize);
|
||||
|
@ -46,15 +59,23 @@ export default function DisplaySettings() {
|
|||
|
||||
// Then check if user has saved preferences in their profile
|
||||
const user = auth.getCurrentUser();
|
||||
if (user && user.display_preferences) {
|
||||
if (user) {
|
||||
let needsDisplayPrefsUpdate = false;
|
||||
let needsAccessibilityUpdate = false;
|
||||
|
||||
// Check and handle display preferences
|
||||
if (user.display_preferences && typeof user.display_preferences === 'string' && user.display_preferences.trim() !== '') {
|
||||
try {
|
||||
const userPrefs = JSON.parse(user.display_preferences);
|
||||
|
||||
// Only update if values exist and are different from localStorage
|
||||
if (userPrefs.theme && userPrefs.theme !== savedTheme) {
|
||||
if (userPrefs.theme && ['light', 'dark'].includes(userPrefs.theme) && userPrefs.theme !== validTheme) {
|
||||
setTheme(userPrefs.theme);
|
||||
localStorage.setItem('theme', userPrefs.theme);
|
||||
document.documentElement.setAttribute('data-theme', userPrefs.theme);
|
||||
} else if (!['light', 'dark'].includes(userPrefs.theme)) {
|
||||
// If theme is not valid, mark for update
|
||||
needsDisplayPrefsUpdate = true;
|
||||
}
|
||||
|
||||
if (userPrefs.fontSize && userPrefs.fontSize !== savedFontSize) {
|
||||
|
@ -64,10 +85,14 @@ export default function DisplaySettings() {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing display preferences:', e);
|
||||
needsDisplayPrefsUpdate = true;
|
||||
}
|
||||
} else {
|
||||
needsDisplayPrefsUpdate = true;
|
||||
}
|
||||
|
||||
if (user && user.accessibility_settings) {
|
||||
// Check and handle accessibility settings
|
||||
if (user.accessibility_settings && typeof user.accessibility_settings === 'string' && user.accessibility_settings.trim() !== '') {
|
||||
try {
|
||||
const accessibilityPrefs = JSON.parse(user.accessibility_settings);
|
||||
|
||||
|
@ -96,17 +121,54 @@ export default function DisplaySettings() {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing accessibility settings:', e);
|
||||
needsAccessibilityUpdate = true;
|
||||
}
|
||||
} else {
|
||||
needsAccessibilityUpdate = true;
|
||||
}
|
||||
|
||||
// Initialize default settings if needed
|
||||
if (needsDisplayPrefsUpdate || needsAccessibilityUpdate) {
|
||||
await initializeDefaultSettings(user.id, needsDisplayPrefsUpdate, needsAccessibilityUpdate);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading preferences:', error);
|
||||
setErrorMessage('Failed to load display preferences');
|
||||
toast.error('Failed to load display preferences');
|
||||
}
|
||||
};
|
||||
|
||||
loadPreferences();
|
||||
}, []);
|
||||
|
||||
// Initialize default settings if not set
|
||||
const initializeDefaultSettings = async (userId: string, updateDisplayPrefs: boolean, updateAccessibility: boolean) => {
|
||||
try {
|
||||
const updateData: any = {};
|
||||
|
||||
if (updateDisplayPrefs) {
|
||||
updateData.display_preferences = JSON.stringify({
|
||||
theme,
|
||||
fontSize
|
||||
});
|
||||
}
|
||||
|
||||
if (updateAccessibility) {
|
||||
updateData.accessibility_settings = JSON.stringify({
|
||||
colorBlindMode,
|
||||
reducedMotion
|
||||
});
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
await update.updateFields(Collections.USERS, userId, updateData);
|
||||
console.log('Initialized default display and accessibility settings');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initializing default settings:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Apply font size to document
|
||||
const applyFontSize = (size: string) => {
|
||||
const htmlElement = document.documentElement;
|
||||
|
@ -191,8 +253,6 @@ export default function DisplaySettings() {
|
|||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
setSuccessMessage('');
|
||||
setErrorMessage('');
|
||||
|
||||
try {
|
||||
const user = auth.getCurrentUser();
|
||||
|
@ -221,15 +281,10 @@ export default function DisplaySettings() {
|
|||
);
|
||||
|
||||
// Show success message
|
||||
setSuccessMessage('Display settings saved successfully!');
|
||||
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
setSuccessMessage('');
|
||||
}, 3000);
|
||||
toast.success('Display settings saved successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error saving display settings:', error);
|
||||
setErrorMessage('Failed to save display settings to your profile');
|
||||
toast.error('Failed to save display settings to your profile');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
@ -237,77 +292,33 @@ export default function DisplaySettings() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{successMessage && (
|
||||
<div className="alert alert-success mb-4">
|
||||
<div>
|
||||
<span>{successMessage}</span>
|
||||
</div>
|
||||
</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">
|
||||
<label className="label">
|
||||
<span className="label-text font-medium">Theme</span>
|
||||
</label>
|
||||
{/* Theme Settings */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-lg mb-2">Theme</h4>
|
||||
<div className="form-control w-full max-w-xs">
|
||||
<select
|
||||
className="select select-bordered w-full"
|
||||
value={theme}
|
||||
onChange={handleThemeChange}
|
||||
className="select select-bordered"
|
||||
>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="cupcake">Cupcake</option>
|
||||
<option value="bumblebee">Bumblebee</option>
|
||||
<option value="emerald">Emerald</option>
|
||||
<option value="corporate">Corporate</option>
|
||||
<option value="synthwave">Synthwave</option>
|
||||
<option value="retro">Retro</option>
|
||||
<option value="cyberpunk">Cyberpunk</option>
|
||||
<option value="valentine">Valentine</option>
|
||||
<option value="halloween">Halloween</option>
|
||||
<option value="garden">Garden</option>
|
||||
<option value="forest">Forest</option>
|
||||
<option value="aqua">Aqua</option>
|
||||
<option value="lofi">Lo-Fi</option>
|
||||
<option value="pastel">Pastel</option>
|
||||
<option value="fantasy">Fantasy</option>
|
||||
<option value="wireframe">Wireframe</option>
|
||||
<option value="black">Black</option>
|
||||
<option value="luxury">Luxury</option>
|
||||
<option value="dracula">Dracula</option>
|
||||
<option value="cmyk">CMYK</option>
|
||||
<option value="autumn">Autumn</option>
|
||||
<option value="business">Business</option>
|
||||
<option value="acid">Acid</option>
|
||||
<option value="lemonade">Lemonade</option>
|
||||
<option value="night">Night</option>
|
||||
<option value="coffee">Coffee</option>
|
||||
<option value="winter">Winter</option>
|
||||
</select>
|
||||
<label className="label">
|
||||
<span className="label-text-alt">Choose a theme for your dashboard</span>
|
||||
<span className="label-text-alt">Select your preferred theme</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Font Size */}
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text font-medium">Font Size</span>
|
||||
</label>
|
||||
{/* Font Size Settings */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-lg mb-2">Font Size</h4>
|
||||
<div className="form-control w-full max-w-xs">
|
||||
<select
|
||||
className="select select-bordered w-full"
|
||||
value={fontSize}
|
||||
onChange={handleFontSizeChange}
|
||||
className="select select-bordered"
|
||||
>
|
||||
<option value="small">Small</option>
|
||||
<option value="medium">Medium</option>
|
||||
|
@ -315,36 +326,37 @@ export default function DisplaySettings() {
|
|||
<option value="extra-large">Extra Large</option>
|
||||
</select>
|
||||
<label className="label">
|
||||
<span className="label-text-alt">Adjust the text size for better readability</span>
|
||||
<span className="label-text-alt">Select your preferred font size</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Accessibility Settings */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-lg mb-2">Accessibility</h4>
|
||||
|
||||
{/* Accessibility Options */}
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text font-medium">Accessibility Options</span>
|
||||
</label>
|
||||
|
||||
<div className="space-y-4 p-4 bg-base-200 rounded-lg">
|
||||
<label className="cursor-pointer label justify-start gap-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle toggle-primary"
|
||||
checked={colorBlindMode}
|
||||
onChange={handleColorBlindModeChange}
|
||||
className="toggle toggle-primary"
|
||||
/>
|
||||
<div>
|
||||
<span className="label-text font-medium">Color Blind Mode</span>
|
||||
<p className="text-xs opacity-70">Enhances color contrast for better visibility</p>
|
||||
<p className="text-xs opacity-70">Enhances color contrast and uses color-blind friendly palettes</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="form-control mt-2">
|
||||
<label className="cursor-pointer label justify-start gap-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="toggle toggle-primary"
|
||||
checked={reducedMotion}
|
||||
onChange={handleReducedMotionChange}
|
||||
className="toggle toggle-primary"
|
||||
/>
|
||||
<div>
|
||||
<span className="label-text font-medium">Reduced Motion</span>
|
||||
|
@ -354,27 +366,11 @@ export default function DisplaySettings() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview */}
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text font-medium">Preview</span>
|
||||
</label>
|
||||
<div className="p-4 bg-base-200 rounded-lg">
|
||||
<div className="card bg-base-100 shadow-md">
|
||||
<div className="card-body">
|
||||
<h3 className="card-title">Theme Preview</h3>
|
||||
<p>This is how your content will look with the selected settings.</p>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<button className="btn btn-primary">Primary</button>
|
||||
<button className="btn btn-secondary">Secondary</button>
|
||||
<button className="btn btn-accent">Accent</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-info">
|
||||
These settings are saved to your browser and your IEEE UCSD account. They will be applied whenever you log in.
|
||||
</p>
|
||||
|
||||
<div className="form-control mt-6">
|
||||
<div className="form-control">
|
||||
<button
|
||||
type="submit"
|
||||
className={`btn btn-primary ${saving ? 'loading' : ''}`}
|
||||
|
|
|
@ -2,24 +2,26 @@ import { useState, useEffect } from 'react';
|
|||
import { Authentication } from '../../../scripts/pocketbase/Authentication';
|
||||
import { Update } from '../../../scripts/pocketbase/Update';
|
||||
import { Collections } from '../../../schemas/pocketbase/schema';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
export default function NotificationSettings() {
|
||||
const auth = Authentication.getInstance();
|
||||
const update = Update.getInstance();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [successMessage, setSuccessMessage] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
// Notification preferences
|
||||
const [preferences, setPreferences] = useState({
|
||||
// Default notification preferences
|
||||
const DEFAULT_NOTIFICATION_PREFERENCES = {
|
||||
emailNotifications: true,
|
||||
eventReminders: true,
|
||||
eventUpdates: true,
|
||||
reimbursementUpdates: true,
|
||||
officerAnnouncements: true,
|
||||
marketingEmails: false
|
||||
});
|
||||
};
|
||||
|
||||
export default function NotificationSettings() {
|
||||
const auth = Authentication.getInstance();
|
||||
const update = Update.getInstance();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
// Notification preferences
|
||||
const [preferences, setPreferences] = useState(DEFAULT_NOTIFICATION_PREFERENCES);
|
||||
|
||||
useEffect(() => {
|
||||
const loadPreferences = async () => {
|
||||
|
@ -28,7 +30,7 @@ export default function NotificationSettings() {
|
|||
if (user) {
|
||||
// If user has notification_preferences, parse and use them
|
||||
// Otherwise use defaults
|
||||
if (user.notification_preferences) {
|
||||
if (user.notification_preferences && typeof user.notification_preferences === 'string' && user.notification_preferences.trim() !== '') {
|
||||
try {
|
||||
const savedPrefs = JSON.parse(user.notification_preferences);
|
||||
setPreferences(prev => ({
|
||||
|
@ -37,12 +39,17 @@ export default function NotificationSettings() {
|
|||
}));
|
||||
} catch (e) {
|
||||
console.error('Error parsing notification preferences:', e);
|
||||
// Initialize with defaults and save to user profile
|
||||
await initializeDefaultPreferences(user.id);
|
||||
}
|
||||
} else {
|
||||
// Initialize with defaults and save to user profile
|
||||
await initializeDefaultPreferences(user.id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading notification preferences:', error);
|
||||
setErrorMessage('Failed to load notification preferences');
|
||||
toast.error('Failed to load notification preferences');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
@ -51,6 +58,21 @@ export default function NotificationSettings() {
|
|||
loadPreferences();
|
||||
}, []);
|
||||
|
||||
// Initialize default preferences if not set
|
||||
const initializeDefaultPreferences = async (userId: string) => {
|
||||
try {
|
||||
await update.updateFields(
|
||||
Collections.USERS,
|
||||
userId,
|
||||
{ notification_preferences: JSON.stringify(DEFAULT_NOTIFICATION_PREFERENCES) }
|
||||
);
|
||||
setPreferences(DEFAULT_NOTIFICATION_PREFERENCES);
|
||||
console.log('Initialized default notification preferences');
|
||||
} catch (error) {
|
||||
console.error('Error initializing default notification preferences:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, checked } = e.target;
|
||||
setPreferences(prev => ({
|
||||
|
@ -62,8 +84,6 @@ export default function NotificationSettings() {
|
|||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
setSuccessMessage('');
|
||||
setErrorMessage('');
|
||||
|
||||
try {
|
||||
const user = auth.getCurrentUser();
|
||||
|
@ -76,15 +96,10 @@ export default function NotificationSettings() {
|
|||
{ notification_preferences: JSON.stringify(preferences) }
|
||||
);
|
||||
|
||||
setSuccessMessage('Notification preferences saved successfully!');
|
||||
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
setSuccessMessage('');
|
||||
}, 3000);
|
||||
toast.success('Notification preferences saved successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error saving notification preferences:', error);
|
||||
setErrorMessage('Failed to save notification preferences');
|
||||
toast.error('Failed to save notification preferences');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
@ -100,22 +115,6 @@ export default function NotificationSettings() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{successMessage && (
|
||||
<div className="alert alert-success mb-4">
|
||||
<div>
|
||||
<span>{successMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{errorMessage && (
|
||||
<div className="alert alert-error mb-4">
|
||||
<div>
|
||||
<span>{errorMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="space-y-4">
|
||||
<div className="form-control">
|
||||
|
@ -215,13 +214,9 @@ export default function NotificationSettings() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="alert alert-info mt-6 mb-6">
|
||||
<div>
|
||||
<span>
|
||||
<p className="text-sm text-info mt-6 mb-6">
|
||||
Note: Some critical notifications about your account cannot be disabled.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div className="form-control">
|
||||
<button
|
||||
|
|
|
@ -3,6 +3,7 @@ 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';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
export default function UserProfileSettings() {
|
||||
const auth = Authentication.getInstance();
|
||||
|
@ -15,13 +16,16 @@ export default function UserProfileSettings() {
|
|||
email: '',
|
||||
major: '',
|
||||
graduation_year: '',
|
||||
zelle_information: ''
|
||||
zelle_information: '',
|
||||
pid: '',
|
||||
member_id: ''
|
||||
});
|
||||
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() !== '');
|
||||
// Parse the majors list from the text file and sort alphabetically
|
||||
const majorsList = allMajors
|
||||
.split('\n')
|
||||
.filter(major => major.trim() !== '')
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
useEffect(() => {
|
||||
const loadUserData = async () => {
|
||||
|
@ -34,12 +38,14 @@ export default function UserProfileSettings() {
|
|||
email: currentUser.email || '',
|
||||
major: currentUser.major || '',
|
||||
graduation_year: currentUser.graduation_year?.toString() || '',
|
||||
zelle_information: currentUser.zelle_information || ''
|
||||
zelle_information: currentUser.zelle_information || '',
|
||||
pid: currentUser.pid || '',
|
||||
member_id: currentUser.member_id || ''
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading user data:', error);
|
||||
setErrorMessage('Failed to load user data. Please try again later.');
|
||||
toast.error('Failed to load user data. Please try again later.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
@ -59,8 +65,6 @@ export default function UserProfileSettings() {
|
|||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
setSuccessMessage('');
|
||||
setErrorMessage('');
|
||||
|
||||
try {
|
||||
if (!user) throw new Error('User not authenticated');
|
||||
|
@ -68,7 +72,9 @@ export default function UserProfileSettings() {
|
|||
const updateData: Partial<User> = {
|
||||
name: formData.name,
|
||||
major: formData.major || undefined,
|
||||
zelle_information: formData.zelle_information || undefined
|
||||
zelle_information: formData.zelle_information || undefined,
|
||||
pid: formData.pid || undefined,
|
||||
member_id: formData.member_id || undefined
|
||||
};
|
||||
|
||||
// Only include graduation_year if it's a valid number
|
||||
|
@ -81,15 +87,10 @@ export default function UserProfileSettings() {
|
|||
// Update local user state
|
||||
setUser(prev => prev ? { ...prev, ...updateData } : null);
|
||||
|
||||
setSuccessMessage('Profile updated successfully!');
|
||||
|
||||
// Clear success message after 3 seconds
|
||||
setTimeout(() => {
|
||||
setSuccessMessage('');
|
||||
}, 3000);
|
||||
toast.success('Profile updated successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error updating profile:', error);
|
||||
setErrorMessage('Failed to update profile. Please try again.');
|
||||
toast.error('Failed to update profile. Please try again.');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
@ -115,22 +116,6 @@ export default function UserProfileSettings() {
|
|||
|
||||
return (
|
||||
<div>
|
||||
{successMessage && (
|
||||
<div className="alert alert-success mb-4">
|
||||
<div>
|
||||
<span>{successMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{errorMessage && (
|
||||
<div className="alert alert-error mb-4">
|
||||
<div>
|
||||
<span>{errorMessage}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
|
@ -163,6 +148,40 @@ export default function UserProfileSettings() {
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text">PID</span>
|
||||
<span className="label-text-alt text-info">UCSD Student ID</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="pid"
|
||||
value={formData.pid}
|
||||
onChange={handleInputChange}
|
||||
className="input input-bordered w-full"
|
||||
placeholder="A12345678"
|
||||
pattern="[A-Za-z][0-9]{8}"
|
||||
title="PID format: A12345678"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
<span className="label-text">IEEE Member ID</span>
|
||||
<span className="label-text-alt text-info">Optional</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="member_id"
|
||||
value={formData.member_id}
|
||||
onChange={handleInputChange}
|
||||
className="input input-bordered w-full"
|
||||
placeholder="IEEE Membership Number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="form-control">
|
||||
<label className="label">
|
||||
|
|
Loading…
Reference in a new issue