179 lines
5.5 KiB
Rust
179 lines
5.5 KiB
Rust
//! Catalog seed data — run via `cargo loco seed`.
|
|
|
|
use loco_rs::prelude::*;
|
|
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
|
|
|
use crate::{
|
|
models::_entities::{categories, products},
|
|
shared::slug::slugify,
|
|
};
|
|
|
|
// -- Categories -----------------------------------------------------------
|
|
|
|
struct CategorySeed {
|
|
name: &'static str,
|
|
description: &'static str,
|
|
position: i32,
|
|
}
|
|
|
|
const CATEGORIES: &[CategorySeed] = &[
|
|
CategorySeed {
|
|
name: "Electronics",
|
|
description: "Audio, computing, and smart devices",
|
|
position: 0,
|
|
},
|
|
CategorySeed {
|
|
name: "Accessories",
|
|
description: "Cables, adapters, cases, and everyday essentials",
|
|
position: 1,
|
|
},
|
|
CategorySeed {
|
|
name: "Home & Office",
|
|
description: "Ergonomic furniture, lighting, and desk organization",
|
|
position: 2,
|
|
},
|
|
];
|
|
|
|
// -- Products -------------------------------------------------------------
|
|
|
|
struct ProductSeed {
|
|
name: &'static str,
|
|
description: &'static str,
|
|
price_cents: i64,
|
|
stock: i32,
|
|
category_slug: &'static str,
|
|
sku: Option<&'static str>,
|
|
}
|
|
|
|
const PRODUCTS: &[ProductSeed] = &[
|
|
ProductSeed {
|
|
name: "Wireless Headphones",
|
|
description: "Over-ear Bluetooth headphones with active noise cancelling, 30-hour battery life, and plush memory-foam cushions.",
|
|
price_cents: 7_999,
|
|
stock: 25,
|
|
category_slug: "electronics",
|
|
sku: Some("WH-1000"),
|
|
},
|
|
ProductSeed {
|
|
name: "Mechanical Keyboard",
|
|
description: "Tenkeyless mechanical keyboard with hot-swappable switches, per-key RGB backlight, and a detachable USB-C cable.",
|
|
price_cents: 12_999,
|
|
stock: 15,
|
|
category_slug: "electronics",
|
|
sku: Some("MK-TKL-RGB"),
|
|
},
|
|
ProductSeed {
|
|
name: "USB-C Hub",
|
|
description: "7-in-1 USB-C hub with HDMI 4K output, 100 W power delivery pass-through, SD card reader, and three USB-A 3.2 ports.",
|
|
price_cents: 3_499,
|
|
stock: 40,
|
|
category_slug: "accessories",
|
|
sku: Some("USBC-HUB7"),
|
|
},
|
|
ProductSeed {
|
|
name: "Laptop Stand",
|
|
description: "Adjustable aluminium laptop stand with ventilated surface. Supports laptops from 10\u{201d} to 17\u{201d}.",
|
|
price_cents: 4_999,
|
|
stock: 30,
|
|
category_slug: "accessories",
|
|
sku: Some("LS-ALU-01"),
|
|
},
|
|
ProductSeed {
|
|
name: "Desk Lamp",
|
|
description: "LED desk lamp with 5 colour temperatures, stepless brightness control, and a flexible gooseneck arm.",
|
|
price_cents: 3_999,
|
|
stock: 20,
|
|
category_slug: "home-office",
|
|
sku: Some("DL-5CT"),
|
|
},
|
|
ProductSeed {
|
|
name: "Ergonomic Mouse",
|
|
description: "Vertical wireless ergonomic mouse with 6 buttons, adjustable DPI up to 4 000, and a sculpted thumb rest.",
|
|
price_cents: 5_999,
|
|
stock: 18,
|
|
category_slug: "electronics",
|
|
sku: Some("EM-VW-01"),
|
|
},
|
|
ProductSeed {
|
|
name: "Webcam Privacy Cover",
|
|
description: "Ultra-thin sliding webcam cover compatible with laptops, tablets, and external monitors. Pack of 3.",
|
|
price_cents: 599,
|
|
stock: 100,
|
|
category_slug: "accessories",
|
|
sku: Some("WPC-3PK"),
|
|
},
|
|
ProductSeed {
|
|
name: "Cable Organizer Set",
|
|
description: "Silicone cable management kit with 6 magnetic clips, 4 velcro straps, and an under-desk cable tray.",
|
|
price_cents: 1_299,
|
|
stock: 50,
|
|
category_slug: "home-office",
|
|
sku: Some("COS-MAG"),
|
|
},
|
|
];
|
|
|
|
// -- Public API -----------------------------------------------------------
|
|
|
|
/// Insert starter categories and products. Called from the `seed()` hook.
|
|
pub async fn seed_catalog(ctx: &AppContext) -> Result<()> {
|
|
for cat in CATEGORIES {
|
|
let slug = slugify(cat.name);
|
|
let exists = categories::Entity::find()
|
|
.filter(categories::Column::Slug.eq(&slug))
|
|
.one(&ctx.db)
|
|
.await?
|
|
.is_some();
|
|
if exists {
|
|
continue;
|
|
}
|
|
categories::ActiveModel {
|
|
name: Set(cat.name.to_string()),
|
|
slug: Set(slug),
|
|
description: Set(Some(cat.description.to_string())),
|
|
position: Set(cat.position),
|
|
published: Set(true),
|
|
..Default::default()
|
|
}
|
|
.insert(&ctx.db)
|
|
.await?;
|
|
}
|
|
|
|
for item in PRODUCTS {
|
|
let product_slug = slugify(item.name);
|
|
let exists = products::Entity::find()
|
|
.filter(products::Column::Slug.eq(&product_slug))
|
|
.one(&ctx.db)
|
|
.await?
|
|
.is_some();
|
|
if exists {
|
|
continue;
|
|
}
|
|
|
|
let cat_slug = slugify(item.category_slug);
|
|
let category = categories::Entity::find()
|
|
.filter(categories::Column::Slug.eq(&cat_slug))
|
|
.one(&ctx.db)
|
|
.await?;
|
|
|
|
let now = chrono::Utc::now();
|
|
|
|
products::ActiveModel {
|
|
name: Set(item.name.to_string()),
|
|
slug: Set(product_slug),
|
|
description: Set(Some(item.description.to_string())),
|
|
price_cents: Set(item.price_cents),
|
|
currency: Set("EUR".to_string()),
|
|
sku: Set(item.sku.map(|s| s.to_string())),
|
|
stock: Set(item.stock),
|
|
published: Set(true),
|
|
published_at: Set(Some(now.into())),
|
|
category_id: Set(category.map(|c| c.id)),
|
|
..Default::default()
|
|
}
|
|
.insert(&ctx.db)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|