feat: Implement game type filter for expansion selection in Cube Manager, adding 'digital' property to Scryfall sets and corresponding UI.
Some checks failed
Build and Deploy / build (push) Failing after 58s

This commit is contained in:
2025-12-16 23:10:59 +01:00
parent faa79906a8
commit 552eba5ba7
4 changed files with 59 additions and 6 deletions

View File

@@ -31,3 +31,4 @@
- [Server-Side Caching](./devlog/2025-12-16-235900_server_side_caching.md): Completed. Implemented logic to cache images and metadata on the server upon bulk parsing, and updated client to use local assets.
- [Peasant Algorithm Implementation](./devlog/2025-12-16-225700_peasant_algorithm.md): Completed. Implemented Peasant-specific pack generation rules including slot logic for commons, uncommons, lands, and wildcards.
- [Multi-Expansion Selection](./devlog/2025-12-16-230500_multi_expansion_selection.md): Completed. Implemented searchable multi-select interface for "From Expansion" pack generation, allowing mixed-set drafts.
- [Game Type Filter](./devlog/2025-12-16-231000_game_type_filter.md): Completed. Added Paper/Digital filter to the expansion selection list.

View File

@@ -0,0 +1,15 @@
# Game Type Filter for Expansion Selection
## Objective
Add a filter to the "From Expansion" set selection to easily distinguish between Paper and Digital (MTGA/MTGO) sets.
## Implementation Details
1. **ScryfallService Update**: Updated `ScryfallSet` interface to include the `digital` boolean property and mapped it in `fetchSets`.
2. **CubeManager UI**: Added a toggle filter bar above the set list with three options:
* **All**: Shows all sets.
* **Paper**: Shows only sets where `digital` is false.
* **Digital**: Shows only sets where `digital` is true.
3. **Filter Logic**: Integrated the game type filter into the existing search filter logic in `CubeManager`.
## Status
Completed. Users can now filter the expansion list by game type.

View File

@@ -96,7 +96,9 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
const saved = localStorage.getItem('cube_selectedSets');
return saved ? JSON.parse(saved) : [];
});
const [searchTerm, setSearchTerm] = useState('');
const [gameTypeFilter, setGameTypeFilter] = useState<'all' | 'paper' | 'digital'>('all'); // Filter state
const [numBoxes, setNumBoxes] = useState<number>(() => {
const saved = localStorage.getItem('cube_numBoxes');
return saved ? parseInt(saved) : 3;
@@ -440,14 +442,47 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
)}
</div>
{/* Game Type Filter */}
<div className="flex border-b border-slate-700 bg-slate-900">
<button
onClick={() => setGameTypeFilter('all')}
className={`flex-1 py-1.5 text-[10px] font-bold uppercase tracking-wider transition-colors ${gameTypeFilter === 'all' ? 'bg-slate-700 text-white' : 'text-slate-500 hover:text-slate-300 hover:bg-slate-800'}`}
>
All
</button>
<button
onClick={() => setGameTypeFilter('paper')}
className={`flex-1 py-1.5 text-[10px] font-bold uppercase tracking-wider transition-colors ${gameTypeFilter === 'paper' ? 'bg-emerald-900/40 text-emerald-400' : 'text-slate-500 hover:text-emerald-400 hover:bg-slate-800'}`}
title="Show only Paper sets"
>
Paper
</button>
<button
onClick={() => setGameTypeFilter('digital')}
className={`flex-1 py-1.5 text-[10px] font-bold uppercase tracking-wider transition-colors ${gameTypeFilter === 'digital' ? 'bg-blue-900/40 text-blue-400' : 'text-slate-500 hover:text-blue-400 hover:bg-slate-800'}`}
title="Show only Digital sets (Arena/MTGO)"
>
Digital
</button>
</div>
{/* List */}
<div className="max-h-60 overflow-y-auto custom-scrollbar p-1 space-y-0.5">
{availableSets
.filter(s =>
!searchTerm ||
.filter(s => {
// Search Filter
const matchesSearch = !searchTerm ||
s.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
s.code.toLowerCase().includes(searchTerm.toLowerCase())
)
s.code.toLowerCase().includes(searchTerm.toLowerCase());
// Game Type Filter
const matchesType =
gameTypeFilter === 'all' ? true :
gameTypeFilter === 'paper' ? !s.digital :
gameTypeFilter === 'digital' ? s.digital : true;
return matchesSearch && matchesType;
})
.map(s => {
const isSelected = selectedSets.includes(s.code);
return (

View File

@@ -168,7 +168,8 @@ export class ScryfallService {
name: s.name,
set_type: s.set_type,
released_at: s.released_at,
icon_svg_uri: s.icon_svg_uri
icon_svg_uri: s.icon_svg_uri,
digital: s.digital
}));
}
} catch (e) {
@@ -226,4 +227,5 @@ export interface ScryfallSet {
set_type: string;
released_at: string;
icon_svg_uri: string;
digital: boolean;
}