Change Log

100 entries
Add local shipping method overrides for orders

Introduce LocalShippingOverride type and Deno KV storage. Add patchOrderShipping utility that applies local overrides to fetched BLOrder objects. Create PUT /api/orders/{orderId}/shipping-method endpoint for persisting overrides. Update ShipList island: shipping method is now an editable select backed by cached BL methods. Patch order shipping on retrieval in ship-list, print-labels, pick-list, order detail, drive-thru, and the orders API endpoint. Show a 'local' badge on the order detail page when an override is active. Drive-thru template rules now evaluate against the locally overridden method.

Add delete confirmation dialog when editing inventory item with unsaved changes

Snapshot the original item values when entering edit mode via originalItem signal. Add hasChanges() helper comparing current form state against the snapshot. Gate both Delete Item and Delete + Next actions through confirmDeleteIfNeeded(). Show a confirmation modal when unsaved changes exist; delete immediately otherwise. Clear the snapshot on cancelEdit to avoid stale state.

Add "Next" action buttons and auto-scroll for pending inventory items

When editing a pending item, the Delete Item and Update Item buttons now have join-item 'Next' counterparts that perform the same action and automatically move to the next pending item. This speeds up bulk editing of imported items. Extract buildPendingItem() to share item construction between add and update paths. Add getNextPendingItem(), deleteAndNext(), and updateAndNext() helpers. Wrap action buttons in DaisyUI join groups for a compact unified look. Disable 'Next' buttons when there is no subsequent pending item. Add row refs and a useEffect so the active pending row scrolls into view automatically when navigating with the Next buttons.

Increase marketplace page size to 50 and expose pageSize in API response

Add shared MARKETPLACE_PAGE_SIZE constant in utils/format.ts. Update marketplace API route to use constant and return pageSize in JSON. Update Inventory island to read dynamic pageSize from API for pagination.

Show non-AU country code in Ship To and edit dialog

No additional details.

Add Drive Thru template selection rules with auto-evaluation

Introduce TemplateRule and RuleCondition types. Persist rules and default template ID in Deno KV. Add evaluateTemplateRules engine with eq/ne/gt/gte/lt/lte/contains operators. Create /drive-thru/rules management UI with condition builder and reordering. Pre-select templates on send page based on order/customer criteria. Add Drive Thru Rules to sidebar navigation.

Use dynamic Referer headers for BrickLink AJAX requests and centralise catalog URL generation

Replaces hardcoded AJAX_HEADERS with bricklinkAjaxHeaders(itemType, itemNo) so the Referer matches the requested catalog page. Adds bricklinkCatalogUrl() to utils/format.ts for reuse across the app. Updates Inventory, OrderItemsTable, PickList, and PartImageDialog to use the shared utility, fixing hardcoded P= prefixes in OrderItemsTable and PickList.

Improve styling of non-exportable items on the ship list

No additional details.

Add label printing for non-exportable orders

No additional details.

Refactor inline print styles into assets/core/print-labels.css

No additional details.

Move sender address from env var to Deno KV with UI configuration

Adds getSenderAddress/saveSenderAddress KV helpers, a new /configuration page with a form to edit the sender address, and a /api/configuration endpoint. The print-labels module now reads from KV instead of SENDER_ADDRESS. Adds a Configuration link to the sidebar under Settings and updates the environment page to reference it.

Preserve color selection during item lookup in inventory editor

Also updates Import button styling to match other primary actions.

Update print labels for HotLabel HL-S8 4x6 thermal printer

Resizes labels and page setup from 105x148mm to 4x6 inches. Forces all text and borders to solid black under @media print for thermal printer clarity (grays do not render well). Increases recipient name to text-2xl and address lines to text-lg for better readability on printed labels.

Improve BrickLink internal API anti-bot evasion

Adds realistic Chrome 135 User-Agent and Origin headers to AJAX requests. Fixes referer casing and varies sec-fetch-site per endpoint. Introduces fetchWithRetry that pauses 2s and retries once on 403. Adds KV caching for marketplace listings (5 min TTL) and store items (10 min TTL) to reduce repeated scraping traffic and lower temporary ban risk.

Decode HTML entities in catalog item names

