Skip to content

fix: stop stripping objectName prefix from record detail URL IDs#935

Merged
hotlong merged 3 commits intomainfrom
copilot/fix-detail-page-error
Mar 1, 2026
Merged

fix: stop stripping objectName prefix from record detail URL IDs#935
hotlong merged 3 commits intomainfrom
copilot/fix-detail-page-error

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 1, 2026

Navigating from a list page to a record detail page shows "Record not found" because RecordDetailView strips the objectName- prefix from the URL recordId before calling findOne. The navigation code puts record._id directly into the URL — no prefix is added artificially — so stripping it truncates backend-generated IDs like opportunity-1772370455623-11772370455623-1, which doesn't match.

Changes

  • RecordDetailView.tsx — Remove prefix stripping; pass URL recordId through as-is
  • DetailView.tsx — Add ID format fallback: if findOne returns null, retry with the alternate format (strip or prepend objectName- prefix) for backward compatibility with either ID convention
  • RecordDetailEdit.test.tsx — Update expectations to assert findOne receives the full URL recordId

Before/After

URL: /apps/crm_app/opportunity/record/opportunity-1772370455623-1

Before: findOne("opportunity", "1772370455623-1")  → null → "Record not found"
After:  findOne("opportunity", "opportunity-1772370455623-1") → record ✓

The DetailView fallback ensures both ID formats resolve correctly regardless of backend convention:

dataSource.findOne(objectName, resourceId).then((result) => {
  if (result) { setData(result); return; }
  // Fallback: try alternate ID format
  const altId = resourceId.startsWith(prefix)
    ? resourceId.slice(prefix.length)
    : `${prefix}${resourceId}`;
  return dataSource.findOne(objectName, altId)...
});
Original prompt

你是顶牛的低代码企业管理软件设计专家,列表页点击进入记录,详情页报错,请完整的评估,目前的路由及处理流程是否合理?

https://demo.objectui.org/apps/crm_app/opportunity/record/opportunity-1772370455623-1

Record not found
The record you are looking for does not exist or may have been deleted.
Go back


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Canceled Canceled Mar 1, 2026 1:30pm
objectui-demo Ready Ready Preview, Comment Mar 1, 2026 1:30pm
objectui-storybook Ready Ready Preview, Comment Mar 1, 2026 1:30pm

Request Review

Copilot AI and others added 2 commits March 1, 2026 13:24
…ilView

The navigation code passes record._id directly into the URL without adding
any prefix. Stripping the prefix incorrectly truncated backend-generated IDs
(e.g. "opportunity-1772370455623-1" → "1772370455623-1"), causing findOne
to fail with "Record not found".

Also adds robust fallback in DetailView: if findOne returns null, retry with
the prefix stripped or prepended for maximum backward compatibility.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…ling

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix record not found error on detail page fix: stop stripping objectName prefix from record detail URL IDs Mar 1, 2026
@hotlong hotlong marked this pull request as ready for review March 1, 2026 13:42
Copilot AI review requested due to automatic review settings March 1, 2026 13:42
@hotlong hotlong merged commit 3e4a0d8 into main Mar 1, 2026
5 checks passed
Copy link
Copy Markdown
Contributor

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

Fixes record detail navigation failures caused by stripping the objectName- prefix from URL record IDs, while keeping compatibility with mixed ID conventions across deployments.

Changes:

  • Stop stripping the objectName- prefix from recordId in the console’s RecordDetailView.
  • Add a fallback retry in DetailView to resolve records using either prefixed or unprefixed ID formats.
  • Update console tests to assert findOne receives the full URL recordId.

Reviewed changes

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

File Description
packages/plugin-detail/src/DetailView.tsx Adds findOne fallback retry with alternate ID format when the initial lookup returns null.
apps/console/src/components/RecordDetailView.tsx Stops stripping objectName- from the URL recordId before passing it downstream.
apps/console/src/tests/RecordDetailEdit.test.tsx Updates expectations to match the new “pass-through” recordId behavior.
Comments suppressed due to low confidence (1)

