@@ -16,6 +16,7 @@ import { pluralize, present, titleize } from "../../lib/utils"
1616import { CardsDocument } from "../cards/data"
1717import type { DeckDetail , DeckZone } from "./deck-types"
1818import { ADD_CARD_ZONES , NON_COMMANDER_ADD_CARD_ZONES } from "./deck-types"
19+ import { selectedDeckCardNameForMutation } from "./add-card-dialog-model"
1920import { AddDeckCardDocument } from "./queries"
2021
2122const ADD_CARD_SEARCH_DEBOUNCE_MS = 250
@@ -46,12 +47,19 @@ export function AddDeckCardDialog({
4647 variables : { q : cardSearchTerm , limit : 5 } ,
4748 skip : ! open || cardSearchTerm . length < 2 ,
4849 } )
49- const cardOptions = isCardSearchSettled
50- ? cardSearchQuery . data ?. cards ?. edges ?. map ( ( edge ) => edge ?. node ) . filter ( present ) || [ ]
51- : [ ]
50+ const cardOptions =
51+ isCardSearchSettled && ! cardSearchQuery . loading
52+ ? cardSearchQuery . data ?. cards ?. edges ?. map ( ( edge ) => edge ?. node ) . filter ( present ) || [ ]
53+ : [ ]
5254 const selectedCard =
5355 cardOptions . find ( ( card ) => card . name . toLowerCase ( ) === cardSearchTerm . toLowerCase ( ) ) ||
5456 cardOptions [ 0 ]
57+ const selectedCardName = selectedDeckCardNameForMutation ( name , selectedCard )
58+ const isCardSearchPending =
59+ open && cardSearchDraftTerm . length >= 2 && ( ! isCardSearchSettled || cardSearchQuery . loading )
60+ const selectedCardMatchesInput = Boolean (
61+ selectedCard && selectedCardName . toLowerCase ( ) === cardSearchDraftTerm . toLowerCase ( ) ,
62+ )
5563 const printingOptions =
5664 selectedCard ?. printings ?. edges ?. map ( ( edge ) => edge ?. node ) . filter ( present ) || [ ]
5765 const selectedPrinting =
@@ -71,7 +79,7 @@ export function AddDeckCardDialog({
7179 variables : {
7280 deckId : deck ?. id || "" ,
7381 input : {
74- name : name . trim ( ) ,
82+ name : selectedCardName ,
7583 quantity,
7684 zone,
7785 finish,
@@ -93,6 +101,7 @@ export function AddDeckCardDialog({
93101 setError ( error instanceof Error ? error . message : "Could not add card to deck" ) ,
94102 } ) ,
95103 }
104+ const canSubmit = Boolean ( deck && selectedCard && ! isCardSearchPending && ! addDeckCard . isPending )
96105
97106 useEffect ( ( ) => {
98107 const timeout = window . setTimeout ( ( ) => setDebouncedName ( name ) , ADD_CARD_SEARCH_DEBOUNCE_MS )
@@ -130,10 +139,21 @@ export function AddDeckCardDialog({
130139
131140 function submit ( event : FormEvent < HTMLFormElement > ) {
132141 event . preventDefault ( )
133- if ( ! name . trim ( ) ) {
142+ if ( ! cardSearchDraftTerm ) {
134143 setError ( "Choose a card." )
135144 return
136145 }
146+
147+ if ( isCardSearchPending ) {
148+ setError ( "Wait for the card search to finish." )
149+ return
150+ }
151+
152+ if ( ! selectedCard ) {
153+ setError ( "Choose a matching card." )
154+ return
155+ }
156+
137157 addDeckCard . mutate ( )
138158 }
139159
@@ -149,19 +169,49 @@ export function AddDeckCardDialog({
149169 </ DialogHeader >
150170
151171 < form className = "space-y-4 p-5" onSubmit = { submit } >
152- < label className = "form-control" >
153- < span className = "label-text mb-1 text-sm font-semibold" > Card</ span >
172+ < div className = "form-control" >
173+ < label
174+ htmlFor = "add-deck-card-search"
175+ className = "label-text mb-1 text-sm font-semibold"
176+ >
177+ Card
178+ </ label >
154179 < CardNameSearchField
180+ id = "add-deck-card-search"
155181 value = { name }
156182 onValueChange = { setName }
157183 onSuggestionSelect = { setName }
158184 placeholder = "Search card name"
185+ selectFirstSuggestionOnEnter
159186 disabled = { addDeckCard . isPending }
160187 />
161- </ label >
188+ < p className = "mt-1 text-xs text-base-content/60" >
189+ { isCardSearchPending
190+ ? "Searching for the matching card..."
191+ : selectedCard
192+ ? selectedCardMatchesInput
193+ ? "Exact match selected."
194+ : `Selected match: ${ selectedCard . name } `
195+ : ! cardSearchDraftTerm
196+ ? "Type a card name; the selected match below is what will be added."
197+ : cardSearchDraftTerm . length < 2
198+ ? "Enter at least 2 characters."
199+ : "No matching card found." }
200+ </ p >
201+ </ div >
162202
163203 { selectedPrinting ? (
164204 < div className = "rounded-box border border-base-300 bg-base-200/35 p-3" >
205+ < div className = "mb-3 flex flex-wrap items-center justify-between gap-2" >
206+ < p className = "text-xs font-black uppercase tracking-[0.18em] text-accent" >
207+ Selected card
208+ </ p >
209+ { ! selectedCardMatchesInput ? (
210+ < p className = "rounded-full border border-primary/30 px-2 py-0.5 text-xs font-semibold text-primary" >
211+ Matched from search
212+ </ p >
213+ ) : null }
214+ </ div >
165215 < div className = "flex gap-3" >
166216 { selectedPrinting . imageUrl ? (
167217 < img
@@ -269,8 +319,12 @@ export function AddDeckCardDialog({
269319 >
270320 Cancel
271321 </ Button >
272- < Button type = "submit" disabled = { addDeckCard . isPending || ! name . trim ( ) } >
273- { addDeckCard . isPending ? "Adding..." : "Add card" }
322+ < Button type = "submit" disabled = { ! canSubmit } >
323+ { addDeckCard . isPending
324+ ? "Adding..."
325+ : selectedCard
326+ ? `Add ${ selectedCard . name } `
327+ : "Add card" }
274328 </ Button >
275329 </ div >
276330 </ form >
0 commit comments