@salesforce/templates 66.3.0 → 66.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/lib/templates/flexipage/RecordPage/_flexipage.flexipage-meta.xml +0 -24
  2. package/lib/templates/project/reactb2e/.a4drules/{graphql.md → features/feature-graphql-graphql-data-access-rule.md} +186 -125
  3. package/lib/templates/project/reactb2e/.a4drules/features/feature-react-agentforce-conversation-client-embedded-agent-rule.md +32 -0
  4. package/lib/templates/project/reactb2e/.a4drules/features/feature-react-chart-analytics-charts-rule.md +27 -0
  5. package/lib/templates/project/reactb2e/.a4drules/skills/feature-graphql-graphql-data-access/SKILL.md +155 -0
  6. package/lib/templates/project/{reactb2x/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md → reactb2e/.a4drules/skills/feature-graphql-graphql-data-access/docs/explore-schema.md} +58 -29
  7. package/lib/templates/project/{reactb2x/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md → reactb2e/.a4drules/skills/feature-graphql-graphql-data-access/docs/generate-mutation-query.md} +52 -42
  8. package/lib/templates/project/{reactb2x/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md → reactb2e/.a4drules/skills/feature-graphql-graphql-data-access/docs/generate-read-query.md} +32 -22
  9. package/lib/templates/project/{reactb2x/.a4drules/graphql/tools/schemas/shared.graphqls → reactb2e/.a4drules/skills/feature-graphql-graphql-data-access/docs/shared-schema.graphqls} +1 -1
  10. package/lib/templates/project/reactb2e/.a4drules/skills/feature-micro-frontend-micro-frontend/SKILL.md +137 -0
  11. package/lib/templates/project/reactb2e/.a4drules/skills/feature-react-agentforce-conversation-client-embedded-agent/SKILL.md +108 -0
  12. package/lib/templates/project/reactb2e/.a4drules/skills/feature-react-agentforce-conversation-client-embedded-agent/docs/embed-examples.md +182 -0
  13. package/lib/templates/project/reactb2e/.a4drules/skills/feature-react-chart-analytics-charts/SKILL.md +41 -0
  14. package/lib/templates/project/reactb2e/.a4drules/skills/feature-react-chart-analytics-charts/docs/schema-mapping.md +4 -0
  15. package/lib/templates/project/reactb2e/.a4drules/webapp-code-quality.md +136 -0
  16. package/lib/templates/project/reactb2e/.a4drules/{images.md → webapp-images.md} +6 -4
  17. package/lib/templates/project/reactb2e/.a4drules/webapp-no-node-e.md +3 -2
  18. package/lib/templates/project/reactb2e/.a4drules/webapp-react.md +149 -0
  19. package/lib/templates/project/reactb2e/.a4drules/{typescript.md → webapp-typescript.md} +10 -29
  20. package/lib/templates/project/reactb2e/.a4drules/webapp.md +60 -45
  21. package/lib/templates/project/reactb2e/AGENT.md +1 -1
  22. package/lib/templates/project/reactb2e/CHANGELOG.md +377 -0
  23. package/lib/templates/project/reactb2e/README.md +38 -4
  24. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/CHANGELOG.md +10 -0
  25. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/README.md +35 -0
  26. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/package.json +17 -7
  27. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/appLayout.tsx +76 -10
  28. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/__inherit_AgentforceConversationClient.tsx +3 -0
  29. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/alerts/status-alert.tsx +49 -0
  30. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/layouts/card-layout.tsx +29 -0
  31. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/alert.tsx +76 -0
  32. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/button.tsx +67 -0
  33. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/card.tsx +103 -0
  34. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/dialog.tsx +162 -0
  35. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/field.tsx +237 -0
  36. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/index.ts +84 -0
  37. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/input.tsx +19 -0
  38. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/label.tsx +22 -0
  39. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/pagination.tsx +132 -0
  40. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/select.tsx +193 -0
  41. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/separator.tsx +26 -0
  42. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/skeleton.tsx +14 -0
  43. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/spinner.tsx +16 -0
  44. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/table.tsx +114 -0
  45. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/tabs.tsx +88 -0
  46. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/components.json +18 -0
  47. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/constants.ts +39 -0
  48. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/api/index.ts +19 -0
  49. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/api/objectDetailService.ts +125 -0
  50. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/api/objectInfoGraphQLService.ts +194 -0
  51. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/api/objectInfoService.ts +199 -0
  52. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/api/recordListGraphQLService.ts +365 -0
  53. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/DetailFields.tsx +55 -0
  54. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/DetailForm.tsx +146 -0
  55. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/DetailHeader.tsx +34 -0
  56. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/DetailLayoutSections.tsx +80 -0
  57. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/Section.tsx +108 -0
  58. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/SectionRow.tsx +20 -0
  59. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/UiApiDetailForm.tsx +140 -0
  60. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FieldValueDisplay.tsx +73 -0
  61. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FormattedAddress.tsx +29 -0
  62. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FormattedEmail.tsx +17 -0
  63. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FormattedPhone.tsx +24 -0
  64. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FormattedText.tsx +11 -0
  65. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/FormattedUrl.tsx +29 -0
  66. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/detail/formatted/index.ts +6 -0
  67. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/filters/FilterField.tsx +54 -0
  68. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/filters/FilterInput.tsx +55 -0
  69. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/filters/FilterSelect.tsx +72 -0
  70. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/filters/FiltersPanel.tsx +380 -0
  71. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/forms/filters-form.tsx +114 -0
  72. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/forms/submit-button.tsx +47 -0
  73. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/GlobalSearchInput.tsx +114 -0
  74. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/ResultCardFields.tsx +71 -0
  75. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/SearchHeader.tsx +31 -0
  76. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/SearchPagination.tsx +144 -0
  77. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/SearchResultCard.tsx +136 -0
  78. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/search/SearchResultsPanel.tsx +197 -0
  79. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/components/shared/LoadingFallback.tsx +61 -0
  80. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/filters/FilterInput.tsx +55 -0
  81. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/filters/FilterSelect.tsx +72 -0
  82. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/form.tsx +209 -0
  83. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/index.ts +22 -0
  84. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/useObjectInfoBatch.ts +65 -0
  85. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/useObjectSearchData.ts +395 -0
  86. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/useRecordDetailLayout.ts +156 -0
  87. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/hooks/useRecordListGraphQL.ts +135 -0
  88. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/pages/DetailPage.tsx +109 -0
  89. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/pages/GlobalSearch.tsx +229 -0
  90. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/filters/filters.ts +120 -0
  91. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/filters/picklist.ts +32 -0
  92. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/index.ts +4 -0
  93. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/objectInfo/objectInfo.ts +166 -0
  94. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/recordDetail/recordDetail.ts +61 -0
  95. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/types/search/searchResults.ts +229 -0
  96. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/apiUtils.ts +125 -0
  97. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/cacheUtils.ts +76 -0
  98. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/debounce.ts +89 -0
  99. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/fieldUtils.ts +354 -0
  100. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/fieldValueExtractor.ts +67 -0
  101. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/filterUtils.ts +32 -0
  102. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/formDataTransformUtils.ts +260 -0
  103. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/formUtils.ts +142 -0
  104. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/graphQLNodeFieldUtils.ts +186 -0
  105. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/graphQLObjectInfoAdapter.ts +319 -0
  106. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/graphQLRecordAdapter.ts +90 -0
  107. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/index.ts +59 -0
  108. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/layoutTransformUtils.ts +236 -0
  109. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/linkUtils.ts +14 -0
  110. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/paginationUtils.ts +49 -0
  111. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/recordUtils.ts +159 -0
  112. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/features/global-search/utils/sanitizationUtils.ts +49 -0
  113. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/lib/utils.ts +6 -0
  114. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/Home.tsx +11 -10
  115. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/TestAccPage.tsx +19 -0
  116. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/routes.tsx +28 -0
  117. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/styles/global.css +122 -0
  118. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/vite.config.ts +2 -1
  119. package/lib/templates/project/reactb2e/package.json +1 -1
  120. package/lib/templates/project/reactb2x/.a4drules/{graphql.md → features/feature-graphql-graphql-data-access-rule.md} +186 -125
  121. package/lib/templates/project/reactb2x/.a4drules/features/feature-react-agentforce-conversation-client-embedded-agent-rule.md +32 -0
  122. package/lib/templates/project/reactb2x/.a4drules/features/feature-react-chart-analytics-charts-rule.md +27 -0
  123. package/lib/templates/project/reactb2x/.a4drules/skills/feature-graphql-graphql-data-access/SKILL.md +155 -0
  124. package/lib/templates/project/{reactb2e/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md → reactb2x/.a4drules/skills/feature-graphql-graphql-data-access/docs/explore-schema.md} +58 -29
  125. package/lib/templates/project/{reactb2e/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md → reactb2x/.a4drules/skills/feature-graphql-graphql-data-access/docs/generate-mutation-query.md} +52 -42
  126. package/lib/templates/project/{reactb2e/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md → reactb2x/.a4drules/skills/feature-graphql-graphql-data-access/docs/generate-read-query.md} +32 -22
  127. package/lib/templates/project/{reactb2e/.a4drules/graphql/tools/schemas/shared.graphqls → reactb2x/.a4drules/skills/feature-graphql-graphql-data-access/docs/shared-schema.graphqls} +1 -1
  128. package/lib/templates/project/reactb2x/.a4drules/skills/feature-micro-frontend-micro-frontend/SKILL.md +137 -0
  129. package/lib/templates/project/reactb2x/.a4drules/skills/feature-react-agentforce-conversation-client-embedded-agent/SKILL.md +108 -0
  130. package/lib/templates/project/reactb2x/.a4drules/skills/feature-react-agentforce-conversation-client-embedded-agent/docs/embed-examples.md +182 -0
  131. package/lib/templates/project/reactb2x/.a4drules/skills/feature-react-chart-analytics-charts/SKILL.md +41 -0
  132. package/lib/templates/project/reactb2x/.a4drules/skills/feature-react-chart-analytics-charts/docs/schema-mapping.md +4 -0
  133. package/lib/templates/project/reactb2x/.a4drules/webapp-code-quality.md +136 -0
  134. package/lib/templates/project/reactb2x/.a4drules/{images.md → webapp-images.md} +6 -4
  135. package/lib/templates/project/reactb2x/.a4drules/webapp-no-node-e.md +3 -2
  136. package/lib/templates/project/reactb2x/.a4drules/webapp-react.md +149 -0
  137. package/lib/templates/project/reactb2x/.a4drules/{typescript.md → webapp-typescript.md} +10 -29
  138. package/lib/templates/project/reactb2x/.a4drules/webapp.md +60 -45
  139. package/lib/templates/project/reactb2x/AGENT.md +1 -1
  140. package/lib/templates/project/reactb2x/CHANGELOG.md +377 -0
  141. package/lib/templates/project/reactb2x/README.md +58 -4
  142. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/CHANGELOG.md +10 -0
  143. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/README.md +35 -0
  144. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/package.json +8 -7
  145. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/appLayout.tsx +75 -3
  146. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/alerts/status-alert.tsx +36 -32
  147. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/layouts/card-layout.tsx +29 -0
  148. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/alert.tsx +61 -54
  149. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/button.tsx +57 -57
  150. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/card.tsx +86 -75
  151. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/dialog.tsx +129 -110
  152. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/field.tsx +208 -193
  153. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/index.ts +65 -53
  154. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/input.tsx +14 -14
  155. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/label.tsx +17 -14
  156. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/pagination.tsx +108 -88
  157. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/select.tsx +156 -146
  158. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/separator.tsx +19 -19
  159. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/skeleton.tsx +10 -10
  160. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/spinner.tsx +12 -11
  161. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/table.tsx +96 -69
  162. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/tabs.tsx +71 -61
  163. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components.json +1 -1
  164. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/constants.ts +39 -0
  165. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{context → features/authentication/context}/AuthContext.tsx +1 -1
  166. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components → features/authentication}/footers/footer-link.tsx +1 -1
  167. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components → features/authentication}/forms/auth-form.tsx +4 -4
  168. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components → features/authentication}/forms/submit-button.tsx +4 -4
  169. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{hooks → features/authentication/hooks}/form.tsx +13 -9
  170. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components → features/authentication}/layout/card-skeleton.tsx +2 -2
  171. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components → features/authentication}/layout/centered-page-layout.tsx +2 -3
  172. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{layouts → features/authentication/layouts}/AuthAppLayout.tsx +2 -2
  173. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication/layouts}/authenticationRouteLayout.tsx +2 -2
  174. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication/layouts}/privateRouteLayout.tsx +2 -2
  175. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/ChangePassword.tsx +6 -6
  176. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/ForgotPassword.tsx +5 -5
  177. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/Login.tsx +6 -6
  178. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/Profile.tsx +22 -13
  179. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/Register.tsx +6 -11
  180. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{pages → features/authentication/pages}/ResetPassword.tsx +8 -8
  181. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication}/sessionTimeout/SessionTimeoutValidator.tsx +6 -6
  182. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/index.ts +19 -0
  183. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectDetailService.ts +125 -0
  184. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoGraphQLService.ts +194 -0
  185. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoService.ts +199 -0
  186. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/recordListGraphQLService.ts +365 -0
  187. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailFields.tsx +55 -0
  188. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailForm.tsx +146 -0
  189. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailHeader.tsx +34 -0
  190. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailLayoutSections.tsx +80 -0
  191. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/Section.tsx +108 -0
  192. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/SectionRow.tsx +20 -0
  193. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/UiApiDetailForm.tsx +140 -0
  194. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FieldValueDisplay.tsx +73 -0
  195. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedAddress.tsx +29 -0
  196. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedEmail.tsx +17 -0
  197. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedPhone.tsx +24 -0
  198. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedText.tsx +11 -0
  199. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedUrl.tsx +29 -0
  200. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/index.ts +6 -0
  201. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterField.tsx +54 -0
  202. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterInput.tsx +55 -0
  203. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterSelect.tsx +72 -0
  204. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FiltersPanel.tsx +380 -0
  205. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/forms/filters-form.tsx +114 -0
  206. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/forms/submit-button.tsx +47 -0
  207. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/GlobalSearchInput.tsx +114 -0
  208. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/ResultCardFields.tsx +71 -0
  209. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchHeader.tsx +31 -0
  210. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchPagination.tsx +144 -0
  211. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchResultCard.tsx +136 -0
  212. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchResultsPanel.tsx +197 -0
  213. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/shared/LoadingFallback.tsx +61 -0
  214. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/filters/FilterInput.tsx +55 -0
  215. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/filters/FilterSelect.tsx +72 -0
  216. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/form.tsx +209 -0
  217. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/index.ts +22 -0
  218. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useObjectInfoBatch.ts +65 -0
  219. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useObjectSearchData.ts +395 -0
  220. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useRecordDetailLayout.ts +156 -0
  221. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useRecordListGraphQL.ts +135 -0
  222. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/pages/DetailPage.tsx +109 -0
  223. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/pages/GlobalSearch.tsx +229 -0
  224. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/filters/filters.ts +120 -0
  225. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/filters/picklist.ts +32 -0
  226. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/index.ts +4 -0
  227. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/objectInfo/objectInfo.ts +166 -0
  228. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/recordDetail/recordDetail.ts +61 -0
  229. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/search/searchResults.ts +229 -0
  230. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/apiUtils.ts +125 -0
  231. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/cacheUtils.ts +76 -0
  232. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/debounce.ts +89 -0
  233. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/fieldUtils.ts +354 -0
  234. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/fieldValueExtractor.ts +67 -0
  235. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/filterUtils.ts +32 -0
  236. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/formDataTransformUtils.ts +260 -0
  237. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/formUtils.ts +142 -0
  238. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLNodeFieldUtils.ts +186 -0
  239. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLObjectInfoAdapter.ts +319 -0
  240. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLRecordAdapter.ts +90 -0
  241. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/index.ts +59 -0
  242. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/layoutTransformUtils.ts +236 -0
  243. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/linkUtils.ts +14 -0
  244. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/paginationUtils.ts +49 -0
  245. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/recordUtils.ts +159 -0
  246. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/sanitizationUtils.ts +49 -0
  247. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/lib/utils.ts +3 -3
  248. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/pages/Home.tsx +11 -10
  249. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/routes.tsx +33 -11
  250. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/styles/global.css +107 -107
  251. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/vite.config.ts +2 -1
  252. package/lib/templates/project/reactb2x/package.json +1 -1
  253. package/lib/templates/webapplication/reactbasic/CHANGELOG.md +10 -0
  254. package/lib/templates/webapplication/reactbasic/README.md +75 -0
  255. package/lib/templates/webapplication/reactbasic/package.json +13 -5
  256. package/lib/templates/webapplication/reactbasic/src/appLayout.tsx +75 -3
  257. package/lib/templates/webapplication/reactbasic/src/components/alerts/status-alert.tsx +49 -0
  258. package/lib/templates/webapplication/reactbasic/src/components/layouts/card-layout.tsx +29 -0
  259. package/lib/templates/webapplication/reactbasic/src/components/ui/alert.tsx +76 -0
  260. package/lib/templates/webapplication/reactbasic/src/components/ui/button.tsx +67 -0
  261. package/lib/templates/webapplication/reactbasic/src/components/ui/card.tsx +103 -0
  262. package/lib/templates/webapplication/reactbasic/src/components/ui/dialog.tsx +162 -0
  263. package/lib/templates/webapplication/reactbasic/src/components/ui/field.tsx +237 -0
  264. package/lib/templates/webapplication/reactbasic/src/components/ui/index.ts +84 -0
  265. package/lib/templates/webapplication/reactbasic/src/components/ui/input.tsx +19 -0
  266. package/lib/templates/webapplication/reactbasic/src/components/ui/label.tsx +22 -0
  267. package/lib/templates/webapplication/reactbasic/src/components/ui/pagination.tsx +132 -0
  268. package/lib/templates/webapplication/reactbasic/src/components/ui/select.tsx +193 -0
  269. package/lib/templates/webapplication/reactbasic/src/components/ui/separator.tsx +26 -0
  270. package/lib/templates/webapplication/reactbasic/src/components/ui/skeleton.tsx +14 -0
  271. package/lib/templates/webapplication/reactbasic/src/components/ui/spinner.tsx +16 -0
  272. package/lib/templates/webapplication/reactbasic/src/components/ui/table.tsx +114 -0
  273. package/lib/templates/webapplication/reactbasic/src/components/ui/tabs.tsx +88 -0
  274. package/lib/templates/webapplication/reactbasic/src/components.json +18 -0
  275. package/lib/templates/webapplication/reactbasic/src/lib/utils.ts +6 -0
  276. package/lib/templates/webapplication/reactbasic/src/styles/global.css +122 -0
  277. package/lib/templates/webapplication/reactbasic/vite.config.ts +2 -1
  278. package/lib/templates/webapplication/webappbasic/README.md +15 -0
  279. package/lib/utils/webappTemplateUtils.d.ts +10 -0
  280. package/lib/utils/webappTemplateUtils.js +55 -7
  281. package/lib/utils/webappTemplateUtils.js.map +1 -1
  282. package/package.json +5 -5
  283. package/lib/templates/project/reactb2e/.a4drules/README.md +0 -35
  284. package/lib/templates/project/reactb2e/.a4drules/a4d-webapp-generate.md +0 -27
  285. package/lib/templates/project/reactb2e/.a4drules/build-validation.md +0 -78
  286. package/lib/templates/project/reactb2e/.a4drules/code-quality.md +0 -137
  287. package/lib/templates/project/reactb2e/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +0 -205
  288. package/lib/templates/project/reactb2e/.a4drules/react.md +0 -387
  289. package/lib/templates/project/reactb2e/.a4drules/react_image_processing.md +0 -45
  290. package/lib/templates/project/reactb2e/.a4drules/ui-layout.md +0 -23
  291. package/lib/templates/project/reactb2e/.a4drules/webapp-nav-and-placeholders.md +0 -33
  292. package/lib/templates/project/reactb2e/.a4drules/webapp-ui-first.md +0 -32
  293. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/build/vite.config.d.ts +0 -2
  294. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/build/vite.config.js +0 -93
  295. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/package-lock.json +0 -14392
  296. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/api/graphql-operations-types.ts +0 -116
  297. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/api/utils/accounts.ts +0 -33
  298. package/lib/templates/project/reactb2e/force-app/main/default/webapplications/appreacttemplateb2e/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  299. package/lib/templates/project/reactb2x/.a4drules/README.md +0 -35
  300. package/lib/templates/project/reactb2x/.a4drules/a4d-webapp-generate.md +0 -27
  301. package/lib/templates/project/reactb2x/.a4drules/build-validation.md +0 -78
  302. package/lib/templates/project/reactb2x/.a4drules/code-quality.md +0 -137
  303. package/lib/templates/project/reactb2x/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +0 -205
  304. package/lib/templates/project/reactb2x/.a4drules/react.md +0 -387
  305. package/lib/templates/project/reactb2x/.a4drules/react_image_processing.md +0 -45
  306. package/lib/templates/project/reactb2x/.a4drules/ui-layout.md +0 -23
  307. package/lib/templates/project/reactb2x/.a4drules/webapp-nav-and-placeholders.md +0 -33
  308. package/lib/templates/project/reactb2x/.a4drules/webapp-ui-first.md +0 -32
  309. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/build/vite.config.d.ts +0 -2
  310. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/build/vite.config.js +0 -93
  311. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/package-lock.json +0 -18408
  312. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/api/graphql-operations-types.ts +0 -116
  313. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/api/utils/accounts.ts +0 -33
  314. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  315. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/components/layout/card-layout.tsx +0 -23
  316. package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/lib/data-sdk.ts +0 -21
  317. package/lib/templates/webapplication/reactbasic/build/vite.config.d.ts +0 -2
  318. package/lib/templates/webapplication/reactbasic/build/vite.config.js +0 -93
  319. package/lib/templates/webapplication/reactbasic/package-lock.json +0 -14373
  320. package/lib/templates/webapplication/reactbasic/src/api/graphql-operations-types.ts +0 -116
  321. package/lib/templates/webapplication/reactbasic/src/api/utils/accounts.ts +0 -33
  322. package/lib/templates/webapplication/reactbasic/src/api/utils/query/highRevenueAccountsQuery.graphql +0 -29
  323. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication}/authHelpers.ts +0 -0
  324. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication}/authenticationConfig.ts +0 -0
  325. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{hooks → features/authentication/hooks}/useCountdownTimer.ts +0 -0
  326. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{hooks → features/authentication/hooks}/useRetryWithBackoff.ts +0 -0
  327. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication}/sessionTimeout/sessionTimeService.ts +0 -0
  328. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{components/auth → features/authentication}/sessionTimeout/sessionTimeoutConfig.ts +0 -0
  329. /package/lib/templates/project/reactb2x/force-app/main/default/webapplications/appreacttemplateb2x/src/{utils → features/authentication/utils}/helpers.ts +0 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Record GraphQL Service
