Skip to content

eng-231#368

Merged
Connorbelez merged 3 commits intomainfrom
eng-231
Apr 3, 2026
Merged

eng-231#368
Connorbelez merged 3 commits intomainfrom
eng-231

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Apr 2, 2026

eng-228_eng-229_eng-230

feat(admin): build shell navigation and route states

Add provider-backed admin record sidebar

  • Move admin detail state into a shared sidebar context
  • Render a richer record detail surface with tabs, fields, and navigation
  • Add entity registry support for properties and object-def lookup

Summary by CodeRabbit

  • New Features

    • Added a tabbed record detail sidebar with Details, Relations, Notes, Files, and History.
    • Improved field rendering: formatted dates, numbers, booleans, arrays, and object previews.
    • Added in-app sidebar navigation with stack-based open/push/replace/back controls.
  • Refactor

    • Moved detail UI into a dedicated surface and sidebar provider; routes now render the table separately.
    • Consolidated admin entity lookup and routing resolution.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Connorbelez, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Warning

Rate limit exceeded

@Connorbelez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 34 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 13 minutes and 34 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7992fc7-2ef6-4e01-b9fa-b433ee4b7b84

📥 Commits

Reviewing files that changed from the base of the PR and between 001f05d and 1ac9b28.

📒 Files selected for processing (7)
  • specs/ENG-228/chunks/chunk-01-shell-foundation/context.md
  • src/components/admin/shell/AdminRouteStates.tsx
  • src/components/admin/shell/EntityTable.tsx
  • src/components/admin/shell/nav-user.tsx
  • src/hooks/useAdminDetailSheet.tsx
  • src/lib/admin-detail-route-state.ts
  • src/test/admin/useAdminDetailSheet.test.ts
📝 Walkthrough

Walkthrough

Refactors admin detail UI from URL/search-param-driven sheets to a provider-backed sidebar. Adds RecordSidebar, RecordSidebarProvider, FieldRenderer, and entity registry lookups; migrates useAdminDetailSheet to context; simplifies AdminDetailSheet and delegates page rendering to AdminRecordDetailSurface. Routes are wired to the new provider.

Changes

Cohort / File(s) Summary
Sidebar state & provider
src/components/admin/shell/RecordSidebarProvider.tsx, src/hooks/useAdminDetailSheet.tsx
Add context-backed sidebar state with stack navigation (open, push, replace, back, close); migrate useAdminDetailSheet from URL search params to useRecordSidebar and change setSearch API.
Sidebar UI & surface
src/components/admin/shell/RecordSidebar.tsx, src/components/admin/shell/AdminDetailSheet.tsx, src/components/admin/shell/AdminRecordDetailPage.tsx
New RecordSidebar and AdminRecordDetailSurface implement a tabbed detail sidebar; AdminDetailSheet reduced to a thin wrapper; AdminRecordDetailPage delegates to the surface.
Field rendering
src/components/admin/shell/FieldRenderer.tsx
Add FieldRenderer component and helpers to format and display scalar/array/boolean/date/object values and show "No value"/"No values" states.
Entity registry
src/components/admin/shell/entity-registry.ts
Add properties admin entity, export ADMIN_ENTITIES_BY_TABLE_NAME, getAdminEntityByTableName, and getAdminEntityForObjectDef resolution helpers.
Routes integration
src/routes/admin/route.tsx, src/routes/admin/$entitytype.tsx, src/routes/admin/listings/route.tsx, src/routes/admin/mortgages/route.tsx, src/routes/admin/properties/route.tsx
Wrap admin routes with RecordSidebarProvider, remove per-route inline AdminDetailSheet usage, and render the sidebar via root admin route.
Misc / wiring
src/components/admin/shell/...
Various helpers and internal adapters added to support title/status resolution, placeholder record detection, and tab availability (Notes/Files/History).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Route as Admin Route
    participant Provider as RecordSidebarProvider
    participant Sidebar as RecordSidebar
    participant Surface as AdminRecordDetailSurface
    participant Query as Data Layer (useQuery)

    User->>Route: Render admin page
    Route->>Provider: Mount within RecordSidebarProvider
    Provider->>Sidebar: Provide context (isOpen=false)

    User->>Sidebar: Click record row -> open(recordRef)
    Sidebar->>Provider: open({ entityType, recordId })
    Provider->>Provider: Update state (isOpen=true, stack=[ref])
    Provider->>Sidebar: current updated

    Sidebar->>Surface: Render AdminRecordDetailSurface(reference)
    Surface->>Query: Fetch objectDef, fieldDefs, recordReference
    Query-->>Surface: Return data
    Surface->>Sidebar: Render header + tabs (Details, Relations, Notes, Files, History)

    User->>Sidebar: Click close or back
    Sidebar->>Provider: close() or back()
    Provider->>Sidebar: Update state (isOpen false or stack popped)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 In the warren a sidebar pops open wide,
