feat(tiles): let users arrange the dashboard and profile tiles
Build, Test & Push Frontend / quality-check (push) Successful in 18s
Build, Test & Push Frontend / docker (push) Successful in 28s

This commit is contained in:
lobo committed 2026-09-28 15:03:16 +02:00
1 parent b55fcfec92
commit aa9e83e0cb
24 files changed
+792 -426

No files matched your search

+4 -31
View File
@@ -18,7 +18,6 @@ import BuildVersionCard from '../components/BuildVersionCard.jsx';
import DeleteAccountCard from '../components/DeleteAccountCard.jsx';
import FormField from '../components/FormField.jsx';
import SteamAccountCard from '../components/SteamAccountCard.jsx';
import ProfileStatsSettings from '../components/stats/ProfileStatsSettings.jsx';
import {settingsSchema} from '../utils/validation.js';
import {usePreferences} from '../context/PreferencesContext.jsx';
import useSessionExpired from '../hooks/useSessionExpired.js';
@@ -28,7 +27,6 @@ import ProfileStyleSettings from '../components/ProfileStyleSettings.jsx';
import {normalizeProfileStyle} from '../utils/profileStyle.js';
import useSaveProfileStyle from '../hooks/useSaveProfileStyle.js';
import {coverUrl} from '../utils/games.js';
import {saveProfileStats} from '../utils/stats.js';
import {apiFetch, refreshSession} from '../utils/api.js';
import {hasSession, markSession} from '../utils/auth.js';
@@ -66,7 +64,6 @@ export default function SettingsPage() {
const [profile, setProfile] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [loadError, setLoadError] = useState(null);
const [savingProfileStats, setSavingProfileStats] = useState(false);
const [savingProfileStyle, setSavingProfileStyle] = useState(false);
const [searchParams, setSearchParams] = useSearchParams();
// Read once: removing the parameter below must not load the profile again.
@@ -206,31 +203,7 @@ export default function SettingsPage() {
}
};
// Shown at once; the switches wait for the answer, so the saves reach Auth one after the other.
const handleProfileStatsChange = async (groups) => {
const previous = profile?.settings?.profileStats ?? [];
const showProfileStats = (next) => setProfile((p) => ({...p, settings: {...p.settings, profileStats: next}}));
showProfileStats(groups);
setSavingProfileStats(true);
try {
const response = await saveProfileStats(groups);
if (response.status === 401) {
expireSession();
return;
}
if (!response.ok) {
showProfileStats(previous);
toast.error(t('SETTINGS.ERROR_SAVE'));
}
} catch (err) {
console.error('Profile stats save error:', err);
showProfileStats(previous);
toast.error(t('INFO.ERROR.NETWORK_FAILURE'));
} finally {
setSavingProfileStats(false);
}
};
// Shown at once; the pickers wait for the answer, so the saves reach Auth one after the other.
const handleProfileStyleChange = async (changes) => {
const previous = normalizeProfileStyle(profile?.settings?.profileStyle);
const showProfileStyle = (next) => setProfile((p) => ({...p, settings: {...p.settings, profileStyle: next}}));
@@ -447,9 +420,9 @@ export default function SettingsPage() {
</Button>
</div>
)}
<ProfileStatsSettings selected={profile?.settings?.profileStats ?? []}
disabled={!publicProfile} saving={savingProfileStats}
onChange={handleProfileStatsChange}/>
{publicProfile && profileLink && (
<p className="text-xs text-muted-foreground">{t('SETTINGS.PROFILE_TILES_HINT')}</p>
)}
</div>
<div className="mt-6 border-t pt-6">