Files
Arnaud 67246c69e5 fix: settings mobile layout, silent failure on language save
- Settings sidebar was a fixed-width vertical list with no responsive
  breakpoint, squeezing the form into a sliver on mobile. Turn it into
  a horizontal scrollable tab bar under md, vertical sidebar above.
- setLocale() fired the PATCH and swallowed the result with
  .catch(() => {}) — never checked res.ok, no success/failure feedback.
  A failed save (expired session, network blip) looked identical to a
  successful one. Now returns a boolean, reverts local state on
  failure, and the settings form toasts success/failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 11:00:48 +02:00

79 lines
2.3 KiB
TypeScript

"use client";
import { createContext, useContext, useEffect, useState } from "react";
import { NextIntlClientProvider } from "next-intl";
export const SUPPORTED_LOCALES = [
{ code: "en", label: "English" },
{ code: "fr", label: "Français" },
] as const;
export type Locale = (typeof SUPPORTED_LOCALES)[number]["code"];
const STORAGE_KEY = "epicure-locale";
const LocaleContext = createContext<{
locale: Locale;
setLocale: (locale: Locale) => Promise<boolean>;
}>({ locale: "en", setLocale: async () => false });
export function useLocale() {
return useContext(LocaleContext);
}
export function I18nProvider({
children,
messages,
initialLocale,
}: {
children: React.ReactNode;
messages: Record<string, Record<string, unknown>>;
initialLocale?: Locale;
}) {
const resolved = initialLocale ?? ("en" as Locale);
const [locale, setLocaleState] = useState<Locale>(resolved);
const [currentMessages, setCurrentMessages] = useState(messages[resolved] ?? messages["en"]!);
useEffect(() => {
// If no server-side locale, fall back to localStorage
if (!initialLocale) {
const stored = localStorage.getItem(STORAGE_KEY) as Locale | null;
if (stored && messages[stored]) {
setLocaleState(stored);
setCurrentMessages(messages[stored]!);
}
}
}, [messages, initialLocale]);
async function setLocale(next: Locale): Promise<boolean> {
const previous = locale;
const previousMessages = currentMessages;
setLocaleState(next);
setCurrentMessages(messages[next]!);
localStorage.setItem(STORAGE_KEY, next);
try {
const res = await fetch("/api/v1/users/me", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ locale: next }),
});
if (!res.ok) throw new Error("Failed to save locale");
return true;
} catch {
// Revert on failure so the UI doesn't claim a language that never saved.
setLocaleState(previous);
setCurrentMessages(previousMessages);
localStorage.setItem(STORAGE_KEY, previous);
return false;
}
}
return (
<LocaleContext.Provider value={{ locale, setLocale }}>
<NextIntlClientProvider locale={locale} messages={currentMessages} timeZone="UTC">
{children}
</NextIntlClientProvider>
</LocaleContext.Provider>
);
}