Context holds the stack where records hide.
Badges and fields in tidy rows hop,
Back or close — the rabbit won't stop.
Hooray for neat admin trails inside! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'eng-231' is a ticket identifier with no descriptive context about the actual changes. Use a descriptive title that summarizes the main change, e.g., 'feat(admin): build shell navigation and record detail surface' to clearly communicate the purpose of this changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eng-231

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Connorbelez Connorbelez changed the title eng-228_eng-229_eng-230 eng-231 Apr 2, 2026
@Connorbelez Connorbelez marked this pull request as ready for review April 2, 2026 15:18
Copilot AI review requested due to automatic review settings April 2, 2026 15:18
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Builds out the admin shell foundation (navigation + layout + route states) and introduces a provider-backed record detail sidebar surface, aligning /admin routes with the new shell patterns.

Changes:

  • Add a typed admin entity registry and use it to render grouped sidebar navigation + breadcrumbs.
  • Introduce a provider-backed record sidebar/detail surface (sheet + full-page record detail routes).
  • Add route-level pending/error states and admin-route detection helpers (e.g. header suppression, active-route matching).

Reviewed changes

Copilot reviewed 46 out of 48 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/test/admin/admin-shell.test.ts Adds tests for admin route helper behaviors (active matching + pathname detection).
src/routeTree.gen.ts Regenerates TanStack Router route tree to include new admin entity/detail routes.
src/routes/admin/underwriting/route.tsx Adds admin route pending/error states and updates permission gating.
src/routes/admin/route.tsx Wraps admin routes with the new layout + record sidebar provider, and keeps typed search validation.
src/routes/admin/properties/route.tsx Adds properties list route with table shell + nested record route outlet behavior.
src/routes/admin/properties/$recordid.tsx Adds properties record detail page route.
src/routes/admin/mortgages/route.tsx Adds mortgages list route with table shell + nested record route outlet behavior.
src/routes/admin/mortgages/$recordid.tsx Adds mortgages record detail page route.
src/routes/admin/listings/route.tsx Adds listings list route with table shell + nested record route outlet behavior.
src/routes/admin/listings/$recordid.tsx Adds listings record detail page route.
src/routes/admin/deals/route.tsx Updates deals route to support nested record detail routes + admin pending/error states.
src/routes/admin/deals/$recordid.tsx Adds deals record detail page route.
src/routes/admin/$entitytype.tsx Adds generic entity list route with entity-type validation via registry.
src/routes/admin/$entitytype.$recordid.tsx Adds generic entity record detail route with entity-type validation via registry.
src/routes/__root.tsx Suppresses the global header on admin routes.
src/lib/admin-routes.ts Adds isAdminPathname helper for admin route detection.
src/lib/admin-detail-search.ts Adds typed parsing for admin detail-sheet search params.
src/hooks/useAdminDetailSheet.tsx Introduces a compatibility hook that now drives the record sidebar via context.
src/components/workos-user.tsx Enables view transitions for sign-in/sign-up links.
src/components/header.tsx Enables view transitions for header navigation links.
src/components/admin/shell/team-switcher.tsx Adds admin sidebar team switcher UI.
src/components/admin/shell/RecordSidebarProvider.tsx Adds context provider managing record sidebar open state + navigation stack.
src/components/admin/shell/RecordSidebar.tsx Implements the record detail sheet surface (tabs, relations, linked navigation).
src/components/admin/shell/nav-user.tsx Adds admin sidebar user nav UI (unused by the new layout).
src/components/admin/shell/nav-projects.tsx Adds admin sidebar “projects” nav UI (unused by the new layout).
src/components/admin/shell/nav-main.tsx Adds admin sidebar “platform” nav UI (unused by the new layout).
src/components/admin/shell/FieldRenderer.tsx Adds a generic field renderer for record detail pages.
src/components/admin/shell/EntityTable.tsx Adds a generic entity table with loading/empty/error states and row click support.
src/components/admin/shell/entity-registry.ts Defines admin domains/entities and provides navigation + active-route helpers.
src/components/admin/shell/DashboardShell.tsx Adds a thin wrapper around AdminLayout (legacy compatibility).
src/components/admin/shell/AdminUserMenu.tsx Adds the admin sidebar user menu (auth-aware).
src/components/admin/shell/AdminRouteStates.tsx Adds shared admin route error boundary + skeleton states.
src/components/admin/shell/AdminRecordDetailPage.tsx Adds full-page wrapper around the record detail surface.
src/components/admin/shell/AdminNavigation.tsx Implements registry-driven admin sidebar navigation.
src/components/admin/shell/AdminLayout.tsx Introduces the canonical admin layout (sidebar + header + breadcrumb + content).
src/components/admin/shell/AdminDetailSheet.tsx Adds the admin record detail sheet mounted under /admin.
src/components/admin/shell/AdminBreadcrumbs.tsx Adds registry-aware admin breadcrumbs.
src/components/admin/shell/admin-spa-href.ts Adds helper to detect in-app admin SPA links.
specs/ENG-228/tasks.md Adds ENG-228 task checklist.
specs/ENG-228/chunks/manifest.md Adds chunk manifest for ENG-228.
specs/ENG-228/chunks/chunk-01-shell-foundation/tasks.md Adds per-chunk task checklist.
specs/ENG-228/chunks/chunk-01-shell-foundation/status.md Records status and tooling notes for the chunk.
specs/ENG-228/chunks/chunk-01-shell-foundation/context.md Adds implementation context/constraints for ENG-228.
package.json Updates lucide-react and adds zustand.
CLAUDE.md Updates repository guidance/context documentation.
bun.lock Updates lockfile for dependency changes.
.gitnexus/meta.json Removes local indexing metadata from the repo.
.gitnexus/lbug Adds an empty/placeholder .gitnexus artifact.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/routes/admin/underwriting/route.tsx
Comment thread src/lib/admin-detail-search.ts
Comment thread src/components/admin/shell/EntityTable.tsx Outdated
Comment thread src/components/admin/shell/EntityTable.tsx Outdated
Comment thread specs/ENG-228/chunks/chunk-01-shell-foundation/context.md Outdated
Copy link
Copy Markdown
Owner Author

@coderabbitai please re-review You didn't leave any comments

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

@Connorbelez: Apologies for missing the comments in the last review! I'll kick off a full review of PR #368 right away.


🧠 Learnings used
Learnt from: Connorbelez
Repo: Connorbelez/tanstackTemplate PR: 5
File: specs/governed-transitions/chunks/chunk-01-schema-machine/context.md:209-213
Timestamp: 2026-03-13T03:17:28.559Z
Learning: In the Connorbelez/tanstackTemplate repository, do not flag .md files for markdown formatting/linting issues (e.g., MD040 fenced code block language). Only reference .md files (specs, PRDs, design docs) when they reveal architectural drift or implementation inconsistencies against actual code.

Learnt from: CR
Repo: Connorbelez/tanstackTemplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T18:28:26.698Z
Learning: After completing a major unit of work, run `coderabbit review --plain` to get a code review summary and check for potential issues or improvements

Learnt from: CR
Repo: Connorbelez/tanstackTemplate PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-16T06:17:10.192Z
Learning: After completing a major unit of work, run `coderabbit review --plain` to get a code review summary and check for potential issues or improvements

Learnt from: CR
Repo: Connorbelez/tanstackTemplate PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-16T16:49:07.666Z
Learning: After completing a major unit of work, run `coderabbit review --plain` to get a code review summary and check for potential issues or improvements
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/routes/admin/route.tsx (1)

