diff --git a/app/app/account/profile/decks/page.tsx b/app/app/account/profile/decks/page.tsx index 0a74cb7..ce0f6ff 100644 --- a/app/app/account/profile/decks/page.tsx +++ b/app/app/account/profile/decks/page.tsx @@ -1,28 +1,41 @@ 'use client' -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog" +import { + Dialog, + DialogContent, + DialogFooter, + DialogClose, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" -import { useEffect, useState } from "react" +import { useEffect, useState, useRef } from "react" import { useRouter } from 'next/navigation' import { getCookie } from "@/lib/utils" import { Textarea } from "@/components/ui/textarea" +import { MTGCardTextHover } from "@/components/ui/mtg-card-text-hover" +import { Toaster } from "@/components/ui/toaster" +import { useToast } from "@/hooks/use-toast" import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table" + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/deck-accordion" import { Command, CommandEmpty, @@ -36,37 +49,67 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover" +import { Spinner } from "@/components/ui/spinner" -import type { deck, bset } from "@prisma/client"; +import type { deck, bset, carte, cartes_dans_deck } from "@prisma/client"; interface bsetJson extends bset { set_codes: string[], icons: string[] } +interface deckExtended extends deck { + commander: carte + cartes: cartes_dans_deckExtended[] +} + +interface cartes_dans_deckExtended extends cartes_dans_deck { + carte: carte +} + interface cardEntryAPIProps { - amount: number, + amount: number, sanitized_name: string, } interface deckAPIProps { - name: string, - selected_bset: string, - commander_name: string, - cards: cardEntryAPIProps[] + id?: string, // For Update API + name: string, + selected_bset?: string, // For Create API + url?: string, + commander_name?: string, + cards: cardEntryAPIProps[] } export default function Signin() { const [deckName, setDeckName] = useState("") + const [deckUrl, setDeckUrl] = useState("") const [deckCommanderName, setDeckCommanderName] = useState("") const [deckImporter, setDeckImporter] = useState("") const [selectedBset, setSelectedBset] = useState("") - const [decks, setDecks] = useState([]) + const [decks, setDecks] = useState([]) + const [displayedDecks, setDisplayedDecks] = useState([]) + + const [loading, setLoading] = useState(true) + const [bsets, setBsets] = useState([]) + const [openSelectBset, setOpenSelectBset] = useState(false) const router = useRouter() const token = getCookie('JWT') + const [deckIdToDelete, setDeckIdToDelete] = useState("") + const deleteDialogRef = useRef(null) + + const [editDeckUrl, setEditDeckUrl] = useState("") + const [editDeckId, setEditDeckId] = useState("") + const [editDeckName, setEditDeckName] = useState("") + const [editDeckImporter, setEditDeckImporter] = useState("") + const [editDeckCommanderName, setEditDeckCommanderName] = useState("") + const editDialogRef = useRef(null) + + const { toast } = useToast() + useEffect(() => { if(getCookie('JWT') == "") { router.refresh() @@ -79,7 +122,10 @@ export default function Signin() { }).then((res) => { if(res.status == 200) { res.json().then((apiData) => { + console.log(apiData.data) setDecks(apiData.data) + setDisplayedDecks(apiData.data) + setLoading(false) }) } }) @@ -114,6 +160,10 @@ export default function Signin() { }).then((res) => { if(res.status == 200) { setDecks(old => old.filter((deck: deck) => deck.id != id)) + setDisplayedDecks(old => old.filter((deck: deck) => deck.id != id)) + toast({ + title: "Deck supprimé", + }) } }) } @@ -121,21 +171,34 @@ export default function Signin() { function updateDeckInput(txt:string){ setDeckImporter(txt) const lines = txt.split("\n") - setDeckCommanderName(lines[lines.length - 1]) + setDeckCommanderName(lines[lines.length - 1].slice(1)) + } + + function updateEditDeckInput(txt:string){ + setEditDeckImporter(txt) + const lines = txt.split("\n") + setEditDeckCommanderName(lines[lines.length - 1].slice(1)) + } + + function editDialogHandler(id:string){ + const deck_to_edit = decks.find((deck: deck) => deck.id === id) + setEditDeckId(id) + setEditDeckName(deck_to_edit ? deck_to_edit.name : "") + setEditDeckUrl(deck_to_edit && deck_to_edit.url != null ? deck_to_edit.url : "") + editDialogRef.current?.click() } function importDeck(){ const deckText = deckImporter let lines = deckText.split("\n") lines = lines.filter((line) => line.match(/[0-9]+\s[\w]+/) != undefined) - const dataToSend : deckAPIProps = { name: deckName, selected_bset: selectedBset.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase() ,commander_name: getDataFromLine(deckCommanderName)!.sanitized_name, cards: [] } + const dataToSend : deckAPIProps = { name: deckName, url: deckUrl, selected_bset: selectedBset.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase() ,commander_name: getDataFromLine(deckCommanderName)!.sanitized_name, cards: [] } lines.slice(0, lines.length - 1).forEach((line: string) => { const data = getDataFromLine(line) if(data != null) { dataToSend.cards.push(data) } }); - console.log(dataToSend) fetch('/api/account/decks/create', { method: "POST", @@ -144,107 +207,234 @@ export default function Signin() { }).then((res) => { if(res.status == 200) { res.json().then((apiData) => { - const new_deck: deck = apiData.data + const new_deck: deckExtended = apiData.data + setDisplayedDecks([...decks, new_deck]) setDecks(oldDecks => [...oldDecks, new_deck]) setDeckName("") setDeckImporter("") setSelectedBset("") setDeckCommanderName("") + setDeckUrl("") + toast({ + title: "Deck " + new_deck.name + " créé.", + }) + }) + } else if (res.status == 401) { + res.json().then((apiData) => { + toast({ + title: "ERROR : " + apiData.message, + }) }) } }) } + function updateDeck(){ + const deckText = editDeckImporter + let lines = deckText.split("\n") + lines = lines.filter((line) => line.match(/[0-9]+\s[\w]+/) != undefined) + const dataToSend : deckAPIProps = { id: editDeckId, name: editDeckName, url: editDeckUrl, commander_name: editDeckCommanderName != "" ? getDataFromLine(editDeckCommanderName)!.sanitized_name : undefined, cards: [] } + if (editDeckImporter != "") { + lines.slice(0, lines.length - 1).forEach((line: string) => { + const data = getDataFromLine(line) + if(data != null) { + dataToSend.cards.push(data) + } + }) + } + console.log(dataToSend) + + fetch('/api/account/decks/update', { + method: "PUT", + headers: {Authorization: 'Bearer ' + token}, + body: JSON.stringify(dataToSend) + }).then((res) => { + if(res.status == 200) { + res.json().then((apiData) => { + const new_deck = apiData.data + + const displayedDecksIndex = displayedDecks.findIndex((deck) => deck.id == new_deck.id) + if (displayedDecksIndex != -1) { setDisplayedDecks(oldDecks => [...oldDecks.slice(0, displayedDecksIndex),new_deck,...oldDecks.slice(displayedDecksIndex + 1)]) } + + const decksIndex = decks.findIndex((deck) => deck.id == new_deck.id) + if (decksIndex != -1) { setDecks(oldDecks => [...oldDecks.slice(0, decksIndex),new_deck,...oldDecks.slice(decksIndex + 1)]) } + + console.log(apiData) + toast({ + title: "Deck " + new_deck.name + " mis à jour.", + }) + }) + } else if (res.status == 401) { + res.json().then((apiData) => { + toast({ + title: "ERROR : " + apiData.message, + }) + }) + } + }) + } + + function deleteAlertHandler(id:string, mode:string) { + if (mode == "set") { + deleteDialogRef.current?.click() + } else if (mode == "delete") { + deleteDeck(deckIdToDelete) + } + setDeckIdToDelete(id) + } + + function filterDecks(searchString:string) { + if(searchString != "") { + setDisplayedDecks(decks.filter((deck) => deck.name.toLowerCase().includes(searchString.toLowerCase()))) + } else { + setDisplayedDecks(decks) + } + } + return (
- - - Importer un deck - Depuis moxfield - - -
-
- - setDeckName(e.target.value)} placeholder="Nom du deck" /> -
-
- - - - - - - - - - Pas de BSet trouvé. - - {bsets.map((bset) => ( - { - setSelectedBset(currentValue === selectedBset ? "" : currentValue) - setOpenSelectBset(false) - }} - > -
- {bset.icons.map((icon) => ( - - ))} -
- {bset.name} -
- ))} -
-
-
-
-
-
-
- - - -
-
- -