feat(scraper): implement One Piece data scraper for devil fruits and characters

- Added a new script to scrape devil fruits and characters from One Piece fandom.
- Implemented functions to fetch, normalize, and save data in JSON, CSV, and SQL formats.
- Created a structured output directory for scraped data.

feat(database): update schema for devil fruits and characters

- Defined new types for devil fruits and haki in the database schema.
- Updated the character table to include fields for age, affiliations, devil fruit, haki, bounty, height, origin, first appearance, and picture URL.

feat(ui): enhance main page and daily mode layout

- Redesigned the main page with a new layout and styling for the OnePieceDle game.
- Created a new daily mode page with sections for clues and user input for guesses.
- Removed demo authentication routes and pages to streamline the application.
This commit is contained in:
2026-02-27 01:14:44 +01:00
parent c494866a70
commit 6f7bae2307
17 changed files with 2407 additions and 165 deletions

View File

@@ -1,2 +1,63 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
<svelte:head>
<title>OnePieceDle</title>
</svelte:head>
<main
class="relative min-h-screen overflow-hidden bg-slate-950 text-slate-100"
style="background-image: url('/one-piece-bg.jpg');"
>
<div class="absolute inset-0 bg-gradient-to-br from-slate-950/85 via-slate-900/60 to-slate-950/80"></div>
<div class="absolute inset-0 mix-blend-screen opacity-20 bg-[radial-gradient(circle_at_top,rgba(255,215,84,0.35),transparent_55%)]"></div>
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col items-center justify-between px-6 py-24 sm:py-28">
<div class="flex w-full flex-1 flex-col items-center justify-between gap-12">
<div class="rounded-full border border-amber-200/30 bg-amber-100/10 px-5 py-2 text-xs font-semibold uppercase tracking-[0.35em] text-amber-100">
Jeu de devinettes Grand Line
</div>
<div class="text-center">
<h1 class="text-4xl font-black uppercase tracking-[0.3em] text-amber-50 sm:text-6xl">
OnePieceDle
</h1>
<p class="mt-4 max-w-2xl text-base text-slate-200 sm:text-lg">
Devine le personnage de l'equipage, des marines ou du vaste monde. Chaque indice te rapproche du tresor.
</p>
</div>
<div class="grid w-full gap-4 sm:grid-cols-2">
<div class="rounded-2xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<h2 class="text-sm font-semibold uppercase tracking-[0.2em] text-amber-100">Voyage du jour</h2>
<p class="mt-3 text-lg font-semibold text-white">Nouveau mystere toutes les 24 heures</p>
<p class="mt-2 text-sm text-slate-200">Compare tes essais, debloque des indices et garde ta serie.</p>
<a
href="/daily"
class="mt-5 inline-flex w-full items-center justify-center rounded-full bg-amber-300 px-5 py-3 text-sm font-semibold text-slate-900 transition hover:bg-amber-200"
>
Commencer
</a>
</div>
<div class="rounded-2xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<h2 class="text-sm font-semibold uppercase tracking-[0.2em] text-amber-100">Partie libre</h2>
<p class="mt-3 text-lg font-semibold text-white">Entraine-toi avec des pirates legendaires</p>
<p class="mt-2 text-sm text-slate-200">Choisis une epoque, regle la difficulte et vogue a ton rythme.</p>
<button class="mt-5 w-full rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50">
Choisir un voyage
</button>
</div>
</div>
<div class="w-full rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur sm:p-8">
<div class="flex flex-col items-center gap-5 text-center sm:flex-row sm:text-left">
<div class="flex h-20 w-20 items-center justify-center rounded-full border border-amber-200/40 bg-slate-900/70 text-xs uppercase tracking-[0.25em] text-amber-100">
Photo
</div>
<div class="flex-1">
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">Personnage de la veille</p>
<p class="mt-2 text-lg font-semibold text-white">Placeholder</p>
<p class="mt-1 text-sm text-slate-200">Le revele sera visible apres la partie du jour.</p>
</div>
<button class="w-full rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50 sm:w-auto">
Voir l'archive
</button>
</div>
</div>
</div>
</div>
</main>

View File

