Skip to content

Commit eb19dcf

Browse files
committed
add support for deck filters and navigation updates
- Introduced `/decks/filter/:kind` route for deck filtering. - Updated default deck kind to "popular". - Adjusted navigation and state handling for consistent user experience. - Improved UI components for deck views.
1 parent 2480b84 commit eb19dcf

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed

frontend/src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ function App() {
8282
{ path: '/collection/missions', element: <Missions /> },
8383
{ path: '/collection/:friendId?', element: <Collection /> },
8484
{ path: '/collection/:friendId/trade', element: <TradeWithRedirect /> }, // support old trading path
85-
{ path: '/decks', element: <Decks /> },
85+
{ path: '/decks', element: <Navigate to="/decks/filter/popular" replace /> },
86+
{ path: '/decks/filter/:kind', element: <Decks /> },
8687
{ path: '/decks/edit/:id?', element: <DeckBuilder /> },
8788
{ path: '/decks/:id', element: <DeckView /> },
8889
{ path: '/scan', element: <Scan /> },

frontend/src/pages/decks/DeckView.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Slot } from '@radix-ui/react-slot'
22
import { useQuery } from '@tanstack/react-query'
3-
import { ChevronRight, Heart, HeartMinus, HeartPlus } from 'lucide-react'
4-
import { Link, useLocation, useParams } from 'react-router'
3+
import { ChevronLeft, ChevronRight, Heart, HeartMinus, HeartPlus } from 'lucide-react'
4+
import { Link, useLocation, useNavigate, useParams } from 'react-router'
55
import { CardLine } from '@/components/CardLine'
66
import { Spinner } from '@/components/Spinner'
77
import { Button } from '@/components/ui/button'
@@ -17,6 +17,7 @@ import { getDeckCardCounts, getMissingCardsCount } from './utils'
1717
export default function DeckView() {
1818
const { id: deckId } = useParams()
1919
const location = useLocation()
20+
const navigate = useNavigate()
2021

2122
const { data: account } = useAccount()
2223
const { data: ownedCards } = useCollection()
@@ -49,7 +50,11 @@ export default function DeckView() {
4950
<h2 className="text-lg font-semibold">{deck.name}</h2>
5051
<div className="flex items-center gap-2">
5152
<h3>Energy</h3>
52-
<span className="inline-flex gap-1">{deck.energy.map(showCardType)}</span>
53+
<span className="inline-flex gap-1">
54+
{deck.energy.map((x) => (
55+
<span key={x}>{showCardType(x)}</span>
56+
))}
57+
</span>
5358
</div>
5459
<h3>Cards {missingCards > 0 && <span className="text-neutral-400 text-sm"> ({missingCards} missing)</span>}</h3>
5560
<ul className="overflow-y-auto space-y-1">
@@ -74,7 +79,7 @@ export default function DeckView() {
7479
)
7580
})}
7681
</ul>
77-
<div className="flex items-center mt-2">
82+
<div className="flex items-center mt-2 justify-between">
7883
{deck.is_public ? (
7984
deck.email !== undefined && deck.email === account?.email ? (
8085
<p className="flex items-center gap-1">
@@ -97,12 +102,19 @@ export default function DeckView() {
97102
) : (
98103
<span className="italic text-neutral-400 text-sm">Private</span>
99104
)}
100-
<Link className="ml-auto" to={`/decks/edit/${isOwn ? deck.id : ''}`} state={isOwn ? deck : { ...deck, id: undefined }}>
101-
<Button>
102-
{isOwn ? 'Edit' : 'Copy and edit'}
103-
<ChevronRight />
105+
106+
<div className="flex items-center gap-2">
107+
<Button variant="ghost" onClick={() => navigate(-1)}>
108+
<ChevronLeft /> Back
104109
</Button>
105-
</Link>
110+
111+
<Link to={`/decks/edit/${isOwn ? deck.id : ''}`} state={isOwn ? deck : { ...deck, id: undefined }}>
112+
<Button>
113+
{isOwn ? 'Edit' : 'Copy and edit'}
114+
<ChevronRight />
115+
</Button>
116+
</Link>
117+
</div>
106118
</div>
107119
</div>
108120
)

frontend/src/pages/decks/Decks.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ChevronFirst, ChevronLeft, ChevronRight } from 'lucide-react'
22
import { useState } from 'react'
3-
import { Link } from 'react-router'
3+
import { Link, useNavigate, useParams } from 'react-router'
44
import ErrorAlert from '@/components/ErrorAlert'
55
import { TabsFilter, ToggleFilter } from '@/components/Filters'
66
import { Spinner } from '@/components/Spinner'
@@ -12,9 +12,18 @@ import { energies } from '@/types'
1212
import { DeckItem } from './DeckItem'
1313

