Merge pull request #19 from Yiling-J/refactor-equipment-single-select-and-fitting-room-header-13808441959024977531

feat: single select equipment category and updated fitting room header
This commit is contained in:
Yiling-J authored and GitHub committed 2026-02-14 09:57:09 +08:00
commit 155744374d
4 files changed
+43 -46

No files matched your search

+17 -12
View File
@@ -6,6 +6,7 @@ interface PageHeaderProps {
title: ReactNode
subtitle?: ReactNode
actions?: ReactNode
backButton?: ReactNode
children?: ReactNode
className?: string
sticky?: boolean
@@ -16,6 +17,7 @@ export function PageHeader({
title,
subtitle,
actions,
backButton,
children,
className,
sticky = true,
@@ -32,18 +34,21 @@ export function PageHeader({
style={sticky ? { top: topOffset } : undefined}
>
<div className="flex justify-between items-end animate-fade-in-down">
<div>
{subtitle && (
<div className="flex items-center gap-2 text-cyan-600 mb-2 tracking-[0.3em] text-xs font-mono uppercase">
<Hexagon size={12} className="animate-spin-slow" />
{subtitle}
</div>
)}
<h1 className="text-3xl md:text-5xl font-display font-black uppercase text-slate-900 tracking-tighter">
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-600 to-blue-600">
{title}
</span>
</h1>
<div className="flex items-center gap-4">
{backButton}
<div>
{subtitle && (
<div className="flex items-center gap-2 text-cyan-600 mb-2 tracking-[0.3em] text-xs font-mono uppercase">
<Hexagon size={12} className="animate-spin-slow" />
{subtitle}
</div>
)}
<h1 className="text-3xl md:text-5xl font-display font-black uppercase text-slate-900 tracking-tighter">
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-600 to-blue-600">
{title}
</span>
</h1>
</div>
</div>
{actions && <div className="flex items-center gap-4">{actions}</div>}
</div>
+1 -1
View File
@@ -61,7 +61,7 @@ export default function Characters() {
}
return (
<div className="w-full h-full p-4 pt-2 flex flex-col font-sans text-slate-900 relative">
<div className="w-full h-full p-4 pt-2 flex flex-col text-slate-900 relative">
<PageHeader
title="Characters"
subtitle="System // Character_List"
+6 -8
View File
@@ -36,7 +36,7 @@ export default function Equipments() {
const [items, setItems] = useState<EquipmentItem[]>([])
const [outfits, setOutfits] = useState<OutfitItem[]>([])
const [loading, setLoading] = useState(true)
const [selectedCategories, setSelectedCategories] = useState<string[]>([])
const [selectedCategory, setSelectedCategory] = useState<string | null>(null)
const [extractorOpen, setExtractorOpen] = useState(false)
const [createOutfitOpen, setCreateOutfitOpen] = useState(false)
const [viewMode, setViewMode] = useState<'equipments' | 'outfits'>('equipments')
@@ -49,7 +49,7 @@ export default function Equipments() {
} else {
fetchOutfits()
}
}, [viewMode, selectedCategories])
}, [viewMode, selectedCategory])
const fetchItems = async () => {
setLoading(true)
@@ -58,8 +58,8 @@ export default function Equipments() {
limit: '100',
}
if (selectedCategories.length > 0) {
query.category = selectedCategories
if (selectedCategory) {
query.category = [selectedCategory]
}
const res = await client.equipments.$get({
@@ -106,9 +106,7 @@ export default function Equipments() {
}
const toggleCategory = (category: string) => {
setSelectedCategories((prev) =>
prev.includes(category) ? prev.filter((c) => c !== category) : [...prev, category],
)
setSelectedCategory((prev) => (prev === category ? null : category))
}
return (
@@ -157,7 +155,7 @@ export default function Equipments() {
<ScrollArea className="w-full whitespace-nowrap">
<div className="flex w-max space-x-2 p-1">
{EQUIPMENT_CATEGORIES.map((cat) => {
const isSelected = selectedCategories.includes(cat.main_category)
const isSelected = selectedCategory === cat.main_category
return (
<Badge
key={cat.main_category}
+19 -25
View File
@@ -5,6 +5,7 @@ import { useNavigate, useParams } from 'react-router-dom'
import { client } from '../client'
import { CreateLookDialog } from '../components/CreateLookDialog'
import { LookDetailsDialog } from '../components/LookDetailsDialog'
import { PageHeader } from '../components/PageHeader'
import { Button } from '../components/ui/button'
import { Card } from '../components/ui/card'
@@ -72,37 +73,30 @@ export default function FittingRoom() {
}
return (
<div className="p-8 animate-fade-in-up h-screen flex flex-col overflow-hidden bg-stone-50/30">
{/* Header */}
<div className="flex justify-between items-center mb-6 shrink-0">
<div className="flex items-center gap-4">
<div className="p-8 pt-2 h-full flex flex-col bg-stone-50/30 relative">
<PageHeader
title="Fitting Room"
subtitle="System // FITTING_ROOM"
backButton={
<Button
variant="ghost"
size="icon"
onClick={() => navigate('/characters')}
className="rounded-full hover:bg-stone-200"
className="rounded-full hover:bg-stone-200 text-stone-600"
>
<ArrowLeft className="w-5 h-5 text-stone-600" />
<ArrowLeft className="w-5 h-5" />
</Button>
<div>
<h1 className="text-3xl font-black text-stone-800 uppercase tracking-tighter">
Fitting Room
</h1>
<p className="text-stone-500 text-sm font-medium flex items-center gap-2">
Manage looks for{' '}
<span className="text-stone-900 font-bold bg-stone-200 px-2 py-0.5 rounded text-xs uppercase">
{character.name}
</span>
</p>
</div>
</div>
<Button
onClick={() => setCreateLookOpen(true)}
className="bg-stone-900 hover:bg-stone-800 text-white shadow-lg hover:shadow-xl transition-all"
>
<Plus className="mr-2 h-4 w-4" /> Create New Look
</Button>
</div>
}
actions={
<Button
onClick={() => setCreateLookOpen(true)}
className="bg-stone-900 hover:bg-stone-800 text-white shadow-lg hover:shadow-xl transition-all"
>
<Plus className="mr-2 h-4 w-4" /> Create New Look
</Button>
}
className="mb-6 shrink-0"
/>
<div className="flex flex-col lg:flex-row flex-1 gap-6 overflow-hidden pb-4">
{/* Left: Character Preview */}