# Catalog importer/exporter repair This patch is based on your uploaded kikomiko-main.zip. It has NOT been deployed. Copy the supplied files into the matching paths in the project containing docker-compose.yml. The patch does not include or replace your database, media, secrets or catalog JSON. ## Install safely 1. Back up the project and use SQLite's backup API to take a consistent backup of the running database (not a plain copy of an active WAL database). Retain the original code for rollback. 2. Test this patch on a staging copy first. Apply only these files; retain admin/data, the Docker admin_data volume, dist/data and images. Never run docker compose down -v. 3. Rebuild/restart the admin: `docker compose up -d --build admin`. Startup adds `products.content_json` and `catalog_metadata` without replacing existing tables. On a non-Docker install restart the existing Python service. 4. Deploy the included dist JS and product template. Purge any cached copies of /products/* and the changed JS files. No CSS or unrelated page templates are changed. 5. Back up before importing. Start with sample-catalog-v2.json in staging; its example product is inactive and its prices are TEST DATA. ## Import / export - Admin JSON upload accepts `{ "products": [...] }` and a bare product array. Each parent accepts `colorVariants` (preferred), `variants`, or `color_variants`. Export consistently uses `colorVariants`. - A new dashboard button downloads the complete JSON interchange file, including inactive products, categories and image mappings. It uses authenticated GET /api/export/json. - The existing publish button writes the active storefront catalog, now retaining descriptions, SEO, variant galleries and details. Writes use atomic replacement so readers never see a half-written file. - Local file import avoids HTTP size limits: from admin/, run `python3 import_catalog.py /private/path/catalog.json --export`. - Private backup export: `python3 export_catalog.py /private/path/new-backup.json`. It refuses to overwrite an existing file. Docker: `docker compose exec admin python3 export_catalog.py /app/data/catalog-backup-v2.json`, then `docker compose cp admin:/app/data/catalog-backup-v2.json ./catalog-backup-v2.json`. - Browser upload size limits are unchanged. Use the CLI for large files. Exported files can be larger because they now include complete details. ## Content contract Product and each colorVariant support: id, slug, official name, nameFa, description, descriptionIntro, seoTitle, seoDescription, details, specifications, blocks, images, heroImage, source identifiers and pricing/stock. Unknown JSON fields are preserved for future extensions; this is not permission to insert secrets into a public catalog. Use `details: [{"key":"finish","label":"فینیش","value":"مات"}]` or `specifications` with the same format. A key/value object is also accepted. Prefer stable keys for finish, texture, coverage, shade, usage, skinType, benefits, collection, size, madeIn, howToUse. Do not invent unverified values. Product details are shared defaults; variant rows override matching keys and add other rows. An explicit empty value suppresses an inherited row. Empty variant descriptions/SEO fall back to the parent. Full blocks are retained as structured data, but arbitrary legacy block layouts are NOT rendered by the simple detail-table component: supply details/specifications for the table. The admin editor has Persian description, SEO and JSON detail-table controls for both product and variants. Saving preserves additional imported fields. On the dynamic product page, selecting a shade updates the description and table, alongside the existing price, gallery and stock controls. Direct variant URLs resolve the parent and select the corresponding shade. Metadata updates are client-side; this patch does not implement static SEO prerendering or repair existing canonical/JSON-LD templates. Image arrays remain ordered references. Top-level images mappings are retained and merged across batches, but image binaries are NOT uploaded/downloaded by this patch. Put media files on the server and use reachable URLs in heroImage/images. Missing media remains missing; /i/ proxy links still rely on the existing proxy unless you rewrite them to local paths. ## Existing imported shade rows Upserts match an existing product by ID, then slug, retaining its database ID. Re-importing a family does not duplicate that family. Previously imported standalone shade rows are NOT automatically deleted/merged: use the prior migration-map.json to reconcile them after reviewing orders and links, or validate a fresh staging catalog first. Do not remove the live database to solve duplicates. This patch accepts already grouped parent/variant JSON; it does NOT infer families from names. Use the supplied earlier nested family export, not the old flat shade list. Review catalog drift before importing old prices: no live price refresh or KIKO scraping is performed. Important: the earlier data/site.json snapshot has empty blocks and no structured details for many/all records. Import support cannot recover content that was never exported. Supply reviewed details from the old renderer or fresh verified source data. Existing values are preserved, not fabricated. ## Verification From admin/: `python3 -m unittest -v test_catalog_roundtrip.py`. Tests use disposable databases: schema upgrade, Persian/variant/details/galleries round trip, repeat imports, inactive records, invalid variants and merged image manifests. Browser QA additionally checks admin save/reopen, authenticated JSON download, publish, variant description/table switching, sold-out state and mobile width. No changes to login policy, currency conversion, scraping, or stock-sync matching are included. Existing stock-sync code still targets parent rows; per-variant scheduled stock sync remains separate work.