- New master classes section on landing page with upcoming events grid - Admin CRUD for master classes (image, slots, trainer, style, cost, location) - User signup modal (name + Instagram required, Telegram optional) - Admin registration management: view, add, edit, delete with quick-contact links - Customizable success message for signup confirmation - Auto-filter past events, Russian date formatting, duration auto-calculation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
98 lines
2.5 KiB
TypeScript
98 lines
2.5 KiB
TypeScript
/**
|
|
* Seed script — populates the SQLite database from content.ts
|
|
* Run: npx tsx src/data/seed.ts
|
|
*/
|
|
|
|
import Database from "better-sqlite3";
|
|
import path from "path";
|
|
import { siteContent } from "./content";
|
|
|
|
const DB_PATH =
|
|
process.env.DATABASE_PATH ||
|
|
path.join(process.cwd(), "db", "blackheart.db");
|
|
|
|
const db = new Database(DB_PATH);
|
|
db.pragma("journal_mode = WAL");
|
|
|
|
// Create tables
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS sections (
|
|
key TEXT PRIMARY KEY,
|
|
data TEXT NOT NULL,
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS team_members (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
image TEXT NOT NULL,
|
|
instagram TEXT,
|
|
description TEXT,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
`);
|
|
|
|
// Seed sections (team members go in their own table)
|
|
const sectionData: Record<string, unknown> = {
|
|
meta: siteContent.meta,
|
|
hero: siteContent.hero,
|
|
about: siteContent.about,
|
|
classes: siteContent.classes,
|
|
masterClasses: siteContent.masterClasses,
|
|
faq: siteContent.faq,
|
|
pricing: siteContent.pricing,
|
|
schedule: siteContent.schedule,
|
|
contact: siteContent.contact,
|
|
};
|
|
|
|
// Team section stores only the title
|
|
sectionData.team = { title: siteContent.team.title };
|
|
|
|
const upsertSection = db.prepare(
|
|
`INSERT INTO sections (key, data, updated_at) VALUES (?, ?, datetime('now'))
|
|
ON CONFLICT(key) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at`
|
|
);
|
|
|
|
const insertMember = db.prepare(
|
|
`INSERT INTO team_members (name, role, image, instagram, description, sort_order)
|
|
VALUES (?, ?, ?, ?, ?, ?)`
|
|
);
|
|
|
|
const tx = db.transaction(() => {
|
|
// Upsert all sections
|
|
for (const [key, data] of Object.entries(sectionData)) {
|
|
upsertSection.run(key, JSON.stringify(data));
|
|
}
|
|
|
|
// Clear existing team members and re-insert
|
|
db.prepare("DELETE FROM team_members").run();
|
|
|
|
siteContent.team.members.forEach((m, i) => {
|
|
insertMember.run(
|
|
m.name,
|
|
m.role,
|
|
m.image,
|
|
m.instagram ?? null,
|
|
m.description ?? null,
|
|
i
|
|
);
|
|
});
|
|
});
|
|
|
|
tx();
|
|
|
|
const sectionCount = (
|
|
db.prepare("SELECT COUNT(*) as c FROM sections").get() as { c: number }
|
|
).c;
|
|
const memberCount = (
|
|
db.prepare("SELECT COUNT(*) as c FROM team_members").get() as { c: number }
|
|
).c;
|
|
|
|
console.log(`Seeded ${sectionCount} sections and ${memberCount} team members.`);
|
|
console.log(`Database: ${DB_PATH}`);
|
|
|
|
db.close();
|