Feat: Display deck list in decks
This commit is contained in:
parent
9014c17a07
commit
ae76b3db1e
5 changed files with 203 additions and 59 deletions
|
@ -5,3 +5,4 @@ Le backend du site Brawlset est codé en [GO](https://go.dev/) et utilise [Pocke
|
||||||
- [ ] Feat : Créer le système d'ELO
|
- [ ] Feat : Créer le système d'ELO
|
||||||
- [x] Fix : Changer le recto / verso des cartes
|
- [x] Fix : Changer le recto / verso des cartes
|
||||||
- [ ] Feat : Système d'évènements à ajouter sur la page principale
|
- [ ] Feat : Système d'évènements à ajouter sur la page principale
|
||||||
|
- [ ] Fix : Refuse to create deck when cards are not found
|
||||||
|
|
|
@ -6,11 +6,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pocketbase/pocketbase/core"
|
|
||||||
"github.com/pocketbase/pocketbase/apis"
|
"github.com/pocketbase/pocketbase/apis"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
const frontEndDevPort = "5173"
|
const frontEndDevPort = "5173"
|
||||||
|
@ -30,6 +31,17 @@ type DeckImport struct {
|
||||||
Cards []DeckImportCard `json:"cards"`
|
Cards []DeckImportCard `json:"cards"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeckAnswerCard struct {
|
||||||
|
Carte Carte `json:"carte"`
|
||||||
|
Amount int `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeckAnswer struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Commander Carte `json:"commander"`
|
||||||
|
Cartes []DeckAnswerCard `json:"cartes"`
|
||||||
|
}
|
||||||
|
|
||||||
func (app *application) SetupRoutes() {
|
func (app *application) SetupRoutes() {
|
||||||
app.pb.OnServe().BindFunc(func(se *core.ServeEvent) error {
|
app.pb.OnServe().BindFunc(func(se *core.ServeEvent) error {
|
||||||
mode := os.Getenv("GO_ENV")
|
mode := os.Getenv("GO_ENV")
|
||||||
|
@ -57,7 +69,29 @@ func (app *application) SetupRoutes() {
|
||||||
http.Error(w, fmt.Sprintf("proxy error: %v", err), http.StatusBadGateway)
|
http.Error(w, fmt.Sprintf("proxy error: %v", err), http.StatusBadGateway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache Route
|
// Cache Route - GET "/json/{...}"
|
||||||
|
GET_Cache(se, app)
|
||||||
|
|
||||||
|
// Search API - GET "/api/search"
|
||||||
|
GET_Search(se, app)
|
||||||
|
|
||||||
|
// Create deck API - POST "api/deck/create"
|
||||||
|
POST_CreateDeck(se, app)
|
||||||
|
|
||||||
|
// Get deck list API - GET "/api/deck"
|
||||||
|
GET_AllDeck(se, app)
|
||||||
|
|
||||||
|
// Proxy to svelte app
|
||||||
|
se.Router.Any("/{path...}", func(re *core.RequestEvent) error {
|
||||||
|
proxy.ServeHTTP(re.Response, re.Request)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return se.Next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GET_Cache(se *core.ServeEvent, app *application) {
|
||||||
se.Router.GET("/json/{path...}", func(re *core.RequestEvent) error {
|
se.Router.GET("/json/{path...}", func(re *core.RequestEvent) error {
|
||||||
path := fmt.Sprintf("json/%s",re.Request.PathValue("path"))
|
path := fmt.Sprintf("json/%s",re.Request.PathValue("path"))
|
||||||
|
|
||||||
|
@ -68,8 +102,9 @@ func (app *application) SetupRoutes() {
|
||||||
return re.JSON(http.StatusNotFound, map[string]string{"message": "json not found in cache"})
|
return re.JSON(http.StatusNotFound, map[string]string{"message": "json not found in cache"})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Search API
|
func GET_Search(se *core.ServeEvent, app *application) {
|
||||||
se.Router.GET("/api/search", func(re *core.RequestEvent) error {
|
se.Router.GET("/api/search", func(re *core.RequestEvent) error {
|
||||||
searchData := app.pb.Store().Get("searchData").([]CacheCarteSearch)
|
searchData := app.pb.Store().Get("searchData").([]CacheCarteSearch)
|
||||||
search := re.Request.URL.Query().Get("q")
|
search := re.Request.URL.Query().Get("q")
|
||||||
|
@ -77,8 +112,65 @@ func (app *application) SetupRoutes() {
|
||||||
resultData := filter(searchData, func(card CacheCarteSearch) bool { return strings.Contains(strings.ToLower(card.Name), strings.ToLower(search)) })
|
resultData := filter(searchData, func(card CacheCarteSearch) bool { return strings.Contains(strings.ToLower(card.Name), strings.ToLower(search)) })
|
||||||
return re.JSON(http.StatusOK, resultData)
|
return re.JSON(http.StatusOK, resultData)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Create deck API
|
func GET_AllDeck(se *core.ServeEvent, app *application) {
|
||||||
|
se.Router.GET("/api/deck", func(re *core.RequestEvent) error {
|
||||||
|
authRecord := re.Auth
|
||||||
|
|
||||||
|
deckList := []Deck{}
|
||||||
|
err := app.pb.DB().
|
||||||
|
NewQuery(fmt.Sprintf("SELECT name, url, commander, cartes FROM deck WHERE owner = '%v'", authRecord.Id)).
|
||||||
|
All(&deckList)
|
||||||
|
if err != nil {
|
||||||
|
return re.BadRequestError("Problem with " + err.Error(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
answer := []DeckAnswer{}
|
||||||
|
|
||||||
|
cardsIds := []string{}
|
||||||
|
for _, v := range deckList {
|
||||||
|
if !slices.Contains(cardsIds, v.Commander) {
|
||||||
|
cardsIds = append(cardsIds, fmt.Sprintf("id = '%s'",v.Commander))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range v.Cards {
|
||||||
|
if !slices.Contains(cardsIds, c.ID) {
|
||||||
|
cardsIds = append(cardsIds, fmt.Sprintf("id = '%s'",c.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardsData := []Carte{}
|
||||||
|
err = app.pb.DB().
|
||||||
|
NewQuery(fmt.Sprintf("SELECT * FROM carte WHERE %s", strings.Join(cardsIds, " OR "))).
|
||||||
|
All(&cardsData)
|
||||||
|
if err != nil {
|
||||||
|
return re.BadRequestError("Problem with " + err.Error(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range deckList {
|
||||||
|
tempDeck := DeckAnswer{
|
||||||
|
Name: v.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDeck.Commander = filter(cardsData, func(carte Carte) bool { return carte.ID == v.Commander })[0]
|
||||||
|
for _, c := range v.Cards {
|
||||||
|
cardData := filter(cardsData, func(carte Carte) bool { return carte.ID == c.ID})[0]
|
||||||
|
tempDeck.Cartes = append(tempDeck.Cartes, DeckAnswerCard{
|
||||||
|
Amount: c.Amount,
|
||||||
|
Carte: cardData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
answer = append(answer, tempDeck)
|
||||||
|
}
|
||||||
|
|
||||||
|
return re.JSON(http.StatusOK, answer)
|
||||||
|
}).Bind(apis.RequireAuth())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func POST_CreateDeck(se *core.ServeEvent, app *application) {
|
||||||
se.Router.POST("/api/deck/create", func(re *core.RequestEvent) error {
|
se.Router.POST("/api/deck/create", func(re *core.RequestEvent) error {
|
||||||
authRecord := re.Auth
|
authRecord := re.Auth
|
||||||
|
|
||||||
|
@ -134,6 +226,7 @@ func (app *application) SetupRoutes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cardInDeck := []DeckCard{}
|
cardInDeck := []DeckCard{}
|
||||||
|
cardsNotFound := []string{}
|
||||||
for _, v := range data.Cards {
|
for _, v := range data.Cards {
|
||||||
cardId := ""
|
cardId := ""
|
||||||
for _, c := range possibleCards {
|
for _, c := range possibleCards {
|
||||||
|
@ -143,9 +236,15 @@ func (app *application) SetupRoutes() {
|
||||||
}
|
}
|
||||||
if cardId == "" {
|
if cardId == "" {
|
||||||
log.Println(fmt.Sprintf("Card not found : %s",v.Name))
|
log.Println(fmt.Sprintf("Card not found : %s",v.Name))
|
||||||
}
|
cardsNotFound = append(cardsNotFound, v.Name)
|
||||||
|
} else {
|
||||||
cardInDeck = append(cardInDeck, DeckCard{ID: cardId, Amount: v.Amount})
|
cardInDeck = append(cardInDeck, DeckCard{ID: cardId, Amount: v.Amount})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cardsNotFound) > 0 {
|
||||||
|
return re.BadRequestError("Cards not found : " + strings.Join(cardsNotFound, ", "), err)
|
||||||
|
}
|
||||||
record.Set("cartes", cardInDeck)
|
record.Set("cartes", cardInDeck)
|
||||||
|
|
||||||
err = app.pb.Save(record)
|
err = app.pb.Save(record)
|
||||||
|
@ -153,20 +252,13 @@ func (app *application) SetupRoutes() {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
re.BadRequestError("Problem with creating deck", err)
|
re.BadRequestError("Problem with creating deck", err)
|
||||||
}
|
}
|
||||||
|
log.Println("Deck created")
|
||||||
return re.JSON(http.StatusOK, map[string]string{"message": "deck created"})
|
return re.JSON(http.StatusOK, map[string]string{"message": "deck created"})
|
||||||
}).Bind(apis.RequireAuth())
|
}).Bind(apis.RequireAuth())
|
||||||
|
|
||||||
// Proxy to svelte app
|
|
||||||
se.Router.Any("/{path...}", func(re *core.RequestEvent) error {
|
|
||||||
proxy.ServeHTTP(re.Response, re.Request)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return se.Next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func filter(ss []CacheCarteSearch, test func(CacheCarteSearch) bool) (ret []CacheCarteSearch) {
|
func filter[T any](ss []T, test func(T) bool) (ret []T) {
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
if test(s) {
|
if test(s) {
|
||||||
ret = append(ret, s)
|
ret = append(ret, s)
|
||||||
|
|
|
@ -5,53 +5,53 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type MtgSet struct {
|
type MtgSet struct {
|
||||||
ID string `db:"id" json:"id"`
|
ID string `db:"id" json:"id,omitempty"`
|
||||||
Code string `db:"code" json:"code"`
|
Code string `db:"code" json:"code,omitempty"`
|
||||||
Name string `db:"name" json:"name"`
|
Name string `db:"name" json:"name,omitempty"`
|
||||||
SanitizedName string `db:"sanitized_name" json:"sanitized_name"`
|
SanitizedName string `db:"sanitized_name" json:"sanitized_name,omitempty"`
|
||||||
ReleasedAt string `db:"release_at" json:"released_at"`
|
ReleasedAt string `db:"release_at" json:"released_at,omitempty"`
|
||||||
IconUri string `db:"icon_uri" json:"icon_uri"`
|
IconUri string `db:"icon_uri" json:"icon_uri,omitempty"`
|
||||||
SetType string `db:"type" json:"type"`
|
SetType string `db:"type" json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Brawlset struct {
|
type Brawlset struct {
|
||||||
ID string `db:"id" json:"id"`
|
ID string `db:"id" json:"id,omitempty"`
|
||||||
Name string `db:"name" json:"name"`
|
Name string `db:"name" json:"name,omitempty"`
|
||||||
SanitizedName string `db:"sanitized_name" json:"sanitized_name"`
|
SanitizedName string `db:"sanitized_name" json:"sanitized_name,omitempty"`
|
||||||
Sets types.JSONArray[string] `db:"sets" json:"sets"`
|
Sets types.JSONArray[string] `db:"sets" json:"sets,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Carte struct {
|
type Carte struct {
|
||||||
ID string `db:"id" json:"id"`
|
ID string `db:"id" json:"id,omitempty"`
|
||||||
Name string `db:"name" json:"name"`
|
Name string `db:"name" json:"name,omitempty"`
|
||||||
SanitizedName string `db:"sanitized_name" json:"sanitized_name"`
|
SanitizedName string `db:"sanitized_name" json:"sanitized_name,omitempty"`
|
||||||
Layout string `db:"layout" json:"layout"`
|
Layout string `db:"layout" json:"layout,omitempty"`
|
||||||
SmallImage string `db:"small_image" json:"small_image"`
|
SmallImage string `db:"small_image" json:"small_image,omitempty"`
|
||||||
SmallImageBack string `db:"small_image_back" json:"small_image_back"`
|
SmallImageBack string `db:"small_image_back" json:"small_image_back,omitempty"`
|
||||||
NormalImage string `db:"normal_image" json:"normal_image"`
|
NormalImage string `db:"normal_image" json:"normal_image,omitempty"`
|
||||||
NormalImageBack string `db:"normal_image_back" json:"normal_image_back"`
|
NormalImageBack string `db:"normal_image_back" json:"normal_image_back,omitempty"`
|
||||||
CardType string `db:"type" json:"type"`
|
CardType string `db:"type" json:"type,omitempty"`
|
||||||
ColorIdentity types.JSONArray[string] `db:"color_identity" json:"color_identity"`
|
ColorIdentity types.JSONArray[string] `db:"color_identity" json:"color_identity,omitempty"`
|
||||||
ReleasedAt string `db:"released_at" json:"released_at"`
|
ReleasedAt string `db:"released_at" json:"released_at,omitempty"`
|
||||||
MtgSet string `db:"mtg_set" json:"mtg_set"`
|
MtgSet string `db:"mtg_set" json:"mtg_set,omitempty"`
|
||||||
SetCode string `db:"set_code" json:"set_code"`
|
SetCode string `db:"set_code" json:"set_code,omitempty"`
|
||||||
Price string `db:"price" json:"price"`
|
Price string `db:"price" json:"price,omitempty"`
|
||||||
CardmarketUri string `db:"cardmarket_url" json:"cardmarket_url"`
|
CardmarketUri string `db:"cardmarket_url" json:"cardmarket_url,omitempty"`
|
||||||
CanBeCommander bool `db:"can_be_commander" json:"can_be_commander"`
|
CanBeCommander bool `db:"can_be_commander" json:"can_be_commander,omitempty"`
|
||||||
Banned bool `db:"banned" json:"banned"`
|
Banned bool `db:"banned" json:"banned,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeckCard struct {
|
type DeckCard struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id,omitempty"`
|
||||||
Amount int `json:"amount"`
|
Amount int `json:"amount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deck struct {
|
type Deck struct {
|
||||||
ID string `db:"id"`
|
ID string `db:"id" json:"id,omitempty"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name" json:"name,omitempty"`
|
||||||
ColorIdentity types.JSONArray[string] `db:"color_identity"`
|
ColorIdentity types.JSONArray[string] `db:"color_identity" json:"color_identity,omitempty"`
|
||||||
Owner string `db:"owner"`
|
Owner string `db:"owner" json:"owner,omitempty"`
|
||||||
Commander string `db:"commander"`
|
Commander string `db:"commander" json:"commander,omitempty"`
|
||||||
Brawlset string `db:"brawlset"`
|
Brawlset string `db:"brawlset" json:"brawslet,omitempty"`
|
||||||
Cards types.JSONArray[DeckCard] `db:"cartes"`
|
Cards types.JSONArray[DeckCard] `db:"cartes" json:"cartes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ Une fois compilé le frontend utilise [Bun](https://bun.sh/).
|
||||||
- [ ] Fix : Ajouter les icones de couleur pour les tops commandants
|
- [ ] Fix : Ajouter les icones de couleur pour les tops commandants
|
||||||
- [ ] Fix : Changer le menu mobile
|
- [ ] Fix : Changer le menu mobile
|
||||||
- [x] Fix : Changer les cartes recto / verso
|
- [x] Fix : Changer les cartes recto / verso
|
||||||
- [ ] Feat : Ajouter la liste des decks dans l'espace decks
|
- [x] Feat : Ajouter la liste des decks dans l'espace decks
|
||||||
|
- [ ] Fix : Mettre en forme la liste des decks
|
||||||
|
- [ ] Feat : Supprimer ou éditer un deck
|
||||||
- [ ] Feat : En créant un deck, warning si on possède déjà un deck avec ce commandant
|
- [ ] Feat : En créant un deck, warning si on possède déjà un deck avec ce commandant
|
||||||
- [ ] Feat : Ajouter un calendrier des évènements Brawlset sur la page d'accueil
|
- [ ] Feat : Ajouter un calendrier des évènements Brawlset sur la page d'accueil
|
||||||
- [ ] Feat : Ajouter une vue globale des statistiques ELO
|
- [ ] Feat : Ajouter une vue globale des statistiques ELO
|
||||||
|
|
|
@ -5,12 +5,17 @@
|
||||||
|
|
||||||
let brawlsets = $state([])
|
let brawlsets = $state([])
|
||||||
let deckImporter = $state("")
|
let deckImporter = $state("")
|
||||||
let selectedBset = $state("")
|
let selectedBset = $state("0")
|
||||||
let deckName = $state("")
|
let deckName = $state("")
|
||||||
let deckUrl = $state("")
|
let deckUrl = $state("")
|
||||||
let commander = $state("")
|
let commander = $state("")
|
||||||
let token = $state("")
|
let token = $state("")
|
||||||
|
|
||||||
|
let decks = $state("")
|
||||||
|
|
||||||
|
let error = $state("")
|
||||||
|
let valid = $state("")
|
||||||
|
|
||||||
onMount( async () => {
|
onMount( async () => {
|
||||||
const storageData = window.localStorage.getItem("pocketbase_auth")
|
const storageData = window.localStorage.getItem("pocketbase_auth")
|
||||||
if (storageData != null) {
|
if (storageData != null) {
|
||||||
|
@ -25,6 +30,22 @@
|
||||||
sortedData.sort((a,b) => a.Name.localeCompare(b.Name))
|
sortedData.sort((a,b) => a.Name.localeCompare(b.Name))
|
||||||
brawlsets = sortedData
|
brawlsets = sortedData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fetch("/api/deck", {
|
||||||
|
headers: {Authorization: token, "Content-Type": "application/json"},
|
||||||
|
}).then((res) => {
|
||||||
|
if(res.status == 200) {
|
||||||
|
res.json().then((apiData) => {
|
||||||
|
decks = apiData
|
||||||
|
console.log(apiData)
|
||||||
|
})
|
||||||
|
} else if (res.status == 401 || res.status == 400) {
|
||||||
|
res.json().then((apiData) => {
|
||||||
|
console.log(apiData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function setCommander(txt) {
|
function setCommander(txt) {
|
||||||
|
@ -44,6 +65,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function importDeck(){
|
function importDeck(){
|
||||||
|
error = ""
|
||||||
|
valid = ""
|
||||||
const deckText = deckImporter
|
const deckText = deckImporter
|
||||||
let lines = deckText.split("\n")
|
let lines = deckText.split("\n")
|
||||||
lines = lines.filter((line) => line.match(/[0-9]+\s[\w]+/) != undefined)
|
lines = lines.filter((line) => line.match(/[0-9]+\s[\w]+/) != undefined)
|
||||||
|
@ -70,10 +93,12 @@
|
||||||
deckUrl = ""
|
deckUrl = ""
|
||||||
commander = ""
|
commander = ""
|
||||||
console.log(apiData)
|
console.log(apiData)
|
||||||
|
valid = apiData["message"]
|
||||||
})
|
})
|
||||||
} else if (res.status == 401) {
|
} else if (res.status == 401 || res.status == 400) {
|
||||||
res.json().then((apiData) => {
|
res.json().then((apiData) => {
|
||||||
console.log(apiData)
|
console.log(apiData)
|
||||||
|
error = apiData["message"]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -81,10 +106,10 @@
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col w-full items-center p-4">
|
<div class="flex flex-col w-full items-center p-4 gap-4">
|
||||||
<div class="flex flex-col-reverse md:flex-row max-w-4xl w-full gap-4">
|
<div class="flex flex-col-reverse md:flex-row max-w-6xl w-full gap-4">
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full gap-2">
|
||||||
<textarea placeholder="Deck list dans le format MTGO" class="h-60" bind:value={deckImporter} oninput={(e) => setCommander(e.target.value)}></textarea>
|
<textarea placeholder="Deck list dans le format MTGO" class="h-60 p-2 border-black border-1" bind:value={deckImporter} oninput={(e) => setCommander(e.target.value)}></textarea>
|
||||||
<span>Commandant : {commander}</span>
|
<span>Commandant : {commander}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full gap-4">
|
<div class="flex flex-col w-full gap-4">
|
||||||
|
@ -99,6 +124,30 @@
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
<Button onclick={importDeck}>Importer</Button>
|
<Button onclick={importDeck}>Importer</Button>
|
||||||
|
{#if error != ""}
|
||||||
|
<span class="bg-red-200 text-sm rounded-md p-2">{error}</span>
|
||||||
|
{/if}
|
||||||
|
{#if valid != ""}
|
||||||
|
<span class="bg-green-200 text-sm rounded-md p-2">{valid}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h2 class="font-beleren text-2xl w-full max-w-6xl">Liste de decks</h2>
|
||||||
|
<div class="w-full max-w-6xl grid grid-cols-2">
|
||||||
|
{#each decks as deck}
|
||||||
|
<details>
|
||||||
|
<summary class="flex-row flex">
|
||||||
|
<h3 class="text-xl">{deck.name}</h3>
|
||||||
|
</summary>
|
||||||
|
<div class="mt-4">
|
||||||
|
<span>{deck.commander.name}</span>
|
||||||
|
<ul class="grid grid-cols-2">
|
||||||
|
{#each deck.cartes as carteData}
|
||||||
|
<li>{carteData.amount}x {carteData.carte.name}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue