6 plans across 3 waves covering market-aware pricing, exchange rates, community price data, and currency-normalized display. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 33-currency-system | 06 | execute | 3 |
|
|
true |
|
|
Purpose: User-facing display of market prices, community data, and currency-normalized comparisons — the visible payoff of the currency system. Output: Updated global item detail with market prices, comparison table with conversion, setup card with currency, MCP tools with currency context.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/33-currency-system/33-CONTEXT.md @.planning/phases/33-currency-system/33-UI-SPEC.md @.planning/phases/33-currency-system/33-05-SUMMARY.md From src/client/hooks/useExchangeRates.ts (Plan 05): ```typescript export function useExchangeRates(): UseQueryResult; export function convertClientPrice(cents: number, from: string, to: string, rates: Record): number; ```From src/client/hooks/useCurrency.ts (Plan 05):
export interface CurrencyContext {
currency: Currency;
market: string;
showConversions: boolean;
}
export function useCurrency(): CurrencyContext;
From src/client/lib/formatters.ts (Plan 05):
export function formatDualPrice(options: DualPriceOptions): { source: string; converted: string };
From src/client/hooks/useGlobalItems.ts (existing):
export function useGlobalItem(id: number): UseQueryResult<GlobalItem>;
From src/client/components/ComparisonTable.tsx (existing):
interface ComparisonTableProps {
candidates: CandidateWithCategory[];
resolvedCandidateId: number | null;
deltas?: Record<number, CandidateDelta>;
}
First, add a new hook in src/client/hooks/useGlobalItems.ts:
export function useGlobalItemPrices(globalItemId: number) {
return useQuery({
queryKey: ["global-item-prices", globalItemId],
queryFn: () => apiGet<{
marketPrices: Array<{ market: string; currency: string; priceCents: number; source: string | null }>;
}>(`/api/market-prices/global-items/${globalItemId}/prices`),
enabled: globalItemId > 0,
});
}
export function useGlobalItemCommunityStats(globalItemId: number) {
return useQuery({
queryKey: ["global-item-community-stats", globalItemId],
queryFn: () => apiGet<Array<{ market: string; currency: string; medianPrice: number; reportCount: number }>>(`/api/community-prices/${globalItemId}`),
enabled: globalItemId > 0,
});
}
Then update src/client/routes/global-items/$globalItemId.tsx:
Add a MarketPricesSection component within the detail page:
- Uses
useCurrency()to get{ currency, market } - Uses
useGlobalItemPrices(id)anduseGlobalItemCommunityStats(id) - Uses
useExchangeRates()for conversion when needed
Layout per UI-SPEC section 4:
- Section heading: "Price" (
text-sm font-medium text-gray-900) - User's market MSRP shown prominently: find marketPrice where market matches user's market
- If found:
text-lg font-semibold text-gray-900+ "MSRP ({MARKET})" label intext-xs text-gray-500 ml-2 - If not found but other markets exist: show converted price from nearest market with dual display format using
formatDualPrice
- If found:
- Community stats for user's market: filter communityStats where market matches
- Per D-21: "Community ({MARKET}): {SYMBOL}{median} median ({N} reports)" in
text-sm text-gray-700with report count intext-xs text-gray-400 - Only show if reportCount >= 3 (server already filters, but handle empty gracefully)
- Per D-21: "Community ({MARKET}): {SYMBOL}{median} median ({N} reports)" in
- Collapsible "Other Markets" section:
- Use useState for expanded state, default collapsed
- Toggle: "Other Markets" text with Lucide
chevron-right/chevron-downicon (14px) - Style:
text-sm text-gray-500 cursor-pointer hover:text-gray-700 - Inner rows: same price/label styling, indented with
pl-4 - Show all market prices except user's market
- Show community stats for other markets
Place this section below the existing weight/price display area in the detail page.
<acceptance_criteria>
- src/client/hooks/useGlobalItems.ts exports useGlobalItemPrices and useGlobalItemCommunityStats
- src/client/routes/global-items/$globalItemId.tsx contains a MarketPricesSection component
- User's market MSRP shown prominently with market label
- Community stats displayed as "Community ({MARKET}): {median} median ({N} reports)"
- "Other Markets" section is collapsible and collapsed by default
- bun run build succeeds
</acceptance_criteria>
cd /home/jlmak/Projects/jlmak/GearBox && bun run build && grep -c "MarketPricesSection|useGlobalItemPrices|useGlobalItemCommunityStats" src/client/routes/global-items/$globalItemId.tsx src/client/hooks/useGlobalItems.ts
Global item detail page shows market prices with user's market MSRP, community stats, and collapsible other markets
Per D-18: Update src/client/components/SetupCard.tsx:
- If SetupCard shows a price total, ensure it uses
useFormatters().price()which now uses the correct currency - This should already work if the component uses
useFormatters()— verify and adjust if it uses hardcoded "$" or similar
Per MCP tool updates: Update src/server/mcp/tools/index.ts:
- In
list_itemsandget_itemtool responses: includepriceCurrencyfield alongsidepriceCents - In
get_setuptool response: include currency info with totals - Add a new tool
get_exchange_rates:- Description: "Get current exchange rates for currency conversion"
- No parameters required
- Returns:
{ base, date, rates }from getExchangeRates()
- In
create_itemandupdate_itemtools: accept optionalpriceCurrencyparameter - In
add_candidateandupdate_candidatetools: accept optionalfoundPriceCents,foundPriceCurrency,foundPriceDateparameters - Follow existing MCP tool patterns for parameter/response structure
<acceptance_criteria>
- ComparisonTable.tsx imports useExchangeRates and convertClientPrice
- ComparisonTable price cells show ~ prefix when price is converted from different currency
- ComparisonTable has "Found Price" row for candidate research prices
- SetupCard uses useFormatters().price() for currency-aware display
- MCP tools/index.ts contains get_exchange_rates tool definition
- MCP list_items and get_item responses include priceCurrency
bun run buildsucceeds </acceptance_criteria> cd /home/jlmak/Projects/jlmak/GearBox && bun run build && grep -c "convertClientPrice|foundPrice|get_exchange_rates|priceCurrency" src/client/components/ComparisonTable.tsx src/server/mcp/tools/index.ts Comparison table normalizes currencies, MCP tools include currency context, setup cards display correct currency
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| server→client | Market prices and exchange rates served to public clients |
| MCP client→server | MCP tool invocations with currency parameters |
STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-33-15 | Tampering | ComparisonTable conversion | accept | Client-side conversion uses server-provided rates — worst case is stale rates, not a security issue |
| T-33-16 | Information Disclosure | market prices display | accept | Market prices are intentionally public data — MSRP is not sensitive |
| T-33-17 | Tampering | MCP priceCurrency param | mitigate | MCP tools validate priceCurrency against known currency list before persisting |
| </threat_model> |
<success_criteria>
- Catalog detail page shows market prices + community data
- Comparison table normalizes and labels converted prices
- Setup cards show correct currency
- MCP tools expose currency data and exchange rates
- Full build succeeds </success_criteria>