All files / src/components/PriceHistoryPanel priceHistoryRows.ts

100% Statements 27/27
100% Branches 2/2
100% Functions 1/1
100% Lines 27/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 801x                                                               1x                                           1x 1x 15x 15x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 84x 15x 15x  
import { describeTrend } from '@/helpers/priceHistory';
 
/**
 * One logged price change, flattened from a {@link PriceHistoryEntry}'s point
 * series: the price observed immediately before the change, the price observed
 * after, and when the change was recorded.
 */
export interface PriceChangeRow {
  /** Stable row id: `${entry.id}::${pointIndex}`. */
  id: string;
  /** The product's stable identity key, shared by base and variant series. */
  productKey: string;
  /** Supplier display name. */
  supplier: string;
  /** Product/variant title. */
  title: string;
  /** Human-facing link back to the product, when known. */
  permalink?: string;
  /** Epoch ms timestamp of the new price (when the change was observed). */
  changedAt: number;
  /** The price in USD immediately before this change. */
  oldPriceUsd: number;
  /** The price in USD immediately after this change. */
  newPriceUsd: number;
  /** Signed USD delta (new − old). */
  deltaUsd: number;
  /** Signed percent change relative to the old price. */
  pctChange: number;
  /** Whether the change was a rise, a drop, or (only possible via rounding) flat. */
  direction: 'up' | 'down' | 'flat';
}
 
/**
 * Flattens every price-history series into one row per logged price change,
 * pairing each point with the one before it. A series with fewer than two
 * points has never changed and contributes no rows — this is what filters the
 * "which products actually do have price changes" view down to real movement.
 * @category Helpers
 * @group Formatters
 * @param entries - The price-history series to flatten, in any order.
 * @returns One {@link PriceChangeRow} per consecutive point pair, unsorted.
 * @example
 * ```ts
 * buildPriceChangeRows([
 *   {
 *     id: 'p1', productKey: 'p1', supplier: 'Loudwolf', title: 'Acetone 500ml',
 *     points: [{ t: 1, usd: 19.99 }, { t: 2, usd: 21.5 }], updatedAt: 2,
 *   },
 * ]);
 * // => [{ id: 'p1::1', productKey: 'p1', supplier: 'Loudwolf', title: 'Acetone 500ml',
 * //       changedAt: 2, oldPriceUsd: 19.99, newPriceUsd: 21.5, deltaUsd: 1.51,
 * //       pctChange: 7.55..., direction: 'up' }]
 * ```
 * @source
 */
export function buildPriceChangeRows(entries: readonly PriceHistoryEntry[]): PriceChangeRow[] {
  const rows: PriceChangeRow[] = [];
  for (const entry of entries) {
    for (let i = 1; i < entry.points.length; i++) {
      const prev = entry.points[i - 1];
      const curr = entry.points[i];
      const trend = describeTrend([prev, curr]);
      rows.push({
        id: `${entry.id}::${i}`,
        productKey: entry.productKey,
        supplier: entry.supplier,
        title: entry.title,
        permalink: entry.permalink,
        changedAt: curr.t,
        oldPriceUsd: prev.usd,
        newPriceUsd: curr.usd,
        deltaUsd: trend.deltaUsd,
        pctChange: trend.pctChange,
        direction: trend.direction,
      });
    }
  }
  return rows;
}