UNO polish: center nav-rail items, drop per-page XP bar, shop category tabs
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 6m33s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m6s
CI/CD / Deploy - local stack (db + server + web) (push) Successful in 1m0s

- NavRail: vertically center items in the side rail (was top-aligned).
- ScreenHeader: showXp defaults off — the level/XP bar no longer clutters every
  sub-page (it lives on Home's chip + the Profile page).
- Shop: category tabs (avatars / fronts / backs / reactions / stickers / titles
  / XP) so only one category shows at a time — no more endless scroll.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-11 11:53:10 +03:30
parent 34678c4e0e
commit 3d3241b976
4 changed files with 40 additions and 16 deletions
+35 -13
View File
@@ -78,6 +78,7 @@ export function ShopScreen() {
const [items, setItems] = useState<ShopItem[]>([]);
const [msg, setMsg] = useState("");
const [detail, setDetail] = useState<ShopItem | null>(null);
const [cat, setCat] = useState<ShopItem["kind"]>("avatar");
useEffect(() => {
getService().getShopItems().then(setItems);
@@ -187,19 +188,40 @@ export function ShopScreen() {
</div>
)}
{sections.map((sec) => {
const list = items.filter((i) => i.kind === sec.kind);
if (!list.length) return null;
return (
<Section key={sec.kind} title={sec.title} hint={sec.hint}>
<div className="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-6 gap-3">
{list.map((item) => (
<ItemCard key={item.id} item={item} owned={owns(item)} reqLabel={lockLabel(item)} onOpen={() => setDetail(item)} />
))}
</div>
</Section>
);
})}
{/* category tabs */}
<div className="flex gap-2 overflow-x-auto pb-2 -mx-1 px-1 mb-3">
{sections.map((sec) => (
<button
key={sec.kind}
onClick={() => setCat(sec.kind)}
className={cn(
"shrink-0 rounded-full px-4 py-2 text-sm font-bold transition whitespace-nowrap",
cat === sec.kind ? "btn-gold" : "panel text-cream/70 hover:text-cream"
)}
>
{sec.title}
</button>
))}
</div>
{sections
.filter((sec) => sec.kind === cat)
.map((sec) => {
const list = items.filter((i) => i.kind === sec.kind);
return (
<Section key={sec.kind} title={sec.title} hint={sec.hint}>
{list.length === 0 ? (
<p className="text-center text-cream/40 text-sm py-8">{t("shop.emptyCat")}</p>
) : (
<div className="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-6 gap-3">
{list.map((item) => (
<ItemCard key={item.id} item={item} owned={owns(item)} reqLabel={lockLabel(item)} onOpen={() => setDetail(item)} />
))}
</div>
)}
</Section>
);
})}
<AnimatePresence>
{detail && (