d9af8420bc
REVIEW-5 / PUBLISH-45: owner edits the stored photo lineup in Review — tap a card to keep/remove, drag to reorder, leftmost kept photo = primary (Woo cover). The kept set must contain a clean calibration + its dimension-overlay copy; publish is blocked until it does. - backend: role column on slab_photos (schema + idempotent _migrate ALTER), role in photo CRUD + new DELETE endpoint, Woo image order by role-rank (clean→overlay→batch) per PUBLISH-45 - frontend: Review photo strip (render from listPhotos, keep/remove, HTML5 drag-reorder, dynamic PRIMARY badge from .primary class), persistLineup() re-seq kept + drop removed before entering Publish - tests: lineup.ts unit, e2e review-photo-strip.spec, backend role + order Refs: #90, REVIEW-5, PUBLISH-45
166 lines
5.4 KiB
SQL
166 lines
5.4 KiB
SQL
-- SlabUploader SQLite schema (S0)
|
|
-- Every table/column from docs/contracts/DATA-MODEL.md using CREATE TABLE IF NOT EXISTS
|
|
-- Taxonomy tables exist but remain EMPTY (S4 populates). Stdlib sqlite3 only.
|
|
-- Pragmas applied by db factory: journal_mode=WAL, foreign_keys=ON, busy_timeout=5000, synchronous=NORMAL
|
|
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
-- 2. slabs
|
|
CREATE TABLE IF NOT EXISTS slabs (
|
|
id TEXT PRIMARY KEY,
|
|
status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','calibrated','ready','publishing','published','failed')),
|
|
sku TEXT UNIQUE CHECK(length(sku) BETWEEN 3 AND 24),
|
|
length_in REAL,
|
|
thickness_in REAL,
|
|
sqft REAL,
|
|
bdft REAL,
|
|
width_min_in REAL,
|
|
width_max_in REAL,
|
|
width_avg_in REAL,
|
|
species_id INTEGER,
|
|
wood_category_ids TEXT, -- JSON array of leaf ids
|
|
edge_type_term_id INTEGER,
|
|
figure_term_ids TEXT, -- JSON array
|
|
grade_term_id INTEGER,
|
|
thickness_term_id INTEGER,
|
|
moisture_term_id INTEGER,
|
|
fig_tag_ids TEXT, -- JSON array optional
|
|
feat_tag_ids TEXT, -- JSON array optional
|
|
price REAL,
|
|
price_source TEXT CHECK(price_source IN ('recommendation','override') OR price_source IS NULL),
|
|
title TEXT,
|
|
short_description TEXT,
|
|
description TEXT,
|
|
content_source TEXT CHECK(content_source IN ('manual','llm','template') OR content_source IS NULL),
|
|
species_confidence REAL,
|
|
figure_confidence REAL,
|
|
grade_confidence REAL,
|
|
-- #89: explicit owner lock of the confirmed taxonomy set. Deterministic
|
|
-- title/short_description are built and persisted only once this is 1.
|
|
metadata_locked INTEGER CHECK(metadata_locked IN (0,1) OR metadata_locked IS NULL),
|
|
inference_status TEXT CHECK(inference_status IN ('inferring','done','failed') OR inference_status IS NULL),
|
|
calibration_photo_id TEXT,
|
|
client_rev INTEGER NOT NULL DEFAULT 1,
|
|
server_rev INTEGER NOT NULL DEFAULT 1,
|
|
woo_product_id INTEGER,
|
|
published_at TEXT,
|
|
deleted_at TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (species_id) REFERENCES woo_categories(id),
|
|
FOREIGN KEY (calibration_photo_id) REFERENCES slab_photos(id)
|
|
);
|
|
|
|
-- 3. slab_photos
|
|
CREATE TABLE IF NOT EXISTS slab_photos (
|
|
id TEXT PRIMARY KEY,
|
|
slab_id TEXT NOT NULL,
|
|
seq INTEGER NOT NULL,
|
|
is_calibration INTEGER NOT NULL DEFAULT 0 CHECK(is_calibration IN (0,1)),
|
|
-- #90: lineup kind (clean cal / dimension-overlay copy / batch sibling).
|
|
-- Drives the Review photo strip labels and the PUBLISH-45 Woo image order.
|
|
role TEXT NOT NULL DEFAULT 'batch' CHECK(role IN ('calibration','overlay','batch')),
|
|
original_blob BLOB,
|
|
processed_blob BLOB,
|
|
width_px INTEGER,
|
|
height_px INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (slab_id) REFERENCES slabs(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- 4. pricing_rules
|
|
CREATE TABLE IF NOT EXISTS pricing_rules (
|
|
id INTEGER PRIMARY KEY,
|
|
species_id INTEGER NOT NULL UNIQUE,
|
|
price_per_bdft REAL NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (species_id) REFERENCES woo_categories(id)
|
|
);
|
|
|
|
-- 5. Taxonomy cache (empty at S0; columns chosen to support FKs + S4 sync; ambiguity resolved in learnings)
|
|
CREATE TABLE IF NOT EXISTS woo_categories (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
parent_id INTEGER,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_attributes (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_attribute_terms (
|
|
id INTEGER PRIMARY KEY,
|
|
attribute_id INTEGER NOT NULL,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL,
|
|
woo_id INTEGER,
|
|
FOREIGN KEY (attribute_id) REFERENCES woo_attributes(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS woo_tags (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
woo_id INTEGER
|
|
);
|
|
|
|
-- 6. settings (key/value/value_enc per §6; secrets in value_enc = nonce||ciphertext)
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT,
|
|
value_enc TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY);
|
|
|
|
-- 7. prompts + prompt_revisions (append-only revisions)
|
|
CREATE TABLE IF NOT EXISTS prompts (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL CHECK(name IN ('call1','call2')),
|
|
active_revision_id INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (active_revision_id) REFERENCES prompt_revisions(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS prompt_revisions (
|
|
id INTEGER PRIMARY KEY,
|
|
prompt_id INTEGER NOT NULL,
|
|
content TEXT NOT NULL,
|
|
content_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
created_by TEXT,
|
|
FOREIGN KEY (prompt_id) REFERENCES prompts(id)
|
|
);
|
|
|
|
-- 8. sync_log (append-only)
|
|
CREATE TABLE IF NOT EXISTS sync_log (
|
|
id INTEGER PRIMARY KEY,
|
|
slab_id TEXT,
|
|
woo_product_id INTEGER,
|
|
status TEXT NOT NULL CHECK(status IN ('success','failed')),
|
|
payload_hash TEXT,
|
|
message TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- 9. inference_log (append-only audit)
|
|
CREATE TABLE IF NOT EXISTS inference_log (
|
|
id INTEGER PRIMARY KEY,
|
|
slab_id TEXT,
|
|
call INTEGER NOT NULL CHECK(call IN (1,2)),
|
|
prompt_revision_id INTEGER,
|
|
model TEXT,
|
|
request_payload TEXT,
|
|
response_payload TEXT,
|
|
latency_ms INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (prompt_revision_id) REFERENCES prompt_revisions(id)
|
|
);
|