v1.0
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import initSqlJs from 'sql.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dataDir = path.join(__dirname, '..', 'data');
|
||||
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
||||
const dbPath = process.env.DATABASE_PATH || path.join(dataDir, 'configurateur.db');
|
||||
|
||||
let db = null;
|
||||
|
||||
function toSqlitePlaceholders(sql) {
|
||||
return sql.replace(/\$(\d+)/g, '?');
|
||||
}
|
||||
|
||||
function save() {
|
||||
if (db && dbPath) {
|
||||
const data = db.export();
|
||||
fs.writeFileSync(dbPath, Buffer.from(data));
|
||||
}
|
||||
}
|
||||
|
||||
async function getDb() {
|
||||
if (db) return db;
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file) => path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', file)
|
||||
});
|
||||
let buffer;
|
||||
if (fs.existsSync(dbPath)) {
|
||||
buffer = fs.readFileSync(dbPath);
|
||||
}
|
||||
db = new SQL.Database(buffer || null);
|
||||
return db;
|
||||
}
|
||||
|
||||
export const dbReady = getDb();
|
||||
|
||||
export const query = async (sql, params = []) => {
|
||||
const database = await getDb();
|
||||
const sqliteSql = toSqlitePlaceholders(sql);
|
||||
const stmt = database.prepare(sqliteSql);
|
||||
stmt.bind(Array.isArray(params) ? params : []);
|
||||
const rows = [];
|
||||
while (stmt.step()) rows.push(stmt.getAsObject());
|
||||
stmt.free();
|
||||
return { rows };
|
||||
};
|
||||
|
||||
export const run = async (sql, params = []) => {
|
||||
const database = await getDb();
|
||||
const sqliteSql = toSqlitePlaceholders(sql);
|
||||
database.run(sqliteSql, Array.isArray(params) ? params : []);
|
||||
save();
|
||||
return {};
|
||||
};
|
||||
|
||||
const pool = {
|
||||
query: (sql, params) => query(sql, params)
|
||||
};
|
||||
|
||||
export default pool;
|
||||
@@ -0,0 +1,19 @@
|
||||
import initSqlJs from 'sql.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dataDir = path.join(__dirname, '..', 'data');
|
||||
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
||||
const dbPath = process.env.DATABASE_PATH || path.join(dataDir, 'configurateur.db');
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file) => path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', file)
|
||||
});
|
||||
const db = new SQL.Database();
|
||||
const schema = fs.readFileSync(path.join(__dirname, 'schema.sql'), 'utf8');
|
||||
db.exec(schema);
|
||||
fs.writeFileSync(dbPath, Buffer.from(db.export()));
|
||||
db.close();
|
||||
console.log('Schema applied.');
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Run pending migrations on an existing database
|
||||
*/
|
||||
import initSqlJs from 'sql.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dataDir = path.join(__dirname, '..', 'data');
|
||||
const dbPath = process.env.DATABASE_PATH || path.join(dataDir, 'configurateur.db');
|
||||
const migrationsDir = path.join(__dirname, 'migrations');
|
||||
|
||||
async function migrate() {
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.error(`Database not found at ${dbPath}. Run db:init first.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file) => path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', file)
|
||||
});
|
||||
|
||||
const buffer = fs.readFileSync(dbPath);
|
||||
const db = new SQL.Database(buffer);
|
||||
|
||||
// Get migration files
|
||||
const migrationFiles = fs.readdirSync(migrationsDir)
|
||||
.filter(f => f.endsWith('.sql'))
|
||||
.sort();
|
||||
|
||||
console.log(`Found ${migrationFiles.length} migration(s)`);
|
||||
|
||||
for (const file of migrationFiles) {
|
||||
console.log(`Running migration: ${file}`);
|
||||
const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8');
|
||||
try {
|
||||
db.exec(sql);
|
||||
console.log(` ✓ ${file} applied`);
|
||||
} catch (err) {
|
||||
// IF NOT EXISTS should prevent errors on re-run
|
||||
console.log(` ✓ ${file} (already applied or no changes)`);
|
||||
}
|
||||
}
|
||||
|
||||
// Save database
|
||||
fs.writeFileSync(dbPath, Buffer.from(db.export()));
|
||||
db.close();
|
||||
|
||||
console.log('Migrations complete.');
|
||||
}
|
||||
|
||||
migrate().catch(err => {
|
||||
console.error('Migration failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Migration: Add Formex materials and sync logs tables
|
||||
-- Run this on existing databases to add crawler support
|
||||
|
||||
-- Matériaux Formex (données scrapées)
|
||||
CREATE TABLE IF NOT EXISTS formex_materials (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
formex_code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
collection TEXT NOT NULL,
|
||||
width_mm INTEGER,
|
||||
height_mm INTEGER,
|
||||
thickness_mm INTEGER,
|
||||
formex_url TEXT,
|
||||
last_synced_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Journal de synchronisation
|
||||
CREATE TABLE IF NOT EXISTS sync_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
items_count INTEGER,
|
||||
error TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Migration: Enhanced Formex materials with dimensions, variants, images
|
||||
-- Adds support for multiple dimension formats and color variants per product
|
||||
|
||||
-- Add new columns to formex_materials
|
||||
ALTER TABLE formex_materials ADD COLUMN category TEXT;
|
||||
ALTER TABLE formex_materials ADD COLUMN image_url TEXT;
|
||||
ALTER TABLE formex_materials ADD COLUMN price_chf REAL;
|
||||
ALTER TABLE formex_materials ADD COLUMN active INTEGER DEFAULT 1;
|
||||
|
||||
-- Dimension variants (multiple dimensions per material)
|
||||
CREATE TABLE IF NOT EXISTS formex_dimensions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
material_id INTEGER NOT NULL REFERENCES formex_materials(id) ON DELETE CASCADE,
|
||||
width_mm INTEGER NOT NULL,
|
||||
height_mm INTEGER NOT NULL,
|
||||
thickness_mm INTEGER NOT NULL,
|
||||
is_default INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
-- Color/decor variants (linked products)
|
||||
CREATE TABLE IF NOT EXISTS formex_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
material_id INTEGER NOT NULL REFERENCES formex_materials(id) ON DELETE CASCADE,
|
||||
variant_code TEXT NOT NULL,
|
||||
variant_name TEXT NOT NULL,
|
||||
variant_url TEXT,
|
||||
image_url TEXT
|
||||
);
|
||||
|
||||
-- Create indexes for better query performance
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_dimensions_material ON formex_dimensions(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_variants_material ON formex_variants(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_materials_active ON formex_materials(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_materials_collection ON formex_materials(collection);
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Migration: Room configurations table for AI-analyzed room layouts
|
||||
-- Stores room analysis results linked to orders
|
||||
|
||||
CREATE TABLE IF NOT EXISTS room_configs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER REFERENCES orders(id),
|
||||
image_path TEXT,
|
||||
analysis_json TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_room_configs_order ON room_configs(order_id);
|
||||
@@ -0,0 +1,72 @@
|
||||
-- Migration: Modules, hardware, and variants for V2 configurator
|
||||
|
||||
-- Catalogue de modules standards
|
||||
CREATE TABLE IF NOT EXISTS modules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
category TEXT NOT NULL, -- 'penderie', 'rayonnage', 'tiroirs', 'mixte'
|
||||
width_mm INTEGER NOT NULL,
|
||||
height_mm INTEGER,
|
||||
depth_mm INTEGER,
|
||||
shelves_count INTEGER DEFAULT 0,
|
||||
drawers_count INTEGER DEFAULT 0,
|
||||
has_rod INTEGER DEFAULT 0, -- penderie
|
||||
customizable INTEGER DEFAULT 1,
|
||||
base_price_chf REAL NOT NULL,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Quincaillerie Blum
|
||||
CREATE TABLE IF NOT EXISTS blum_hardware (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'drawer_system', 'hinge', 'lift_system', 'runner'
|
||||
variant TEXT, -- 'legrabox', 'tandembox', 'clip_top', etc.
|
||||
price_chf REAL NOT NULL,
|
||||
specs_json TEXT,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Variantes poignées
|
||||
CREATE TABLE IF NOT EXISTS handle_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
style TEXT, -- 'moderne', 'classique', 'minimaliste', etc.
|
||||
length_mm INTEGER,
|
||||
price_chf REAL NOT NULL,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Variantes charnières
|
||||
CREATE TABLE IF NOT EXISTS hinge_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'vissee', 'plantee'
|
||||
brand TEXT DEFAULT 'Blum',
|
||||
price_chf REAL NOT NULL,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Configuration couleurs par élément (pour sauvegarder les configs)
|
||||
CREATE TABLE IF NOT EXISTS element_colors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
|
||||
element_type TEXT NOT NULL, -- 'plintheBas', 'portes', 'cotes', etc.
|
||||
finish_id INTEGER NOT NULL,
|
||||
finish_label TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_modules_category ON modules(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_modules_active ON modules(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_blum_hardware_type ON blum_hardware(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_handle_variants_style ON handle_variants(style);
|
||||
CREATE INDEX IF NOT EXISTS idx_hinge_variants_type ON hinge_variants(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_element_colors_order ON element_colors(order_id);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add dominant_color column to formex_materials for 3D preview color representation
|
||||
ALTER TABLE formex_materials ADD COLUMN dominant_color TEXT;
|
||||
|
||||
-- Create index for quick lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_materials_dominant_color ON formex_materials(dominant_color);
|
||||
@@ -0,0 +1,202 @@
|
||||
-- Configurateur armoires sur mesure — Schéma V1 (SQLite)
|
||||
|
||||
-- Paramètres globaux (forfait kit, plinthe, épaisseur panneaux, etc.)
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Produits / gammes (ex. SIGNATURE)
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
active INTEGER DEFAULT 1,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Types de blocs (penderie, rayonnage, mixte vertical, mixte horizontal)
|
||||
CREATE TABLE IF NOT EXISTS block_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
base_price_chf REAL NOT NULL,
|
||||
width_min_mm INTEGER NOT NULL,
|
||||
width_max_mm INTEGER NOT NULL,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Formats de panneaux (par finition : dimensions + prix panneau entier)
|
||||
CREATE TABLE IF NOT EXISTS panel_formats (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
width_mm INTEGER NOT NULL,
|
||||
height_mm INTEGER NOT NULL,
|
||||
price_chf REAL NOT NULL
|
||||
);
|
||||
|
||||
-- Finitions visibles (stratifié décor, couleur, effet bois, etc.)
|
||||
CREATE TABLE IF NOT EXISTS finishes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
panel_format_id INTEGER NOT NULL REFERENCES panel_formats(id),
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Options de service (prise de mesure, montage)
|
||||
CREATE TABLE IF NOT EXISTS service_options (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
price_chf REAL NOT NULL,
|
||||
price_type TEXT NOT NULL CHECK (price_type IN ('fixed', 'per_hour', 'per_block')),
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Commandes / leads (config snapshot + coordonnées)
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
config TEXT NOT NULL,
|
||||
total_chf REAL,
|
||||
email TEXT,
|
||||
name TEXT,
|
||||
phone TEXT,
|
||||
notes TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Matériaux Formex (données scrapées)
|
||||
CREATE TABLE IF NOT EXISTS formex_materials (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
formex_code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
collection TEXT NOT NULL,
|
||||
category TEXT,
|
||||
width_mm INTEGER,
|
||||
height_mm INTEGER,
|
||||
thickness_mm INTEGER,
|
||||
image_url TEXT,
|
||||
formex_url TEXT,
|
||||
price_chf REAL,
|
||||
dominant_color TEXT,
|
||||
active INTEGER DEFAULT 1,
|
||||
last_synced_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Dimension variants (multiple dimensions per material)
|
||||
CREATE TABLE IF NOT EXISTS formex_dimensions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
material_id INTEGER NOT NULL REFERENCES formex_materials(id) ON DELETE CASCADE,
|
||||
width_mm INTEGER NOT NULL,
|
||||
height_mm INTEGER NOT NULL,
|
||||
thickness_mm INTEGER NOT NULL,
|
||||
is_default INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
-- Color/decor variants (linked products)
|
||||
CREATE TABLE IF NOT EXISTS formex_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
material_id INTEGER NOT NULL REFERENCES formex_materials(id) ON DELETE CASCADE,
|
||||
variant_code TEXT NOT NULL,
|
||||
variant_name TEXT NOT NULL,
|
||||
variant_url TEXT,
|
||||
image_url TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_dimensions_material ON formex_dimensions(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_variants_material ON formex_variants(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_materials_active ON formex_materials(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_formex_materials_collection ON formex_materials(collection);
|
||||
|
||||
-- Journal de synchronisation
|
||||
CREATE TABLE IF NOT EXISTS sync_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
items_count INTEGER,
|
||||
error TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Room configurations (AI-analyzed room layouts)
|
||||
CREATE TABLE IF NOT EXISTS room_configs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER REFERENCES orders(id),
|
||||
image_path TEXT,
|
||||
analysis_json TEXT NOT NULL,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_room_configs_order ON room_configs(order_id);
|
||||
|
||||
-- Catalogue de modules standards
|
||||
CREATE TABLE IF NOT EXISTS modules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
category TEXT NOT NULL, -- 'penderie', 'rayonnage', 'tiroirs', 'mixte'
|
||||
width_mm INTEGER NOT NULL,
|
||||
height_mm INTEGER,
|
||||
depth_mm INTEGER,
|
||||
shelves_count INTEGER DEFAULT 0,
|
||||
drawers_count INTEGER DEFAULT 0,
|
||||
has_rod INTEGER DEFAULT 0, -- penderie
|
||||
customizable INTEGER DEFAULT 1,
|
||||
base_price_chf REAL NOT NULL,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Quincaillerie Blum
|
||||
CREATE TABLE IF NOT EXISTS blum_hardware (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'drawer_system', 'hinge', 'lift_system', 'runner'
|
||||
variant TEXT, -- 'legrabox', 'tandembox', 'clip_top', etc.
|
||||
price_chf REAL NOT NULL,
|
||||
specs_json TEXT,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Variantes poignées
|
||||
CREATE TABLE IF NOT EXISTS handle_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
style TEXT, -- 'moderne', 'classique', 'minimaliste', etc.
|
||||
length_mm INTEGER,
|
||||
price_chf REAL NOT NULL,
|
||||
image_url TEXT,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Variantes charnières
|
||||
CREATE TABLE IF NOT EXISTS hinge_variants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'vissee', 'plantee'
|
||||
brand TEXT DEFAULT 'Blum',
|
||||
price_chf REAL NOT NULL,
|
||||
active INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
-- Configuration couleurs par élément (pour sauvegarder les configs)
|
||||
CREATE TABLE IF NOT EXISTS element_colors (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
|
||||
element_type TEXT NOT NULL, -- 'plintheBas', 'portes', 'cotes', etc.
|
||||
finish_id INTEGER NOT NULL,
|
||||
finish_label TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_modules_category ON modules(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_modules_active ON modules(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_blum_hardware_type ON blum_hardware(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_handle_variants_style ON handle_variants(style);
|
||||
CREATE INDEX IF NOT EXISTS idx_hinge_variants_type ON hinge_variants(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_element_colors_order ON element_colors(order_id);
|
||||
@@ -0,0 +1,23 @@
|
||||
import initSqlJs from 'sql.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dbPath = process.env.DATABASE_PATH || path.join(__dirname, '..', 'data', 'configurateur.db');
|
||||
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
console.error('Database file not found. Run npm run db:init first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const SQL = await initSqlJs({
|
||||
locateFile: (file) => path.join(__dirname, '..', 'node_modules', 'sql.js', 'dist', file)
|
||||
});
|
||||
const buffer = fs.readFileSync(dbPath);
|
||||
const db = new SQL.Database(buffer);
|
||||
const seedSql = fs.readFileSync(path.join(__dirname, 'seeds.sql'), 'utf8');
|
||||
db.exec(seedSql);
|
||||
fs.writeFileSync(dbPath, Buffer.from(db.export()));
|
||||
db.close();
|
||||
console.log('Seeds applied.');
|
||||
@@ -0,0 +1,75 @@
|
||||
-- Données initiales V1 (SQLite)
|
||||
|
||||
INSERT OR REPLACE INTO settings (key, value) VALUES
|
||||
('kit_preparation_fee_chf', '350'),
|
||||
('panel_thickness_mm', '19'),
|
||||
('plinth_height_min_mm', '60'),
|
||||
('plinth_height_max_mm', '160'),
|
||||
('niche_clearance_multiplier', '2'),
|
||||
('min_clearance_mm', '20'),
|
||||
('sliding_doors_min_width_mm', '1200');
|
||||
|
||||
INSERT OR IGNORE INTO products (code, label, active) VALUES
|
||||
('SIGNATURE', 'Armoire SIGNATURE', 1);
|
||||
|
||||
INSERT OR IGNORE INTO block_types (code, label, base_price_chf, width_min_mm, width_max_mm) VALUES
|
||||
('penderie', 'Penderie', 550, 400, 1200),
|
||||
('rayonnage', 'Rayonnage', 550, 400, 1200),
|
||||
('mixte_vertical', 'Mixte vertical', 550, 400, 1200),
|
||||
('mixte_horizontal', 'Mixte horizontal', 550, 400, 1200);
|
||||
|
||||
INSERT INTO panel_formats (name, width_mm, height_mm, price_chf) VALUES
|
||||
('Stratifié standard', 2800, 2070, 85),
|
||||
('Stratifié couleur', 2800, 2070, 95),
|
||||
('Stratifié effet bois', 2800, 2070, 110),
|
||||
('Stratifié vieux bois', 2800, 2070, 120);
|
||||
|
||||
INSERT OR IGNORE INTO finishes (code, label, panel_format_id) VALUES
|
||||
('stratifie_decor', 'Stratifié décor', (SELECT id FROM panel_formats WHERE name = 'Stratifié standard' LIMIT 1)),
|
||||
('stratifie_couleur', 'Stratifié couleur', (SELECT id FROM panel_formats WHERE name = 'Stratifié couleur' LIMIT 1)),
|
||||
('stratifie_effet_bois', 'Stratifié effet bois', (SELECT id FROM panel_formats WHERE name = 'Stratifié effet bois' LIMIT 1)),
|
||||
('stratifie_vieux_bois', 'Stratifié vieux bois', (SELECT id FROM panel_formats WHERE name = 'Stratifié vieux bois' LIMIT 1));
|
||||
|
||||
INSERT OR IGNORE INTO service_options (code, label, price_chf, price_type) VALUES
|
||||
('prise_mesure', 'Prise de mesure', 95, 'per_hour'),
|
||||
('montage', 'Montage', 95, 'per_hour');
|
||||
|
||||
-- Modules standards V2
|
||||
INSERT OR IGNORE INTO modules (code, name, category, width_mm, height_mm, depth_mm, shelves_count, drawers_count, has_rod, customizable, base_price_chf) VALUES
|
||||
('penderie_simple', 'Penderie simple', 'penderie', 600, NULL, 580, 1, 0, 1, 1, 450),
|
||||
('penderie_double', 'Penderie double', 'penderie', 900, NULL, 580, 2, 0, 1, 1, 550),
|
||||
('penderie_large', 'Penderie large', 'penderie', 1200, NULL, 580, 2, 0, 1, 1, 650),
|
||||
('rayonnage_4', 'Rayonnage 4 étagères', 'rayonnage', 600, NULL, 580, 4, 0, 0, 1, 380),
|
||||
('rayonnage_5', 'Rayonnage 5 étagères', 'rayonnage', 600, NULL, 580, 5, 0, 0, 1, 420),
|
||||
('rayonnage_6', 'Rayonnage 6 étagères', 'rayonnage', 600, NULL, 580, 6, 0, 0, 1, 460),
|
||||
('tiroirs_3', 'Bloc 3 tiroirs', 'tiroirs', 600, 600, 580, 0, 3, 0, 0, 520),
|
||||
('tiroirs_4', 'Bloc 4 tiroirs', 'tiroirs', 600, 800, 580, 0, 4, 0, 0, 620),
|
||||
('tiroirs_5', 'Bloc 5 tiroirs', 'tiroirs', 600, 1000, 580, 0, 5, 0, 0, 720),
|
||||
('mixte_penderie_tiroirs', 'Penderie + tiroirs', 'mixte', 600, NULL, 580, 0, 2, 1, 1, 580),
|
||||
('mixte_rayonnage_tiroirs', 'Rayonnage + tiroirs', 'mixte', 600, NULL, 580, 3, 2, 0, 1, 550),
|
||||
('mixte_complet', 'Module complet', 'mixte', 900, NULL, 580, 2, 2, 1, 1, 720);
|
||||
|
||||
-- Quincaillerie Blum
|
||||
INSERT OR IGNORE INTO blum_hardware (code, name, type, variant, price_chf, specs_json) VALUES
|
||||
('legrabox_pure', 'LEGRABOX pure', 'drawer_system', 'legrabox', 85, '{"height_mm": 177, "load_kg": 70, "soft_close": true}'),
|
||||
('legrabox_free', 'LEGRABOX free', 'drawer_system', 'legrabox', 95, '{"height_mm": 177, "load_kg": 70, "soft_close": true, "tip_on": true}'),
|
||||
('tandembox_antaro', 'TANDEMBOX antaro', 'drawer_system', 'tandembox', 65, '{"height_mm": 83, "load_kg": 65, "soft_close": true}'),
|
||||
('tandembox_intivo', 'TANDEMBOX intivo', 'drawer_system', 'tandembox', 75, '{"height_mm": 83, "load_kg": 65, "soft_close": true}'),
|
||||
('movento_runner', 'MOVENTO coulisse', 'runner', 'movento', 45, '{"load_kg": 60, "soft_close": true}'),
|
||||
('clip_top_soft', 'CLIP top BLUMOTION', 'hinge', 'clip_top', 12, '{"opening_angle": 110, "soft_close": true}'),
|
||||
('clip_top_standard', 'CLIP top Standard', 'hinge', 'clip_top', 8, '{"opening_angle": 110, "soft_close": false}'),
|
||||
('aventos_hf', 'AVENTOS HF', 'lift_system', 'aventos', 180, '{"type": "bi-fold", "soft_close": true}'),
|
||||
('aventos_hl', 'AVENTOS HL', 'lift_system', 'aventos', 150, '{"type": "lift_up", "soft_close": true}');
|
||||
|
||||
-- Variantes poignées
|
||||
INSERT OR IGNORE INTO handle_variants (code, name, style, length_mm, price_chf) VALUES
|
||||
('barre_moderne', 'Barre moderne', 'moderne', 160, 25),
|
||||
('barre_longue', 'Barre longue', 'moderne', 320, 35),
|
||||
('bouton_rond', 'Bouton rond', 'classique', 35, 15),
|
||||
('coquille', 'Coquille encastrée', 'minimaliste', 100, 28),
|
||||
('profil_alu', 'Profil aluminium', 'minimaliste', NULL, 45);
|
||||
|
||||
-- Variantes charnières
|
||||
INSERT OR IGNORE INTO hinge_variants (code, name, type, brand, price_chf) VALUES
|
||||
('blum_vissee', 'Blum vissée', 'vissee', 'Blum', 12),
|
||||
('blum_plantee', 'Blum plantée (INSERTA)', 'plantee', 'Blum', 18);
|
||||
Reference in New Issue
Block a user