This commit is contained in:
gpatruno
2026-06-11 17:30:13 +02:00
parent 36b4acb355
commit e45e9f154e
108 changed files with 9163 additions and 167 deletions
@@ -0,0 +1,22 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL PRIMARY KEY,
"email" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "Waffle" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"ingredients" TEXT NOT NULL,
"price" REAL NOT NULL,
"isNew" BOOLEAN NOT NULL DEFAULT false,
"category" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Waffle" ADD COLUMN "imageUrl" TEXT;
@@ -0,0 +1,9 @@
-- CreateTable
CREATE TABLE "GalleryImage" (
"id" TEXT NOT NULL PRIMARY KEY,
"imageUrl" TEXT NOT NULL,
"alt" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
@@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "SiteContent" (
"id" TEXT NOT NULL PRIMARY KEY,
"heroEyebrow" TEXT NOT NULL,
"heroTitle" TEXT NOT NULL,
"heroSubtitle" TEXT NOT NULL,
"menuIntro" TEXT NOT NULL,
"galleryIntro" TEXT NOT NULL,
"hours" TEXT NOT NULL,
"updatedAt" DATETIME NOT NULL
);
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "SiteContent" ADD COLUMN "heroBackgroundImageUrl" TEXT;
@@ -0,0 +1,4 @@
-- Add configurable default upload image resolution.
ALTER TABLE "SiteContent" ADD COLUMN "uploadImageWidth" INTEGER NOT NULL DEFAULT 1920;
ALTER TABLE "SiteContent" ADD COLUMN "uploadImageHeight" INTEGER NOT NULL DEFAULT 1080;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "SiteContent" ADD COLUMN "heroTextOverImage" BOOLEAN NOT NULL DEFAULT true;
@@ -0,0 +1,16 @@
-- AlterTable
ALTER TABLE "SiteContent" ADD COLUMN "scheduleIntro" TEXT NOT NULL DEFAULT '';
-- CreateTable
CREATE TABLE "TruckEvent" (
"id" TEXT NOT NULL PRIMARY KEY,
"eventDate" TEXT NOT NULL,
"timeLabel" TEXT NOT NULL DEFAULT '',
"location" TEXT NOT NULL,
"note" TEXT NOT NULL DEFAULT '',
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateIndex
CREATE INDEX "TruckEvent_eventDate_idx" ON "TruckEvent"("eventDate");
@@ -0,0 +1,14 @@
-- CreateTable
CREATE TABLE "TruckEventRecurrence" (
"id" TEXT NOT NULL PRIMARY KEY,
"dayOfWeek" INTEGER NOT NULL,
"timeLabel" TEXT NOT NULL DEFAULT '',
"location" TEXT NOT NULL,
"note" TEXT NOT NULL DEFAULT '',
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateIndex
CREATE INDEX "TruckEventRecurrence_dayOfWeek_idx" ON "TruckEventRecurrence"("dayOfWeek");
@@ -0,0 +1,23 @@
-- CreateTable
CREATE TABLE "Ingredient" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"price" REAL NOT NULL,
"imageUrl" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "_IngredientToWaffle" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_IngredientToWaffle_A_fkey" FOREIGN KEY ("A") REFERENCES "Ingredient" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "_IngredientToWaffle_B_fkey" FOREIGN KEY ("B") REFERENCES "Waffle" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "_IngredientToWaffle_AB_unique" ON "_IngredientToWaffle"("A", "B");
-- CreateIndex
CREATE INDEX "_IngredientToWaffle_B_index" ON "_IngredientToWaffle"("B");
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "SiteContent" ADD COLUMN "logoUrl" TEXT;
@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "SiteContent" ADD COLUMN "customWaffleTitle" TEXT NOT NULL DEFAULT 'La gaufre sur mesure';
ALTER TABLE "SiteContent" ADD COLUMN "customWaffleDescription" TEXT NOT NULL DEFAULT 'Composez votre gaufre en choisissant parmi nos ingrédients : chacun avec sa photo et son prix.';
ALTER TABLE "SiteContent" ADD COLUMN "customWaffleBasePrice" REAL NOT NULL DEFAULT 3;
ALTER TABLE "SiteContent" ADD COLUMN "customWaffleImageUrl" TEXT;
ALTER TABLE "SiteContent" ADD COLUMN "customWaffleVisible" BOOLEAN NOT NULL DEFAULT true;
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "TruckEvent" ADD COLUMN "imageUrl" TEXT;
ALTER TABLE "TruckEventRecurrence" ADD COLUMN "imageUrl" TEXT;
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "sqlite"
+105
View File
@@ -0,0 +1,105 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "sqlite"
}
model User {
id String @id @default(cuid())
email String @unique
passwordHash String
createdAt DateTime @default(now())
}
model Ingredient {
id String @id @default(cuid())
name String
price Float
imageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
waffles Waffle[]
}
model Waffle {
id String @id @default(cuid())
name String
/** Description textuelle affichée sur la fiche menu. */
ingredients String
price Float
isNew Boolean @default(false)
category String
imageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ingredientItems Ingredient[]
}
model GalleryImage {
id String @id @default(cuid())
imageUrl String
alt String
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
/** Une seule ligne (id = default) : textes affichés sur la page daccueil et horaires. */
model SiteContent {
id String @id @default("default")
heroEyebrow String
heroTitle String
heroSubtitle String
heroBackgroundImageUrl String?
/** Logo affiché dans len-tête du site public */
logoUrl String?
/** true : texte sur limage (héros) ; false : image seule, texte au-dessus du menu */
heroTextOverImage Boolean @default(true)
menuIntro String
galleryIntro String
scheduleIntro String @default("")
customWaffleTitle String @default("La gaufre sur mesure")
customWaffleDescription String @default("Composez votre gaufre en choisissant parmi nos ingrédients : chacun avec sa photo et son prix.")
customWaffleBasePrice Float @default(3)
customWaffleImageUrl String?
customWaffleVisible Boolean @default(true)
uploadImageWidth Int @default(1920)
uploadImageHeight Int @default(1080)
hours Json
updatedAt DateTime @updatedAt
}
/** Présence du food truck (calendrier « Où me trouver »). */
model TruckEvent {
id String @id @default(cuid())
eventDate String
timeLabel String @default("")
location String
note String @default("")
imageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([eventDate])
}
/** Règle hebdomadaire : répétée chaque semaine (0 = lundi … 6 = dimanche). */
model TruckEventRecurrence {
id String @id @default(cuid())
dayOfWeek Int
timeLabel String @default("")
location String
note String @default("")
imageUrl String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([dayOfWeek])
}
+178
View File
@@ -0,0 +1,178 @@
import "dotenv/config";
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
import { PrismaClient } from "../src/generated/prisma/client";
import { hashPassword } from "../src/lib/password";
import { DEFAULT_PUBLIC_SITE_COPY } from "../src/lib/site-content-defaults";
import { WAFFLE_CATEGORIES } from "../src/lib/constants";
import { formatDateIso } from "../src/lib/week-calendar";
const url = process.env.DATABASE_URL ?? "file:./dev.db";
const adapter = new PrismaBetterSqlite3({ url });
const prisma = new PrismaClient({ adapter });
async function main() {
const email =
process.env.ADMIN_EMAIL?.trim().toLowerCase() || "admin@lagaufredor.local";
const password = process.env.ADMIN_PASSWORD || "changeme123";
const existing = await prisma.user.findUnique({ where: { email } });
if (!existing) {
await prisma.user.create({
data: {
email,
passwordHash: await hashPassword(password),
},
});
console.log(
`Compte admin créé : ${email} (mot de passe : variable ADMIN_PASSWORD ou défaut changeme123)`
);
}
const count = await prisma.waffle.count();
if (count === 0) {
await prisma.waffle.createMany({
data: [
{
name: "Coco-Belge",
ingredients:
"Pâte Liège, perles de sucre, noix de coco râpée, touche de vanille Bourbon.",
price: 6.5,
isNew: true,
category: WAFFLE_CATEGORIES.DU_MOIS,
imageUrl: "/gallery/hero-coco.png",
},
{
name: "Classique dorée",
ingredients: "Sucre perlé, beurre AOP, sirop d’érable ou chocolat.",
price: 5.9,
isNew: false,
category: WAFFLE_CATEGORIES.SUCREE,
imageUrl: "/gallery/gaufres-sucrees.png",
},
{
name: "Bubble chantilly",
ingredients: "Bubble waffle, chantilly maison, copeaux de chocolat noir.",
price: 7.2,
isNew: false,
category: WAFFLE_CATEGORIES.SUCREE,
imageUrl: "/gallery/bubble-waffle.png",
},
{
name: "Montagnarde",
ingredients:
"Pâte aux herbes, comté fondant, jambon cru, poivre noir concassé.",
price: 8.5,
isNew: false,
category: WAFFLE_CATEGORIES.SALEE,
imageUrl: "/gallery/gaufre-salee.png",
},
{
name: "Bacon & ciboulette",
ingredients: "Crème légère, bacon croustillant, ciboulette fraîche.",
price: 7.9,
isNew: false,
category: WAFFLE_CATEGORIES.SALEE,
imageUrl: "/gallery/gaufre-salee.png",
},
],
});
console.log("Gaufres dexemple insérées.");
}
const galleryCount = await prisma.galleryImage.count();
if (galleryCount === 0) {
await prisma.galleryImage.createMany({
data: [
{
imageUrl: "/gallery/hero-coco.png",
alt: "Gaufres belges caramélisées sur ardoise, noix de coco et drapeau belge.",
sortOrder: 0,
},
{
imageUrl: "/gallery/gaufres-sucrees.png",
alt: "Stack de gaufres dorées, sucre glace et sirop.",
sortOrder: 1,
},
{
imageUrl: "/gallery/bubble-waffle.png",
alt: "Bubble waffles en cornet, chantilly et chocolat.",
sortOrder: 2,
},
{
imageUrl: "/gallery/gaufre-salee.png",
alt: "Gaufre salée aux herbes, crème, bacon et ciboulette.",
sortOrder: 3,
},
],
});
console.log("Galerie dexemple insérée.");
}
await prisma.siteContent.upsert({
where: { id: "default" },
create: {
id: "default",
...DEFAULT_PUBLIC_SITE_COPY,
},
update: {},
});
const truckCount = await prisma.truckEvent.count();
if (truckCount === 0) {
const start = new Date();
const day = start.getDay();
const diff = day === 0 ? -6 : 1 - day;
start.setDate(start.getDate() + diff);
const dateAt = (offset: number) => {
const d = new Date(start);
d.setDate(start.getDate() + offset);
return formatDateIso(d);
};
await prisma.truckEvent.createMany({
data: [
{
eventDate: dateAt(1),
timeLabel: "9h — 13h",
location: "Marché de Bagnols-sur-Cèze",
note: "Place du marché",
},
{
eventDate: dateAt(3),
timeLabel: "11h — 19h",
location: "Festival gourmand — Uzès",
note: "",
},
{
eventDate: dateAt(5),
timeLabel: "10h — 18h",
location: "Place de la République, Alès",
note: "Food truck zone nord",
},
],
});
console.log("Événements food truck dexemple insérés.");
}
const recurCount = await prisma.truckEventRecurrence.count();
if (recurCount === 0) {
await prisma.truckEventRecurrence.create({
data: {
dayOfWeek: 1,
timeLabel: "9h — 13h",
location: "Marché hebdomadaire — Bagnols-sur-Cèze",
note: "Place du marché",
},
});
console.log("Récurrence food truck dexemple insérée.");
}
}
main()
.then(() => prisma.$disconnect())
.catch((e) => {
console.error(e);
prisma.$disconnect();
process.exit(1);
});