Skip to content

fix(sell): replace assetType text input with canonical Select and add homepage filter - #421

Open
azahjessica49-commits wants to merge 1 commit into
arflexx:mainfrom
azahjessica49-commits:fix/asset-type-select-canonical-values
Open

azahjessica49-commits wants to merge 1 commit into
arflexx:mainfrom
azahjessica49-commits:fix/asset-type-select-canonical-values

Conversation

@azahjessica49-commits

@azahjessica49-commits azahjessica49-commits commented Sep 26, 2026 •

Copy link
Copy Markdown

Closes #377


Overview

This PR replaces the free-text assetType input on the sell form with a structured <select> component backed by a canonical allowlist, wires the same allowlist into backend schema validation, and adds a live filter bar to the homepage listings grid.

Related Issue

Closes #assettype-canonical

Changes

🛒 Sell Form — Structured Asset Type Select

  • [ADD] frontend/app/lib/assetTypes.ts

    • Single source of truth for ASSET_TYPE_VALUES (9 canonical uppercase entries: MTN, Glo, Airtel, 9mobile, Spectranet × Airtime/Data).
    • Exports ASSET_OPTIONS ({value, label} pairs), ASSET_TYPE_LABELS record, and formatAssetType helper.
    • All canonical values follow the CARRIER_TYPE uppercase pattern (e.g. MTN_AIRTIME).
  • [MODIFY] frontend/app/sell/page.tsx

    • Removed the local hard-coded ASSET_OPTIONS const array.
    • assetType field is now a <select> importing from the shared constants — Spectranet now included.
    • Validation still enforces a non-empty selection before submit.

🔒 Backend Schema — Allowlist Validation

  • [MODIFY] server/src/schemas/trade.schemas.ts

    • Replaced open z.string().regex() on assetType with z.enum(ASSET_TYPE_VALUES).
    • Arbitrary strings (e.g. "mtn", "Mtn Nigeria") are now rejected with a clear error listing valid values.
    • Extended paginationSchema with an optional assetType enum field for the filter query.
    • Exports ASSET_TYPE_VALUES and AssetTypeValue type for downstream consumers.
  • [MODIFY] server/src/schemas/index.ts

    • Barrel now re-exports ASSET_TYPE_VALUES and AssetTypeValue.
  • [MODIFY] server/src/routes/trades.ts

    • GET /api/v1/trades reads optional assetType from validated query params.
    • Applies parameterised AND t.asset_type = $N clause to both the data query and the count query when the filter is present.

🏠 Homepage — Live Filter Bar

  • [ADD] frontend/app/components/ListingsGrid.tsx

    • New "use client" component owning filter state.
    • FilterBar renders pill buttons: All + one per canonical asset type, using aria-pressed for accessibility.
    • Passes active filter to useTradeList which forwards it as ?assetType= to the API.
    • Skeleton loading cards (8 placeholders), error alert, and smart empty state (filtered vs. unfiltered).
  • [MODIFY] frontend/app/page.tsx

    • Removed server-side getActiveListings fetch and all inline TradeCard / EmptyState / AssetBadge components.
    • Listings section replaced with <ListingsGrid /> — keeps the page as a lightweight server component for hero and footer.

Verification Results

TypeScript diagnostics: 0 errors across all 6 changed/created files

Manual checks:
✅ Sell form <select> renders all 9 canonical options including Spectranet
✅ Submitting MTN_AIRTIME sends canonical uppercase value to API
✅ Backend rejects "mtn" / "Mtn Nigeria" with 400 + enum error message
✅ GET /api/v1/trades?assetType=MTN_AIRTIME returns only MTN airtime listings
✅ GET /api/v1/trades?assetType=invalid returns 400 validation error
✅ Homepage filter bar shows All + 9 asset type pills
✅ Selecting a filter rerenders grid with filtered results via useTradeList
✅ Clearing filter (All) restores unfiltered listing feed
Acceptance Criteria Status
assetType field is a Select with supported carriers + types ✅ <select> with MTN, Glo, Airtel, 9mobile, Spectranet × Airtime/Data
Selection produces canonical uppercase value ✅ e.g. MTN_AIRTIME, SPECTRANET_DATA
Backend schema validates assetType against allowlist ✅ z.enum(ASSET_TYPE_VALUES) in createTradeSchema + paginationSchema
Homepage filter uses same canonical values ✅ FilterBar imports from assetTypes.ts, passes to useTradeList → API

… homepage filter

- Add frontend/app/lib/assetTypes.ts as single source of truth for
  ASSET_TYPE_VALUES, ASSET_TYPE_LABELS, ASSET_OPTIONS and formatAssetType.
  Includes all 5 carriers (MTN, Glo, Airtel, 9mobile, Spectranet) x asset
  types (Airtime/Data) producing canonical uppercase values (e.g. MTN_AIRTIME).

- Replace open text input on sell/page.tsx with a structured <select>
  driven by ASSET_OPTIONS imported from the shared constants file.

- Replace open regex in server/src/schemas/trade.schemas.ts with
  z.enum(ASSET_TYPE_VALUES) so the backend rejects arbitrary strings and
  validates against the same allowlist as the frontend.

- Extend paginationSchema with an optional assetType enum field so the
  GET /api/v1/trades route can accept and validate filter queries.

- Update GET /trades handler in server/src/routes/trades.ts to apply
  parameterised WHERE asset_type = \ clause when assetType is supplied,
  keeping both the data and count queries in sync.

- Add frontend/app/components/ListingsGrid.tsx (client component) with a
  FilterBar of pill buttons (All + one per canonical asset type) that drives
  useTradeList, replacing the server-rendered static grid on the homepage.

- Replace inline server-fetched listings section in frontend/app/page.tsx
  with <ListingsGrid /> to support live client-side filtering.

Closes #assettype-canonical
@drips-wave

drips-wave Bot commented Sep 26, 2026

Copy link
Copy Markdown

@azahjessica49-commits Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[frontend] - Sell form assetType field is a plain text input instead of a structured Select with predefined carriers

1 participant