feat: add customizations page

This commit is contained in:
2026-06-18 21:54:12 -03:00
parent 7b08860b4e
commit 952de164b9
6 changed files with 787 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
'use client';
import shared from '../../styles/shared.module.css';
import styles from '../../styles/Customizations.module.css';
import { CustomizationItem, Tab, getCosmeticIconUrl } from './types';
type Props = {
tab: Tab;
allFilteredItems: CustomizationItem[];
pagedItems: CustomizationItem[];
page: number;
totalPages: number;
search: string;
unlockedSet: Set<string>;
characterMap: Map<number, string>;
onSearchChange: (s: string) => void;
onUnlockAll: () => void;
onLockAll: () => void;
onUnlockPage: () => void;
onLockPage: () => void;
onToggle: (id: string) => void;
onPageChange: (p: number) => void;
};
export default function FlatCategory({
tab,
allFilteredItems,
pagedItems,
page,
totalPages,
search,
unlockedSet,
characterMap,
onSearchChange,
onUnlockAll,
onLockAll,
onUnlockPage,
onLockPage,
onToggle,
onPageChange,
}: Props) {
const buildPageNumbers = () => {
const pages: number[] = [];
const start = Math.max(1, Math.min(page - 2, totalPages - 4));
const end = Math.min(totalPages, start + 4);
for (let i = start; i <= end; i++) pages.push(i);
return pages;
};
return (
<>
<div className={shared.toolbar}>
<input
className={shared.searchInput}
type="text"
placeholder={`Search ${tab}...`}
value={search}
onChange={e => onSearchChange(e.target.value)}
/>
<span className={shared.spacer} />
<span className={shared.resultCount}>{allFilteredItems.length} items</span>
<button className={shared.unlockAllBtn} onClick={onUnlockAll}>
Unlock all ({allFilteredItems.length})
</button>
<button className={shared.lockAllBtn} onClick={onLockAll}>
Lock all
</button>
<button className={styles.pageActionBtn} onClick={onUnlockPage}>
Unlock visible
</button>
<button className={styles.pageActionBtn} onClick={onLockPage}>
Lock visible
</button>
</div>
{allFilteredItems.length === 0 ? (
<div className={shared.empty}>No items found</div>
) : (
<>
<div className={styles.grid}>
{pagedItems.map(item => {
const unlocked = unlockedSet.has(item.id);
return (
<div
key={item.id}
className={`${shared.card} ${unlocked ? shared.cardUnlocked : ''}`}
onClick={() => onToggle(item.id)}
>
<img
className={shared.cardIcon}
src={getCosmeticIconUrl(item, characterMap)}
alt={item.name}
loading="lazy"
/>
<span className={shared.cardName}>{item.name}</span>
</div>
);
})}
</div>
{totalPages > 1 && (
<div className={shared.pagination}>
<button
className={`${shared.pageBtn} ${page === 1 ? shared.pageBtnDisabled : ''}`}
onClick={() => onPageChange(1)}
>«</button>
<button
className={`${shared.pageBtn} ${page === 1 ? shared.pageBtnDisabled : ''}`}
onClick={() => onPageChange(page - 1)}
></button>
{buildPageNumbers().map(n => (
<button
key={n}
className={`${shared.pageBtn} ${n === page ? shared.pageBtnActive : ''}`}
onClick={() => onPageChange(n)}
>{n}</button>
))}
<button
className={`${shared.pageBtn} ${page === totalPages ? shared.pageBtnDisabled : ''}`}
onClick={() => onPageChange(page + 1)}
></button>
<button
className={`${shared.pageBtn} ${page === totalPages ? shared.pageBtnDisabled : ''}`}
onClick={() => onPageChange(totalPages)}
>»</button>
<span className={shared.pageInfo}>{page} / {totalPages}</span>
</div>
)}
</>
)}
</>
);
}