Feat: Change deck management UI
This commit is contained in:
parent
36439c0837
commit
3a766620b7
21 changed files with 1955 additions and 187 deletions
|
@ -1,28 +1,41 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Input } from "@/components/ui/input"
|
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 { useRouter } from 'next/navigation'
|
||||||
import { getCookie } from "@/lib/utils"
|
import { getCookie } from "@/lib/utils"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
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 {
|
import {
|
||||||
Table,
|
Accordion,
|
||||||
TableBody,
|
AccordionContent,
|
||||||
TableCell,
|
AccordionItem,
|
||||||
TableHead,
|
AccordionTrigger,
|
||||||
TableHeader,
|
} from "@/components/ui/deck-accordion"
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table"
|
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
|
@ -36,37 +49,67 @@ import {
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} 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 {
|
interface bsetJson extends bset {
|
||||||
set_codes: string[],
|
set_codes: string[],
|
||||||
icons: 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 {
|
interface cardEntryAPIProps {
|
||||||
amount: number,
|
amount: number,
|
||||||
sanitized_name: string,
|
sanitized_name: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface deckAPIProps {
|
interface deckAPIProps {
|
||||||
name: string,
|
id?: string, // For Update API
|
||||||
selected_bset: string,
|
name: string,
|
||||||
commander_name: string,
|
selected_bset?: string, // For Create API
|
||||||
cards: cardEntryAPIProps[]
|
url?: string,
|
||||||
|
commander_name?: string,
|
||||||
|
cards: cardEntryAPIProps[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Signin() {
|
export default function Signin() {
|
||||||
const [deckName, setDeckName] = useState("")
|
const [deckName, setDeckName] = useState("")
|
||||||
|
const [deckUrl, setDeckUrl] = useState("")
|
||||||
const [deckCommanderName, setDeckCommanderName] = useState("")
|
const [deckCommanderName, setDeckCommanderName] = useState("")
|
||||||
const [deckImporter, setDeckImporter] = useState("")
|
const [deckImporter, setDeckImporter] = useState("")
|
||||||
const [selectedBset, setSelectedBset] = useState("")
|
const [selectedBset, setSelectedBset] = useState("")
|
||||||
const [decks, setDecks] = useState<deck[]>([])
|
const [decks, setDecks] = useState<deckExtended[]>([])
|
||||||
|
const [displayedDecks, setDisplayedDecks] = useState<deckExtended[]>([])
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
|
||||||
const [bsets, setBsets] = useState<bsetJson[]>([])
|
const [bsets, setBsets] = useState<bsetJson[]>([])
|
||||||
|
|
||||||
const [openSelectBset, setOpenSelectBset] = useState(false)
|
const [openSelectBset, setOpenSelectBset] = useState(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const token = getCookie('JWT')
|
const token = getCookie('JWT')
|
||||||
|
|
||||||
|
const [deckIdToDelete, setDeckIdToDelete] = useState("")
|
||||||
|
const deleteDialogRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
const [editDeckUrl, setEditDeckUrl] = useState("")
|
||||||
|
const [editDeckId, setEditDeckId] = useState("")
|
||||||
|
const [editDeckName, setEditDeckName] = useState("")
|
||||||
|
const [editDeckImporter, setEditDeckImporter] = useState("")
|
||||||
|
const [editDeckCommanderName, setEditDeckCommanderName] = useState("")
|
||||||
|
const editDialogRef = useRef<HTMLButtonElement>(null)
|
||||||
|
|
||||||
|
const { toast } = useToast()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(getCookie('JWT') == "") {
|
if(getCookie('JWT') == "") {
|
||||||
router.refresh()
|
router.refresh()
|
||||||
|
@ -79,7 +122,10 @@ export default function Signin() {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if(res.status == 200) {
|
if(res.status == 200) {
|
||||||
res.json().then((apiData) => {
|
res.json().then((apiData) => {
|
||||||
|
console.log(apiData.data)
|
||||||
setDecks(apiData.data)
|
setDecks(apiData.data)
|
||||||
|
setDisplayedDecks(apiData.data)
|
||||||
|
setLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -114,6 +160,10 @@ export default function Signin() {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if(res.status == 200) {
|
if(res.status == 200) {
|
||||||
setDecks(old => old.filter((deck: deck) => deck.id != id))
|
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){
|
function updateDeckInput(txt:string){
|
||||||
setDeckImporter(txt)
|
setDeckImporter(txt)
|
||||||
const lines = txt.split("\n")
|
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(){
|
function importDeck(){
|
||||||
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)
|
||||||
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) => {
|
lines.slice(0, lines.length - 1).forEach((line: string) => {
|
||||||
const data = getDataFromLine(line)
|
const data = getDataFromLine(line)
|
||||||
if(data != null) {
|
if(data != null) {
|
||||||
dataToSend.cards.push(data)
|
dataToSend.cards.push(data)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(dataToSend)
|
|
||||||
|
|
||||||
fetch('/api/account/decks/create', {
|
fetch('/api/account/decks/create', {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -144,107 +207,234 @@ export default function Signin() {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if(res.status == 200) {
|
if(res.status == 200) {
|
||||||
res.json().then((apiData) => {
|
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])
|
setDecks(oldDecks => [...oldDecks, new_deck])
|
||||||
setDeckName("")
|
setDeckName("")
|
||||||
setDeckImporter("")
|
setDeckImporter("")
|
||||||
setSelectedBset("")
|
setSelectedBset("")
|
||||||
setDeckCommanderName("")
|
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 (
|
return (
|
||||||
<div className="flex flex-col items-center mt-24" >
|
<div className="flex flex-col items-center mt-24" >
|
||||||
<Card className="max-w-xl w-full">
|
<div className="flex flex-col max-w-5xl w-full gap-12 items-center">
|
||||||
<CardHeader>
|
<div className="flex flex-row gap-12 w-full">
|
||||||
<CardTitle>Importer un deck</CardTitle>
|
<div className="flex flex-col gap-4 grow-4 w-full">
|
||||||
<CardDescription>Depuis moxfield</CardDescription>
|
<Textarea value={deckImporter} onChange={(e) => updateDeckInput(e.target.value)} placeholder="Deck List dans le format MTGO. Exemple : 1 Agate-Blade Assassin 1 Agate-Blade Assassin 1 Agate-Blade Assassin 1 Agate-Blade Assassin" className="h-60" />
|
||||||
</CardHeader>
|
<div><span className="font-bold">Commandant :</span> <span className="italic">{deckCommanderName}</span></div>
|
||||||
<CardContent>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label className="font-bold">Nom du deck</Label>
|
|
||||||
<Input value={deckName} onChange={(e) => setDeckName(e.target.value)} placeholder="Nom du deck" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label className="font-bold">BSet selectionné</Label>
|
|
||||||
<Popover open={openSelectBset} onOpenChange={setOpenSelectBset}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
aria-expanded={openSelectBset}
|
|
||||||
className="w-full justify-between"
|
|
||||||
>
|
|
||||||
{selectedBset
|
|
||||||
? bsets.find((bset: bset) => bset.sanitized_name === selectedBset)?.name
|
|
||||||
: "Selectionnez un Bset..."}
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-full p-0">
|
|
||||||
<Command className="w-full">
|
|
||||||
<CommandInput placeholder="Search framework..." />
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>Pas de BSet trouvé.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{bsets.map((bset) => (
|
|
||||||
<CommandItem
|
|
||||||
key={bset.sanitized_name}
|
|
||||||
value={bset.sanitized_name}
|
|
||||||
onSelect={(currentValue) => {
|
|
||||||
setSelectedBset(currentValue === selectedBset ? "" : currentValue)
|
|
||||||
setOpenSelectBset(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
{bset.icons.map((icon) => (
|
|
||||||
<img key={icon} src={icon} className="h-3" loading="lazy" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{bset.name}
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label className="font-bold">Commandant</Label>
|
|
||||||
<Label className="text-xs">Correspond à la première ligne de l'import Moxfield</Label>
|
|
||||||
<Input value={deckCommanderName} placeholder="Commandant du Deck" disabled />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Label className="font-bold">Liste des cartes</Label>
|
|
||||||
<Textarea value={deckImporter} onChange={(e) => updateDeckInput(e.target.value)} placeholder="Collez votre deck ici." className="h-60" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row w-full">
|
|
||||||
<Button onClick={importDeck}>Importer</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
<div className="flex flex-col gap-4 w-full">
|
||||||
</Card>
|
<Input className="text-3xl" value={deckName} onChange={(e) => setDeckName(e.target.value)} placeholder="Nom du deck" />
|
||||||
<div className="max-w-3xl w-full mt-12">
|
<Input placeholder="URL du Deck (Facultatif)" />
|
||||||
<Table className="w-full">
|
<Popover open={openSelectBset} onOpenChange={setOpenSelectBset}>
|
||||||
<TableHeader>
|
<PopoverTrigger asChild>
|
||||||
<TableRow>
|
<Button
|
||||||
<TableHead>Name</TableHead>
|
variant="outline"
|
||||||
<TableHead>Actions</TableHead>
|
role="combobox"
|
||||||
</TableRow>
|
aria-expanded={openSelectBset}
|
||||||
</TableHeader>
|
className="w-full justify-between"
|
||||||
<TableBody>
|
>
|
||||||
{ decks.map((deck) => (
|
{ selectedBset && (
|
||||||
<TableRow key={deck.id}>
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<TableCell>{deck.name}</TableCell>
|
<div className="flex flex-row gap-1">
|
||||||
<TableCell className="flex flex-row gap-4 items-center"><a href={"/deck/" + deck.id}>Voir la page</a><Button disabled>Editer</Button><Button onClick={() => {deleteDeck(deck.id)}} variant="destructive">Supprimer</Button></TableCell>
|
{ bsets.find((bset: bset) => bset.sanitized_name === selectedBset)?.icons.map((icon) => (
|
||||||
</TableRow>
|
<img key={icon} src={icon} className="h-3" loading="lazy" />
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</div>
|
||||||
</Table>
|
<span>{ bsets.find((bset: bset) => bset.sanitized_name === selectedBset)?.name }</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{ !selectedBset && (
|
||||||
|
<span>Selectionnez un Bset...</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search framework..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>Pas de BSet trouvé.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{bsets.map((bset) => (
|
||||||
|
<CommandItem
|
||||||
|
key={bset.sanitized_name}
|
||||||
|
value={bset.sanitized_name}
|
||||||
|
onSelect={(currentValue) => {
|
||||||
|
setSelectedBset(currentValue === selectedBset ? "" : currentValue)
|
||||||
|
setOpenSelectBset(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
{bset.icons.map((icon) => (
|
||||||
|
<img key={icon} src={icon} className="h-3" loading="lazy" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{bset.name}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Button onClick={importDeck}>Importer</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Edit Dialog */}
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger className="hidden" asChild>
|
||||||
|
<Button ref={editDialogRef} variant="outline">Edit Profile</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Modifier le deck</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<Input className="text-3xl" value={editDeckName} onChange={(e) => setEditDeckName(e.target.value)} placeholder="Nom du deck" />
|
||||||
|
<Input value={editDeckUrl} onChange={(e) => setEditDeckUrl(e.target.value)} placeholder="URL du deck" />
|
||||||
|
<Textarea value={editDeckImporter} onChange={(e) => updateEditDeckInput(e.target.value)} placeholder="Deck List dans le format MTGO. Exemple : 1 Agate-Blade Assassin 1 Agate-Blade Assassin 1 Agate-Blade Assassin 1 Agate-Blade Assassin" className="min-h-48" />
|
||||||
|
<div><span className="font-bold">Commandant :</span> <span className="italic">{editDeckCommanderName}</span></div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose>
|
||||||
|
<Button onClick={updateDeck} type="submit">Sauvegarder</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
|
||||||
|
{/* Delete Dialog */}
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger className="hidden" ref={deleteDialogRef}>Open</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Es-tu sûr·e?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
Cette action ne peut être annulée et le deck sera effacé de nos serveurs.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel onClick={() => deleteAlertHandler("","set")}>Annuler</AlertDialogCancel>
|
||||||
|
<AlertDialogAction onClick={() => deleteAlertHandler("","delete")}>Supprimer</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
<div className="w-full h-0.5 rounded-md bg-stone-200"></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{/* Decks Display */}
|
||||||
|
<div className="flex flex-col gap-4 w-full items-center">
|
||||||
|
<Input className="text-3xl" placeholder="Rechercher des Decks..." onChange={(e) => filterDecks(e.target.value)}/>
|
||||||
|
{ loading && (
|
||||||
|
<Spinner className="mt-12" />
|
||||||
|
)}
|
||||||
|
{ !loading && (
|
||||||
|
<div className="w-full">
|
||||||
|
<Accordion type="single" collapsible className="w-full">
|
||||||
|
{ displayedDecks.map((deck) => (
|
||||||
|
<AccordionItem key={deck.id} value={deck.id}>
|
||||||
|
<AccordionTrigger trashFunction={() => deleteAlertHandler(deck.id, "set")} editFunction={() => editDialogHandler(deck.id)} className="text-3xl"><div className="flex flex-col"><span>{deck.name}</span></div></AccordionTrigger>
|
||||||
|
<a className="text-sm text-orange-500" target={deck.url ? "_blank" : ""} href={deck.url ? deck.url : "#"}>{deck.url ? deck.url : "No url"}</a>
|
||||||
|
<AccordionContent className="flex flex-col gap-4">
|
||||||
|
<MTGCardTextHover carte={deck.commander} className="text-lg" />
|
||||||
|
<div className="grid grid-flow-rows grid-cols-3 gap-2">
|
||||||
|
{ deck.cartes.map((carte_parent: cartes_dans_deckExtended) => (
|
||||||
|
<div key={carte_parent.carte.id} className="flex flex-row gap-2">
|
||||||
|
{carte_parent.amount}x <MTGCardTextHover carte={carte_parent.carte} className="text-stone-500" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Toaster />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ interface cardEntryAPIProps {
|
||||||
export async function POST(req: NextRequest) {
|
export async function POST(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const token = req?.headers.get("authorization")?.split(" ")[1]
|
const token = req?.headers.get("authorization")?.split(" ")[1]
|
||||||
const { name, cards, selected_bset, commander_name } = await req.json()
|
const { name, cards, url, selected_bset, commander_name } = await req.json()
|
||||||
|
|
||||||
|
console.log(url)
|
||||||
|
|
||||||
if(token == undefined) {
|
if(token == undefined) {
|
||||||
return NextResponse.json({"message": "You did not provide a token."},{
|
return NextResponse.json({"message": "You did not provide a token."},{
|
||||||
|
@ -73,14 +75,12 @@ export async function POST(req: NextRequest) {
|
||||||
let allCardFound = true
|
let allCardFound = true
|
||||||
if(cardsData.findIndex(cardData => cardData.sanitized_name == commander_name) == -1){
|
if(cardsData.findIndex(cardData => cardData.sanitized_name == commander_name) == -1){
|
||||||
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(commander_name)) == -1){
|
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(commander_name)) == -1){
|
||||||
console.log(commander_name)
|
|
||||||
allCardFound = false
|
allCardFound = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cards.forEach((card: cardEntryAPIProps) => {
|
cards.forEach((card: cardEntryAPIProps) => {
|
||||||
if(cardsData.findIndex(cardData => cardData.sanitized_name == card.sanitized_name) == -1){
|
if(cardsData.findIndex(cardData => cardData.sanitized_name == card.sanitized_name) == -1){
|
||||||
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(card.sanitized_name)) == -1 ){
|
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(card.sanitized_name)) == -1 ){
|
||||||
console.log(card)
|
|
||||||
allCardFound = false
|
allCardFound = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ export async function POST(req: NextRequest) {
|
||||||
const deck = await db.deck.create({
|
const deck = await db.deck.create({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
|
url,
|
||||||
color_identity: commander_card.color_identity,
|
color_identity: commander_card.color_identity,
|
||||||
utilisateurice: {
|
utilisateurice: {
|
||||||
connect: {
|
connect: {
|
||||||
|
@ -136,7 +137,23 @@ export async function POST(req: NextRequest) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json({"data": deck, "message": "Deck created !"},{
|
// Necessary to fetch complete deck with cards to display it to the user
|
||||||
|
const deck_complete = await db.deck.findFirst({
|
||||||
|
where: {
|
||||||
|
id: deck.id
|
||||||
|
},
|
||||||
|
relationLoadStrategy: "join",
|
||||||
|
include: {
|
||||||
|
cartes: {
|
||||||
|
include: {
|
||||||
|
carte: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commander: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return NextResponse.json({"data": deck_complete, "message": "Deck created !"},{
|
||||||
status: 200,
|
status: 200,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -23,6 +23,15 @@ export async function GET(req: NextRequest) {
|
||||||
const decks = await db.deck.findMany({
|
const decks = await db.deck.findMany({
|
||||||
where: {
|
where: {
|
||||||
utilisateurice_id: tokenData.id
|
utilisateurice_id: tokenData.id
|
||||||
|
},
|
||||||
|
relationLoadStrategy: "join",
|
||||||
|
include: {
|
||||||
|
cartes: {
|
||||||
|
include: {
|
||||||
|
carte: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commander: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
194
app/app/api/account/decks/update/route.ts
Normal file
194
app/app/api/account/decks/update/route.ts
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
import { NextResponse, NextRequest } from 'next/server'
|
||||||
|
import { validateToken } from '@/lib/jwt'
|
||||||
|
import { db } from "@/lib/db"
|
||||||
|
|
||||||
|
interface orCardFilterProps {
|
||||||
|
sanitized_name: string
|
||||||
|
OR: orSetFilterProps[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface orSetFilterProps {
|
||||||
|
set_code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface cardEntryAPIProps {
|
||||||
|
amount: number,
|
||||||
|
sanitized_name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function PUT(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
const token = req?.headers.get("authorization")?.split(" ")[1]
|
||||||
|
const { id, name, cards, url, commander_name } = await req.json()
|
||||||
|
|
||||||
|
if(token == undefined) {
|
||||||
|
return NextResponse.json({"message": "You did not provide a token."},{
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!validateToken(token)) {
|
||||||
|
return NextResponse.json({"message": "Your token is not valid."},{
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if( id == undefined ) {
|
||||||
|
return NextResponse.json({"message": "Wrong data in the request."},{
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const deck_to_update = await db.deck.findFirst({
|
||||||
|
where: {
|
||||||
|
id: id
|
||||||
|
},
|
||||||
|
relationLoadStrategy: "join",
|
||||||
|
include: {
|
||||||
|
bset: {
|
||||||
|
include: {
|
||||||
|
sets: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(deck_to_update == undefined) {
|
||||||
|
return NextResponse.json({"message": "No deck with this ID."},{
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cards.length != 0 && commander_name != undefined) {
|
||||||
|
// Delete previous cards from deck
|
||||||
|
await db.cartes_dans_deck.deleteMany({
|
||||||
|
where: {
|
||||||
|
deck_id: id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const bset = deck_to_update?.bset
|
||||||
|
|
||||||
|
const set_codes: orSetFilterProps[] = []
|
||||||
|
bset?.sets.forEach((set) => {
|
||||||
|
set_codes.push({set_code: set.code})
|
||||||
|
})
|
||||||
|
|
||||||
|
const cardsFilter: orCardFilterProps[] = [{sanitized_name: commander_name, OR: set_codes}]
|
||||||
|
cards.forEach((card:cardEntryAPIProps) => {
|
||||||
|
cardsFilter.push({sanitized_name: card.sanitized_name, OR: set_codes})
|
||||||
|
})
|
||||||
|
|
||||||
|
let cardsData = await db.carte.findMany({
|
||||||
|
where: {
|
||||||
|
OR: set_codes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sort cards to select non promo types first and fallback to promo cards if not found
|
||||||
|
cardsData = cardsData.sort((a,b) => +a.is_promo - +b.is_promo)
|
||||||
|
|
||||||
|
let allCardFound = true
|
||||||
|
if(cardsData.findIndex(cardData => cardData.sanitized_name == commander_name) == -1){
|
||||||
|
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(commander_name)) == -1){
|
||||||
|
allCardFound = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cards.forEach((card: cardEntryAPIProps) => {
|
||||||
|
if(cardsData.findIndex(cardData => cardData.sanitized_name == card.sanitized_name) == -1){
|
||||||
|
if(cardsData.findIndex(cardData => cardData.sanitized_name.includes(card.sanitized_name)) == -1 ){
|
||||||
|
allCardFound = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(!allCardFound) {
|
||||||
|
return NextResponse.json({"message": "Some cards were not found..."},{
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const commander_card = cardsData.findIndex(cardData => cardData.sanitized_name == commander_name) == -1 ? cardsData[cardsData.findIndex(cardData => cardData.sanitized_name.includes(commander_name))] : cardsData[cardsData.findIndex(cardData => cardData.sanitized_name == commander_name)]
|
||||||
|
|
||||||
|
|
||||||
|
cards.forEach(async (card: cardEntryAPIProps) => {
|
||||||
|
const cardData_id = cardsData.findIndex(cardData => cardData.sanitized_name == card.sanitized_name) == -1 ? cardsData[cardsData.findIndex(cardData => cardData.sanitized_name.includes(card.sanitized_name))].id : cardsData[cardsData.findIndex(cardData => cardData.sanitized_name == card.sanitized_name)].id
|
||||||
|
console.log(card.sanitized_name)
|
||||||
|
await db.cartes_dans_deck.create({
|
||||||
|
data: {
|
||||||
|
amount: card.amount,
|
||||||
|
carte: {
|
||||||
|
connect: {
|
||||||
|
id: cardData_id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deck: {
|
||||||
|
connect: {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const deck = await db.deck.update({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
color_identity: commander_card.color_identity,
|
||||||
|
commander: {
|
||||||
|
connect: {
|
||||||
|
id: commander_card.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relationLoadStrategy: "join",
|
||||||
|
include: {
|
||||||
|
cartes: {
|
||||||
|
include: {
|
||||||
|
carte: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commander: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return NextResponse.json({"data": deck, "message": "Deck created !"},{
|
||||||
|
status: 200,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const deck = await db.deck.update({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
relationLoadStrategy: "join",
|
||||||
|
include: {
|
||||||
|
cartes: {
|
||||||
|
include: {
|
||||||
|
carte: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commander: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return NextResponse.json({"data": deck, "message": "Deck created !"},{
|
||||||
|
status: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Failed, check console" },
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
interface bsetJsonObject {
|
interface bsetJsonObject {
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -14,11 +15,13 @@ interface bsetJsonObject {
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [displayedBsetList, setDisplayedBsetList] = useState([])
|
const [displayedBsetList, setDisplayedBsetList] = useState([])
|
||||||
const [originalBsetList, setOriginalBsetList] = useState([])
|
const [originalBsetList, setOriginalBsetList] = useState([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/api/json/misc/bsets.json').then((res) => {
|
fetch('/api/json/misc/bsets.json').then((res) => {
|
||||||
if(res.status == 200) {
|
if(res.status == 200) {
|
||||||
res.json().then((data) => {
|
res.json().then((data) => {
|
||||||
|
setLoading(false)
|
||||||
setOriginalBsetList(data)
|
setOriginalBsetList(data)
|
||||||
setDisplayedBsetList(data)
|
setDisplayedBsetList(data)
|
||||||
})
|
})
|
||||||
|
@ -36,20 +39,23 @@ export default function Home() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center mt-16">
|
<div className="flex flex-col items-center mt-16">
|
||||||
<div className="flex flex-col p-16 max-w-6xl w-full gap-4">
|
<div className="flex flex-col p-16 max-w-6xl w-full gap-4 items-center">
|
||||||
<Input placeholder="Rechercher des BrawlSets..." onChange={(e) => filterBsetList(e.target.value)}/>
|
<Input placeholder="Rechercher des BrawlSets..." onChange={(e) => filterBsetList(e.target.value)}/>
|
||||||
<div className="grid grid-cols-3 gap-4 p-2 w-full">
|
{ loading && <Spinner className='mt-24'/> }
|
||||||
{ displayedBsetList.map((bset: bsetJsonObject) => (
|
{ !loading && (
|
||||||
<a key={bset.name} className="flex flex-row gap-2 items-center text-stone-500" href={"/bset/" + bset.sanitized_name}>
|
<div className="grid grid-cols-3 gap-4 p-2 w-full">
|
||||||
<div className="flex flex-row gap-1">
|
{ displayedBsetList.map((bset: bsetJsonObject) => (
|
||||||
{ bset.icons.map((icon) => (
|
<a key={bset.name} className="flex flex-row gap-2 items-center text-stone-500" href={"/bset/" + bset.sanitized_name}>
|
||||||
<img key={icon} src={icon} loading="lazy" className="w-5 h-5"/>
|
<div className="flex flex-row gap-1">
|
||||||
))}
|
{ bset.icons.map((icon) => (
|
||||||
</div>
|
<img key={icon} src={icon} loading="lazy" className="w-5 h-5"/>
|
||||||
<span>{bset.name}</span>
|
))}
|
||||||
</a>
|
</div>
|
||||||
))}
|
<span>{bset.name}</span>
|
||||||
</div>
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { CardGroup } from '@/components/ui/card-group'
|
import { CardGroup } from '@/components/ui/card-group'
|
||||||
|
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
interface PageContentProps {
|
interface PageContentProps {
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PageContent({color}: PageContentProps) {
|
export default function PageContent({color}: PageContentProps) {
|
||||||
const [commanderCardList, setCommanderCardList] = useState([])
|
const [commanderCardList, setCommanderCardList] = useState([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/api/json/commander/'+color+'.json').then((res) => {
|
fetch('/api/json/commander/'+color+'.json').then((res) => {
|
||||||
|
@ -16,6 +19,7 @@ export default function PageContent({color}: PageContentProps) {
|
||||||
res.json().then((data) => {
|
res.json().then((data) => {
|
||||||
const limit = 20
|
const limit = 20
|
||||||
setCommanderCardList(data.slice(0,limit))
|
setCommanderCardList(data.slice(0,limit))
|
||||||
|
setLoading(false)
|
||||||
console.log(data)
|
console.log(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -24,7 +28,8 @@ export default function PageContent({color}: PageContentProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center w-full">
|
<div className="flex flex-col items-center w-full">
|
||||||
<div className="flex flex-col items-center mt-24 mb-24 max-w-6xl">
|
<div className="flex flex-col items-center mt-24 mb-24 max-w-6xl">
|
||||||
<CardGroup groupName={"Top commandants - " + color} cards={commanderCardList} />
|
{ loading && ( <Spinner className='mt-36' /> )}
|
||||||
|
{ !loading && ( <CardGroup groupName={"Top commandants - " + color} cards={commanderCardList} /> )}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { CardGroup } from '@/components/ui/card-group'
|
import { CardGroup } from '@/components/ui/card-group'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [commanderCardList, setCommanderCardList] = useState([])
|
const [commanderCardList, setCommanderCardList] = useState([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/api/json/commander/top.json').then((res) => {
|
fetch('/api/json/commander/top.json').then((res) => {
|
||||||
|
@ -12,6 +14,7 @@ export default function Home() {
|
||||||
res.json().then((data) => {
|
res.json().then((data) => {
|
||||||
const limit = 20
|
const limit = 20
|
||||||
setCommanderCardList(data.slice(0,limit))
|
setCommanderCardList(data.slice(0,limit))
|
||||||
|
setLoading(false)
|
||||||
console.log(data)
|
console.log(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,7 +23,8 @@ export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center w-full">
|
<div className="flex flex-col items-center w-full">
|
||||||
<div className="flex flex-col items-center mt-24 mb-24 max-w-6xl">
|
<div className="flex flex-col items-center mt-24 mb-24 max-w-6xl">
|
||||||
<CardGroup groupName="Top commandants" cards={commanderCardList} />
|
{ loading && ( <Spinner className='mt-36' /> )}
|
||||||
|
{ !loading && ( <CardGroup groupName="Top commandants" cards={commanderCardList} /> )}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
141
app/components/ui/alert-dialog.tsx
Normal file
141
app/components/ui/alert-dialog.tsx
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { buttonVariants } from "@/components/ui/button"
|
||||||
|
|
||||||
|
const AlertDialog = AlertDialogPrimitive.Root
|
||||||
|
|
||||||
|
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
||||||
|
|
||||||
|
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
||||||
|
|
||||||
|
const AlertDialogOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Overlay
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const AlertDialogContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPortal>
|
||||||
|
<AlertDialogOverlay />
|
||||||
|
<AlertDialogPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</AlertDialogPortal>
|
||||||
|
))
|
||||||
|
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const AlertDialogHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||||
|
|
||||||
|
const AlertDialogFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
AlertDialogFooter.displayName = "AlertDialogFooter"
|
||||||
|
|
||||||
|
const AlertDialogTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-lg font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
||||||
|
|
||||||
|
const AlertDialogDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogDescription.displayName =
|
||||||
|
AlertDialogPrimitive.Description.displayName
|
||||||
|
|
||||||
|
const AlertDialogAction = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Action
|
||||||
|
ref={ref}
|
||||||
|
className={cn(buttonVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
||||||
|
|
||||||
|
const AlertDialogCancel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AlertDialogPrimitive.Cancel
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"mt-2 sm:mt-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogPortal,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
}
|
|
@ -10,11 +10,11 @@ const buttonVariants = cva(
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
"bg-orange-500 text-primary-foreground shadow hover:bg-orange-400",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||||
outline:
|
outline:
|
||||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
"border border-orange-500 bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
|
61
app/components/ui/deck-accordion.tsx
Normal file
61
app/components/ui/deck-accordion.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { ChevronDownIcon, Pencil1Icon, TrashIcon } from "@radix-ui/react-icons"
|
||||||
|
import { Fn } from "@prisma/client/runtime/library"
|
||||||
|
|
||||||
|
const Accordion = AccordionPrimitive.Root
|
||||||
|
|
||||||
|
const AccordionItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn("py-4 border-b", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AccordionItem.displayName = "AccordionItem"
|
||||||
|
|
||||||
|
interface AccordionFunctionProps extends AccordionPrimitive.AccordionTriggerProps {
|
||||||
|
trashFunction: any,
|
||||||
|
editFunction: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccordionTrigger = (({ className, children, trashFunction, editFunction, ...props }: AccordionFunctionProps) => (
|
||||||
|
<AccordionPrimitive.Header className="flex">
|
||||||
|
<AccordionPrimitive.Trigger
|
||||||
|
className={cn(
|
||||||
|
"flex flex-1 items-center justify-between text-sm font-medium transition-all text-left [&[data-state=open]>div>#chevronDown]:rotate-180",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<div className="flex flex-row gap-4 shrink-0">
|
||||||
|
<Pencil1Icon onClick={editFunction} className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||||
|
<TrashIcon onClick={trashFunction} className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||||
|
<ChevronDownIcon id="chevronDown" className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||||
|
</div>
|
||||||
|
</AccordionPrimitive.Trigger>
|
||||||
|
</AccordionPrimitive.Header>
|
||||||
|
))
|
||||||
|
|
||||||
|
const AccordionContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||||
|
</AccordionPrimitive.Content>
|
||||||
|
))
|
||||||
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
|
@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
"flex h-auto min-h-9 w-full border-l-orange-500 border-l-4 bg-transparent px-3 py-1 text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
36
app/components/ui/mtg-card-text-hover.tsx
Normal file
36
app/components/ui/mtg-card-text-hover.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
import type { carte } from '@prisma/client'
|
||||||
|
import { Spinner } from "./spinner"
|
||||||
|
|
||||||
|
interface carteWithProps {
|
||||||
|
carte: carte
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const MTGCardTextHover = ({ carte, className }: carteWithProps) => {
|
||||||
|
const [loaded, setLoaded] = React.useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col group cursor-pointer",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="w-fit border-transparent border-dotted border-b-stone-500 border-b-2">{carte.name}</span>
|
||||||
|
<div className="MTGCardTooltip absolute hidden group-hover:block group-active:block">
|
||||||
|
{!loaded &&
|
||||||
|
<div className="mt-8 flex flex-col items-center justify-center h-96 w-64 bg-stone-500 rounded-md">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<img src={carte.normal_image} className={ loaded ? "rounded-md mt-8 h-96" : "absolute opacity-0 h-96" } onLoad={() => {setLoaded(true)}} loading="lazy" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
MTGCardTextHover.displayName = "MTGCardTextHover"
|
||||||
|
|
||||||
|
export { MTGCardTextHover }
|
|
@ -1,6 +1,7 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Spinner } from "./spinner"
|
||||||
|
|
||||||
interface MTGCardProps {
|
interface MTGCardProps {
|
||||||
className?: string,
|
className?: string,
|
||||||
|
@ -26,9 +27,11 @@ const MTGCard = ({ className, imageURI, cardname, url, nbrDecks, totalDecks, per
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!loaded &&
|
{!loaded &&
|
||||||
<span className="h-64 shadow">Loading...</span>
|
<div className="flex flex-col items-center justify-center h-64 bg-stone-500 rounded-md">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
<img src={imageURI} className="rounded" height={loaded ? 'auto' : '0'} onLoad={() => {setLoaded(true)}} loading="lazy" />
|
<img src={imageURI} className={ loaded ? "rounded h-64" : "absolute opacity-0" } onLoad={() => {setLoaded(true)}} loading="lazy" />
|
||||||
<div className="flex flex-col items-center gap-0">
|
<div className="flex flex-col items-center gap-0">
|
||||||
{ price != undefined && (
|
{ price != undefined && (
|
||||||
<a className="text-xs" href={cardmarketURI != undefined ? cardmarketURI : "#"} target={cardmarketURI != undefined ? "_blank" : "_self"}>{price}€</a>
|
<a className="text-xs" href={cardmarketURI != undefined ? cardmarketURI : "#"} target={cardmarketURI != undefined ? "_blank" : "_self"}>{price}€</a>
|
||||||
|
|
15
app/components/ui/spinner.tsx
Normal file
15
app/components/ui/spinner.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
interface SpinnerProps {
|
||||||
|
className?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Spinner = ({ className }: SpinnerProps) => {
|
||||||
|
return (
|
||||||
|
<div className={cn("inline-block w-12 h-12 border-4 border-orange-200 border-t-orange-500 rounded-full animate-spin", className)}></div>
|
||||||
|
)}
|
||||||
|
Spinner.displayName = "Spinner"
|
||||||
|
|
||||||
|
export { Spinner }
|
128
app/components/ui/toast.tsx
Normal file
128
app/components/ui/toast.tsx
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ToastPrimitives from "@radix-ui/react-toast"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Cross2Icon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
const ToastProvider = ToastPrimitives.Provider
|
||||||
|
|
||||||
|
const ToastViewport = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||||
|
|
||||||
|
const toastVariants = cva(
|
||||||
|
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "border bg-background text-foreground",
|
||||||
|
destructive:
|
||||||
|
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Toast = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||||
|
VariantProps<typeof toastVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<ToastPrimitives.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(toastVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
Toast.displayName = ToastPrimitives.Root.displayName
|
||||||
|
|
||||||
|
const ToastAction = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Action>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Action
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastAction.displayName = ToastPrimitives.Action.displayName
|
||||||
|
|
||||||
|
const ToastClose = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Close>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Close
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
toast-close=""
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Cross2Icon className="h-4 w-4" />
|
||||||
|
</ToastPrimitives.Close>
|
||||||
|
))
|
||||||
|
ToastClose.displayName = ToastPrimitives.Close.displayName
|
||||||
|
|
||||||
|
const ToastTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
||||||
|
|
||||||
|
const ToastDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm opacity-90", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||||
|
|
||||||
|
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||||
|
|
||||||
|
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||||
|
|
||||||
|
export {
|
||||||
|
type ToastProps,
|
||||||
|
type ToastActionElement,
|
||||||
|
ToastProvider,
|
||||||
|
ToastViewport,
|
||||||
|
Toast,
|
||||||
|
ToastTitle,
|
||||||
|
ToastDescription,
|
||||||
|
ToastClose,
|
||||||
|
ToastAction,
|
||||||
|
}
|
35
app/components/ui/toaster.tsx
Normal file
35
app/components/ui/toaster.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useToast } from "@/hooks/use-toast"
|
||||||
|
import {
|
||||||
|
Toast,
|
||||||
|
ToastClose,
|
||||||
|
ToastDescription,
|
||||||
|
ToastProvider,
|
||||||
|
ToastTitle,
|
||||||
|
ToastViewport,
|
||||||
|
} from "@/components/ui/toast"
|
||||||
|
|
||||||
|
export function Toaster() {
|
||||||
|
const { toasts } = useToast()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastProvider>
|
||||||
|
{toasts.map(function ({ id, title, description, action, ...props }) {
|
||||||
|
return (
|
||||||
|
<Toast key={id} {...props}>
|
||||||
|
<div className="grid gap-1">
|
||||||
|
{title && <ToastTitle>{title}</ToastTitle>}
|
||||||
|
{description && (
|
||||||
|
<ToastDescription>{description}</ToastDescription>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{action}
|
||||||
|
<ToastClose />
|
||||||
|
</Toast>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<ToastViewport />
|
||||||
|
</ToastProvider>
|
||||||
|
)
|
||||||
|
}
|
194
app/hooks/use-toast.ts
Normal file
194
app/hooks/use-toast.ts
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
// Inspired by react-hot-toast library
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ToastActionElement,
|
||||||
|
ToastProps,
|
||||||
|
} from "@/components/ui/toast"
|
||||||
|
|
||||||
|
const TOAST_LIMIT = 3
|
||||||
|
const TOAST_REMOVE_DELAY = 1000000
|
||||||
|
|
||||||
|
type ToasterToast = ToastProps & {
|
||||||
|
id: string
|
||||||
|
title?: React.ReactNode
|
||||||
|
description?: React.ReactNode
|
||||||
|
action?: ToastActionElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionTypes = {
|
||||||
|
ADD_TOAST: "ADD_TOAST",
|
||||||
|
UPDATE_TOAST: "UPDATE_TOAST",
|
||||||
|
DISMISS_TOAST: "DISMISS_TOAST",
|
||||||
|
REMOVE_TOAST: "REMOVE_TOAST",
|
||||||
|
} as const
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
function genId() {
|
||||||
|
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||||
|
return count.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionType = typeof actionTypes
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| {
|
||||||
|
type: ActionType["ADD_TOAST"]
|
||||||
|
toast: ToasterToast
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["UPDATE_TOAST"]
|
||||||
|
toast: Partial<ToasterToast>
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["DISMISS_TOAST"]
|
||||||
|
toastId?: ToasterToast["id"]
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType["REMOVE_TOAST"]
|
||||||
|
toastId?: ToasterToast["id"]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
toasts: ToasterToast[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
|
const addToRemoveQueue = (toastId: string) => {
|
||||||
|
if (toastTimeouts.has(toastId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
toastTimeouts.delete(toastId)
|
||||||
|
dispatch({
|
||||||
|
type: "REMOVE_TOAST",
|
||||||
|
toastId: toastId,
|
||||||
|
})
|
||||||
|
}, TOAST_REMOVE_DELAY)
|
||||||
|
|
||||||
|
toastTimeouts.set(toastId, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reducer = (state: State, action: Action): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case "ADD_TOAST":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||||
|
}
|
||||||
|
|
||||||
|
case "UPDATE_TOAST":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.map((t) =>
|
||||||
|
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
case "DISMISS_TOAST": {
|
||||||
|
const { toastId } = action
|
||||||
|
|
||||||
|
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||||
|
// but I'll keep it here for simplicity
|
||||||
|
if (toastId) {
|
||||||
|
addToRemoveQueue(toastId)
|
||||||
|
} else {
|
||||||
|
state.toasts.forEach((toast) => {
|
||||||
|
addToRemoveQueue(toast.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.map((t) =>
|
||||||
|
t.id === toastId || toastId === undefined
|
||||||
|
? {
|
||||||
|
...t,
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
: t
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "REMOVE_TOAST":
|
||||||
|
if (action.toastId === undefined) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const listeners: Array<(state: State) => void> = []
|
||||||
|
|
||||||
|
let memoryState: State = { toasts: [] }
|
||||||
|
|
||||||
|
function dispatch(action: Action) {
|
||||||
|
memoryState = reducer(memoryState, action)
|
||||||
|
listeners.forEach((listener) => {
|
||||||
|
listener(memoryState)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Toast = Omit<ToasterToast, "id">
|
||||||
|
|
||||||
|
function toast({ ...props }: Toast) {
|
||||||
|
const id = genId()
|
||||||
|
|
||||||
|
const update = (props: ToasterToast) =>
|
||||||
|
dispatch({
|
||||||
|
type: "UPDATE_TOAST",
|
||||||
|
toast: { ...props, id },
|
||||||
|
})
|
||||||
|
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: "ADD_TOAST",
|
||||||
|
toast: {
|
||||||
|
...props,
|
||||||
|
id,
|
||||||
|
open: true,
|
||||||
|
onOpenChange: (open) => {
|
||||||
|
if (!open) dismiss()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
dismiss,
|
||||||
|
update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useToast() {
|
||||||
|
const [state, setState] = React.useState<State>(memoryState)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
listeners.push(setState)
|
||||||
|
return () => {
|
||||||
|
const index = listeners.indexOf(setState)
|
||||||
|
if (index > -1) {
|
||||||
|
listeners.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toast,
|
||||||
|
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useToast, toast }
|
802
app/package-lock.json
generated
802
app/package-lock.json
generated
|
@ -9,6 +9,8 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "^5.22.0",
|
||||||
|
"@radix-ui/react-accordion": "^1.2.3",
|
||||||
|
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||||
"@radix-ui/react-avatar": "^1.1.1",
|
"@radix-ui/react-avatar": "^1.1.1",
|
||||||
"@radix-ui/react-checkbox": "^1.1.2",
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^1.1.2",
|
"@radix-ui/react-dialog": "^1.1.2",
|
||||||
|
@ -17,7 +19,8 @@
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.1",
|
"@radix-ui/react-navigation-menu": "^1.2.1",
|
||||||
"@radix-ui/react-popover": "^1.1.3",
|
"@radix-ui/react-popover": "^1.1.3",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
"@tabler/icons": "^3.22.0",
|
"@tabler/icons": "^3.22.0",
|
||||||
"@tabler/icons-react": "^3.22.0",
|
"@tabler/icons-react": "^3.22.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
@ -1017,6 +1020,179 @@
|
||||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==",
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-collapsible": "1.1.3",
|
||||||
|
"@radix-ui/react-collection": "1.1.2",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-direction": "1.1.0",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-dialog": "1.1.6",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-arrow": {
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||||
|
@ -1096,6 +1272,104 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-presence": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-collection": {
|
"node_modules/@radix-ui/react-collection": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
|
||||||
|
@ -1137,6 +1411,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||||
|
@ -1168,25 +1460,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-dialog": {
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz",
|
||||||
"integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==",
|
"integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.0",
|
"@radix-ui/primitive": "1.1.1",
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
"@radix-ui/react-context": "1.1.1",
|
"@radix-ui/react-context": "1.1.1",
|
||||||
"@radix-ui/react-dismissable-layer": "1.1.1",
|
"@radix-ui/react-dismissable-layer": "1.1.5",
|
||||||
"@radix-ui/react-focus-guards": "1.1.1",
|
"@radix-ui/react-focus-guards": "1.1.1",
|
||||||
"@radix-ui/react-focus-scope": "1.1.0",
|
"@radix-ui/react-focus-scope": "1.1.2",
|
||||||
"@radix-ui/react-id": "1.1.0",
|
"@radix-ui/react-id": "1.1.0",
|
||||||
"@radix-ui/react-portal": "1.1.2",
|
"@radix-ui/react-portal": "1.1.4",
|
||||||
"@radix-ui/react-presence": "1.1.1",
|
"@radix-ui/react-presence": "1.1.2",
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
"@radix-ui/react-slot": "1.1.0",
|
"@radix-ui/react-slot": "1.1.2",
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
"aria-hidden": "^1.1.1",
|
"aria-hidden": "^1.2.4",
|
||||||
"react-remove-scroll": "2.6.0"
|
"react-remove-scroll": "^2.6.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
@ -1203,6 +1495,175 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-remove-scroll-bar": "^2.3.7",
|
||||||
|
"react-style-singleton": "^2.2.3",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"use-callback-ref": "^1.3.3",
|
||||||
|
"use-sidecar": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-direction": {
|
"node_modules/@radix-ui/react-direction": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
||||||
|
@ -1404,6 +1865,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-navigation-menu": {
|
"node_modules/@radix-ui/react-navigation-menu": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.1.tgz",
|
||||||
|
@ -1812,6 +2291,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-roving-focus": {
|
"node_modules/@radix-ui/react-roving-focus": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
||||||
|
@ -1859,12 +2356,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-compose-refs": "1.1.0"
|
"@radix-ui/react-compose-refs": "1.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
@ -1876,6 +2373,223 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-collection": "1.1.2",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.5",
|
||||||
|
"@radix-ui/react-portal": "1.1.4",
|
||||||
|
"@radix-ui/react-presence": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||||
|
"@radix-ui/react-visually-hidden": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-collection": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-visually-hidden": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
@ -4815,15 +5529,6 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/invariant": {
|
|
||||||
"version": "2.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
|
||||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-arguments": {
|
"node_modules/is-arguments": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||||
|
@ -6395,20 +7100,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-remove-scroll-bar": {
|
"node_modules/react-remove-scroll-bar": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||||
"integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==",
|
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-style-singleton": "^2.2.1",
|
"react-style-singleton": "^2.2.2",
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
"@types/react": "*",
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
@ -6417,21 +7122,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-style-singleton": {
|
"node_modules/react-style-singleton": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||||
"integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
|
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-nonce": "^1.0.0",
|
"get-nonce": "^1.0.0",
|
||||||
"invariant": "^2.2.4",
|
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
"@types/react": "*",
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
@ -7450,9 +8154,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/use-callback-ref": {
|
"node_modules/use-callback-ref": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||||
"integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==",
|
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
|
@ -7461,8 +8165,8 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
"@types/react": "*",
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
@ -7471,9 +8175,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/use-sidecar": {
|
"node_modules/use-sidecar": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||||
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
|
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"detect-node-es": "^1.1.0",
|
"detect-node-es": "^1.1.0",
|
||||||
|
@ -7483,8 +8187,8 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
"@types/react": "*",
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "^5.22.0",
|
||||||
|
"@radix-ui/react-accordion": "^1.2.3",
|
||||||
|
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||||
"@radix-ui/react-avatar": "^1.1.1",
|
"@radix-ui/react-avatar": "^1.1.1",
|
||||||
"@radix-ui/react-checkbox": "^1.1.2",
|
"@radix-ui/react-checkbox": "^1.1.2",
|
||||||
"@radix-ui/react-dialog": "^1.1.2",
|
"@radix-ui/react-dialog": "^1.1.2",
|
||||||
|
@ -18,7 +20,8 @@
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.1",
|
"@radix-ui/react-navigation-menu": "^1.2.1",
|
||||||
"@radix-ui/react-popover": "^1.1.3",
|
"@radix-ui/react-popover": "^1.1.3",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
"@tabler/icons": "^3.22.0",
|
"@tabler/icons": "^3.22.0",
|
||||||
"@tabler/icons-react": "^3.22.0",
|
"@tabler/icons-react": "^3.22.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
|
|
|
@ -45,6 +45,7 @@ model carte {
|
||||||
|
|
||||||
model deck {
|
model deck {
|
||||||
id String @id @default(uuid()) @db.Uuid
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
url String?
|
||||||
color_identity String[]
|
color_identity String[]
|
||||||
name String
|
name String
|
||||||
utilisateurice_id String @db.Uuid
|
utilisateurice_id String @db.Uuid
|
||||||
|
|
|
@ -55,6 +55,28 @@ const config: Config = {
|
||||||
lg: 'var(--radius)',
|
lg: 'var(--radius)',
|
||||||
md: 'calc(var(--radius) - 2px)',
|
md: 'calc(var(--radius) - 2px)',
|
||||||
sm: 'calc(var(--radius) - 4px)'
|
sm: 'calc(var(--radius) - 4px)'
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
'accordion-down': {
|
||||||
|
from: {
|
||||||
|
height: '0'
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
height: 'var(--radix-accordion-content-height)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'accordion-up': {
|
||||||
|
from: {
|
||||||
|
height: 'var(--radix-accordion-content-height)'
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
height: '0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||||
|
'accordion-up': 'accordion-up 0.2s ease-out'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue