Skip to content

feat(frontend): unify async state handling and complete page metadata - #1483

Merged
pope-h merged 4 commits into
Shelterflex:mainfrom
nonso7:feat/consistent-async-states-and-page-metadata
Jul 31, 2026
Merged

feat(frontend): unify async state handling and complete page metadata#1483
pope-h merged 4 commits into
Shelterflex:mainfrom
nonso7:feat/consistent-async-states-and-page-metadata

Conversation

@nonso7

@nonso7 nonso7 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Async state handling (#1440)

Surfaces migrated from mock data to live APIs each grew their own loading, empty, and error treatment. Adds shared primitives in components/ui/data-state.tsx — LoadingState/LoadingAnnouncer, ErrorState, EmptyState, MoneyValue, plus StatCardSkeleton/ListRowSkeleton — and migrates the dashboards and every money-displaying surface onto them.

MoneyValue is the load-bearing piece: a balance rendered from ?? 0 is indistinguishable from a real zero, so it renders a skeleton while loading and an explicit dash when the amount is unknown, and only ever formats a number it was actually given. Inspector earnings and admin analytics both rendered ₦0 on a failed fetch; they now dash out.

Error states take a required onRetry and re-run the failed fetch instead of reloading the page. Empty states carry the action that would populate them. Loading is announced via a polite live region, skeletons are aria-hidden, and placeholders mirror the real content's dimensions.

lib/tests/no-money-fallbacks.test.ts scans the source tree and fails if either a money-from-fallback or a reload-based retry reappears.

Page metadata (#1441)

Only 10 of 84 routes had metadata, with no metadataBase, canonical URLs, or robots exclusions. Adds lib/seo.ts, site-wide defaults with a title template, and per-route metadata for the priority public routes.

Property detail pages now build a per-listing title, a description from the listing's own copy, and an absolute OpenGraph image from its first photo, plus Residence JSON-LD carrying the rent as an Offer. The homepage, /properties, and /landlords were client components and so could not export metadata; each is now a server route file rendering a client component.

Private and token-addressed routes are excluded by segment layout, with noarchive/nosnippet alongside noindex — for the rating-card routes the token is the only access control, so a cached copy or search snippet would outlive its revocation. app/robots.ts repeats the list at the crawler level.

Summary

Briefly describe the change. This section is required for CI PR validation.
If this is a contract upgrade, include:

  • Which contract is being upgraded
  • Why the upgrade is needed
  • Link to any discussion/issues

Linked issue (recommended)

Example: Closes #123

Changes

This section is required for CI PR validation.

Contract Upgrade Details (if applicable)

This section is required for CI PR validation if this is a contract upgrade.

Network

  • Testnet
  • Mainnet

New Contract

  • Contract ID: C...
  • WASM Hash: sha256:...
  • Deployer Public Key: G...
  • Deploy Transaction: [link to transaction explorer]

Upgrade Governance

  • Admin/upgrade authority is a multisig requiring maintainer sign-off
  • Maintainer has reviewed and approved the upgrade
  • Upgrade transaction is ready for maintainer signature (provide transaction XDR if applicable)

Verification Steps

  • New contract deployed successfully
  • All existing tests pass against the new contract
  • Manual testing checklist completed (describe what you tested)
  • No breaking changes for existing integrations (or list them)

How to test

This section is required for CI PR validation.

  • All automated tests pass
  • Integration tests pass (if applicable)
  • Manual testing completed (describe what you tested)

Security Considerations

This section is required for CI PR validation.

  • No secrets or sensitive data are logged
  • No changes to authentication/authorization logic without review
  • No changes to admin/upgrade logic without review

Screenshots (if UI)

Include before/after screenshots for any UI changes. For new features, show different states (loading, error, success). For responsive changes, include mobile/tablet/desktop views.

Checklist

This section is required for CI PR validation.

  • I linked an issue (or explained why one is not needed)
  • I tested locally
  • I did not commit secrets
  • I updated docs if needed
  • Code follows the project's style guidelines
  • CI checks pass
  • If UI changes: I included before/after screenshots
  • If images added/changed: I verified they are optimized and accessible

Closes #1440
Closes #1441

Closes Shelterflex#1440, closes Shelterflex#1441.

Async state handling (Shelterflex#1440)
----------------------------
Surfaces migrated from mock data to live APIs each grew their own
loading, empty, and error treatment. Adds shared primitives in
components/ui/data-state.tsx — LoadingState/LoadingAnnouncer, ErrorState,
EmptyState, MoneyValue, plus StatCardSkeleton/ListRowSkeleton — and
migrates the dashboards and every money-displaying surface onto them.

MoneyValue is the load-bearing piece: a balance rendered from `?? 0` is
indistinguishable from a real zero, so it renders a skeleton while
loading and an explicit dash when the amount is unknown, and only ever
formats a number it was actually given. Inspector earnings and admin
analytics both rendered ₦0 on a failed fetch; they now dash out.

Error states take a required onRetry and re-run the failed fetch instead
of reloading the page. Empty states carry the action that would populate
them. Loading is announced via a polite live region, skeletons are
aria-hidden, and placeholders mirror the real content's dimensions.

lib/__tests__/no-money-fallbacks.test.ts scans the source tree and fails
if either a money-from-fallback or a reload-based retry reappears.

Page metadata (Shelterflex#1441)
---------------------
Only 10 of 84 routes had metadata, with no metadataBase, canonical URLs,
or robots exclusions. Adds lib/seo.ts, site-wide defaults with a title
template, and per-route metadata for the priority public routes.

Property detail pages now build a per-listing title, a description from
the listing's own copy, and an absolute OpenGraph image from its first
photo, plus Residence JSON-LD carrying the rent as an Offer. The
homepage, /properties, and /landlords were client components and so
could not export metadata; each is now a server route file rendering a
client component.

Private and token-addressed routes are excluded by segment layout, with
noarchive/nosnippet alongside noindex — for the rating-card routes the
token is the only access control, so a cached copy or search snippet
would outlive its revocation. app/robots.ts repeats the list at the
crawler level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

@nonso7 is attempting to deploy a commit to the pope-h's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 31, 2026

Copy link
Copy Markdown

@nonso7 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

nonso7 and others added 3 commits July 31, 2026 10:59
…nc-states-and-page-metadata

# Conflicts:
#	frontend/app/dashboard/tenant/page.tsx
#	frontend/app/dashboard/user/page.tsx
c016cbf resolved the same main-merge via the GitHub UI and produced two
build-breaking defects that failed the Frontend (lint, build) job:

  app/dashboard/user/page.tsx   Parsing error: Expected corresponding JSX
                                closing tag for 'LoadingState' — the
                                resolution kept both sides, duplicating the
                                walletError branch and orphaning the tag.
  app/dashboard/tenant/page.tsx 'Loader2' is not defined — the resolution
                                restored a <Loader2> usage whose import this
                                branch had removed.

Both merges share the same parents, so this keeps the resolution in
1c4f7f8 and discards nothing from main.
@pope-h
pope-h merged commit b3d6465 into Shelterflex:main Jul 31, 2026
3 of 4 checks passed
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: Page metadata and social sharing previews are incomplete Frontend: Loading and empty state treatment is inconsistent across the app

2 participants