@@ -0,0 +1,87 @@
<svelte:head>
<title>OnePieceDle - Mode du jour</title>
</svelte:head>
<main
class="relative min-h-screen overflow-hidden bg-slate-950 text-slate-100"
style="background-image: url('/one-piece-bg.jpg');"
>
<div class="absolute inset-0 bg-gradient-to-br from-slate-950/85 via-slate-900/60 to-slate-950/80"></div>
<div class="absolute inset-0 mix-blend-screen opacity-20 bg-[radial-gradient(circle_at_top,rgba(255,215,84,0.35),transparent_55%)]"></div>
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col px-6 py-16 sm:py-20">
<header class="flex flex-col items-start gap-6">
<div class="rounded-full border border-amber-200/30 bg-amber-100/10 px-5 py-2 text-xs font-semibold uppercase tracking-[0.35em] text-amber-100">
Mode du jour
</div>
<h1 class="text-3xl font-black uppercase tracking-[0.25em] text-amber-50 sm:text-5xl">
Mystere du jour
</h1>
<p class="max-w-2xl text-base text-slate-200 sm:text-lg">
Devine le personnage. Chaque indice deblocque une nouvelle piste.
</p>
</header>
<section class="mt-10 grid gap-6">
<div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<h2 class="text-sm font-semibold uppercase tracking-[0.2em] text-amber-100">Indices du jour</h2>
<div class="mt-4 grid gap-3 sm:grid-cols-3">
<div class="rounded-2xl border border-white/10 bg-slate-950/60 px-3 py-3">
<p class="text-xs uppercase tracking-[0.25em] text-amber-100">Indice 1</p>
<p class="mt-2 text-sm text-slate-200">Origine: ???</p>
</div>
<div class="rounded-2xl border border-white/10 bg-slate-950/60 px-3 py-3">
<p class="text-xs uppercase tracking-[0.25em] text-amber-100">Indice 2</p>
<p class="mt-2 text-sm text-slate-200">Fruit du demon: ???</p>
</div>
<div class="rounded-2xl border border-white/10 bg-slate-950/60 px-3 py-3">
<p class="text-xs uppercase tracking-[0.25em] text-amber-100">Indice 3</p>
<p class="mt-2 text-sm text-slate-200">Affiliation: ???</p>
</div>
</div>
</div>
<div class="rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<h2 class="text-sm font-semibold uppercase tracking-[0.2em] text-amber-100">Entrer une supposition</h2>
<div class="mt-4 flex flex-col gap-3 sm:flex-row">
<input
class="w-full rounded-full border border-amber-200/30 bg-slate-900/60 px-5 py-3 text-sm text-slate-100 placeholder:text-slate-400 focus:border-amber-200/70 focus:outline-none"
placeholder="Nom du personnage"
type="text"
/>
<button class="rounded-full bg-amber-300 px-6 py-3 text-sm font-semibold text-slate-900 transition hover:bg-amber-200">
Valider
</button>
</div>
</div>
</section>
<section class="mt-8 rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">Historique</p>
<p class="mt-2 text-sm text-slate-200">Aucune tentative pour le moment.</p>
</div>
<button class="rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50">
Recommencer
</button>
</div>
</section>
<section class="mt-8 rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
<div class="flex flex-col items-center gap-5 text-center sm:flex-row sm:text-left">
<div class="flex h-20 w-20 items-center justify-center rounded-full border border-amber-200/40 bg-slate-900/70 text-xs uppercase tracking-[0.25em] text-amber-100">
Photo
</div>
<div class="flex-1">
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">Personnage d'hier</p>
<p class="mt-2 text-lg font-semibold text-white">Placeholder</p>
<p class="mt-1 text-sm text-slate-200">Revele apres validation de ta tentative.</p>
</div>
<button class="w-full rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50 sm:w-auto">
Voir l'archive
</button>
</div>
</section>
</div>
</main>

View File

@@ -1,5 +0,0 @@
<script lang="ts">
import { resolve } from '$app/paths';
</script>
<a href={resolve('/demo/better-auth')}>better-auth</a>

View File

@@ -1,20 +0,0 @@
import { redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import type { PageServerLoad } from './$types';
import { auth } from '$lib/server/auth';
export const load: PageServerLoad = async (event) => {
if (!event.locals.user) {
return redirect(302, '/demo/better-auth/login');
}
return { user: event.locals.user };
};
export const actions: Actions = {
signOut: async (event) => {
await auth.api.signOut({
headers: event.request.headers
});
return redirect(302, '/demo/better-auth/login');
}
};

View File

@@ -1,14 +0,0 @@
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageServerData } from './$types';
let { data }: { data: PageServerData } = $props();
</script>
<h1>Hi, {data.user.name}!</h1>
<p>Your user ID is {data.user.id}.</p>
<form method="post" action="?/signOut" use:enhance>
<button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700"
>Sign out</button
>
</form>

View File

@@ -1,61 +0,0 @@
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import type { PageServerLoad } from './$types';
import { auth } from '$lib/server/auth';
import { APIError } from 'better-auth/api';
export const load: PageServerLoad = async (event) => {
if (event.locals.user) {
return redirect(302, '/demo/better-auth');
}
return {};
};
export const actions: Actions = {
signInEmail: async (event) => {
const formData = await event.request.formData();
const email = formData.get('email')?.toString() ?? '';
const password = formData.get('password')?.toString() ?? '';
try {
await auth.api.signInEmail({
body: {
email,
password,
callbackURL: '/auth/verification-success'
}
});
} catch (error) {
if (error instanceof APIError) {
return fail(400, { message: error.message || 'Signin failed' });
}
return fail(500, { message: 'Unexpected error' });
}
return redirect(302, '/demo/better-auth');
},
signUpEmail: async (event) => {
const formData = await event.request.formData();
const email = formData.get('email')?.toString() ?? '';
const password = formData.get('password')?.toString() ?? '';
const name = formData.get('name')?.toString() ?? '';
try {
await auth.api.signUpEmail({
body: {
email,
password,
name,
callbackURL: '/auth/verification-success'
}
});
} catch (error) {
if (error instanceof APIError) {
return fail(400, { message: error.message || 'Registration failed' });
}
return fail(500, { message: 'Unexpected error' });
}
return redirect(302, '/demo/better-auth');
}
};

View File

@@ -1,42 +0,0 @@
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let { form }: { form: ActionData } = $props();
</script>
<h1>Login</h1>
<form method="post" action="?/signInEmail" use:enhance>
<label>
Email
<input
type="email"
name="email"
class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
/>
</label>
<label>
Password
<input
type="password"
name="password"
class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
/>
</label>
<label>
Name (for registration)
<input
name="name"
class="mt-1 rounded-md border border-gray-300 bg-white px-3 py-2 shadow-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
/>
</label>
<button class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700"
>Login</button
>
<button
formaction="?/signUpEmail"
class="rounded-md bg-blue-600 px-4 py-2 text-white transition hover:bg-blue-700"
>Register</button
>
</form>
<p class="text-red-500">{form?.message ?? ''}</p>