feat: add 404 page, migrate to UUID v7, and consolidate DB

This commit is contained in:
jessy-david-dev
2026-04-10 22:48:15 +02:00
parent ba7a625298
commit 19d9afd2b4
8 changed files with 95 additions and 60 deletions
+36
View File
@@ -0,0 +1,36 @@
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "Page introuvable - WikiRush",
description: "Cette page n'existe pas.",
};
export default function NotFound() {
return (
<div className="min-h-dvh bg-[#0f0f0f] text-[#f0f0f0] px-4 py-12 flex flex-col items-center justify-center">
<div className="w-full max-w-md flex flex-col items-center gap-8 text-center">
<div className="flex flex-col gap-2">
<span className="text-7xl font-bold text-[#2e2e2e]">404</span>
<h1 className="text-xl font-bold">Page introuvable</h1>
<p className="text-[#888] text-sm leading-relaxed">
Cet article n&apos;existe pas sur WikiRush.
<br />
Tu t&apos;es peut-être perdu en chemin ?
</p>
</div>
<Link
href="/"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-medium
bg-[#1f1f1f] border border-[#2e2e2e] text-[#e5e5e5]
hover:bg-[#2a2a2a] hover:border-[#3a3a3a]
transition-all duration-200"
>
<span className="text-base"></span>
Retour à l&apos;accueil
</Link>
</div>
</div>
);
}
+1 -1
View File
@@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
"clientVersion": "7.7.0", "clientVersion": "7.7.0",
"engineVersion": "75cbdc1eb7150937890ad5465d861175c6624711", "engineVersion": "75cbdc1eb7150937890ad5465d861175c6624711",
"activeProvider": "sqlite", "activeProvider": "sqlite",
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../lib/generated/prisma\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\nmodel User {\n id String @id @default(cuid())\n name String\n email String @unique\n password String\n createdAt DateTime @default(now())\n games Game[]\n dailyResults DailyResult[]\n}\n\nmodel Game {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n mode String // \"solo\" | \"multi\" | \"daily\" | \"blitz\"\n startArticle String\n targetArticle String\n path String // JSON array de titres\n clicks Int\n timeSeconds Float\n won Boolean @default(true)\n playedAt DateTime @default(now())\n\n @@index([userId])\n}\n\nmodel DailyPuzzle {\n id String @id @default(cuid())\n date String @unique // \"YYYY-MM-DD\"\n startArticle String\n targetArticle String\n results DailyResult[]\n}\n\nmodel DailyResult {\n id String @id @default(cuid())\n puzzleId String\n puzzle DailyPuzzle @relation(fields: [puzzleId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n path String // JSON\n clicks Int\n timeSeconds Float\n won Boolean\n playedAt DateTime @default(now())\n\n @@unique([puzzleId, userId])\n @@index([puzzleId])\n}\n", "inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../lib/generated/prisma\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\nmodel User {\n id String @id @default(uuid(7))\n name String\n email String @unique\n password String\n createdAt DateTime @default(now())\n games Game[]\n dailyResults DailyResult[]\n}\n\nmodel Game {\n id String @id @default(uuid(7))\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n mode String // \"solo\" | \"multi\" | \"daily\" | \"blitz\"\n startArticle String\n targetArticle String\n path String // JSON array de titres\n clicks Int\n timeSeconds Float\n won Boolean @default(true)\n playedAt DateTime @default(now())\n\n @@index([userId])\n}\n\nmodel DailyPuzzle {\n id String @id @default(uuid(7))\n date String @unique // \"YYYY-MM-DD\"\n startArticle String\n targetArticle String\n results DailyResult[]\n}\n\nmodel DailyResult {\n id String @id @default(uuid(7))\n puzzleId String\n puzzle DailyPuzzle @relation(fields: [puzzleId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n path String // JSON\n clicks Int\n timeSeconds Float\n won Boolean\n playedAt DateTime @default(now())\n\n @@unique([puzzleId, userId])\n @@index([puzzleId])\n}\n",
"runtimeDataModel": { "runtimeDataModel": {
"models": {}, "models": {},
"enums": {}, "enums": {},
+1 -1
View File
@@ -5,7 +5,7 @@ import { PrismaClient } from "./generated/prisma/client";
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
function createPrisma() { function createPrisma() {
const adapter = new PrismaBetterSqlite3({ url: "./wikirace.db" }); const adapter = new PrismaBetterSqlite3({ url: "./wikirush.db" });
return new PrismaClient({ adapter }); return new PrismaClient({ adapter });
} }
+1 -1
View File
@@ -4,7 +4,7 @@ const config = {
path: "prisma/migrations", path: "prisma/migrations",
}, },
datasource: { datasource: {
url: "file:./wikirace.db", url: "file:./wikirush.db",
}, },
}; };
@@ -1,29 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "Game" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"mode" TEXT NOT NULL,
"startArticle" TEXT NOT NULL,
"targetArticle" TEXT NOT NULL,
"path" TEXT NOT NULL,
"clicks" INTEGER NOT NULL,
"timeSeconds" REAL NOT NULL,
"won" BOOLEAN NOT NULL DEFAULT true,
"playedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Game_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE INDEX "Game_userId_idx" ON "Game"("userId");
@@ -1,3 +1,27 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "Game" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"mode" TEXT NOT NULL,
"startArticle" TEXT NOT NULL,
"targetArticle" TEXT NOT NULL,
"path" TEXT NOT NULL,
"clicks" INTEGER NOT NULL,
"timeSeconds" REAL NOT NULL,
"won" BOOLEAN NOT NULL DEFAULT true,
"playedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Game_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable -- CreateTable
CREATE TABLE "DailyPuzzle" ( CREATE TABLE "DailyPuzzle" (
"id" TEXT NOT NULL PRIMARY KEY, "id" TEXT NOT NULL PRIMARY KEY,
@@ -20,6 +44,12 @@ CREATE TABLE "DailyResult" (
CONSTRAINT "DailyResult_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE CONSTRAINT "DailyResult_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
); );
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE INDEX "Game_userId_idx" ON "Game"("userId");
-- CreateIndex -- CreateIndex
CREATE UNIQUE INDEX "DailyPuzzle_date_key" ON "DailyPuzzle"("date"); CREATE UNIQUE INDEX "DailyPuzzle_date_key" ON "DailyPuzzle"("date");
+26 -28
View File
@@ -7,36 +7,34 @@ datasource db {
provider = "sqlite" provider = "sqlite"
} }
model User { model User {
id String @id @default(cuid()) id String @id @default(uuid(7))
name String name String
email String @unique email String @unique
password String password String
createdAt DateTime @default(now()) createdAt DateTime @default(now())
games Game[] games Game[]
dailyResults DailyResult[] dailyResults DailyResult[]
} }
model Game { model Game {
id String @id @default(cuid()) id String @id @default(uuid(7))
userId String userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
mode String // "solo" | "multi" | "daily" | "blitz" mode String // "solo" | "multi" | "daily" | "blitz"
startArticle String startArticle String
targetArticle String targetArticle String
path String // JSON array de titres path String // JSON array de titres
clicks Int clicks Int
timeSeconds Float timeSeconds Float
won Boolean @default(true) won Boolean @default(true)
playedAt DateTime @default(now()) playedAt DateTime @default(now())
@@index([userId]) @@index([userId])
} }
model DailyPuzzle { model DailyPuzzle {
id String @id @default(cuid()) id String @id @default(uuid(7))
date String @unique // "YYYY-MM-DD" date String @unique // "YYYY-MM-DD"
startArticle String startArticle String
targetArticle String targetArticle String
@@ -44,16 +42,16 @@ model DailyPuzzle {
} }
model DailyResult { model DailyResult {
id String @id @default(cuid()) id String @id @default(uuid(7))
puzzleId String puzzleId String
puzzle DailyPuzzle @relation(fields: [puzzleId], references: [id], onDelete: Cascade) puzzle DailyPuzzle @relation(fields: [puzzleId], references: [id], onDelete: Cascade)
userId String userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
path String // JSON path String // JSON
clicks Int clicks Int
timeSeconds Float timeSeconds Float
won Boolean won Boolean
playedAt DateTime @default(now()) playedAt DateTime @default(now())
@@unique([puzzleId, userId]) @@unique([puzzleId, userId])
@@index([puzzleId]) @@index([puzzleId])
BIN
View File
Binary file not shown.