Developer Docs

Murchases & Multi-Malet Grouping โ€” Integration Guide

Overview

The murchases system displays Visitor murchase history with support for multi-malet grouping โ€” murchases from different Malets can be viewed in a flat list or visually grouped by Malet. Each murchase shows its status progression, total amount, and which Malet it originated from.

Quick Start

For Users

View Murchases: Navigate to /murchases to see all your murchase history.

Switch Views:

  • List view (default): Flat chronological list of all murchases
  • Grouped view: Click the grouped icon (stacked boxes) to see murchases organized by Malet

The selected view is remembered across sessions via localStorage.

Filter & Search:

  • Filter by status: All, Pending, Processing, Shipped, Delivered
  • Search by murchase number (partial match)

For Developers

Key entry point: src/routes/murchases/+page.svelte


File Structure

src/
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ””โ”€โ”€ murchases/
โ”‚   โ”‚       โ”œโ”€โ”€ MaletMurchaseGroup.svelte    # Collapsible malet-grouped display
โ”‚   โ”‚       โ”œโ”€โ”€ MurchaseCard.svelte          # Individual murchase card (with malet badge)
โ”‚   โ”‚       โ”œโ”€โ”€ MurchaseStatus.svelte        # Status badge component
โ”‚   โ”‚       โ””โ”€โ”€ MurchaseTimeline.svelte      # Step-by-step status timeline
โ”‚   โ”œโ”€โ”€ queries/
โ”‚   โ”‚   โ””โ”€โ”€ murchases.ts                     # GraphQL queries + types
โ”‚   โ”œโ”€โ”€ utils/
โ”‚   โ”‚   โ””โ”€โ”€ murchaseUtils.ts                 # Grouping + formatting utilities
โ”‚   โ””โ”€โ”€ stores/
โ”‚       โ””โ”€โ”€ murchaseStore.ts                 # Murchase state management (writable store)
โ”œโ”€โ”€ routes/
โ”‚   โ””โ”€โ”€ murchases/
โ”‚       โ”œโ”€โ”€ +page.svelte                  # Main murchases page (flat + grouped views)
โ”‚       โ”œโ”€โ”€ +page.ts                      # Data loader
โ”‚       โ””โ”€โ”€ [id]/
โ”‚           โ”œโ”€โ”€ +page.svelte              # Single murchase detail
โ”‚           โ””โ”€โ”€ workroom/
โ”‚               โ””โ”€โ”€ +page.svelte          # Murchase workroom (fulfillment)

Component API

MaletMurchaseGroup.svelte

Purpose: Collapsible wrapper showing murchases from a single Malet.

Props:

export let group: MaletGroup;

Where MaletGroup is:

interface MaletGroup {
	maletId: string;
	murchases: Murchase[];
	totalAmount: number; // Sum of all murchase amounts (cents)
	count: number; // Number of murchases in group
}

Features:

  • Malet avatar with gradient icon
  • Label showing Malet #XXXXXXXX (last 8 chars of ID, uppercased)
  • Murchase count badge + total spend
  • Clickable header to expand/collapse
  • Smooth expand/collapse animation
  • Dark mode support via $mall tokens

Usage:

<script>
	import MaletMurchaseGroup from '$lib/components/murchases/MaletMurchaseGroup.svelte';
	import { groupMurchasesByMalet } from '$lib/utils/murchaseUtils';

	let groups = groupMurchasesByMalet(murchases);
</script>

