- Native HTML5 drag & drop between categories with drop-target highlight
- Virtualized item lists via @tanstack/react-virtual (fixed 34px rows)
- Auto-batching of large groups (500 items / 50 groups per run)
- Duplicate detection against the destination page with badge + summary
- Per-category layout preview grid
- Undo history for item moves (single + batch, tracks source groups)
- Days-range selector to load older imports (30/60/90/180)
- LocalStorage persistence of user settings (mode, destination, price, days)
- Added translation keys across all 25 locales
Give the home page a clear, professional information hierarchy:
- Add eyebrow labels + headings for Features, Live stats and Community
sections using reusable, theme-aware .eyebrow / .section-title styles.
- Loosen the vertical rhythm (gap-8/10) so each block breathes.
- New messages resolve via the existing English fallback for all locales.
Adds a second mode to the organize-imports dialog: instead of creating
one new page per approved group (which could produce dozens of tiny
pages), the user can pick an existing destination page and have every
approved item moved into it. No catalog page is created in this mode.
- organizeImportFurni: groups accept destinationPageId; when set, the
existing page is reused, new offers append after its current highest
order, moved offers keep their original name, and the real page
caption is used for logging and results
- OrganizeImportsDialog: mode toggle (create pages / move into page),
searchable destination picker via /api/admin/catalog/tree?search=,
name/icon/layout editors hidden in move mode, button shows a move
count, and the success toast reports moved/added instead of pages
- en + nl translations for the new mode, destination, and move keys
The original GET route used a correlated NOT EXISTS / FIND_IN_SET
subquery over the entire catalog_items table for every recent import
audit entry, causing server timeouts when the audit log or catalog
grew large. The per-item host-page validation inside the create
action also issued one SELECT + one UPDATE per moved offer.
Changes:
- GET /api/admin/import/organize: replace the correlated subquery
with a bounded candidate list and a JS-side placed-set check, then
resolve all needed base items in a single indexed SELECT. This
bounds the query cost regardless of catalog or audit log size.
- organizeImportFurni action: validate mover ids in one SELECT, then
batch every move per group into a single UPDATE with a CASE
expression instead of one UPDATE per item.
- OrganizeImportsDialog: add a 45-second abort timeout on the fetch
and a distinct load-error state so the UI never silently hangs.
- Add 'loadError' translation key (en + nl).
Adds a Studio 'Organize imports' dialog that groups recently imported
furniture and furniture already sitting in the auto-created import
pages into suggested catalog categories. Each group is presented with
its suggested name, icon, and layout which can be overridden before
approval; approved groups are turned into real catalog pages in a
single atomic export run. Offers already inside the import subtree are
moved to the new pages; brand-new furniture gets a fresh offer.
- groupSuggestedCategories: generic pure helper reusing the same
per-item label heuristic that drives suggestCategoryName; items with
no label land in a 'Other Furni' remainder bucket
- GET /api/admin/import/organize: returns items from the imported
furniture tree (catalog_items JOIN items_base via page id set from
the imported-furniture root) union audit-logged but not-yet-placed
recent imports; marks alreadyPlaced vs new
- organizeImportFurni server action: validates import-page membership
before moving any offer, creates pages + inserts/moves items in one
withCatalogExport snapshot, logs activity
- OrganizeImportsDialog: full-featured Studio dialog with price panel
(applies to new offers only), parent select, per-group approval,
editable name/icon/layout with suggestion reset chips, source tags
- import-pages.ts server helper: locates the imported-furniture tree
- Studio nav: 'Organize imports' button with FolderTree icon,
gated on CATALOG_EDIT, wired next to the catalog manager
- en + nl translations for organizeImports.* keys with ICU plurals
- groupSuggestedCategories unit tests (deterministic grouping,
remainder handling, size+alpha ordering, label consistency)
- Add AutoCategoryDialog: pick furni (or empty page), suggest caption/icon/layout, live preview
- Add createAutoCategory server action (page + offers in one export) with CatalogKind
- Extend furni search API with interactionType
- Share ShopTile and refactor inline-editor/items-shop-preview to use it
- Add suggestion heuristics (suggestCategoryName/dIcon/layout) with tests
- Add autoCategory translations (en/nl)
- Redesign in-game client toolbar with refined glass styling and buttons
- Live online count + emulator status streamed over SSE multicast
- Who's-online tooltip throttled to reduce repeated requests
- Fix show button so it always returns the hidden toolbar
- Use theme CSS variables instead of hardcoded colors
- Add toolbar translations across all 25 locales
- Add theme_scopes and theme_scope_values database tables for scoped themes
- Implement theme resolver engine with inheritance: global > site > module > route
- Add module detection for 20+ routes (shop, guilds, radio, news, etc.)
- Create admin UI at /admin/theme-builder with scope tree and color editor
- Add ScopedThemeVars component for injecting scoped CSS via data-attributes
- Add ThemeScopeDetector client component for runtime module/route detection
- Add site-resolver for multi-site domain detection
- Add /api/themes/export endpoint (JSON, CSS, variables formats)
- Add /api/themes/export/embed.js for external integration widget
- Add server actions for full CRUD on scopes and theme values
- Add admin nav link and EN/NL translations