orders: restore the design the prototype had, and stop repeating the row
ci / lint-test (push) Successful in 49s
ci / lint-test (push) Successful in 49s
Rebuilding the prototype in React quietly dropped most of what made it read well. Side by side, the live page had lost the year strip entirely — which was both the hero and the context for the date range — along with the masthead totals, the A$ prefix on amounts, and the Fraunces wordmark. The trust marker rendered as a tofu box. Restored, with the strip clickable so a bar scopes to that year. Also three content fixes, all visible the moment anyone looked at a real page. The title and the manifest were printing the same text twice on 1,964 of 4,649 itemised rows — 42% — because on a single-item order the order title IS the line item. The title now earns its line only when it says something the items do not. Food line items carry the full customisation: one Subway order runs to 450 characters naming every topping, and it swamped everything around it. Rows show a short form, cut at the first bracket where there is one, since "Footlong (Italian Herb…" is the product and the bracket is the customisation. Rows now expand, following the disclosure pattern the transactions page already uses, and the query fetches twelve items rather than three so opening one costs no round trip. Merchant names get a presentational tidy — corporate suffixes dropped, a lower-cased domain capitalised. This never merges two merchants: the estate holds amazon.com.au, Amazon.in and Amazon Services Australia as three distinct entities, and Amazon.in must stay separate because it is a different marketplace rather than a name variant. Actually unifying them is the merchant-alias bridge.
This commit is contained in:
+31
-3
@@ -229,7 +229,10 @@ export async function getOrderFeed(filters: OrderFilters) {
|
||||
CASE WHEN jsonb_typeof(f.details->'line_items') = 'array'
|
||||
THEN f.details->'line_items' ELSE '[]'::jsonb END)
|
||||
WITH ORDINALITY t(li, n)
|
||||
WHERE t.n <= 3) AS item_preview
|
||||
-- 12, not 3: the row shows the first few and the expansion shows
|
||||
-- the rest without a second round trip. Beyond 12 the detail page
|
||||
-- is the right surface.
|
||||
WHERE t.n <= 12) AS item_preview
|
||||
${FROM_CLAUSE}
|
||||
${where}
|
||||
-- NULLS LAST always: eta_date is NULL on ~65% of retail and ~95% of food,
|
||||
@@ -244,6 +247,14 @@ export async function getOrderFeed(filters: OrderFilters) {
|
||||
}
|
||||
|
||||
export interface OrderFacets {
|
||||
/** Orders per calendar year across the WHOLE feed — deliberately not scoped
|
||||
* to the active date range, because the strip's job is to show where the
|
||||
* current range sits in the twenty-one years available. */
|
||||
years: { year: number; n: number }[];
|
||||
/** Corpus totals for the masthead, so a filtered count is never mistaken
|
||||
* for the whole archive. */
|
||||
all_time: number;
|
||||
with_amount: number;
|
||||
lanes: { lane: string; n: number }[];
|
||||
platforms: { platform: string; n: number }[];
|
||||
statuses: { status: string; n: number }[];
|
||||
@@ -264,7 +275,19 @@ export async function getOrderFacets(filters: OrderFilters): Promise<OrderFacets
|
||||
const { where, params } = buildWhere(base);
|
||||
const laneScoped = buildWhere({ ...base, lane: filters.lane });
|
||||
|
||||
const [lanes, platforms, statuses, currencies] = await Promise.all([
|
||||
// The year strip and the all-time totals ignore the date range on purpose.
|
||||
const spanFilters: OrderFilters = { hide_lifecycle_only: filters.hide_lifecycle_only };
|
||||
const span = buildWhere(spanFilters);
|
||||
|
||||
const [years, totals, lanes, platforms, statuses, currencies] = await Promise.all([
|
||||
queryRaw<{ year: number; n: number }>(
|
||||
`SELECT extract(year from f.ordered_at)::int AS year, count(*)::int AS n
|
||||
${FROM_CLAUSE} ${span.where}${span.where ? " AND" : " WHERE"} f.ordered_at IS NOT NULL
|
||||
GROUP BY 1 ORDER BY 1`, span.params),
|
||||
queryRaw<{ all_time: number; with_amount: number }>(
|
||||
`SELECT count(*)::int AS all_time,
|
||||
count(*) FILTER (WHERE f.order_total IS NOT NULL AND f.order_total > 0)::int AS with_amount
|
||||
${FROM_CLAUSE} ${span.where}`, span.params),
|
||||
queryRaw<{ lane: string; n: number }>(
|
||||
`SELECT f.lane, count(*)::int AS n ${FROM_CLAUSE} ${where} GROUP BY 1 ORDER BY 2 DESC`, params),
|
||||
queryRaw<{ platform: string; n: number }>(
|
||||
@@ -277,7 +300,12 @@ export async function getOrderFacets(filters: OrderFilters): Promise<OrderFacets
|
||||
`SELECT f.currency, count(*)::int AS n ${FROM_CLAUSE} ${laneScoped.where}
|
||||
GROUP BY 1 ORDER BY 2 DESC`, laneScoped.params),
|
||||
]);
|
||||
return { lanes, platforms, statuses, currencies };
|
||||
return {
|
||||
years,
|
||||
all_time: totals[0]?.all_time ?? 0,
|
||||
with_amount: totals[0]?.with_amount ?? 0,
|
||||
lanes, platforms, statuses, currencies,
|
||||
};
|
||||
}
|
||||
|
||||
export interface OrderLifecycleEvent {
|
||||
|
||||
Reference in New Issue
Block a user