BrickLink returns item names with inline numeric character references (e.g. ( for parentheses). Adds a decodeHtmlEntities helper that uses a temporary textarea to decode these, and applies it wherever catalogItem.name is rendered: the overview link text, both image alt attributes, and the zoom dialog caption.

Add printLabel flag to shipping methods and ship list

No additional details.

Resolve color names during XML import

Look up imported item colors in the KV cache so the pending items table displays color names instead of raw numeric IDs.

Ensure remarks input is required

No additional details.

Link item name in Inventory overview card to BrickLink catalog page

No additional details.

Make minifigs the default on inventory page

No additional details.

Blank extra cover amount in manifest CSV when value is zero

No additional details.

Add XML import to Inventory pending items

Adds an Import button that opens a modal dialog for selecting a BrickLink XML inventory file. Parsing is handled server-side via a new /api/xml/import endpoint. Imported items are appended to the pending list with a warning row tint that turns to success once edited. The pending items table is now scrollable with a max-h-128 cap and sticky headers.

Add pending item editing and description clipboard copy to Inventory

Pending items now have an edit (pencil) button that populates the form for in-place editing, with the active row highlighted and the panel switching to Edit Item mode. A Delete Item button appears in the form footer during editing. The description field gains an inline copy icon that writes the current value to the clipboard with a brief check confirmation.

Fix subset part count to sum quantities instead of counting entries

No additional details.

Add extra cover amount to manifest CSV export

No additional details.

Remove email and phone from ship list and manifest export

No additional details.

Rewrite manifest CSV export to new AusPost format

No additional details.

Add recipient phone number to manifest CSV export

No additional details.

Change icon for verify addresses

No additional details.

Add bulk verify-all button to ShipList Ship To column

Adds a badge-check icon button in the Ship To header that verifies all exportable addresses in parallel, auto-saving matched results to KV and showing per-row status indicators (verified, unmatched, error).

Rewrite manifest CSV export to new AusPost format

No additional details.

Show repeat-buyer badge on pick list order cards

No additional details.

Only count completed orders in CRM buyer history

Replaces the CANCELLED exclusion with an allowlist of SHIPPED, RECEIVED, COMPLETED, and PURGED — so in-progress and other non-final statuses are also excluded from order counts and totals.

Fix home partial route to use named file instead of directory index

routes/partials/index.tsx mapped to /partials (the directory root) which Fresh could not reliably serve as a partial endpoint, causing the sidebar Home link to update the URL but silently fail to swap the content region. Renaming to home.tsx gives a clean /partials/home URL matching the pattern used by all other flat partials.

Fix orders filter tabs: dual active state and stale partial data

Fresh's f-client-nav marks all anchors sharing a pathname as aria-current="page", causing DaisyUI to render both tabs as active. Fixed with a CSS override. Also corrected the Filed tab's f-partial URL to include query params so the handler receives the correct filter, and replaced a one-time useSignal initialisation in OrdersTable with a plain derived value so partial swaps render fresh rows.

Show buyer history in order detail card

Fetches the Customer record from KV and displays total order count and total value per currency in the Buyer card. Also links the buyer name to the customer page with f-partial.

Link buyer name to customer page and show total order count

Adds a partial route for customers/[username] so in-app navigation swaps only the content region. Buyer cells in OrdersTable now link to that page with f-partial and show the customer's historical order count from KV.

Add home page with orders and customer stats

Replaces the redirect with a proper landing page showing the app logo, title, and three navigation cards for Orders, Inventory, and Customers. The Orders card shows live unfiled order count; Customers shows total count from the buyer index.

Add character counter and over-limit validation to Description input

No additional details.

Update styling and default condition for inventory items

No additional details.

Default Qty to 1 and reset to 1 after adding an item

No additional details.

Add pagination to Marketplace Items with overlay loading UX

Forwards rpp=10 and pi to the upstream catalog API; accepts page and list_only params on the API route so page navigation skips the store and image fetches entirely. Adds prev/next controls with page N of M display. Splits marketplaceLoading from storeLoading so Store Items stays live while only the Marketplace Items section shows a loading state. Keeps existing items visible behind an absolute overlay spinner during page turns instead of clearing the container and collapsing its height.

Limit store items table height with scrolling and sticky header

No additional details.

Add Store Items section with live store listings per item lookup

No additional details.

Add zoomable image modal to item overview with hover indicator

No additional details.

Add part count from Get Subsets API for Sets and Minifigures

No additional details.

Add item overview panel with catalog data and color-synced image

On initial lookup, fetches the BrickLink catalog item (name, year, obsolete flag, image) and displays it in an overview card above Store Items. Selecting a color calls GET /items/{type}/{no}/images/{colorId} to fetch a color-specific thumbnail, swapping the image immediately; reverting to All restores the original catalog image.

Add color selection to inventory item lookup

Fetches known colors for an item via the BrickLink API on initial lookup, enriching names from the KV color cache. Color select enables when the item has real colors; selecting a color re-fetches marketplace listings filtered by that color without repeating the color API call. COLOR is included in pending items and the XML output; COLOR_NAME is display-only and stripped before submission.

Add Colors settings page with BrickLink cache and 24h cron

Adds BLColor and ColorsMeta types, KV helpers for color storage, and BricklinkClient.getColors() via GET /colors. A refreshColors() utility stores each color individually in KV. New POST /api/colors/refresh endpoint returns updated colors and meta. The Colors island shows swatches, IDs, names, and types with a Refresh button. The /colors page follows the standard route/partial pattern, appears in the Settings sidebar with a palette icon, and a Deno cron runs automatic refresh at 01:00 daily.

Add marketplace lookup to Inventory page

Adds a two-step BrickLink API proxy: resolve user-facing item ID to internal ID via the catalog search API (cached in KV on success), then fetch AU marketplace listings. Adds POST /api/xml for JSON-to-XML conversion using @libs/xml and GET /api/marketplace. The Inventory island shows a marketplace table with store feedback score, quantity, price, condition, and description.

Add Inventory data entry UI and XML API

Adds POST /api/xml endpoint using @libs/xml to convert JSON to XML. Adds the Inventory island with item entry form, pending items table with Copy XML and Reset actions, and stub sections for store and marketplace. Two-column layout on wide screens with form fields split across four rows.

Fix double spacing in Inventory page

Remove wrapper div with p-6 from InventoryContent — AppFrame already provides page padding, matching the pattern used by other content components.

Add Inventory page stub and update sidebar

Adds shell Inventory route/partial, inserts it between Orders and Customers in the sidebar (renamed from "Processing" to "Workflow"), and registers the warehouse icon in @source inline.

Optimise @source inline to only include dynamically interpolated icons

Reduced the icon pragma from 35 entries to 9 — only the icons passed dynamically via the Sidebar MenuItem icon prop. Statically referenced icons are detected automatically by Tailwind's scanner. Also removed 9 icons that were unused entirely.

Add Change Log page under Admin section

Adds /admin/changelog route displaying a collapsible list of changes sourced from data/changelog.json. Each entry uses a DaisyUI details/summary collapse with the commit subject as the title and optional body as detail. The changelog data is maintained separately in JSON so new entries can be prepended without touching the route code.

Treat BrickLink 204 response as success in updateOrderStatus

The BrickLink API returns HTTP 204 (no content) on a successful order status update. Previously this caused a JSON parse failure or a spurious API error; now it returns early as a success.

Update ShipList weight input to grams and extra cover to multiples of 100

Weight is now entered in grams and converted to kg on export. Extra cover steps in multiples of 100 with no decimal places.

Reformat pick list order column to show shipping name above order number

No additional details.

Show shipping name instead of buyer username in pick list order column

Falls back to buyerName if shippingName is absent.

Add part image popup dialog to pick list and order detail

Clicking a thumbnail opens a modal showing the full-size image, part number, name, lot description, color, and a link to the BrickLink catalog entry. Extracted the order items table into OrderItemsTable island so it can hold interactive state.

Replace e.target type coercions with e.currentTarget in event handlers

No additional details.

Extract OrderCard into components/OrderCard.tsx

No additional details.

Replace programmatic location.href navigation with anchor tags

Allows f-client-nav to intercept all navigation in OrdersTable and OrdersFilterTabs instead of triggering full page reloads.

Add admin logs page for viewing KV-stored log records

Adds /admin/logs route using listLogRecords() from logtape-kv-sink, rendering entries in a paginated table with cursor-based navigation. Includes sidebar Admin section with Logs link and scroll icon.

Add Fresh v2 partials for SPA-like navigation between pages

Sidebar links now swap only the main content region without reloading the page shell. Deep linking still works via full-page SSR. Fixes stopPropagation on order table links so partial nav is not blocked.

Persist sidebar collapsed state to localStorage across page navigations

No additional details.

Add message count column to orders and customer order history tables

Fetches BrickLink order message counts and displays a chat bubble icon with count next to each order that has messages. Results are cached in Deno KV with a 60-second TTL to avoid hammering the API on every page load.

Add buyer typeahead filter to customers page

Adds a text input with live suggestions fetched from /api/crm/buyers?q=, backed by a KV buyer index built during CRM refresh. Selecting a buyer filters to that customer via server-side lookup. Pagination is hidden while a filter is active.

Add customer detail page with member rating and order history

Adds /customers/{username} detail page showing the BrickLink member rating (PRAISE/NEUTRAL/COMPLAINT with percentages) and a full order history table drawn from the local order cache. Rating fetch failures are shown as a warning rather than a hard error.

Introduces BLMemberRating type matching the actual API wire format. Adds getMemberRating() to BricklinkClient and listCachedOrdersByBuyer() to kv.ts. Converts routes/customers.tsx to routes/customers/index.tsx to support nested routing, and links buyer names in the customers table to the detail page.

Add CSV import for backfilling purged order history in CRM

Adds POST /api/crm/import which accepts a multipart CSV file and maps rows (order_id, buyer_name, date_ordered, value) into BLOrderSummary records in the order cache. Returns a summary of imported/skipped/error counts with per-row error details.

Adds an Import CSV button to the Customers page that opens a dialog with a file picker and displays the import result summary inline.

Add BLOrderSummary type for GET /orders list response

Introduces BLOrderSummary to accurately represent the smaller payload returned by the orders list endpoint, distinct from the full BLOrder shape returned by the detail endpoint. getOrders() now returns BLOrderSummary[], the order cache stores summaries, and crm.ts aggregates from them.

Lazy-memoize Deno KV instance and return commit results from writes

Opens Deno KV at most once per process via a module-level singleton. Write functions now return Promise<Deno.KvCommitResult> by returning the KV promise directly rather than awaiting it, removing the redundant suspend point.

Preserve order history in local cache to survive BrickLink purges

Introduces a local order cache in Deno KV. The CRM refresh is now two phases: first sync live API orders into the cache (skipping PURGED entries to protect the last known-good data), then aggregate customer records from the full cache so history beyond BrickLink's 6-month window is retained.

Add order messages section to order detail page

Fetches messages from BrickLink API and displays them as chat bubbles in a tabbed layout alongside Order Items. Buyer messages are chat-start (primary), seller messages are chat-end (secondary), sorted ascending by date. Messages tab only appears when messages exist.

Add CRM customer aggregation with paginated customers page

Aggregates all inbound BrickLink orders by buyer_name (excluding cancelled) into a Deno KV-backed CRM. Tracks order count, first/last order date, and spend totals per currency. Customers page at /customers supports KV cursor-based pagination with Prev/Next navigation. An Update button triggers a manual refresh; a Deno cron job runs the same refresh automatically at midnight daily.

Split Package column into Package Type and Dimensions in ship list

No additional details.

Fix text colours for non-exportable rows in ship list

No additional details.

Remove ship order button and dialog from orders table

No additional details.

Add Prepare to Ship button to pick list, enabled when all orders are packed

No additional details.

Consolidate recipient email and phone into Recipient Info column in ship list

No additional details.

Consolidate ship list table columns into Order Info and Package

No additional details.

Add recipient phone number to ship list and AusPost manifest export

No additional details.

Disable Export Manifest button until all exportable rows have required fields

No additional details.

Exclude non-tracking shipping methods from exportable rows in ship list

No additional details.

Hide packaging fields for non-exportable rows in ship list

No additional details.

Move Drive Thru Templates to Settings section in sidebar

No additional details.

Rename Navigation section to Processing in sidebar

No additional details.

Move Shipping Methods to Settings section in sidebar

No additional details.

Add order total column to ship list table

No additional details.

Add Export Manifest button to ship list generating dated AusPost CSV

No additional details.

Strike through non-AU orders in ship list as they won't be exported

No additional details.

Add extra cover amount field to ship list table

No additional details.

Add weight field to ship list table

No additional details.

Add recipient email column to ship list, pre-populated from buyer_email

No additional details.

Add AU address verification to ship list edit dialog via AddressFinder

No additional details.

Add address line 3 field to AusPost address in ship list

No additional details.

Replace buyer/ordered columns with item count in ship list table

No additional details.

Add editable AusPost address mapping to ship list with KV persistence

No additional details.

Format shipping address as multi-line label in ship list

No additional details.

Remove status column from ship list table

No additional details.

© 2026 Kitson P. Kelly. All rights reserved.