3
+ *
4
+ * Single service for querying Salesforce object records via GraphQL UI API (uiapi.query).
5
+ * Handles both list (paginated, filter, sort, search) and single-record-by-id using one query shape.
6
+ *
7
+ * @module api/recordListGraphQLService
8
+ */
9
+
10
+ import { getDataSDK } from "@salesforce/sdk-data";
11
+ import type { Column } from "../types/search/searchResults";
12
+ import type { FilterCriteria } from "../types/filters/filters";
13
+
14
+ const DEFAULT_PAGE_SIZE = 50;
15
+
16
+ /** Tree of selection: leaf is "value", branch is nested fields. Keys starting with __on_ are inline fragments (e.g. __on_User). */
17
+ interface SelectionTree {
18
+ [key: string]: "value" | SelectionTree;
19
+ }
20
+
21
+ /**
22
+ * Polymorphic relationship fields and the concrete GraphQL types they can resolve to.
23
+ * Only relationship names listed here use inline fragments; others use direct selection
24
+ * (e.g. Parent -> Parent { Name { value } } because "Parent" is not a schema type).
25
+ * We use a single fragment per field to avoid schema validation errors (e.g. User and Group
26
+ * cannot both be spread in the same selection in some contexts).
27
+ */
28
+ const POLYMORPHIC_RELATIONSHIP_TYPES: Record<string, string[]> = {
29
+ Owner: ["User"],
30
+ CreatedBy: ["User"],
31
+ LastModifiedBy: ["User"],
32
+ };
33
+
34
+ /**
35
+ * Builds a selection tree from columns (fieldApiName). Simple fields (e.g. Name, OwnerId) become
36
+ * top-level leaves. Relationship fields that are in POLYMORPHIC_RELATIONSHIP_TYPES use a single
37
+ * inline fragment on the first concrete type (e.g. ... on User). All other relationship fields
38
+ * (e.g. Parent, Account) use direct selection (e.g. Parent { Name { value } }) because the
39
+ * relationship name is not necessarily a GraphQL type name (e.g. Parent resolves to Account).
40
+ */
41
+ function buildSelectionTree(columns: Column[]): SelectionTree {
42
+ const allFieldNames = new Set(columns.map((c) => (c.fieldApiName ?? "").trim()).filter(Boolean));
43
+ const tree: SelectionTree = { Id: "value" };
44
+ for (const col of columns) {
45
+ const name = (col.fieldApiName ?? "").trim();
46
+ if (!name) continue;
47
+ const parts = name.split(".");
48
+ if (parts.length === 1) {
49
+ const fieldName = parts[0];
50
+ const hasCorrespondingId = allFieldNames.has(`${fieldName}Id`);
51
+ if (hasCorrespondingId) {
52
+ const knownTypes = POLYMORPHIC_RELATIONSHIP_TYPES[fieldName];
53
+ if (knownTypes?.length) {
54
+ // Use a single inline fragment to avoid "User can never be Group" validation errors
55
+ const typeName = knownTypes[0];
56
+ tree[fieldName] = { [`__on_${typeName}`]: { Name: "value" } };
57
+ } else {
58
+ // Relationship name (e.g. Parent) is not a GraphQL type; use direct selection
59
+ tree[fieldName] = { Name: "value" };
60
+ }
61
+ } else {
62
+ tree[fieldName] = "value";
63
+ }
64
+ } else {
65
+ let current = tree;
66
+ for (let i = 0; i < parts.length; i++) {
67
+ const part = parts[i];
68
+ const isLeaf = i === parts.length - 1;
69
+ if (isLeaf) {
70
+ current[part] = "value";
71
+ } else {
72
+ const existing = current[part];
73
+ if (existing === "value") continue;
74
+ if (!existing) {
75
+ current[part] = {};
76
+ }
77
+ current = current[part] as SelectionTree;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return tree;
83
+ }
84
+
85
+ /**
86
+ * Serializes a selection tree to GraphQL selection set string.
87
+ * Keys starting with __on_ are emitted as inline fragments (... on TypeName { ... }).
88
+ * Id is scalar (no subselection); other leaves use { value }.
89
+ */
90
+ function serializeSelectionTree(tree: SelectionTree, indent: string): string {
91
+ const fragmentKeys = Object.keys(tree).filter((k) => k.startsWith("__on_"));
92
+ const normalKeys = Object.keys(tree).filter((k) => !k.startsWith("__on_"));
93
+ normalKeys.sort((a, b) => {
94
+ if (a === "Id") return -1;
95
+ if (b === "Id") return 1;
96
+ return a.localeCompare(b);
97
+ });
98
+ fragmentKeys.sort();
99
+ const lines: string[] = [];
100
+ const childIndent = `${indent} `;
101
+ for (const key of normalKeys) {
102
+ const val = tree[key];
103
+ if (val === "value") {
104
+ if (key === "Id") {
105
+ lines.push(`${indent}Id`);
106
+ } else {
107
+ lines.push(`${indent}${key} { value }`);
108
+ }
109
+ } else {
110
+ lines.push(`${indent}${key} {`);
111
+ lines.push(serializeSelectionTree(val, childIndent));
112
+ lines.push(`${indent}}`);
113
+ }
114
+ }
115
+ for (const key of fragmentKeys) {
116
+ const typeName = key.slice(5);
117
+ const val = tree[key];
118
+ if (val && typeof val === "object") {
119
+ lines.push(`${indent}... on ${typeName} {`);
120
+ lines.push(serializeSelectionTree(val, childIndent));
121
+ lines.push(`${indent}}`);
122
+ }
123
+ }
124
+ return lines.join("\n");
125
+ }
126
+
127
+ function buildNodeSelection(columns: Column[]): string {
128
+ const tree = buildSelectionTree(columns);
129
+ return serializeSelectionTree(tree, " ");
130
+ }
131
+
132
+ /**
133
+ * Builds GraphQL where clause from filter criteria and optional search text.
134
+ * Search text is applied as Name like %query%. Multiple conditions are combined with and.
135
+ *
136
+ * @param criteria - Field filters (fieldPath, operator, values).
137
+ * @param searchQuery - Optional; adds Name like %searchQuery% when provided.
138
+ */
139
+ export function buildWhereFromCriteria(
140
+ criteria: FilterCriteria[],
141
+ searchQuery?: string,
142
+ ): Record<string, unknown> | null {
143
+ const conditions: Record<string, unknown>[] = [];
144
+
145
+ if (searchQuery && searchQuery.trim()) {
146
+ const term = `%${searchQuery.trim()}%`;
147
+ conditions.push({ Name: { like: term } });
148
+ }
149
+
150
+ for (const c of criteria) {
151
+ if (!c.fieldPath || !c.operator || !c.values?.length) continue;
152
+ const value = c.values[0];
153
+ const op = c.operator;
154
+ const parts = c.fieldPath.split(".");
155
+ const fieldClause = { [op]: value };
156
+ if (parts.length === 1) {
157
+ conditions.push({ [parts[0]]: fieldClause });
158
+ } else {
159
+ let nested: Record<string, unknown> = { [parts[parts.length - 1]]: fieldClause };
160
+ for (let i = parts.length - 2; i >= 0; i--) {
161
+ nested = { [parts[i]]: nested };
162
+ }
163
+ conditions.push(nested);
164
+ }
165
+ }
166
+
167
+ if (conditions.length === 0) return null;
168
+ if (conditions.length === 1) return conditions[0] as Record<string, unknown>;
169
+ return { and: conditions };
170
+ }
171
+
172
+ /**
173
+ * Parses sortBy string (e.g. "Name", "Name ASC", "AnnualRevenue DESC") into GraphQL orderBy shape.
174
+ * Default direction is ASC.
175
+ */
176
+ export function buildOrderByFromSort(
177
+ sortBy: string,
178
+ ): Record<string, { order: "ASC" | "DESC" }> | null {
179
+ const trimmed = (sortBy ?? "").trim();
180
+ if (!trimmed || trimmed.toLowerCase() === "relevance") return null;
181
+ const parts = trimmed.split(/\s+/);
182
+ const field = parts[0];
183
+ const dir = parts[1]?.toUpperCase() === "DESC" ? "DESC" : "ASC";
184
+ return { [field]: { order: dir } };
185
+ }
186
+
187
+ /** Variables for the GetRecords GraphQL operation. */
188
+ export interface RecordListGraphQLVariables {
189
+ first?: number;
190
+ after?: string | null;
191
+ where?: Record<string, unknown> | null;
192
+ orderBy?: Record<string, unknown> | null;
193
+ }
194
+
195
+ export interface RecordListGraphQLOptions {
196
+ objectApiName: string;
197
+ columns: Column[];
198
+ /** When set, fetches a single record by Id (first=1, where Id eq); used for detail view. */
199
+ recordId?: string | null;
200
+ first?: number;
201
+ after?: string | null;
202
+ filters?: FilterCriteria[];
203
+ orderBy?: Record<string, unknown> | null;
204
+ searchQuery?: string;
205
+ }
206
+
207
+ /**
208
+ * Builds the GraphQL query string for uiapi.query.{objectApiName}.
209
+ * Used for both list (pagination, where, orderBy) and single record (where Id eq, first 1).
210
+ *
211
+ * @param objectApiName - API name of the object (e.g. Account, Contact).
212
+ * @param columns - Field selection (becomes node selection via buildNodeSelection).
213
+ * @param options - Optional where and orderBy; when recordId is used, where is set to Id eq.
214
+ */
215
+ export function buildGetRecordsQuery(
216
+ objectApiName: string,
217
+ columns: Column[],
218
+ options?: { where?: Record<string, unknown> | null; orderBy?: Record<string, unknown> | null },
219
+ ): string {
220
+ const nodeSelection = buildNodeSelection(columns);
221
+ const hasWhere = options?.where != null && Object.keys(options.where).length > 0;
222
+ const hasOrderBy = options?.orderBy != null && Object.keys(options.orderBy).length > 0;
223
+
224
+ const filterType = `${objectApiName}_Filter`;
225
+ const orderByType = `${objectApiName}_OrderBy`;
226
+
227
+ const varDecls = [
228
+ "$first: Int",
229
+ "$after: String",
230
+ ...(hasWhere ? [`$where: ${filterType}`] : []),
231
+ ...(hasOrderBy ? [`$orderBy: ${orderByType}`] : []),
232
+ ];
233
+ const opArgs = [
234
+ "first: $first",
235
+ "after: $after",
236
+ ...(hasWhere ? ["where: $where"] : []),
237
+ ...(hasOrderBy ? ["orderBy: $orderBy"] : []),
238
+ ];
239
+
240
+ return `query GetRecords(${varDecls.join(", ")}) {
241
+ uiapi {
242
+ query {
243
+ ${objectApiName}(${opArgs.join(", ")}) {
244
+ edges {
245
+ node {
246
+ ${nodeSelection}
247
+ }
248
+ }
249
+ pageInfo {
250
+ hasNextPage
251
+ hasPreviousPage
252
+ endCursor
253
+ startCursor
254
+ },
255
+ totalCount,
256
+ pageResultCount
257
+ }
258
+ }
259
+ }
260
+ }`;
261
+ }
262
+
263
+ export interface RecordListGraphQLResult {
264
+ uiapi?: {
265
+ query?: {
266
+ [key: string]: {
267
+ edges?: Array<{ node?: Record<string, unknown> }>;
268
+ pageInfo?: {
269
+ hasNextPage?: boolean;
270
+ hasPreviousPage?: boolean;
271
+ endCursor?: string | null;
272
+ startCursor?: string | null;
273
+ };
274
+ };
275
+ };
276
+ };
277
+ }
278
+
279
+ /** GraphQL node shape for a single record (Id + field selections with value/nested). */
280
+ export type GraphQLRecordNode = Record<string, unknown>;
281
+
282
+ /**
283
+ * Fetches records for the given object via GraphQL (single query for both list and single record).
284
+ *
285
+ * - List: pass first, after, filters, orderBy, searchQuery.
286
+ * - Single record: pass recordId; first is set to 1 and where includes Id eq.
287
+ *
288
+ * @param options.objectApiName - API name of the object (e.g. Account, Contact).
289
+ * @param options.columns - Field selection (from filters-derived columns or layout-derived optionalFields).
290
+ * @param options.recordId - If set, fetches one record by Id (first=1, where Id eq).
291
+ * @param options.first - Page size (default 50; ignored when recordId is set).
292
+ * @param options.after - Cursor for next page.
293
+ * @param options.filters - Filter criteria (mapped to where).
294
+ * @param options.orderBy - GraphQL orderBy; use buildOrderByFromSort(sortBy) when needed.
295
+ * @param options.searchQuery - Text search (Name like %query% in where).
296
+ * @returns Connection result (edges, pageInfo); for recordId callers use edges[0].node.
297
+ */
298
+ export async function getRecordsGraphQL(
299
+ options: RecordListGraphQLOptions,
300
+ ): Promise<RecordListGraphQLResult> {
301
+ const {
302
+ objectApiName,
303
+ columns,
304
+ recordId,
305
+ first = DEFAULT_PAGE_SIZE,
306
+ after = null,
307
+ filters = [],
308
+ orderBy = null,
309
+ searchQuery,
310
+ } = options;
311
+
312
+ const listWhere = buildWhereFromCriteria(filters, searchQuery);
313
+ const where =
314
+ recordId != null && recordId !== ""
315
+ ? listWhere != null && Object.keys(listWhere).length > 0
316
+ ? { and: [{ Id: { eq: recordId } }, listWhere] }
317
+ : { Id: { eq: recordId } }
318
+ : listWhere;
319
+ const effectiveFirst = recordId != null && recordId !== "" ? 1 : first;
320
+ const hasWhere = where != null && Object.keys(where).length > 0;
321
+ const hasOrderBy = orderBy != null && Object.keys(orderBy).length > 0;
322
+
323
+ const query = buildGetRecordsQuery(objectApiName, columns, { where, orderBy });
324
+ const variables: Record<string, unknown> = {
325
+ first: effectiveFirst,
326
+ after: after ?? null,
327
+ ...(hasWhere && where ? { where } : {}),
328
+ ...(hasOrderBy && orderBy ? { orderBy } : {}),
329
+ };
330
+
331
+ const data = await getDataSDK();
332
+ const response = await data.graphql?.<RecordListGraphQLResult>(query, variables);
333
+
334
+ if (response?.errors?.length) {
335
+ const errorMessages = response.errors.map((e) => e.message).join("; ");
336
+ throw new Error(`GraphQL Error: ${errorMessages}`);
337
+ }
338
+
339
+ return response?.data ?? ({} as RecordListGraphQLResult);
340
+ }
341
+
342
+ /**
343
+ * Fetches a single record by Id. Uses the same GraphQL query as list (getRecordsGraphQL with recordId + first 1).
344
+ *
345
+ * @param objectApiName - API name of the object.
346
+ * @param recordId - Record Id.
347
+ * @param columns - Field selection (e.g. layout-derived optionalFields as Column[]).
348
+ * @returns The record node or null if not found.
349
+ */
350
+ export async function getRecordByIdGraphQL(
351
+ objectApiName: string,
352
+ recordId: string,
353
+ columns: Column[],
354
+ ): Promise<GraphQLRecordNode | null> {
355
+ const result = await getRecordsGraphQL({
356
+ objectApiName,
357
+ columns,
358
+ recordId,
359
+ first: 1,
360
+ });
361
+ const edges =
362
+ result?.uiapi?.query?.[objectApiName]?.edges ??
363
+ ([] as Array<{ node?: Record<string, unknown> }>);
364
+ return (edges[0]?.node ?? null) as GraphQLRecordNode | null;
365
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Alternative detail rendering: columns + record → label/value list.
3
+ *
4
+ * Use when you have list columns + record (e.g. from filters-derived columns + searchResults)
5
+ * and do not need the Layout API. The primary detail view (DetailPage) uses DetailForm
6
+ * via UiApiDetailForm (layout + GraphQL record).
7
+ *
8
+ * @param record - Record data to display
9
+ * @param columns - Column definitions (e.g. derived from getObjectListFilters)
10
+ */
11
+ import type { Column, SearchResultRecordData } from "../../types/search/searchResults";
12
+ import { getNestedFieldValue } from "../../utils/fieldUtils";
13
+
14
+ interface DetailFieldsProps {
15
+ record: SearchResultRecordData;
16
+ columns: Column[];
17
+ }
18
+
19
+ function hasVisibleValue(value: string | number | boolean | null | undefined): boolean {
20
+ return value !== null && value !== undefined && value !== "";
21
+ }
22
+
23
+ export default function DetailFields({ record, columns }: DetailFieldsProps) {
24
+ const rows = columns.filter(
25
+ (col) =>
26
+ col?.fieldApiName && hasVisibleValue(getNestedFieldValue(record.fields, col.fieldApiName)),
27
+ );
28
+
29
+ if (columns.length > 0 && rows.length === 0) {
30
+ return (
31
+ <div role="status" className="text-sm text-muted-foreground py-4">
32
+ No field values to display
33
+ </div>
34
+ );
35
+ }
36
+
37
+ return (
38
+ <dl className="space-y-4" role="list">
39
+ {rows.map((column) => {
40
+ const fieldApiName = column.fieldApiName as string;
41
+ const displayValue = getNestedFieldValue(record.fields, fieldApiName);
42
+ return (
43
+ <div key={fieldApiName} className="border-b pb-4 last:border-0" role="listitem">
44
+ <div className="flex flex-col sm:flex-row sm:items-start gap-2">
45
+ <dt className="font-semibold text-sm text-muted-foreground min-w-[150px]">
46
+ {column.label || fieldApiName}:
47
+ </dt>
48
+ <dd className="text-sm text-foreground flex-1">{displayValue}</dd>
49
+ </div>
50
+ </div>
51
+ );
52
+ })}
53
+ </dl>
54
+ );
55
+ }
@@ -0,0 +1,146 @@
1
+ import { useState, useCallback, useMemo, useId } from "react";
2
+ import type { LayoutResponse } from "../../types/recordDetail/recordDetail";
3
+ import type { GraphQLRecordNode } from "../../api/recordListGraphQLService";
4
+ import {
5
+ getDisplayValueForLayoutItemFromNode,
6
+ getDisplayValueForDetailFieldFromNode,
7
+ } from "../../utils/graphQLNodeFieldUtils";
8
+ import type { ObjectInfoMetadata } from "../../utils/formDataTransformUtils";
9
+ import {
10
+ getTransformedSections,
11
+ type LayoutTransformContext,
12
+ type ObjectInfo,
13
+ type PicklistOption,
14
+ type TransformedLayoutItem,
15
+ } from "../../utils/layoutTransformUtils";
16
+ import { FieldValueDisplay } from "./formatted";
17
+ import { Section } from "./Section";
18
+ import { SectionRow } from "./SectionRow";
19
+
20
+ export interface DetailFormProps {
21
+ layout: LayoutResponse;
22
+ record: GraphQLRecordNode;
23
+ metadata?: ObjectInfoMetadata | null;
24
+ objectInfo?: ObjectInfo | null;
25
+ lookupRecords?: Record<string, PicklistOption[] | null> | null;
26
+ showSectionHeaders?: boolean;
27
+ collapsibleSections?: boolean;
28
+ }
29
+
30
+ function FieldCell({
31
+ item,
32
+ record,
33
+ metadata,
34
+ }: {
35
+ item: TransformedLayoutItem;
36
+ record: GraphQLRecordNode;
37
+ metadata?: ObjectInfoMetadata | null;
38
+ }) {
39
+ if (!item.isField || item.apiName == null) return null;
40
+ const label = item.label ?? item.apiName;
41
+ const hasComponents = item.layoutComponentApiNames && item.layoutComponentApiNames.length > 0;
42
+ const layoutResult = hasComponents
43
+ ? getDisplayValueForLayoutItemFromNode(
44
+ record,
45
+ item.layoutComponentApiNames as string[],
46
+ metadata,
47
+ )
48
+ : null;
49
+ const value = hasComponents
50
+ ? (layoutResult?.value ?? null)
51
+ : getDisplayValueForDetailFieldFromNode(record, item.apiName, metadata);
52
+ const dataType =
53
+ (hasComponents ? layoutResult?.dataType : undefined) ?? item.dataType ?? undefined;
54
+ const labelId = useId();
55
+ const valueId = useId();
56
+ return (
57
+ <div
58
+ className="flex flex-col gap-1"
59
+ role="group"
60
+ aria-labelledby={labelId}
61
+ aria-describedby={valueId}
62
+ >
63
+ <dt id={labelId} className="text-sm font-medium text-muted-foreground">
64
+ {label}
65
+ </dt>
66
+ <dd id={valueId} className="text-sm text-foreground">
67
+ <FieldValueDisplay value={value} dataType={dataType} />
68
+ </dd>
69
+ </div>
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Read-only detail form: layout API + record (+ optional object info) drive sections, rows, and
75
+ * field values. Uses layoutComponents to club multi-component items (address, Created By, etc.).
76
+ */
77
+ export function DetailForm({
78
+ layout,
79
+ record,
80
+ metadata = null,
81
+ objectInfo = null,
82
+ lookupRecords = null,
83
+ showSectionHeaders = true,
84
+ collapsibleSections = true,
85
+ }: DetailFormProps) {
86
+ const [collapsedSections, setCollapsedSections] = useState<Record<string, boolean>>({});
87
+
88
+ const recordId = (record.Id as string) ?? "";
89
+
90
+ const layoutObjectInfo = objectInfo ?? metadata;
91
+
92
+ const transformContext: LayoutTransformContext = useMemo(
93
+ () => ({
94
+ recordId,
95
+ objectInfo: layoutObjectInfo,
96
+ lookupRecords,
97
+ getSectionCollapsedState: (sectionId: string) => Boolean(collapsedSections[sectionId]),
98
+ }),
99
+ [recordId, layoutObjectInfo, lookupRecords, collapsedSections],
100
+ );
101
+
102
+ const computedSections = useMemo(
103
+ () => getTransformedSections(layout.sections, transformContext),
104
+ [layout.sections, transformContext],
105
+ );
106
+
107
+ const handleSectionToggle = useCallback((sectionId: string, collapsed: boolean) => {
108
+ setCollapsedSections((prev) => ({ ...prev, [sectionId]: collapsed }));
109
+ }, []);
110
+
111
+ return (
112
+ <div
113
+ className="space-y-6"
114
+ role="region"
115
+ aria-label="Record details"
116
+ aria-roledescription="Detail form"
117
+ >
118
+ {computedSections.map((section) => (
119
+ <Section
120
+ key={section.key}
121
+ sectionId={section.id}
122
+ titleLabel={section.heading}
123
+ showHeader={showSectionHeaders && section.useHeading}
124
+ collapsible={collapsibleSections && section.collapsible}
125
+ collapsed={section.collapsed}
126
+ onToggle={handleSectionToggle}
127
+ >
128
+ <div className="space-y-4">
129
+ {section.layoutRows.map((row) => (
130
+ <SectionRow key={row.key}>
131
+ {row.layoutItems.map((item) => {
132
+ const cellKey = `${section.key}-${row.key}-${item.apiName ?? item.key}`;
133
+ return item.isField ? (
134
+ <FieldCell key={cellKey} item={item} record={record} metadata={metadata} />
135
+ ) : (
136
+ <div key={cellKey} className="min-h-[2.5rem]" aria-hidden="true" />
137
+ );
138
+ })}
139
+ </SectionRow>
140
+ ))}
141
+ </div>
142
+ </Section>
143
+ ))}
144
+ </div>
145
+ );
146
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Back button and title for the record detail page.
3
+ *
4
+ * @param title - Record title (e.g. record name) shown next to the back control.
5
+ * @param onBack - Called when the user activates the back control.
6
+ */
7
+ import { Button } from "../../../../components/ui/button";
8
+ import { ArrowLeft } from "lucide-react";
9
+
10
+ interface DetailHeaderProps {
11
+ title: string;
12
+ onBack: () => void;
13
+ }
14
+
15
+ export default function DetailHeader({ title, onBack }: DetailHeaderProps) {
16
+ return (
17
+ <div className="mb-6 flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-4">
18
+ <Button
19
+ variant="ghost"
20
+ onClick={onBack}
21
+ className="w-fit"
22
+ aria-label="Go back to search results"
23
+ >
24
+ <ArrowLeft className="h-4 w-4 mr-2" aria-hidden="true" />
25
+ Back
26
+ </Button>
27
+ {title ? (
28
+ <h1 className="text-xl font-semibold text-foreground truncate" id="detail-page-title">
29
+ {title}
30
+ </h1>
31
+ ) : null}
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Alternative detail rendering: layout sections → rows → items → label/value grid.
3
+ *
4
+ * Use when you have raw Layout API response + record and do not need the full
5
+ * layoutTransformUtils + formDataTransformUtils pipeline. The primary detail view
6
+ * (DetailPage) uses DetailForm via UiApiDetailForm; use this component for other
7
+ * entry points that already have layout + record in hand.
8
+ */
9
+ import type { LayoutResponse } from "../../types/recordDetail/recordDetail";
10
+ import type { SearchResultRecordData } from "../../types/search/searchResults";
11
+ import { getNestedFieldValue } from "../../utils/fieldUtils";
12
+
13
+ interface DetailLayoutSectionsProps {
14
+ layout: LayoutResponse;
15
+ record: SearchResultRecordData;
16
+ }
17
+
18
+ interface FieldEntry {
19
+ key: string;
20
+ label: string;
21
+ value: string | number | boolean | null;
22
+ }
23
+
24
+ function getSectionFieldEntries(
25
+ section: LayoutResponse["sections"][number],
26
+ record: SearchResultRecordData,
27
+ ): FieldEntry[] {
28
+ const entries: FieldEntry[] = [];
29
+ section.layoutRows.forEach((row, rowIdx) => {
30
+ row.layoutItems.forEach((item, itemIdx) => {
31
+ item.layoutComponents.forEach((comp, compIdx) => {
32
+ if (comp.componentType !== "Field" || !comp.apiName) return;
33
+ const value = getNestedFieldValue(record.fields, comp.apiName);
34
+ const label = comp.label ?? item.label;
35
+ entries.push({
36
+ key: `${section.id}-${rowIdx}-${itemIdx}-${comp.apiName ?? compIdx}`,
37
+ label: label || comp.apiName,
38
+ value: value ?? null,
39
+ });
40
+ });
41
+ });
42
+ });
43
+ return entries;
44
+ }
45
+
46
+ export default function DetailLayoutSections({ layout, record }: DetailLayoutSectionsProps) {
47
+ return (
48
+ <div className="space-y-8" role="region" aria-label="Record details">
49
+ {layout.sections.map((section) => {
50
+ const entries = getSectionFieldEntries(section, record);
51
+ if (entries.length === 0) return null;
52
+
53
+ return (
54
+ <section
55
+ key={section.id}
56
+ className="space-y-4"
57
+ aria-labelledby={section.useHeading ? `section-${section.id}` : undefined}
58
+ >
59
+ {section.useHeading && section.heading ? (
60
+ <h3
61
+ id={`section-${section.id}`}
62
+ className="text-base font-semibold text-foreground border-b pb-2"
63
+ >
64
+ {section.heading}
65
+ </h3>
66
+ ) : null}
67
+ <dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-4">
68
+ {entries.map(({ key, label, value }) => (
69
+ <div key={key} className="flex flex-col gap-1">
70
+ <dt className="text-sm font-medium text-muted-foreground">{label}</dt>
71
+ <dd className="text-sm text-foreground">{value || "—"}</dd>
72
+ </div>
73
+ ))}
74
+ </dl>
75
+ </section>
76
+ );
77
+ })}
78
+ </div>
79
+ );
80
+ }