{#each groups as group (group.maletId)}
	<MaletMurchaseGroup {group} />
{/each}

MurchaseCard.svelte

Purpose: Display a single murchase summary.

Props:

export let murchase: Murchase;

Features:

  • Murchase number (last 8 chars of ID)
  • Date and malet badge (if maletId present)
  • Status badge via MurchaseStatus component
  • Total amount and last updated date
  • "View Details" and "Reorder" action buttons
  • Uses $mall design tokens, dark mode aware

Utility Functions

`murchaseUtils.ts`

groupMurchasesByMalet(murchases: Murchase[]): MaletGroup[]

Groups a flat array of murchases by maletId. Murchases without a maletId are placed in an "Unknown" group. Results are sorted by count (most murchases first).

import { groupMurchasesByMalet } from '$lib/utils/murchaseUtils';

const groups = groupMurchasesByMalet(murchases);
// [{ maletId: 'malet-a', murchases: [...], count: 5, totalAmount: 25000 }, ...]

formatMaletLabel(maletId: string): string

Formats a malet ID for display. Returns "Malet #XXXXXXXX" (last 8 chars) or "Unknown Malet" for the unknown group.

formatMaletLabel('abc123def456gh78'); // "Malet #F456GH78"
formatMaletLabel('unknown'); // "Unknown Malet"

GraphQL Queries

`GET_MY_MURCHASES`

query GetMyMurchases($limit: Int, $offset: Int) {
	myMurchases(limit: $limit, offset: $offset) {
		edges {
			node {
				id
				buyerId
				maletId # Malet this murchase belongs to
				status
				totalAmount {
					amount
					formatted
				}
				currency
				items {
					# Line items
					name
					quantity
					maletId
				}
				createdAt
				updatedAt
			}
		}
		pageInfo {
			hasNextPage
			hasPreviousPage
		}
		totalCount
	}
}

query GetOrdersByEmail($email: String!, $code: String!) { ordersByEmail(email: $email, code: $code) { id status totalAmount { formatted } currency guestEmail guestPhone createdAt } }

`GET_ORDERS_BY_PHONE`

query GetOrdersByPhone($phone: String!, $code: String!) {
	ordersByPhone(phone: $phone, code: $code) {
		id
		status
		totalAmount {
			formatted
		}
		currency
		guestEmail
		guestPhone
		createdAt
	}
}

Murchase Types

enum MurchaseStatus {
	PENDING,
	PAYMENT_AUTHORIZED,
	PROCESSING,
	SHIPPED,
	DELIVERED,
	CANCELLED
}

interface MurchaseLineItem {
	name: string;
	quantity: number;
	maletId?: string;
}

interface Murchase {
	id: string;
	buyerId: string;
	maletId?: string;
	status: MurchaseStatus;
	totalAmount: { amount: number; formatted: string };
	currency: string;
	items?: MurchaseLineItem[];
	createdAt: string;
	updatedAt: string;
}

Store Usage

murchaseStore

import { murchaseStore } from '$stores/murchaseStore';

// Load murchases
await murchaseStore.loadMurchases(limit, offset);

// Load by email (guest)
await murchaseStore.loadMurchasesByEmail(email, code);

// Load by phone (guest)
await murchaseStore.loadMurchasesByPhone(phone, code);

// Load single murchase
await murchaseStore.loadMurchase(murchaseId);

// Re-murchase (Reorder)
await murchaseStore.duplicateMurchase(murchaseId);

View Toggle (Flat โ†” Grouped)

The murchases page provides two view modes:

View Description Icon
Flat (default) Chronological list of all murchases Three horizontal lines
Grouped Murchases grouped by Malet in collapsible sections Stacked boxes

The selected mode is persisted to localStorage key murchases-view-mode.


Testing

Unit Tests (`tests/murchaseUtils.test.ts`)

  • 8 tests covering groupMurchasesByMalet and formatMaletLabel
  • Empty input, single/multi group, unknown malet, total calculation, sort order

E2E Tests (`e2e/multi-malet-murchases.test.ts`)

  • Page loads with title
  • View toggle is present
  • Can switch between flat and grouped views

Data Attributes

Element data-testid
View toggle container view-toggle
Flat view container murchases-flat-view
Grouped view container murchases-grouped-view
Malet group wrapper malet-murchase-group
Malet group murchase list malet-murchase-list