# Phase 11: Projection Pipeline & Sort Fixes - Context

**Gathered:** 2026-03-20
**Status:** Ready for planning

<domain>
## Phase Boundary

Pipe Shopify customer and order URLs from data source through the projection layer to card actions, and wire discovery-mode sorting so cards reorder when switching modes. This is a gap-closure phase for CARD-08 and CARD-09 requirements.

</domain>

<decisions>
## Implementation Decisions

### URL data flow
- Build URLs in the **projection layer** (project_snapshot), not the service/snapshot layer — keeps service layer ID-only and projection responsible for display concerns
- Add `shopify_order_id: Option<String>` to `RecipientCardSnapshot` alongside existing `shopify_customer_id` — flow `latest_order_id` from `ShopifyProjection` through the merge pipeline to the snapshot
- Add `store_slug: &str` parameter to `project_snapshot()` — caller provides from config
- Use `build_shopify_customer_url` and `build_shopify_order_url` (already in `external.rs`) to construct URLs from IDs + store slug
- **Hide** menu items when no customer/order ID exists — no grayed-out dead items

### Sort wiring
- Call `sort_cards_by_mode` **after all filters** (search → status → archive → sort) in `apply_filters`, right before pushing to Slint model
- Sort on `DashboardCardViewModel` vec, then convert to `CardData` — reuses existing sort logic without changes
- Option grid tiles (By Recipient, By Product) also sorted alphabetically for consistency

### Store config
- Shopify store slug provided via config/env variable (non-secret, just store identifier like `bigscreenvr`)
- Passed to `project_snapshot()` as parameter — no global state

### Claude's Discretion
- Exact env variable name for store slug
- How `shopify_order_id` flows through the merge pipeline from `ShopifyProjection` to `RecipientCardSnapshot`
- Whether to sort in-place or create a new sorted vec in apply_filters

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Projection pipeline
- `crates/app/src/dashboard/projection.rs` — Current project_snapshot with hardcoded None URLs
- `crates/app/src/dashboard/view_model.rs` — DashboardCardViewModel with shopify_customer_url/shopify_order_url fields
- `crates/app/src/dashboard/external.rs` — build_shopify_customer_url, build_shopify_order_url helpers
- `crates/app/src/service_client.rs` — RecipientCardSnapshot (has shopify_customer_id, needs shopify_order_id)

### Sort logic
- `crates/app/src/dashboard/discovery.rs` — sort_cards_by_mode implementation and tests
- `crates/app/src/main.rs` — apply_filters function (lines ~401-475) where sort needs to be wired

### Service layer
- `crates/service/src/sync/shopify_projection.rs` — ShopifyProjection with latest_order_id
- `crates/service/src/sync/merge.rs` — Merge pipeline where order_id needs to flow through

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `build_shopify_customer_url(store_slug, customer_id)` and `build_shopify_order_url(store_slug, order_id)` in `external.rs` — ready to use
- `sort_cards_by_mode(cards, mode)` in `discovery.rs` — fully implemented with tests, just needs wiring
- `filter_cards_by_search`, `filter_cards_by_status`, `filter_cards_by_archive` — existing filter pipeline in `apply_filters`

### Established Patterns
- `apply_filters` is the central filter+display pipeline — all card view updates go through it
- `card_data_to_filter_model` converts CardData to DashboardCardViewModel for filtering
- Seed data in `main.rs` already sets hardcoded Shopify URLs for testing

### Integration Points
- `project_snapshot()` in `projection.rs` — where URL construction must be added
- `apply_filters()` in `main.rs` — where sort must be called after filtering
- `RecipientCardSnapshot` in `service_client.rs` — needs `shopify_order_id` field added
- Merge pipeline in service crate — needs to propagate `latest_order_id` to snapshot

</code_context>

<specifics>
## Specific Ideas

- Existing credential security (Windows Credential Manager) is solid — store slug is non-secret and just needs env/config treatment
- Phase 8 explicitly deferred URL construction: "shopify_customer_url/order_url set to None in projection layer - URL construction deferred"

</specifics>

<deferred>
## Deferred Ideas

- Production store slug configuration wiring — Phase 12 (Production Data Client Integration) will wire the actual store slug from config
- Credential security review for API tokens — already handled via WindowsCredentialManagerStore, no changes needed

</deferred>

---

*Phase: 11-projection-pipeline-sort-fixes*
*Context gathered: 2026-03-20*