10-11: ⚠️ Potential issue | 🟡 Minor

Remove unused validateSearch from the admin route configuration.

The detail sheet state has been moved to RecordSidebarProvider context, and no component in the admin route tree accesses the validated search parameters. The useAdminDetailSheet hook is explicitly designed as a "backwards-compatible wrapper" that uses context-based state instead of URL search params. The validateSearch definition and the parseAdminDetailSearch import are dead code and should be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/admin/route.tsx` around lines 10 - 11, Remove the unused
validateSearch property and its parseAdminDetailSearch import from the admin
route configuration because detail sheet state is now managed by
RecordSidebarProvider and accessed via useAdminDetailSheet; locate the
validateSearch: (search: Record<string, unknown>) =>
parseAdminDetailSearch(search) entry in the admin route (route.tsx) and delete
that line, and also remove the corresponding parseAdminDetailSearch import at
the top of the file and any unused references to it so the dead code is
eliminated.
🧹 Nitpick comments (5)
src/components/admin/shell/FieldRenderer.tsx (1)

83-89: Consider parameterizing the locale for better i18n support.

The Intl.NumberFormat uses a hardcoded "en-US" locale. For better internationalization, consider accepting an optional locale parameter or using undefined to let the browser use the user's default locale.

♻️ Optional: Use browser default locale
 if (typeof value === "number") {
   return Number.isInteger(value)
-    ? new Intl.NumberFormat("en-US").format(value)
-    : new Intl.NumberFormat("en-US", {
+    ? new Intl.NumberFormat().format(value)
+    : new Intl.NumberFormat(undefined, {
         maximumFractionDigits: 2,
       }).format(value);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/FieldRenderer.tsx` around lines 83 - 89, The
number formatting in FieldRenderer.tsx currently hardcodes "en-US" (the branch
that checks typeof value === "number"); change the logic to accept an optional
locale parameter (e.g., locale?: string) or default to undefined so the runtime
uses the user's locale, and pass that variable into both Intl.NumberFormat calls
instead of "en-US"; update the function/component signature that renders the
field (where value is handled) to accept and forward this locale argument and
ensure callers propagate or omit it for browser defaults.
src/hooks/useAdminDetailSheet.tsx (2)

36-44: Silent no-op when routeEntityType is unavailable may obscure bugs.

When getAdminEntityByPathname returns undefined (e.g., user is on an unrecognized admin path), open(recordId) silently does nothing. Callers like AdminEntityActionsCell (entity-table-columns.tsx) won't receive feedback that the action failed.

Consider at minimum a console.warn in development, or returning a boolean success indicator so callers can react appropriately.