1414
export default function Decks() {
15-
const [filters, setFilters] = useState<DeckFilters>({ kind: 'my', page: 0, energy: [] })
15+
const navigate = useNavigate()
16+
const { kind } = useParams<{ kind: DeckFilters['kind'] }>()
17+
const validKind = kind && deckKinds.includes(kind) ? kind : 'popular'
18+
19+
const [filters, setFilters] = useState<DeckFilters>({ kind: validKind, page: 0, energy: [] })
1620
const { data, isLoading, isError, error } = useDecksSearch(filters)
1721

22+
const handleKindChange = (newKind: DeckFilters['kind']) => {
23+
setFilters((prev) => ({ ...prev, kind: newKind, page: 0 }))
24+
navigate(`/decks/filter/${newKind}`, { replace: true })
25+
}
26+
1827
return (
1928
<div className="flex gap-4 flex-col sm:flex-row sm:w-fit mx-auto px-1">
2029
<div className="flex flex-col gap-2">
@@ -28,7 +37,7 @@ export default function Decks() {
2837
className="w-full"
2938
options={deckKinds}
3039
value={filters.kind}
31-
onChange={(kind) => setFilters((prev) => ({ ...prev, kind }))}
40+
onChange={handleKindChange}
3241
show={(kind) => `${kind.charAt(0).toUpperCase() + kind.slice(1)} decks`}
3342
/>
3443
<ToggleFilter options={energies} value={filters.energy} onChange={(energy) => setFilters((prev) => ({ ...prev, energy }))} show={showCardType} />

frontend/src/services/decks/deckService.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { supabase } from '@/lib/supabase'
22
import type { Deck, Energy } from '@/types'
33

4-
export const deckKinds = ['my', 'liked', 'community'] as const
4+
export const deckKinds = ['popular', 'liked', 'my'] as const
55

66
export interface DeckFilters {
77
kind: (typeof deckKinds)[number]
@@ -15,13 +15,13 @@ export async function getDeck(id: number) {
1515
2. Public decks have column `likes`, which private decks don't, and private decks have column `email` which public don't.
1616
So to have both columns for a user looking at his public deck, we need to merge the results.
1717
*/
18-
const [personal, community] = await Promise.all([
18+
const [personal, popular] = await Promise.all([
1919
supabase.from('decks').select('*').eq('id', id).maybeSingle(),
2020
supabase.from('public_decks').select('*').eq('id', id).maybeSingle(),
2121
])
2222
let res = {}
23-
if (!community.error && !!community.data) {
24-
res = { ...res, ...community.data, is_public: true }
23+
if (!popular.error && !!popular.data) {
24+
res = { ...res, ...popular.data, is_public: true }
2525
}
2626
if (!personal.error && !!personal.data) {
2727
res = { ...res, ...personal.data }
@@ -30,7 +30,7 @@ export async function getDeck(id: number) {
3030
// at least one query succeded
3131
return res as Deck
3232
}
33-
console.error('supabase error?', personal.error, community.error)
33+
console.error('supabase error?', personal.error, popular.error)
3434
throw new Error('Failed fetching deck')
3535
}
3636

@@ -43,7 +43,7 @@ export async function getDecks(filters: DeckFilters) {
4343
tbl = tbl.from('decks').select('*', { count: 'exact' })
4444
} else if (filters.kind === 'liked') {
4545
tbl = tbl.from('deck_likes').select('*, public_decks!id(*)', { count: 'exact' })
46-
} else if (filters.kind === 'community') {
46+
} else if (filters.kind === 'popular') {
4747
tbl = tbl.from('public_decks').select('*', { count: 'exact' }).order('likes', { ascending: false })
4848
}
4949

0 commit comments

Comments
 (0)