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
Some checks failed
Build and Deploy / build (push) Failing after 58s
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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 ||
|
||||
s.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
s.code.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
.filter(s => {
|
||||
// Search Filter
|
||||
const matchesSearch = !searchTerm ||
|
||||
s.name.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 (
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user