packages/plugin-detail/src/DetailView.tsx:112

  • If the initial findOne call rejects, the catch handler only sets loading to false and leaves data unchanged. If the component previously showed another record, this can incorrectly keep rendering the old record after a navigation/fetch failure. Consider clearing data (e.g., set it to null) in this error path so the UI can transition to the not-found/error state consistently.
      }).catch((err) => {
         console.error('Failed to fetch detail data:', err);
         setLoading(false);
      });

Comment on lines +98 to +104
// Fallback: try alternate ID format for backward compatibility
const altId = resourceId.startsWith(prefix)
? resourceId.slice(prefix.length) // strip prefix
: `${prefix}${resourceId}`; // prepend prefix
return dataSource.findOne(objectName, altId).then((fallbackResult) => {
setData(fallbackResult);
setLoading(false);
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

The new alternate-ID retry logic isn’t covered by the existing DetailView tests (there are tests for the null/not-found state, but none assert that a second findOne call is attempted with the computed alternate ID). Adding a focused test for the retry behavior would help prevent regressions across the supported ID conventions.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to 41
// Use the URL recordId as-is — it contains the actual record _id.
// Navigation code passes `record._id || record.id` directly into the URL
// without adding any prefix, so no stripping is needed.
const pureRecordId = recordId;

Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

pureRecordId is no longer “pure”/stripped (it’s now the raw recordId). Renaming this to something like resourceId/urlRecordId would reduce confusion for future readers since it’s used for presence/comments thread IDs, DetailView.resourceId, and the onEdit payload.

Copilot uses AI. Check for mistakes.
Comment on lines +37 to 41
// Use the URL recordId as-is — it contains the actual record _id.
// Navigation code passes `record._id || record.id` directly into the URL
// without adding any prefix, so no stripping is needed.
const pureRecordId = recordId;

Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

With pureRecordId = recordId and the new DetailView fallback that may resolve a record using an alternate ID format, downstream uses of pureRecordId (presence recordId filter, comment threadId, and the onEdit payload) can become inconsistent with the ID that actually exists in the backend (e.g., when the URL uses a prefixed ID but the backend record ID is unprefixed, or vice versa). Consider deriving these downstream identifiers from the resolved record (or querying both threadId/recordId variants for backward compatibility) so edit/comments/presence stay aligned with whichever ID format was found.

Copilot uses AI. Check for mistakes.
Comment on lines 86 to +90
if (dataSource && schema.objectName && schema.resourceId) {
setLoading(true);
dataSource.findOne(schema.objectName, schema.resourceId).then((result) => {
setData(result);
setLoading(false);
const objectName = schema.objectName;
const resourceId = schema.resourceId;
const prefix = `${objectName}-`;
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

This effect now reads schema.objectName, schema.resourceId, and dataSource (captured into locals here), but the effect’s dependency array doesn’t include schema.objectName or dataSource. If those values change while schema.resourceId stays the same, the fetch won’t rerun and the view can show stale data. Consider updating the effect dependencies (or restructuring the fetch) so it re-triggers whenever the inputs used for findOne change.

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +101
const resourceId = schema.resourceId;
const prefix = `${objectName}-`;

dataSource.findOne(objectName, resourceId).then((result) => {
if (result) {
setData(result);
setLoading(false);
return;
}
// Fallback: try alternate ID format for backward compatibility
const altId = resourceId.startsWith(prefix)
? resourceId.slice(prefix.length) // strip prefix
: `${prefix}${resourceId}`; // prepend prefix
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

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

schema.resourceId is typed as string | number in DetailViewSchema, but this fallback logic calls resourceId.startsWith(prefix), which will throw if resourceId is a number. Consider normalizing resourceId to a string (or guarding by type) before using string methods and constructing altId.

Copilot uses AI. Check for mistakes.
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.

3 participants