diff --git a/.gitignore b/.gitignore
index d465a01..40f5492 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,5 @@ app/*.tsbuildinfo
app/next-env.d.ts
# data
-app/tools/data/
+app/tools/data/*
+app/tools/json/*
diff --git a/app/app/admin/bsets/page.tsx b/app/app/admin/bsets/page.tsx
index 963f87d..7e3ef49 100644
--- a/app/app/admin/bsets/page.tsx
+++ b/app/app/admin/bsets/page.tsx
@@ -95,7 +95,8 @@ export default function Home() {
}).then((res) => {
if(res.status == 200) {
res.json().then((apiData) => {
- setBsetList(old => [...old, { id: apiData.data, name: bset_name, sets: selectedSets }])
+ const addedBset : bsetExtended = { sanitized_name: bset_name.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase() ,id: apiData.data, name: bset_name, sets: selectedSets }
+ setBsetList(old => [...old, addedBset])
setSelectedSets([])
})
}
diff --git a/app/app/api/admin/bsets/create/route.ts b/app/app/api/admin/bsets/create/route.ts
index 8e17153..2af8d07 100644
--- a/app/app/api/admin/bsets/create/route.ts
+++ b/app/app/api/admin/bsets/create/route.ts
@@ -19,7 +19,8 @@ export async function POST(req: NextRequest) {
if(bset_name != undefined && selected_sets != undefined) {
const bset = await db.bset.create({
data: {
- name: bset_name
+ name: bset_name,
+ sanitized_name: bset_name.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase()
}
})
diff --git a/app/app/top/black/page.tsx b/app/app/top/black/page.tsx
new file mode 100644
index 0000000..a6d92a2
--- /dev/null
+++ b/app/app/top/black/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/mono-black.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/blue/page.tsx b/app/app/top/blue/page.tsx
new file mode 100644
index 0000000..95ef6f4
--- /dev/null
+++ b/app/app/top/blue/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/mono-blue.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/colorless/page.tsx b/app/app/top/colorless/page.tsx
new file mode 100644
index 0000000..ed60e96
--- /dev/null
+++ b/app/app/top/colorless/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/colorless.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/green/page.tsx b/app/app/top/green/page.tsx
new file mode 100644
index 0000000..fa1440d
--- /dev/null
+++ b/app/app/top/green/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/mono-green.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/multicolor/page.tsx b/app/app/top/multicolor/page.tsx
new file mode 100644
index 0000000..d303fd7
--- /dev/null
+++ b/app/app/top/multicolor/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/multicolor.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/red/page.tsx b/app/app/top/red/page.tsx
new file mode 100644
index 0000000..68cc78d
--- /dev/null
+++ b/app/app/top/red/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/mono-red.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/app/top/white/page.tsx b/app/app/top/white/page.tsx
new file mode 100644
index 0000000..fa834e4
--- /dev/null
+++ b/app/app/top/white/page.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { MTGCard } from '@/components/ui/mtg-card'
+
+import type { carte } from "@prisma/client";
+
+export default function Home() {
+ const [creatureCardList, setCreatureCardList] = useState([])
+ const [instantCardList, setInstantCardList] = useState([])
+ const [sorceryCardList, setSorceryCardList] = useState([])
+ const [planeswalkerCardList, setPlaneswalkerCardList] = useState([])
+ const [artifactCardList, setArtifactCardList] = useState([])
+ const [enchantmentCardList, setEnchantmentCardList] = useState([])
+ const [landCardList, setLandCardList] = useState([])
+
+ useEffect(() => {
+ fetch('http://localhost:8072/top/mono-white.json').then((res) => {
+ if(res.status == 200) {
+ res.json().then((data) => {
+ const limit = 20
+ setCreatureCardList(data["creature"].slice(0,limit))
+ setInstantCardList(data["instant"].slice(0,limit))
+ setSorceryCardList(data["sorcery"].slice(0,limit))
+ setPlaneswalkerCardList(data["planeswalker"].slice(0,limit))
+ setArtifactCardList(data["artifact"].slice(0,limit))
+ setEnchantmentCardList(data["enchantment"].slice(0,limit))
+ setLandCardList(data["land"].slice(0,limit))
+ console.log(data)
+ })
+ }
+ })
+ }, [])
+ return (
+
+
Creature
+
+ {creatureCardList.map((card: carte) => (
+
+ ))}
+
+
Instants
+
+ {instantCardList.map((card: carte) => (
+
+ ))}
+
+
Sorceries
+
+ {sorceryCardList.map((card: carte) => (
+
+ ))}
+
+
Enchantment
+
+ {enchantmentCardList.map((card: carte) => (
+
+ ))}
+
+
Planeswalker
+
+ {planeswalkerCardList.map((card: carte) => (
+
+ ))}
+
+
Artifact
+
+ {artifactCardList.map((card: carte) => (
+
+ ))}
+
+
Lands
+
+ {landCardList.map((card: carte) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/components/ui/mtg-card.tsx b/app/components/ui/mtg-card.tsx
new file mode 100644
index 0000000..470de1b
--- /dev/null
+++ b/app/components/ui/mtg-card.tsx
@@ -0,0 +1,32 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+interface MTGCardProps {
+ className?: string,
+ imageURI: string,
+ cardname: string,
+ url: string
+}
+
+const MTGCard = ({ className, imageURI, cardname, url }: MTGCardProps) => {
+ const [loaded, setLoaded] = React.useState(false)
+
+ return (
+
+ {!loaded &&
+ Loading...
+ }
+
{setLoaded(true)}} loading="lazy" />
+ {cardname}
+
+ )}
+MTGCard.displayName = "MTGCard"
+
+export { MTGCard }
diff --git a/app/components/ui/navigation-bar.tsx b/app/components/ui/navigation-bar.tsx
index 8afa63d..8bfc735 100644
--- a/app/components/ui/navigation-bar.tsx
+++ b/app/components/ui/navigation-bar.tsx
@@ -54,31 +54,45 @@ export function NavigationBar ({ isLoggedIn, username}: NavigationProps) {
-
- Blanc
+
+
+ Blanc
+
-
- Bleu
+
+
+ Bleu
+
-
- Noir
+
+
+ Noir
+
-
- Rouge
+
+
+ Rouge
+
-
- Vert
+
+
+ Vert
+
-
- Incolor
+
+
+ Incolor
+
- Multicolor
+
+ Multicolor
+
diff --git a/app/docker-compose.yml b/app/docker-compose.yml
new file mode 100644
index 0000000..6a5cf5b
--- /dev/null
+++ b/app/docker-compose.yml
@@ -0,0 +1,10 @@
+services:
+ nginx:
+ container_name: json_files
+ image: nginx
+ ports:
+ - 8072:80
+ volumes:
+ - ./tools/json:/usr/share/nginx/html:ro
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
+ restart: unless-stopped
diff --git a/app/nginx.conf b/app/nginx.conf
new file mode 100644
index 0000000..dc08c25
--- /dev/null
+++ b/app/nginx.conf
@@ -0,0 +1,56 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+
+ #access_log /var/log/nginx/host.access.log main;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+
+ if ($request_method = 'OPTIONS') {
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+ #
+ # Custom headers and headers various browsers *should* be OK with but aren't
+ #
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
+ #
+ # Tell client that this pre-flight info is valid for 20 days
+ #
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Content-Type' 'text/plain; charset=utf-8';
+ add_header 'Content-Length' 0;
+ return 204;
+ }
+ if ($request_method = 'POST') {
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
+ add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
+ }
+ if ($request_method = 'GET') {
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
+ add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
+ }
+ }
+
+ error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ #location ~ /\.ht {
+ # deny all;
+ #}
+}
diff --git a/app/package-lock.json b/app/package-lock.json
index 3f86135..6d30a74 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -28,7 +28,8 @@
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.5.4",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "ts-node": "^10.9.2"
},
"devDependencies": {
"@types/node": "^20.17.6",
@@ -67,6 +68,28 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
@@ -1776,6 +1799,30 @@
"react": ">= 16"
}
},
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "license": "MIT"
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -1787,7 +1834,6 @@
"version": "20.17.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz",
"integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@@ -2048,7 +2094,6 @@
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
- "dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -2067,6 +2112,18 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -2975,6 +3032,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "license": "MIT"
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3175,6 +3238,15 @@
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"license": "Apache-2.0"
},
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
@@ -5127,6 +5199,12 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
}
},
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "license": "ISC"
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -6856,6 +6934,55 @@
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"license": "Apache-2.0"
},
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node/node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "license": "MIT"
+ },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -7002,7 +7129,6 @@
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
- "dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -7032,7 +7158,6 @@
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
- "dev": true,
"license": "MIT"
},
"node_modules/uri-js": {
@@ -7094,6 +7219,12 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "license": "MIT"
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -7324,6 +7455,15 @@
"node": ">= 14"
}
},
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/app/package.json b/app/package.json
index 9a776a4..19f83a0 100644
--- a/app/package.json
+++ b/app/package.json
@@ -29,7 +29,8 @@
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.5.4",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "ts-node": "^10.9.2"
},
"devDependencies": {
"@types/node": "^20.17.6",
diff --git a/app/prisma/schema.prisma b/app/prisma/schema.prisma
index b805b66..443bd5c 100644
--- a/app/prisma/schema.prisma
+++ b/app/prisma/schema.prisma
@@ -20,6 +20,7 @@ model carte {
id String @id @default(uuid()) @db.Uuid
name_en String
name_fr String
+ sanitized_name String
released_at String
small_image String
normal_image String
@@ -36,12 +37,14 @@ model carte {
set set @relation(fields: [set_id], references: [id])
set_id String @db.Uuid
rarity String
+ type String?
cardmarket_uri String?
}
model set {
id String @id @default(uuid()) @db.Uuid
name_en String
+ sanitized_name String
code String
set_type String
released_at String?
@@ -54,5 +57,6 @@ model set {
model bset {
id String @id @default(uuid()) @db.Uuid
name String
+ sanitized_name String
sets set[]
}
diff --git a/app/tools/createJson.mjs b/app/tools/createJson.mjs
new file mode 100644
index 0000000..7350b6c
--- /dev/null
+++ b/app/tools/createJson.mjs
@@ -0,0 +1,122 @@
+import { PrismaClient } from '@prisma/client'
+import { writeFileSync } from 'fs'
+
+const db = new PrismaClient()
+
+const color_names = {
+ "mono-white": ["W"],
+ "mono-black": ["B"],
+ "mono-blue": ["U"],
+ "mono-green": ["G"],
+ "mono-red": ["R"],
+ "colorless": [],
+ "azorius": ["W","U"],
+ "dimir": ["U","B"],
+ "rakdos": ["B","R"],
+ "gruul": ["R","G"],
+ "selesnya": ["G","W"],
+ "orzhov": ["W","B"],
+ "izzet": ["U","R"],
+ "golgari": ["B","G"],
+ "boros": ["R","W"],
+ "simic": ["G","U"],
+ "esper": ["W","U","B"],
+ "grixis": ["U","B","R"],
+ "jund": ["B","R","G"],
+ "naya": ["R","G","W"],
+ "bant": ["G","W","U"],
+ "abzan": ["W","B","G"],
+ "jeskai": ["U","R","W"],
+ "sultai": ["B","G","U"],
+ "mardu": ["R","W","B"],
+ "temur": ["G","U","R"],
+ "yore-tiller": ["W","U","B","R"],
+ "glint-eye": ["U","B","R","G"],
+ "dune-brood": ["B","R","G","W"],
+ "ink-treader": ["R","G","W","U"],
+ "witch-maw": ["G","W","U","B"],
+ "five-color": ["G","W","U","B","R"],
+}
+
+function getColorName(colorArray) {
+ const colorArrayID = colorArray.sort().join(',')
+ for (const colorName of Object.keys(color_names)) {
+ if(colorArrayID === color_names[colorName].sort().join(',')){
+ return(colorName)
+ }
+ }
+ return ""
+}
+
+// I need to create
+// Lands
+// lands.json
+// All jsons lands from colors
+// Types
+
+async function createJson() {
+ console.log("Fetching data...")
+ const bsets = await db.bset.findMany({
+ relationLoadStrategy: "join",
+ include: {
+ sets: {
+ include: {
+ cards: true
+ }
+ }
+ }
+ })
+
+ let all_cards = []
+ bsets.forEach((bset) => {
+ bset.sets.forEach((set) => {
+ all_cards = [...all_cards, ...set.cards]
+ })
+ })
+
+
+
+
+
+ const landsData = {}
+ for (const colorName of Object.keys(color_names)) {
+ landsData[colorName] = []
+ }
+ const type_dict = {"creature": [],"land": [],"instant": [],"sorcery": [], "planeswalker": [], "artifact": [], "enchantment": []}
+ const colorsData = {"mono-white": structuredClone(type_dict),"mono-black": structuredClone(type_dict),"mono-blue": structuredClone(type_dict),"mono-green": structuredClone(type_dict),"mono-red": structuredClone(type_dict),"colorless": structuredClone(type_dict),"multicolor": structuredClone(type_dict)}
+
+ for (const card of all_cards) {
+ const colorName = getColorName(card.colors)
+ if (card.type == "land") {
+ if (colorName != "") {
+ landsData[colorName].push(card)
+ }
+ }
+
+ if (card.colors.length <= 1) {
+ colorsData[colorName][card.type].push(card)
+ } else {
+ colorsData["multicolor"][card.type].push(card)
+ }
+ }
+
+
+ for (const index of Object.keys(colorsData)) {
+ writeFileSync(import.meta.dirname + "/json/top/" + index + ".json",JSON.stringify(colorsData[index]), 'utf8')
+ }
+
+ const landsJsonRoot = [[],[],[],[]]
+ for (const colorName of Object.keys(color_names)){
+ if(color_names[colorName].length <= 3){
+ let index = color_names[colorName].length - 1
+ if(index < 0) { index = 0 }
+ landsJsonRoot[index].push({ "name": colorName, "count": landsData[colorName].length})
+ } else {
+ landsJsonRoot[3].push({ "name": colorName, "count": landsData[colorName].length})
+ }
+ writeFileSync(import.meta.dirname + "/json/lands/" + colorName + ".json",JSON.stringify(landsData[colorName]), 'utf8')
+ }
+ writeFileSync(import.meta.dirname + "/json/lands/lands.json",JSON.stringify(landsJsonRoot), 'utf8')
+}
+
+createJson()
diff --git a/app/tools/updateDatabase.mjs b/app/tools/updateDatabase.mjs
index a933860..613de66 100644
--- a/app/tools/updateDatabase.mjs
+++ b/app/tools/updateDatabase.mjs
@@ -33,7 +33,7 @@ try {
for (const set of sets.data) {
if(!preUpdateSetIds.includes(set.id)){
- const addingSetQuery = await client.query('INSERT INTO set(id, name_en, code, set_type, released_at, icon_svg_uri) VALUES($1, $2, $3, $4, $5, $6)', [set.id, set.name, set.code, set.set_type, set.released_at, set.icon_svg_uri])
+ const addingSetQuery = await client.query('INSERT INTO set(id, name_en, sanitized_name, code, set_type, released_at, icon_svg_uri) VALUES($1, $2, $3, $4, $5, $6, $7)', [set.id, set.name, set.name.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase(), set.code, set.set_type, set.released_at, set.icon_svg_uri])
}
}
@@ -54,6 +54,27 @@ try {
// For each card check if we need to upload it to the database
for (const carte of scryfallData) {
if(!preUpdateCardsIds.includes(carte.id)){
+ let type = null
+ const card_type = carte.type_line.toLowerCase()
+
+ if(card_type.includes("creature")){
+ type = "creature"
+ } else if (card_type.includes("planeswalker")) {
+ type = "planeswalker"
+ } else if (card_type.includes("artifact")) {
+ type = "artifact"
+ } else if (card_type.includes("instant")) {
+ type = "instant"
+ } else if (card_type.includes("enchantment")) {
+ type = "enchantment"
+ } else if (card_type.includes("sorcery")) {
+ type = "sorcery"
+ } else if (card_type.includes("land")) {
+ type = "land"
+ }
+
+
+
if(carte.printed_name == undefined) {
// If the card doesn't have a french name, print it to the console and skip
@@ -65,7 +86,7 @@ try {
}
// Add the card to the database
- const addingCardsQuery = await client.query('INSERT INTO carte(id, name_en, name_fr, released_at, small_image, normal_image, mana_cost, cmc, type_line_en, type_line_fr, oracle_text_en, oracle_text_fr, power, toughness, colors, keywords, set_id, rarity, cardmarket_uri) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)', [carte.id, carte.name, carte.printed_name, carte.released_at, carte.image_uris.small, carte.image_uris.normal, carte.mana_cost, carte.cmc, carte.type_line, carte.printed_type_line, carte.oracle_text, carte.printed_text, carte.power, carte.toughness, carte.colors, carte.keywords, carte.set_id, carte.rarity, carte.purchase_uris?.cardmarket])
+ const addingCardsQuery = await client.query('INSERT INTO carte(id, name_en, name_fr, released_at, small_image, normal_image, mana_cost, cmc, type_line_en, type_line_fr, oracle_text_en, oracle_text_fr, power, toughness, colors, keywords, set_id, rarity, cardmarket_uri, type, sanitized_name) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)', [carte.id, carte.name, carte.printed_name, carte.released_at, carte.image_uris.small, carte.image_uris.normal, carte.mana_cost, carte.cmc, carte.type_line, carte.printed_type_line, carte.oracle_text, carte.printed_text, carte.power, carte.toughness, carte.color_identity, carte.keywords, carte.set_id, carte.rarity, carte.purchase_uris?.cardmarket, type, carte.name.replace(/[^a-zA-Z0-9]/gim,"-").toLowerCase()])
total_inserted = total_inserted + 1
} else {
total_skipped = total_skipped + 1