1
0
Fork 0

feat(preferences): expose XP mini-mode toggle in account settings

Add the mini XP display switch to PreferencesForm, wire it through the account page, and cover it in the integration test.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jason Fraley 2026-08-23 18:52:09 -04:00
parent b581670e72
commit 906af4feb1
3 changed files with 42 additions and 35 deletions

View file

@ -81,23 +81,24 @@ export default async function AccountPage() {
</CardContent> </CardContent>
</Card> </Card>
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Preferences</CardTitle> <CardTitle>Preferences</CardTitle>
<CardDescription>Control in-app notifications and display options.</CardDescription> <CardDescription>Control in-app notifications and display options.</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<PreferencesForm <PreferencesForm
mutedAll={notifications?.mutedAll ?? false} mutedAll={notifications?.mutedAll ?? false}
mutedTypes={notifications?.mutedTypes ?? []} mutedTypes={notifications?.mutedTypes ?? []}
showCallsign={fullUser.preferences?.display?.showCallsign ?? false} showCallsign={fullUser.preferences?.display?.showCallsign ?? false}
showVersionOverlay={fullUser.preferences?.display?.showVersionOverlay ?? true} showVersionOverlay={fullUser.preferences?.display?.showVersionOverlay ?? true}
leadOrActual={fullUser.preferences?.display?.leadOrActual ?? "lead"} leadOrActual={fullUser.preferences?.display?.leadOrActual ?? "lead"}
discordEnabled={discord?.enabled ?? false} discordEnabled={discord?.enabled ?? false}
discordMutedTypes={discord?.mutedTypes ?? []} discordMutedTypes={discord?.mutedTypes ?? []}
/> xpDisplayMini={fullUser.preferences?.display?.xpDisplayMini ?? false}
</CardContent> />
</Card> </CardContent>
</Card>
</div> </div>
</div> </div>
); );

View file

@ -20,6 +20,7 @@ interface PreferencesFormProps {
readonly leadOrActual: "lead" | "actual"; readonly leadOrActual: "lead" | "actual";
readonly discordEnabled: boolean; readonly discordEnabled: boolean;
readonly discordMutedTypes: readonly string[]; readonly discordMutedTypes: readonly string[];
readonly xpDisplayMini: boolean;
} }
export function PreferencesForm({ export function PreferencesForm({
@ -30,6 +31,7 @@ export function PreferencesForm({
leadOrActual: initialLeadOrActual, leadOrActual: initialLeadOrActual,
discordEnabled: initialDiscordEnabled, discordEnabled: initialDiscordEnabled,
discordMutedTypes: initialDiscordMutedTypes, discordMutedTypes: initialDiscordMutedTypes,
xpDisplayMini: initialXPDisplayMini,
}: PreferencesFormProps) { }: PreferencesFormProps) {
const router = useRouter(); const router = useRouter();
const [mutedAll, setMutedAll] = useState(initialMutedAll); const [mutedAll, setMutedAll] = useState(initialMutedAll);
@ -41,6 +43,7 @@ export function PreferencesForm({
const [discordMutedTypes, setDiscordMutedTypes] = useState<string[]>(() => [ const [discordMutedTypes, setDiscordMutedTypes] = useState<string[]>(() => [
...initialDiscordMutedTypes, ...initialDiscordMutedTypes,
]); ]);
const [xpDisplayMini, setXPDisplayMini] = useState(initialXPDisplayMini);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -72,6 +75,7 @@ export function PreferencesForm({
leadOrActual, leadOrActual,
discordEnabled, discordEnabled,
discordMutedTypes, discordMutedTypes,
xpDisplayMini,
}); });
if (!result.success) { if (!result.success) {
@ -101,24 +105,9 @@ export function PreferencesForm({
</CardHeader> </CardHeader>
<CardContent className="flex flex-col gap-4"> <CardContent className="flex flex-col gap-4">
<Label <Label
htmlFor="showVersionOverlay" htmlFor="showCallsign"
className="flex items-center justify-between gap-4 text-sm" className="flex items-center justify-between gap-4 text-sm"
> >
<span className="flex min-w-0 flex-col gap-1">
<span>Show version overlay</span>
<span className="text-sm font-normal text-muted-foreground">
Display the current build version at the top of every page.
</span>
</span>
<Switch
id="showVersionOverlay"
checked={showVersionOverlay}
onCheckedChange={setShowVersionOverlay}
aria-label="Show version overlay"
/>
</Label>
<Label htmlFor="showCallsign" className="flex items-center justify-between gap-4 text-sm">
<span className="flex min-w-0 flex-col gap-1"> <span className="flex min-w-0 flex-col gap-1">
<span>Show callsign instead of display name</span> <span>Show callsign instead of display name</span>
<span className="text-sm font-normal text-muted-foreground"> <span className="text-sm font-normal text-muted-foreground">
@ -131,6 +120,21 @@ export function PreferencesForm({
onCheckedChange={setShowCallsign} onCheckedChange={setShowCallsign}
aria-label="Show callsign instead of display name" aria-label="Show callsign instead of display name"
/> />
</Label>
<Label htmlFor="xpDisplayMini" className="flex items-center justify-between gap-4 text-sm">
<span className="flex min-w-0 flex-col gap-1">
<span>Use mini mode for XP display</span>
<span className="text-sm font-normal text-muted-foreground">
Shows level name and XP instead of progress bar.
</span>
</span>
<Switch
id="xpDisplayMini"
checked={xpDisplayMini}
onCheckedChange={setXPDisplayMini}
aria-label="Use mini mode for XP display"
/>
</Label> </Label>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">

View file

@ -40,11 +40,12 @@ describe("PreferencesForm", () => {
leadOrActual: "lead", leadOrActual: "lead",
discordEnabled: false, discordEnabled: false,
discordMutedTypes: [], discordMutedTypes: [],
xpDisplayMini: false,
}), }),
); );
fireEvent.click(screen.getByRole("switch", { name: "Show version overlay" }));
fireEvent.click(screen.getByRole("switch", { name: "Show callsign instead of display name" })); fireEvent.click(screen.getByRole("switch", { name: "Show callsign instead of display name" }));
fireEvent.click(screen.getByRole("switch", { name: "Use mini mode for XP display" }));
fireEvent.click(screen.getByRole("switch", { name: "Mute all in-app notifications" })); fireEvent.click(screen.getByRole("switch", { name: "Mute all in-app notifications" }));
fireEvent.click(screen.getByRole("radio", { name: "Actual" })); fireEvent.click(screen.getByRole("radio", { name: "Actual" }));
fireEvent.click(screen.getByRole("switch", { name: "Enable Discord notifications" })); fireEvent.click(screen.getByRole("switch", { name: "Enable Discord notifications" }));
@ -60,10 +61,11 @@ describe("PreferencesForm", () => {
mutedAll: true, mutedAll: true,
mutedTypes: ["market:offer"], mutedTypes: ["market:offer"],
showCallsign: true, showCallsign: true,
showVersionOverlay: false, showVersionOverlay: true,
leadOrActual: "actual", leadOrActual: "actual",
discordEnabled: true, discordEnabled: true,
discordMutedTypes: ["market:offer"], discordMutedTypes: ["market:offer"],
xpDisplayMini: true,
}); });
}); });
}); });