From 2602766542ae436c9db33053dd58c20940affb6f Mon Sep 17 00:00:00 2001 From: kutaelee Date: Fri, 6 Mar 2026 15:22:21 +0900 Subject: [PATCH 1/2] fix(ui): improve home nav, mobile admin tabs, and prefs loading fallback --- packages/ui/src/api/mePreferences.ts | 10 +++++++- packages/ui/src/app/AdminShell.css | 37 ++++++++++++++++++++++++++++ packages/ui/src/app/AppShell.css | 3 +++ packages/ui/src/app/AppShell.tsx | 4 ++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/api/mePreferences.ts b/packages/ui/src/api/mePreferences.ts index c8f98e4..f7eb676 100644 --- a/packages/ui/src/api/mePreferences.ts +++ b/packages/ui/src/api/mePreferences.ts @@ -1,3 +1,4 @@ +import { ApiError } from "./errors"; import { createApiClient } from "./client"; import type { components } from "./schema"; @@ -7,7 +8,14 @@ export type UserPreferencesPatch = components["schemas"]["UserPreferencesPatch"] export const createMePreferencesApi = (client: ReturnType) => { return { getPreferences: async (): Promise => { - return client.request({ path: "/me/preferences" }); + try { + return await client.request({ path: "/me/preferences" }); + } catch (error) { + if (error instanceof ApiError && error.status === 404) { + return client.request({ path: "/me" }); + } + throw error; + } }, setPreferences: async (payload: UserPreferencesPatch): Promise => { return client.request({ diff --git a/packages/ui/src/app/AdminShell.css b/packages/ui/src/app/AdminShell.css index 97c3a21..3f626ee 100644 --- a/packages/ui/src/app/AdminShell.css +++ b/packages/ui/src/app/AdminShell.css @@ -75,3 +75,40 @@ color: var(--nd-color-accent-default); font-weight: 600; } + +@media (max-width: 900px) { + .admin-shell { + flex-direction: column; + } + + .admin-shell__sidebar { + width: 100%; + border-right: none; + border-bottom: 1px solid var(--nd-color-border-default); + padding: 10px; + gap: 8px; + } + + .admin-shell__title, + .admin-shell__subtitle { + font-size: 11px; + } + + .admin-shell__nav { + flex-direction: row; + flex-wrap: nowrap; + gap: 6px; + overflow-x: auto; + padding-bottom: 2px; + } + + .admin-shell__link { + margin-bottom: 0; + white-space: nowrap; + flex: 0 0 auto; + } + + .admin-shell__content { + padding: 12px; + } +} diff --git a/packages/ui/src/app/AppShell.css b/packages/ui/src/app/AppShell.css index 5b21e02..7f7edeb 100644 --- a/packages/ui/src/app/AppShell.css +++ b/packages/ui/src/app/AppShell.css @@ -53,6 +53,9 @@ color: var(--nd-color-text-primary); margin-right: var(--nd-space-4); white-space: nowrap; + text-decoration: none; + display: inline-flex; + align-items: center; } .app-shell__search { diff --git a/packages/ui/src/app/AppShell.tsx b/packages/ui/src/app/AppShell.tsx index 4001a6b..3e7f5c6 100644 --- a/packages/ui/src/app/AppShell.tsx +++ b/packages/ui/src/app/AppShell.tsx @@ -105,7 +105,9 @@ export function AppShell() {
-
{t("app.brand")}
+ + {t("app.brand")} + Date: Fri, 6 Mar 2026 15:34:24 +0900 Subject: [PATCH 2/2] fix(ui): persist locale globally and apply immediately after save --- packages/ui/src/app/AdminAppearancePage.tsx | 7 +++++- packages/ui/src/i18n/t.ts | 27 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/app/AdminAppearancePage.tsx b/packages/ui/src/app/AdminAppearancePage.tsx index 105b39e..3e27815 100644 --- a/packages/ui/src/app/AdminAppearancePage.tsx +++ b/packages/ui/src/app/AdminAppearancePage.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useState } from "react"; import { Button, ErrorState, LoadingSkeleton, PageHeader, Toolbar } from "@nimbus/ui-kit"; import type { I18nKey } from "../i18n/t"; -import { t } from "../i18n/t"; +import { setLocale, t } from "../i18n/t"; import { getAuthenticatedApiClient } from "./authenticatedApiClient"; import { createMePreferencesApi } from "../api/mePreferences"; import type { components } from "../api/schema"; @@ -63,6 +63,7 @@ export default function AdminAppearancePage() { try { const me = await api.getPreferences(); + setLocale(me.locale); const profile: AppearanceConfig = { locale: me.locale, theme: themeRef.current, @@ -83,9 +84,13 @@ export default function AdminAppearancePage() { try { await api.setPreferences({ locale: draft.locale }); + setLocale(draft.locale); toggleTheme(draft.theme); setCurrent(draft); setMessageKey("admin.appearance.saved"); + if (typeof window !== "undefined") { + window.setTimeout(() => window.location.reload(), 120); + } } catch { setErrorKey("admin.appearance.saveError"); } finally { diff --git a/packages/ui/src/i18n/t.ts b/packages/ui/src/i18n/t.ts index e64d1ad..4f2bea2 100644 --- a/packages/ui/src/i18n/t.ts +++ b/packages/ui/src/i18n/t.ts @@ -3,6 +3,8 @@ import en from "./locales/en-US.json"; export type Locale = "ko-KR" | "en-US"; +const LOCALE_KEY = "ui.appearance.locale"; + const dictionaries = { "ko-KR": ko, "en-US": en, @@ -10,7 +12,28 @@ const dictionaries = { export type I18nKey = keyof typeof dictionaries["ko-KR"]; -export const t = (key: I18nKey, locale: Locale = "ko-KR"): string => { - const dict = dictionaries[locale] ?? dictionaries["ko-KR"]; +let currentLocale: Locale = "ko-KR"; + +if (typeof window !== "undefined") { + const saved = window.localStorage.getItem(LOCALE_KEY); + if (saved === "ko-KR" || saved === "en-US") { + currentLocale = saved; + } +} + +export function getLocale(): Locale { + return currentLocale; +} + +export function setLocale(locale: Locale) { + currentLocale = locale; + if (typeof window !== "undefined") { + window.localStorage.setItem(LOCALE_KEY, locale); + } +} + +export const t = (key: I18nKey, locale?: Locale): string => { + const effectiveLocale = locale ?? currentLocale; + const dict = dictionaries[effectiveLocale] ?? dictionaries["ko-KR"]; return dict[key] ?? key; };