💡 Optional: Add development warning for missing entity type
 open: (recordId) => {
   if (!routeEntityType) {
+    if (process.env.NODE_ENV === "development") {
+      console.warn(
+        "[useAdminDetailSheet] open() called but no routeEntityType resolved from pathname:",
+        pathname
+      );
+    }
     return;
   }

   open({
     entityType: routeEntityType,
     recordId,
   });
 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useAdminDetailSheet.tsx` around lines 36 - 44, The open(recordId)
path in useAdminDetailSheet.tsx currently silently returns when routeEntityType
is falsy; update the open function (the exported open in
useAdminDetailSheet.tsx) to either (a) emit a development-only warning (e.g.,
console.warn) mentioning getAdminEntityByPathname returned undefined and the
recordId, and/or (b) change its signature to return a boolean success value so
callers (like AdminEntityActionsCell in entity-table-columns.tsx) can react to
failure; ensure you update callers to handle the boolean if you choose that
approach and keep the warning conditional on non-production environments.

46-64: setSearch has multiple silent early-returns.

Lines 52–54 and 57–59 silently return when recordId or resolvedEntityType is missing. While this may be intentional for backwards compatibility, it can make debugging difficult when callers expect the sidebar to open but nothing happens.

If these are expected edge cases, the JSDoc comment could document them. Otherwise, consider adding development-only warnings similar to open().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useAdminDetailSheet.tsx` around lines 46 - 64, The setSearch
handler in useAdminDetailSheet.tsx currently performs silent early returns when
recordId or resolvedEntityType is missing (inside setSearch: ({ detailOpen,
entityType: nextEntityType, recordId }) => { ... }), which hampers debugging;
either add development-only warnings/log calls (using the same mechanism used in
open()) right before the early returns for missing recordId and missing
resolvedEntityType to surface unexpected calls, or update the JSDoc for
setSearch to explicitly document these allowed no-op edge cases so callers know
the behavior—apply the chosen change near the early-return checks in setSearch
and keep behavior otherwise the same.
src/components/admin/shell/RecordSidebar.tsx (2)

282-296: Linked record navigation may fail if objectDefs hasn't loaded yet.

The onNavigate callback accesses objectDefs from the closure, but if the query is still pending (objectDefs === undefined), the entityType will resolve to undefined. This could cause the sidebar to open with incomplete reference data.

Consider guarding this or using optional chaining with a fallback:

💡 Safer navigation when objectDefs is loading
 onNavigate={(recordId, linkedRecordKind, linkedObjectDefId) => {
+  const linkedObjectDef = objectDefs?.find(
+    (candidate) => String(candidate._id) === linkedObjectDefId
+  );
   push({
-    entityType:
-      objectDefs
-        ? getAdminEntityForObjectDef(
-            objectDefs.find(
-              (candidate) =>
-                String(candidate._id) === linkedObjectDefId
-            ) ?? {}
-          )?.entityType
-        : undefined,
+    entityType: linkedObjectDef
+      ? getAdminEntityForObjectDef(linkedObjectDef)?.entityType
+      : undefined,
     objectDefId: linkedObjectDefId,
     recordId,
     recordKind: linkedRecordKind,
   });
 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/RecordSidebar.tsx` around lines 282 - 296, The
onNavigate callback in RecordSidebar.tsx currently reads objectDefs from the
closure and may pass entityType: undefined to push when objectDefs is still
loading; update the onNavigate handler (the arrow passed to onNavigate that
calls push and uses getAdminEntityForObjectDef) to guard against objectDefs
being undefined by either early-returning (do nothing) until objectDefs is
available, or compute entityType with optional chaining and a safe fallback
(e.g., the current record/entityType) before calling push; ensure you reference
the getAdminEntityForObjectDef(objectDefs.find(...)) lookup and the push({
entityType, objectDefId: linkedObjectDefId, recordId, recordKind:
linkedRecordKind }) call when making the change.

592-594: Consider explicit date formatting options for consistency.

toLocaleString() without options relies on browser locale and timezone, which can vary across admin users. For audit/history purposes, consider specifying explicit options or using a shared formatter:

💡 Example with explicit options
 function formatTimestamp(timestamp: number) {
-  return new Date(timestamp).toLocaleString();
+  return new Date(timestamp).toLocaleString(undefined, {
+    dateStyle: "medium",
+    timeStyle: "short",
+  });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/RecordSidebar.tsx` around lines 592 - 594, The
formatTimestamp function uses toLocaleString() with no options which causes
inconsistent date/time across browsers; update formatTimestamp to use an
explicit formatter (e.g., Intl.DateTimeFormat with a chosen locale and options
or format in UTC) so timestamps are consistent for audit/history—modify the
function formatTimestamp(timestamp: number) to pass explicit DateTimeFormat
options (or use a shared formatter util) that define timezone,
dateStyle/timeStyle or specific components, and ensure all callers use that
single formatter for uniform output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/routes/admin/route.tsx`:
- Around line 10-11: Remove the unused validateSearch property and its
parseAdminDetailSearch import from the admin route configuration because detail
sheet state is now managed by RecordSidebarProvider and accessed via
useAdminDetailSheet; locate the validateSearch: (search: Record<string,
unknown>) => parseAdminDetailSearch(search) entry in the admin route (route.tsx)
and delete that line, and also remove the corresponding parseAdminDetailSearch
import at the top of the file and any unused references to it so the dead code
is eliminated.

---

Nitpick comments:
In `@src/components/admin/shell/FieldRenderer.tsx`:
- Around line 83-89: The number formatting in FieldRenderer.tsx currently
hardcodes "en-US" (the branch that checks typeof value === "number"); change the
logic to accept an optional locale parameter (e.g., locale?: string) or default
to undefined so the runtime uses the user's locale, and pass that variable into
both Intl.NumberFormat calls instead of "en-US"; update the function/component
signature that renders the field (where value is handled) to accept and forward
this locale argument and ensure callers propagate or omit it for browser
defaults.

In `@src/components/admin/shell/RecordSidebar.tsx`:
- Around line 282-296: The onNavigate callback in RecordSidebar.tsx currently
reads objectDefs from the closure and may pass entityType: undefined to push
when objectDefs is still loading; update the onNavigate handler (the arrow
passed to onNavigate that calls push and uses getAdminEntityForObjectDef) to
guard against objectDefs being undefined by either early-returning (do nothing)
until objectDefs is available, or compute entityType with optional chaining and
a safe fallback (e.g., the current record/entityType) before calling push;
ensure you reference the getAdminEntityForObjectDef(objectDefs.find(...)) lookup
and the push({ entityType, objectDefId: linkedObjectDefId, recordId, recordKind:
linkedRecordKind }) call when making the change.
- Around line 592-594: The formatTimestamp function uses toLocaleString() with
no options which causes inconsistent date/time across browsers; update
formatTimestamp to use an explicit formatter (e.g., Intl.DateTimeFormat with a
chosen locale and options or format in UTC) so timestamps are consistent for
audit/history—modify the function formatTimestamp(timestamp: number) to pass
explicit DateTimeFormat options (or use a shared formatter util) that define
timezone, dateStyle/timeStyle or specific components, and ensure all callers use
that single formatter for uniform output.

In `@src/hooks/useAdminDetailSheet.tsx`:
- Around line 36-44: The open(recordId) path in useAdminDetailSheet.tsx
currently silently returns when routeEntityType is falsy; update the open
function (the exported open in useAdminDetailSheet.tsx) to either (a) emit a
development-only warning (e.g., console.warn) mentioning
getAdminEntityByPathname returned undefined and the recordId, and/or (b) change
its signature to return a boolean success value so callers (like
AdminEntityActionsCell in entity-table-columns.tsx) can react to failure; ensure
you update callers to handle the boolean if you choose that approach and keep
the warning conditional on non-production environments.
- Around line 46-64: The setSearch handler in useAdminDetailSheet.tsx currently
performs silent early returns when recordId or resolvedEntityType is missing
(inside setSearch: ({ detailOpen, entityType: nextEntityType, recordId }) => {
... }), which hampers debugging; either add development-only warnings/log calls
(using the same mechanism used in open()) right before the early returns for
missing recordId and missing resolvedEntityType to surface unexpected calls, or
update the JSDoc for setSearch to explicitly document these allowed no-op edge
cases so callers know the behavior—apply the chosen change near the early-return
checks in setSearch and keep behavior otherwise the same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c59f5820-6f07-4a5e-8957-d68a7b9b1238

📥 Commits

Reviewing files that changed from the base of the PR and between c62e8bf and cb676cd.

📒 Files selected for processing (12)
  • src/components/admin/shell/AdminDetailSheet.tsx
  • src/components/admin/shell/AdminRecordDetailPage.tsx
  • src/components/admin/shell/FieldRenderer.tsx
  • src/components/admin/shell/RecordSidebar.tsx
  • src/components/admin/shell/RecordSidebarProvider.tsx
  • src/components/admin/shell/entity-registry.ts
  • src/hooks/useAdminDetailSheet.tsx
  • src/routes/admin/$entitytype.tsx
  • src/routes/admin/listings/route.tsx
  • src/routes/admin/mortgages/route.tsx
  • src/routes/admin/properties/route.tsx
  • src/routes/admin/route.tsx

- Move admin detail state into a shared sidebar context
- Render a richer record detail surface with tabs, fields, and navigation
- Add entity registry support for properties and object-def lookup
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/components/admin/shell/FieldRenderer.tsx (2)

44-48: Array key includes index, but consider edge cases with duplicate items.

The key ${String(item)}-${index} handles duplicates correctly by including the index. However, if item is an object or complex type, String(item) yields [object Object], making keys like [object Object]-0, [object Object]-1. This works but may cause React reconciliation issues if items are reordered.

Since this is a display-only component with no reordering, this is acceptable. Just noting for awareness.

♻️ Alternative: Use index alone for simpler keys
-					<Badge key={`${String(item)}-${index}`} variant="secondary">
+					<Badge key={index} variant="secondary">

Since arrays displayed here are read-only snapshots with no reordering, a simple index key is sufficient and avoids the String(item) edge case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/FieldRenderer.tsx` around lines 44 - 48, The array
item keys use `${String(item)}-${index}` in the value.map rendering which can
produce `[object Object]-<index>` for non-primitive items; since this is a
read-only snapshot with no reordering, replace the composite key with index-only
to avoid misleading String(item) results—update the map callback that renders
<Badge> (the value.map(...) that calls formatScalarValue(item, fieldType)) to
use index as the key.

77-89: Minor locale inconsistency between date and number formatting.

Dates use toLocaleString() (browser locale) while numbers explicitly use "en-US". For admin UI consistency, consider either:

  • Using a fixed locale for dates too, or
  • Using the browser locale for both

This is a minor consistency note and acceptable as-is for an internal admin surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/FieldRenderer.tsx` around lines 77 - 89, The date
formatting uses date.toLocaleString() (browser locale) while number formatting
hardcodes "en-US"; update FieldRenderer.tsx to use a consistent locale for both
by introducing a single locale variable (e.g., derive from navigator.language or
a fixed "en-US") and use it for the date and number formatting paths—replace
date.toLocaleString() with Intl.DateTimeFormat(locale).format(date) (or pass
locale into toLocaleString) and replace the hardcoded "en-US" in the
Intl.NumberFormat constructors with the same locale variable so both formats
follow the same locale.
src/components/admin/shell/AdminDetailSheet.tsx (1)

5-6: AdminDetailSheet is a thin wrapper that adds no value; consider removing it in favor of direct RecordSidebar usage.

AdminDetailSheet (lines 5-6) simply renders <RecordSidebar /> without any configuration. Since RecordSidebar already accepts an optional adapters prop and is used directly elsewhere in the codebase without adapters (e.g., src/routes/demo/crm/route.tsx:116), the wrapper component can be removed. Update src/routes/admin/route.tsx:31 to render <RecordSidebar /> directly instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/shell/AdminDetailSheet.tsx` around lines 5 - 6, Remove
the useless wrapper component AdminDetailSheet and replace its usages with
RecordSidebar directly: delete the AdminDetailSheet function (the export named
AdminDetailSheet) and update any imports/usages (e.g., where AdminDetailSheet is
rendered in the admin route) to import and render RecordSidebar instead; ensure
you remove the now-unused AdminDetailSheet export/import and keep
RecordSidebar's existing props (adapters) unchanged so behavior remains
identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/admin/shell/AdminDetailSheet.tsx`:
- Around line 5-6: Remove the useless wrapper component AdminDetailSheet and
replace its usages with RecordSidebar directly: delete the AdminDetailSheet
function (the export named AdminDetailSheet) and update any imports/usages
(e.g., where AdminDetailSheet is rendered in the admin route) to import and
render RecordSidebar instead; ensure you remove the now-unused AdminDetailSheet
export/import and keep RecordSidebar's existing props (adapters) unchanged so
behavior remains identical.

In `@src/components/admin/shell/FieldRenderer.tsx`:
- Around line 44-48: The array item keys use `${String(item)}-${index}` in the
value.map rendering which can produce `[object Object]-<index>` for
non-primitive items; since this is a read-only snapshot with no reordering,
replace the composite key with index-only to avoid misleading String(item)
results—update the map callback that renders <Badge> (the value.map(...) that
calls formatScalarValue(item, fieldType)) to use index as the key.
- Around line 77-89: The date formatting uses date.toLocaleString() (browser
locale) while number formatting hardcodes "en-US"; update FieldRenderer.tsx to
use a consistent locale for both by introducing a single locale variable (e.g.,
derive from navigator.language or a fixed "en-US") and use it for the date and
number formatting paths—replace date.toLocaleString() with
Intl.DateTimeFormat(locale).format(date) (or pass locale into toLocaleString)
and replace the hardcoded "en-US" in the Intl.NumberFormat constructors with the
same locale variable so both formats follow the same locale.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80d43c33-6176-451d-8269-5340993e4e88

📥 Commits

Reviewing files that changed from the base of the PR and between cb676cd and 001f05d.

📒 Files selected for processing (12)
  • src/components/admin/shell/AdminDetailSheet.tsx
  • src/components/admin/shell/AdminRecordDetailPage.tsx
  • src/components/admin/shell/FieldRenderer.tsx
  • src/components/admin/shell/RecordSidebar.tsx
  • src/components/admin/shell/RecordSidebarProvider.tsx
  • src/components/admin/shell/entity-registry.ts
  • src/hooks/useAdminDetailSheet.tsx
  • src/routes/admin/$entitytype.tsx
  • src/routes/admin/listings/route.tsx
  • src/routes/admin/mortgages/route.tsx
  • src/routes/admin/properties/route.tsx
  • src/routes/admin/route.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/routes/admin/properties/route.tsx
  • src/routes/admin/listings/route.tsx
  • src/routes/admin/mortgages/route.tsx
  • src/components/admin/shell/AdminRecordDetailPage.tsx
  • src/components/admin/shell/RecordSidebarProvider.tsx
  • src/components/admin/shell/entity-registry.ts
  • src/hooks/useAdminDetailSheet.tsx
  • src/routes/admin/route.tsx

@Connorbelez Connorbelez merged commit cc2c9c0 into main Apr 3, 2026
1 of 3 checks passed
Connorbelez added a commit that referenced this pull request Apr 20, 2026
eng-228_eng-229_eng-230

feat(admin): build shell navigation and route states

Add provider-backed admin record sidebar

- Move admin detail state into a shared sidebar context
- Render a richer record detail surface with tabs, fields, and navigation
- Add entity registry support for properties and object-def lookup

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added a tabbed record detail sidebar with Details, Relations, Notes, Files, and History.
  * Improved field rendering: formatted dates, numbers, booleans, arrays, and object previews.
  * Added in-app sidebar navigation with stack-based open/push/replace/back controls.

* **Refactor**
  * Moved detail UI into a dedicated surface and sidebar provider; routes now render the table separately.
  * Consolidated admin entity lookup and routing resolution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

2 participants