From 05d0edd09bcbd2f305f57ea2adbdd945c4d11d3f Mon Sep 17 00:00:00 2001 From: Matevz Date: Thu, 6 Mar 2025 15:15:57 +0100 Subject: [PATCH] #180 - Added ability to save to localStorage - They can't edit the decks - On the list they can select their favourite deck by clicking on the star - They can delete mulitple decks at once. - they can enter both QuickGames and private/public lobbies with favourite decks. --- src/app/DeckPage/[DeckId]/page.tsx | 36 +- src/app/DeckPage/page.tsx | 352 ++++++++++++++++-- .../ControlHub/ControlHub.tsx | 6 +- .../CreateGameForm/CreateGameForm.tsx | 112 +++++- .../DeckPage/AddDeckDialog.tsx | 13 + .../QuickGameForm/QuickGameForm.tsx | 130 +++++-- 6 files changed, 560 insertions(+), 89 deletions(-) diff --git a/src/app/DeckPage/[DeckId]/page.tsx b/src/app/DeckPage/[DeckId]/page.tsx index 523720a..b66e905 100644 --- a/src/app/DeckPage/[DeckId]/page.tsx +++ b/src/app/DeckPage/[DeckId]/page.tsx @@ -101,8 +101,19 @@ const DeckDetails: React.FC = () => { setDeleteDialogOpen(true); }; - // Handle delete confirmation + // handle confirm delete const handleConfirmDelete = () => { + if (deckId) { + try { + // Remove the deck from localStorage + const storageKey = `swu_deck_${deckId}`; + localStorage.removeItem(storageKey); + console.log(`Deck ${deckId} removed from localStorage`); + } catch (error) { + console.error('Error deleting deck from localStorage:', error); + } + } + setDeleteDialogOpen(false); router.push('/DeckPage'); }; @@ -177,14 +188,14 @@ const DeckDetails: React.FC = () => { justifyContent: 'space-between', }, sortBy:{ - width: '13rem', display: 'flex', flexDirection: 'row', alignItems: 'center', + justifyContent: 'space-between', }, sortText:{ - width: '5rem', - mb:'0px' + mb:'0px', + minWidth:'65px' }, deckContainer:{ width: '100%', @@ -223,9 +234,8 @@ const DeckDetails: React.FC = () => { }, editButtons:{ display:'flex', - width: '15rem', alignItems: 'center', - justifyContent: 'space-between', + justifyContent: 'end', }, titleTextContainer:{ ml:'10px', @@ -240,6 +250,10 @@ const DeckDetails: React.FC = () => { aspectRatio: '1.4 / 1', width: '21rem', }, + viewDeck:{ + width:'380px', + ml:'40px' + } } return ( @@ -294,9 +308,8 @@ const DeckDetails: React.FC = () => { - {/* Stats go here */} + {/* Stats go here - {/* A row for the big win % circle and quick stats */} @@ -310,13 +323,14 @@ const DeckDetails: React.FC = () => { - {/* A table for Opposing Leaders, Wins, Losses, etc. */} + {/* A table for Opposing Leaders, Wins, Losses, etc. + */} {/* Right side: Sort dropdown & deck cards */} @@ -338,9 +352,11 @@ const DeckDetails: React.FC = () => { ))} + + + - diff --git a/src/app/DeckPage/page.tsx b/src/app/DeckPage/page.tsx index 1c4da2e..6307a0a 100644 --- a/src/app/DeckPage/page.tsx +++ b/src/app/DeckPage/page.tsx @@ -1,6 +1,6 @@ 'use client'; import { Box, MenuItem, Typography } from '@mui/material'; -import React, { ChangeEvent, useState } from 'react'; +import React, { ChangeEvent, useState, useEffect } from 'react'; import { useRouter } from 'next/navigation' import Grid from '@mui/material/Grid2'; import StyledTextField from '@/app/_components/_sharedcomponents/_styledcomponents/StyledTextField'; @@ -8,6 +8,25 @@ import PreferenceButton from '@/app/_components/_sharedcomponents/Preferences/_s import { IDeckData } from '@/app/_utils/fetchDeckData'; import { s3CardImageURL } from '@/app/_utils/s3Utils'; import AddDeckDialog from '@/app/_components/_sharedcomponents/DeckPage/AddDeckDialog'; +import ConfirmationDialog from '@/app/_components/_sharedcomponents/DeckPage/ConfirmationDialog'; + +// Define interfaces for deck data +interface StoredDeck { + leader: { id: string }; + base: { id: string }; + name: string; + favourite: boolean; + deckLink: string; + deckID: string; +} + +interface DisplayDeck { + deckID: string; + leader: { id: string, types:string[] }; + base: { id: string, types:string[] }; + metadata: { name: string }; + favourite: boolean; +} const sortByOptions: string[] = [ 'Recently Played', @@ -16,18 +35,165 @@ const sortByOptions: string[] = [ const DeckPage: React.FC = () => { const [sortBy, setSortBy] = useState(''); - const [decks, setDecks] = useState([]); + const [decks, setDecks] = useState([]); const [addDeckDialogOpen, setAddDeckDialogOpen] = useState(false); + const [selectedDecks, setSelectedDecks] = useState([]); // Track selected decks + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const router = useRouter(); + // Load decks from localStorage on component mount + useEffect(() => { + loadDecksFromStorage(); + }, []); + + // Function to load decks from localStorage + const loadDecksFromStorage = () => { + try { + const displayDecks: DisplayDeck[] = []; + + // Get all localStorage keys + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + // Check if this is a deck key + if (key && key.startsWith('swu_deck_')) { + const deckID = key.replace('swu_deck_', ''); + const deckDataJSON = localStorage.getItem(key); + + if (deckDataJSON) { + const deckData = JSON.parse(deckDataJSON) as StoredDeck; + + // Convert to display format + displayDecks.push({ + deckID, + leader: { id: deckData.leader.id, types:['leader'] }, + base: { id: deckData.base.id, types:['base'] }, + metadata: { name: deckData.name }, + favourite: deckData.favourite + }); + } + } + } + + // Sort decks to show favorites first + const sortedDecks = [...displayDecks].sort((a, b) => { + // If one is favorite and other is not, favorite comes first + if (a.favourite && !b.favourite) return -1; + if (!a.favourite && b.favourite) return 1; + + // Otherwise maintain original order or sort by name if needed + return 0; + }); + + setDecks(sortedDecks); + } catch (error) { + console.error('Error loading decks from localStorage:', error); + } + }; + // Handle successful deck addition const handleAddDeckSuccess = (deckData: IDeckData) => { - setDecks(prevDecks => [...prevDecks, deckData]); + const newDeck: DisplayDeck = { + deckID: deckData.deckID, + leader: { id: deckData.leader.id, types:['leader'] }, + base: { id: deckData.base.id, types:['base'] }, + metadata: { name: deckData.metadata?.name || 'Untitled Deck' }, + favourite: false + }; + + // Add the new deck and re-sort to maintain favorites first + setDecks(prevDecks => { + const updatedDecks = [...prevDecks, newDeck]; + return updatedDecks.sort((a, b) => { + if (a.favourite && !b.favourite) return -1; + if (!a.favourite && b.favourite) return 1; + return 0; + }); + }); }; // Handler to navigate to the deck subpage using the deck's id const handleViewDeck = (deckId: string) => { - router.push(`/DeckPage/${deckId}`); + // Only navigate if no decks are selected + if (selectedDecks.length === 0) { + router.push(`/DeckPage/${deckId}`); + } + }; + + // Handle deck selection + const toggleDeckSelection = (deckId: string) => { + setSelectedDecks(prevSelected => { + // Check if the deck is already selected + if (prevSelected.includes(deckId)) { + // Remove from selection + return prevSelected.filter(id => id !== deckId); + } else { + // Add to selection + return [...prevSelected, deckId]; + } + }); + }; + + // Toggle favorite status for a deck + const toggleFavorite = (deckId: string, e:React.MouseEvent) => { + e.stopPropagation(); + // Update in state and resort + const updatedDecks = decks.map(deck => + deck.deckID === deckId + ? { ...deck, favourite: !deck.favourite } + : deck + ); + + // Re-sort to ensure favorites appear first + const sortedDecks = [...updatedDecks].sort((a, b) => { + if (a.favourite && !b.favourite) return -1; + if (!a.favourite && b.favourite) return 1; + return 0; + }); + + setDecks(sortedDecks); + + // Update in localStorage + try { + const storageKey = `swu_deck_${deckId}`; + const deckDataJSON = localStorage.getItem(storageKey); + + if (deckDataJSON) { + const deckData = JSON.parse(deckDataJSON) as StoredDeck; + deckData.favourite = !deckData.favourite; + localStorage.setItem(storageKey, JSON.stringify(deckData)); + } + } catch (error) { + console.error('Error updating favorite status:', error); + } + }; + + // Open delete confirmation dialog + const openDeleteDialog = () => { + if (selectedDecks.length > 0) { + setDeleteDialogOpen(true); + } + }; + + // Delete selected decks + const handleDeleteSelectedDecks = () => { + // Delete each selected deck from localStorage + selectedDecks.forEach(deckId => { + try { + const storageKey = `swu_deck_${deckId}`; + localStorage.removeItem(storageKey); + } catch (error) { + console.error(`Error deleting deck ${deckId}:`, error); + } + }); + + // Update deck list in state + setDecks(prevDecks => prevDecks.filter(deck => !selectedDecks.includes(deck.deckID))); + + // Reset selection + setSelectedDecks([]); + + // Close dialog + setDeleteDialogOpen(false); }; // ----------------------Styles-----------------------------// @@ -39,31 +205,35 @@ const DeckPage: React.FC = () => { justifyContent: 'space-between', }, sortBy:{ - width:'100px' + minWidth:'100px' }, sortByContainer:{ display:'flex', flexDirection: 'row', - width:'300px', alignItems:'center', }, - deckContainer:{ - background: '#20344280', + dropdown:{ + maxWidth:'10rem', + }, + deckContainer: (isSelected: boolean) => ({ + background: isSelected ? '#2F7DB680' : '#20344280', width: '31rem', height: '13rem', borderRadius: '5px', padding:'5px', display:'flex', flexDirection: 'row', + border: '2px solid transparent', '&:hover': { backgroundColor: '#2F7DB680', }, cursor: 'pointer', - }, + position: 'relative', + }), gridContainer:{ mt: '30px', overflowY: 'auto', - maxHeight: '90%', + maxHeight: '84%', }, CardSetContainerStyle:{ display: 'flex', @@ -109,6 +279,58 @@ const DeckPage: React.FC = () => { viewDeckButton:{ display:'flex', marginBottom: '7%', + gap: '0.5rem', + }, + favoriteIcon: { + position: 'absolute', + top: '10px', + right: '10px', + fontSize: '24px', + color: 'gold', + cursor: 'pointer', + zIndex: 10, + }, + deleteIcon: { + position: 'absolute', + top: '10px', + right: '40px', + fontSize: '20px', + color: '#FF5555', + cursor: 'pointer', + zIndex: 10, + }, + noDecksMessage: { + color: 'white', + width: '100%', + textAlign: 'center', + marginTop: '2rem', + }, + addNewDeck:{ + width:'350px', + ml:'40px' + }, + selectionInfo: { + color: 'white', + margin: '0 20px', + }, + // New style for the selection checkmark + selectionCheckmark: { + position: 'absolute', + bottom: '34px', + right: '10px', + width: '24px', + height: '24px', + borderRadius: '50%', + backgroundColor: '#66E5FF', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + zIndex: 10, + }, + checkmarkSymbol: { + color: '#1E2D32', + fontWeight: 'bold', + fontSize: '16px', } }; @@ -119,6 +341,7 @@ const DeckPage: React.FC = () => { Sort by ) => @@ -131,46 +354,95 @@ const DeckPage: React.FC = () => { ))} + + setAddDeckDialogOpen(true)}/> + - setAddDeckDialogOpen(true)}/> + - {decks.map((deck) => ( - - - - - + {decks.length > 0 ? ( + decks.map((deck) => { + const isSelected = selectedDecks.includes(deck.deckID); + + return ( + toggleDeckSelection(deck.deckID)} + > + {/* Favorite Icon */} + toggleFavorite(deck.deckID, e)} + > + {deck.favourite ? '★' : '☆'} - - + {isSelected && ( + + + + )} + + + + + + + + + + + + + {deck.metadata.name} + + + { + handleViewDeck(deck.deckID); + }} + /> + - - - - {deck.metadata.name} - - - handleViewDeck(deck.deckID)} - /> - - - - ))} - setAddDeckDialogOpen(false)} - onSuccess={handleAddDeckSuccess} - /> + ); + }) + ) : ( + + No decks found. Add a deck to get started! + + )} + + {/* Dialogs */} + setAddDeckDialogOpen(false)} + onSuccess={handleAddDeckSuccess} + /> + + 1 ? 's' : ''}? This action cannot be undone.`} + onCancel={() => setDeleteDialogOpen(false)} + onConfirm={handleDeleteSelectedDecks} + confirmButtonText="Delete" + cancelButtonText="Cancel" + /> ); }; -export default DeckPage; +export default DeckPage; \ No newline at end of file diff --git a/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx b/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx index fb4450f..e28740f 100644 --- a/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx +++ b/src/app/_components/_sharedcomponents/ControlHub/ControlHub.tsx @@ -122,14 +122,14 @@ const ControlHub: React.FC = ({ Unimplemented + + Decks + {user ? ( <> Profile - - Decks - Preferences diff --git a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx index b3287cb..ceb32a0 100644 --- a/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx +++ b/src/app/_components/_sharedcomponents/CreateGameForm/CreateGameForm.tsx @@ -1,4 +1,4 @@ -import React, { useState, FormEvent, ChangeEvent, useRef } from 'react'; +import React, { useState, FormEvent, ChangeEvent, useRef, useEffect } from 'react'; import { Box, Button, @@ -28,6 +28,16 @@ const deckOptions: string[] = [ 'ThisIsTheWay', ]; +// Interface for saved decks +interface StoredDeck { + leader: { id: string }; + base: { id: string }; + name: string; + favourite: boolean; + deckID: string; + deckLink: string; +} + const CreateGameForm = () => { const pathname = usePathname(); const router = useRouter(); @@ -38,6 +48,7 @@ const CreateGameForm = () => { const [favouriteDeck, setFavouriteDeck] = useState(''); const [deckLink, setDeckLink] = useState(''); const [saveDeck, setSaveDeck] = useState(false); + const [savedDecks, setSavedDecks] = useState([]); const [errorModalOpen, setErrorModalOpen] = useState(false); const [errorTitle, setErrorTitle] = useState('Deck Validation Error'); @@ -56,6 +67,46 @@ const CreateGameForm = () => { const [gameName, setGameName] = useState(''); const [privacy, setPrivacy] = useState('Public'); + useEffect(() => { + loadSavedDecks(); + }, []); + + // Load saved decks from localStorage + const loadSavedDecks = () => { + try { + const storedDecks: StoredDeck[] = []; + + // Get all localStorage keys + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + // Check if this is a deck key + if (key && key.startsWith('swu_deck_')) { + const deckID = key.replace('swu_deck_', ''); + const deckDataJSON = localStorage.getItem(key); + + if (deckDataJSON) { + const deckData = JSON.parse(deckDataJSON) as StoredDeck; + + // Add to our list with the ID for reference + storedDecks.push({ + ...deckData, + deckID: deckID + }); + } + } + } + + // Sort to show favorites first + const sortedDecks = [...storedDecks].sort((a, b) => { + if (a.favourite && !b.favourite) return -1; + if (!a.favourite && b.favourite) return 1; + return 0; + }); + setSavedDecks(sortedDecks); + } catch (error) { + console.error('Error loading decks from localStorage:', error); + } + } const handleChangeFormat = (format: SwuGameFormat) => { localStorage.setItem('format', format); setFormat(format); @@ -64,11 +115,25 @@ const CreateGameForm = () => { // Handle Create Game Submission const handleCreateGameSubmit = async (event: FormEvent) => { event.preventDefault(); + let userDeck = ''; + // check whether the favourite deck was selected or a decklink was used. The decklink always has precedence + if(favouriteDeck) { + const selectedDeck = savedDecks.find(deck => deck.deckID === favouriteDeck); + if (selectedDeck?.deckLink && !deckLink) { + userDeck = selectedDeck?.deckLink + }else{ + console.log(deckLink); + userDeck = deckLink; + } + }else{ + userDeck = deckLink; + } + let deckData = null try { - const parsedInput = parseInputAsDeckData(deckLink); + const parsedInput = parseInputAsDeckData(userDeck); if(parsedInput.type === 'url') { - deckData = deckLink ? await fetchDeckData(deckLink, false) : null; + deckData = userDeck ? await fetchDeckData(userDeck, false) : null; }else if(parsedInput.type === 'json') { deckData = parsedInput.data }else{ @@ -87,7 +152,6 @@ const CreateGameForm = () => { [DeckValidationFailureReason.DeckSetToPrivate]: true, }); setErrorModalOpen(true); - console.log('here') }else{ setErrorTitle('Deck Validation Error'); setDeckErrorSummary('Couldn\'t import. Deck is invalid.'); @@ -96,6 +160,7 @@ const CreateGameForm = () => { } return; } + console.log(deckData); try { const payload = { user: { id: user?.id || localStorage.getItem('anonymousUserId'), @@ -129,6 +194,23 @@ const CreateGameForm = () => { } return; } + if (saveDeck && deckData && deckLink){ + // save new deck to local storage and only if its a new deck + try { + const deckToSave = { + leader: deckData.leader, + base: deckData.base, + name: deckData.metadata.name, + favourite: false, + deckID: deckData.deckID, + deckLink: deckLink// Store the original link if we have one + }; + localStorage.setItem(`swu_deck_${deckData.deckID}`, JSON.stringify(deckToSave)); + } catch (error) { + console.error('Error saving deck to favorites:', error); + } + } + setDeckErrorSummary(null); setDeckErrorDetails(undefined); setErrorTitle('Deck Validation Error'); @@ -185,7 +267,7 @@ const CreateGameForm = () => {
{/* Favourite Decks Input */} - {user && + Favourite Decks { onChange={(e: ChangeEvent) => setFavouriteDeck(e.target.value) } - placeholder="Vader Green Ramp" + placeholder="Favourite decks" > - {deckOptions.map((deck) => ( - - {deck} + {savedDecks.length === 0 ? ( + + No saved decks found - ))} + ) : ( + savedDecks.map((deck) => ( + + {deck.favourite ? '★ ' : ''}{deck.name} + + )) + )} - } {/* Deck Link Input */} @@ -244,7 +331,7 @@ const CreateGameForm = () => { {/* Save Deck To Favourites Checkbox */} - {user && { } /> - } {/* Additional Fields for Non-Creategame Path */} {!isCreateGamePath && ( diff --git a/src/app/_components/_sharedcomponents/DeckPage/AddDeckDialog.tsx b/src/app/_components/_sharedcomponents/DeckPage/AddDeckDialog.tsx index d771d8e..383d277 100644 --- a/src/app/_components/_sharedcomponents/DeckPage/AddDeckDialog.tsx +++ b/src/app/_components/_sharedcomponents/DeckPage/AddDeckDialog.tsx @@ -32,6 +32,19 @@ const AddDeckDialog: React.FC = ({ try { const deckData = await fetchDeckData(deckLink, false); if (deckData) { + // Save to localStorage + const deckKey = deckData.deckID; + const simplifiedDeckData = { + leader: { id: deckData.leader.id }, + base: { id: deckData.base.id }, + name: deckData.metadata?.name || 'Untitled Deck', + favourite: false, + deckLink:deckLink, + deckLID:deckKey + }; + + // Save back to localStorage + localStorage.setItem('swu_deck_'+deckKey, JSON.stringify(simplifiedDeckData)); onSuccess(deckData); onClose(); // Reset form diff --git a/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx b/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx index 423e6a4..38fdfd4 100644 --- a/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx +++ b/src/app/_components/_sharedcomponents/QuickGameForm/QuickGameForm.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent, FormEvent, useRef, useState } from 'react'; +import React, { ChangeEvent, FormEvent, useEffect, useRef, useState } from 'react'; import { Box, Button, Checkbox, FormControl, FormControlLabel, Link, MenuItem, Typography } from '@mui/material'; import StyledTextField from '../_styledcomponents/StyledTextField'; import { useRouter } from 'next/navigation'; @@ -17,6 +17,15 @@ interface ICreateGameFormProps { setFormat?: (format: string) => void; } +// Interface for saved decks +interface StoredDeck { + leader: { id: string }; + base: { id: string }; + name: string; + favourite: boolean; + deckID: string; + deckLink: string; +} const QuickGameForm: React.FC = () => { const router = useRouter(); @@ -27,6 +36,7 @@ const QuickGameForm: React.FC = () => { const [deckLink, setDeckLink] = useState(''); const [saveDeck, setSaveDeck] = useState(false); const [queueState, setQueueState] = useState(false) + const [savedDecks, setSavedDecks] = useState([]); const formatOptions = Object.values(SwuGameFormat); const savedFormat = localStorage.getItem('format') || SwuGameFormat.Premier; @@ -46,6 +56,48 @@ const QuickGameForm: React.FC = () => { 'ThisIsTheWay', ]; + // Load saved decks when component mounts + useEffect(() => { + loadSavedDecks(); + }, []); + + // Load saved decks from localStorage + const loadSavedDecks = () => { + try { + const storedDecks: StoredDeck[] = []; + + // Get all localStorage keys + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + // Check if this is a deck key + if (key && key.startsWith('swu_deck_')) { + const deckID = key.replace('swu_deck_', ''); + const deckDataJSON = localStorage.getItem(key); + + if (deckDataJSON) { + const deckData = JSON.parse(deckDataJSON) as StoredDeck; + + // Add to our list with the ID for reference + storedDecks.push({ + ...deckData, + deckID: deckID + }); + } + } + } + + // Sort to show favorites first + const sortedDecks = [...storedDecks].sort((a, b) => { + if (a.favourite && !b.favourite) return -1; + if (!a.favourite && b.favourite) return 1; + return 0; + }); + setSavedDecks(sortedDecks); + } catch (error) { + console.error('Error loading decks from localStorage:', error); + } + }; + const handleChangeFormat = (format: SwuGameFormat) => { localStorage.setItem('format', format); setFormat(format); @@ -55,11 +107,23 @@ const QuickGameForm: React.FC = () => { const handleJoinGameQueue = async (event: FormEvent) => { event.preventDefault(); setQueueState(true); + // Get the deck link - either from selected favorite or direct input + let userDeck = ''; + if(favouriteDeck) { + const selectedDeck = savedDecks.find(deck => deck.deckID === favouriteDeck); + if (selectedDeck?.deckLink && !deckLink) { + userDeck = selectedDeck.deckLink; + } else { + userDeck = deckLink; + } + } else { + userDeck = deckLink; + } let deckData = null try { - const parsedInput = parseInputAsDeckData(deckLink); + const parsedInput = parseInputAsDeckData(userDeck); if(parsedInput.type === 'url') { - deckData = deckLink ? await fetchDeckData(deckLink, false) : null; + deckData = deckLink ? await fetchDeckData(userDeck, false) : null; }else if(parsedInput.type === 'json') { deckData = parsedInput.data }else{ @@ -123,6 +187,23 @@ const QuickGameForm: React.FC = () => { } return } + // Save the deck if needed + if (saveDeck && deckData && userDeck) { + try { + const deckToSave = { + leader: deckData.leader, + base: deckData.base, + name: deckData.metadata.name, + favourite: false, + deckID: deckData.deckID, + deckLink: userDeck // Store the original link + }; + localStorage.setItem(`swu_deck_${deckData.deckID}`, JSON.stringify(deckToSave)); + } catch (error) { + console.error('Error saving deck to favorites:', error); + } + } + setDeckErrorSummary(null); setDeckErrorDetails(undefined); setErrorTitle('Deck Validation Error'); @@ -181,25 +262,29 @@ const QuickGameForm: React.FC = () => { {/* Favourite Decks Input */} - {user && - - Favourite Decks - ) => - setFavouriteDeck(e.target.value) - } - placeholder="Vader Green Ramp" - > - {deckOptions.map((deck) => ( - - {deck} + + Favourite Decks + ) => + setFavouriteDeck(e.target.value) + } + placeholder="Favourite decks" + > + {savedDecks.length === 0 ? ( + + No saved decks found + + ) : ( + savedDecks.map((deck) => ( + + {deck.favourite ? '★ ' : ''}{deck.name} - ))} - - - } + )) + )} + + {/* Deck Link Input */} @@ -259,7 +344,7 @@ const QuickGameForm: React.FC = () => { - {/* Save Deck To Favourites Checkbox + {/* Save Deck To Favourites Checkbox */} = () => { } /> - */} {/* Submit Button */}