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