fix(record-pages): keep the lead's and case's empty to-fill sections on screen - #2005
Merged
objectstack-fleet[bot] merged 2 commits intoOct 3, 2026
Merged
Conversation
…on screen An all-empty `record:details` section renders nothing by the platform default (objectui#8603 ruling A), so on a fresh lead the Details tab dropped the contact numbers, the address, and the revenue, headcount, description and notes a rep fills in; on a fresh case it dropped the origin channel an agent picks. `hideEmpty: false` on the lead's `contact_info`, `address` and `additional` group references and on the case's `origin` keeps them as labelled empty rows to fill in. Derived and system-stamped groups (lead `duplicates`, case `sla` and `resolution`) stay hidden while empty; lead `assignment` derives an empty list once the strip takes `owner_id`, so the key would do nothing there. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
…ions Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2003
Clause-②: no
What changes
Four
{ group }sections now carryhideEmpty: false:src/sales/pages/lead_detail.page.ts:contact_info,address,additional.src/service/pages/case_detail.page.ts:origin.Nothing else in either page changes, except the comment beside the sections that records why. No
fieldGroupschange.The platform default is objectui#8603 ruling A: a section whose fields are all empty renders nothing, with no heading and no skeleton. On a fresh lead that carries only its required fields, that default removed 5 of the 10 groups. On a fresh case it removed 3 of 6. This PR applies the #1211 / PR #1991 reasoning, as the maintainer ruled it (option A, 「决裁的三张同意」). Groups whose members a user is expected to fill stay on screen as labelled empty rows. Derived and system groups stay hidden while empty.
Before / after: fresh records, measured in Chromium on 17.6.0
Setup: every reading comes from a new
objectstack dev -p 4821 --no-watch --fresh --uiboot with its own freshOS_HOME, and the artifact was built under the shared lock just before. Each record was created byPOST /api/v1/data/{object}as the dev admin, with required fields only:first_name,last_name,company,email,status = new;subject,description,status = new,priority = low.Before each probe, the served
GET /api/v1/meta/page/{name}record:detailssections were compared withdist/objectstack.json, and they were equal on both pages. A DOM probe then listed every section card on the Details tab. It opened any collapsed card and read the field labels and values.The "probe only" column is a one-off artifact. It is the HEAD build with
hideEmpty: falseadded to the four groups this PR leaves off, written to a scratch copy of the JSON and not to the source. Nothing from it is committed.Lead (
crm_lead)94668373)1ebe784c)identitysalutation,first_name,last_name,full_namecompany_infocompany,title,industrycontact_infomobile,website(emailandphoneare in the strip)preferences(collapsed)do_not_call,email_opt_outqualificationneed_type,estimated_amount,conversion_approval_status,next_followup_date,last_contacted_date,disqualification_reasonassignmentowner_idis in the strip)address(collapsed)addressconversion(collapsed)is_converted, the three converted lookups,converted_dateduplicates(collapsed)duplicate_of_type,duplicate_of_lead,duplicate_of_contact,duplicate_statusadditional(collapsed)annual_revenue,number_of_employees,description,notesRecords: before
juqdws4hHYcLtTa_, aftereeWK2GZn_s60qT0P, probevE80CUxeMsPQ5TCB.Case (
crm_case)94668373)1ebe784c)basiccase_number,subject,description,crm_contact,typeoriginorigin(owner_idis in the strip)slacreated_date,closed_date,first_response_date,resolution_time_hours(priority,sla_due_dateandis_sla_violatedare in the strip)escalation(collapsed)is_escalated,escalated_date,escalation_reasonresolutionresolution,resolved_by_articlesystem(collapsed)internal_notes,is_closedRecords: before
p4Gcwot6r3hRll7r, afterug6aIeQ3GWO-8Qun, probeohUbMxwy2off1ji0. On all three,created_dateis stored as null (the defect #1992 tracks), so it never counts as a filled member here.Reasons for each choice (from the members)
Lead
contact_info: on. Once the strip takesemailandphone, the members left aremobileandwebsite. A rep types both, and the default create form asks for both.address: on. Its one member,address, is typed by a rep, and the create form has an Address section for it.additional: on. This differs from the card's initial reading, which left it hidden. Every member (annual_revenue,number_of_employees,description,notes) is typed by a rep. None is readonly, and no hook or flow writes any of them.lead.hook.tsonly reads revenue and headcount to compute the startingratingon insert. The default create form asks for all four, in its Company Information and Additional Information sections. This group holds the lead's Description. It is the lead's equivalent of the opportunity'snotesgroup, which The opportunity's 详情 tab renders two fields out of the fourteen it authors — highlight fields are dropped and the sections left empty disappear #1211 turned on, and the page comment records that a Description section always sat on this page. If the maintainer reads this group as optional rather than expected, the revert is one key.assignment: off. The PM'sowner_idis in the strip, so the derived list is empty. With the key the section still renders nothing, the same result The opportunity's 详情 tab renders two fields out of the fourteen it authors — highlight fields are dropped and the sections left empty disappear #1211 measured forfinancials.duplicates: off. Thelead_duplicate_checkhook writes these fields as a machine signal. A person changesduplicate_statusonly after the hook has raised one. On a clean lead every member is empty by nature. With the key it would draw a 4-row empty card.Case
origin: on. Once the strip takesowner_id, its one member isorigin: the channel an agent picks, which the case create form lists. A web-to-case submission already carriesorigin = web(stamped incase.hook.ts), so this changes the page only for agent-created cases, which is where the field is to be filled.sla: off. This differs from the card's initial reading, which showed it. I decided it on the six members other thancreated_date.created_dateis coupled to crm_case.created_date has no writer — a user-created case stores null and drops out of the "Cases Opened by Priority × Day" report and the service dashboard's date range (#575 B2's shape, on the case object) #1992, which is now blocked upstream (A seed row's explicitcreated_atis overwritten with the boot instant on INSERT (seed context sets nopreserveAudit), yet written on the upsert UPDATE of a later boot — seeds cannot backdate creation time consistently objectstack#21646), so it stays onmainfor now.priority,sla_due_dateandis_sla_violatedare in the strip.closed_dateandresolution_time_hoursare readonly and written bycase.hook.tsat close.first_response_datehas a single writer,event.hook.ts.created_dateis later retired: retiring it removes one more stamp from the group.resolution: off.close_casecollects it, so it is empty by nature on a new case. This matches the card's reading.A record that already has values does not change. In the 17.6.0 console bundle (
.objectui-sha31971ff1, the same bundle #1211 read), the record-details renderer reads the key in exactly one branch:hideEmpty === truewith every field of the section empty. A section with any value is therefore not affected by it. This is a code reading. I did not re-measure a seeded record in the browser in this run (NOT MEASURED: seeded-record browser check, reason: the code reading plus #1211's measurement on the same bundle cover it).Legality of the key
RecordDetailsPropsisComponentPropsMap['record:details']in@objectstack/spec@17.6.0. ItssafeParseon each edited page'srecord:detailsproperties succeeds, and keepshideEmpty: falseoncontact_info,addressandadditional(lead) and onorigin(case). Three controls per page are each refused:hideEmpty: 'no'(expected boolean);hideEmptyX(unrecognized key);collapsible: truebesidegroup(cannot be combined).As #1211 found,
PageSchema.safeParseaccepts all three controls too, because a component'spropertiesis an open record at that level. So it is not used as the proof.Verification
OS_VERIFY_LOCK_SLOT=hotcrm-issue-2003 bash scripts/pm/os-verify-lock.sh -c 'pnpm verify'at6e68fa1e, the HEAD of this PR:os-verify-lock: VERDICT command-exit 0. Inside the chain:Validation passed;tsc --noEmitclean;1 warning(s), 18 suggestion(s);0 i18n/missing-* issues;source hygiene clean;source token ratchet clean;Build complete(9 author-time warnings);Test Files 177 passed (177),Tests 3794 passed | 1 skipped.os linton the two pages at94668373and at HEAD gave identical output (1 warning(s), 18 suggestion(s)both times). The author-time warning list is identical at 9 → 9.src/sales: interaction layer ~27,968 → ~27,981 (+13; chars 111,870 → 111,924), authored total ~99,929 → ~99,943.src/service: interaction layer ~5,592 → ~5,597 (chars 22,369 → 22,387), authored total ~20,544 → ~20,549.detail-section-dedup,lead-duplicate-visibility,case-create-form-narrowing) do not readhideEmpty, and they stay green.Acceptance notes
additionalis on, and caseslais off. The reasons are above. The card asked for its reading to be confirmed by measurement, not taken as decided.--artifactfrom the worktree. It served the worktree'sdist/objectstack.jsoninstead: the served sections did not equal the probe artifact on either page, which is theobjectstack dev -a PATHprintsArtifact: PATHbut servesdist/objectstack.json, andobjectstack start --artifact PATHrun beside anobjectstack.config.tsserves the config — the explicit artifact flag loses to the cwd objectstack#21501 shape. That reading was discarded. The probe column above comes from a reboot in a directory with noobjectstack.config.ts(No objectstack.config.ts found — booting from artifact), where served and probe were equal..changeset/806-detail-sections-group-ref.md(still unreleased) says that SLA & Priority and Resolution appear once the case has a value. That stays true after this PR.src/salesinteraction layer to ~30,000,src/serviceinteraction layer to ~6,000). That is outside this PR's file surface.Generated by Claude Code