@salesforce/templates 66.13.4 → 66.13.6

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 (236) hide show
  1. package/lib/generators/baseGenerator.d.ts +2 -0
  2. package/lib/generators/baseGenerator.js +353 -0
  3. package/lib/generators/baseGenerator.js.map +1 -1
  4. package/lib/index.d.ts +1 -0
  5. package/lib/index.js +3 -1
  6. package/lib/index.js.map +1 -1
  7. package/lib/templates/project/angularextapp/CHANGELOG.md +258 -0
  8. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/angular.json +1 -1
  9. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
  10. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
  11. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package-lock.json +421 -394
  12. package/lib/templates/project/angularextapp/_p_/_m_/_w_/_a_/package.json +2 -2
  13. package/lib/templates/project/angularextapp/package.json +1 -1
  14. package/lib/templates/project/angularextapp/scripts/org-setup-config-schema.mjs +17 -0
  15. package/lib/templates/project/angularextapp/scripts/org-setup-utils.mjs +33 -3
  16. package/lib/templates/project/angularextapp/scripts/org-setup.mjs +535 -3
  17. package/lib/templates/project/angularintapp/CHANGELOG.md +266 -0
  18. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/angular.json +1 -1
  19. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint-plugin-angular-graphql.js +148 -0
  20. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/eslint.config.js +9 -1
  21. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package-lock.json +431 -404
  22. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/package.json +2 -2
  23. package/lib/templates/project/angularintapp/_p_/_m_/_w_/_a_/src/app/features/agentforce/agentforce-embed.service.ts +6 -1
  24. package/lib/templates/project/angularintapp/package.json +1 -1
  25. package/lib/templates/project/angularintapp/scripts/org-setup-config-schema.mjs +17 -0
  26. package/lib/templates/project/angularintapp/scripts/org-setup-utils.mjs +33 -3
  27. package/lib/templates/project/angularintapp/scripts/org-setup.mjs +535 -3
  28. package/lib/templates/project/reactexternalapp/CHANGELOG.md +1313 -0
  29. package/lib/templates/project/reactexternalapp/PRD.md +38 -0
  30. package/lib/templates/project/reactexternalapp/README.md +14 -10
  31. package/lib/templates/project/reactexternalapp/_p_/_m_/_ps_/reactexternalapp_Guest_User_Api_Access.permissionset-meta.xml +25 -0
  32. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +660 -692
  33. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +9 -7
  34. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/scripts/get-graphql-schema.mjs +8 -5
  35. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/components/SocialLogin.tsx +141 -0
  36. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +2 -0
  37. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
  38. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +71 -0
  39. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +277 -0
  40. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +185 -0
  41. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +77 -0
  42. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +182 -0
  43. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
  44. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +55 -0
  45. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
  46. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
  47. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
  48. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
  49. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
  50. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
  51. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
  52. package/lib/templates/project/{reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
  53. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
  54. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
  55. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
  56. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
  57. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
  58. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +83 -0
  59. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
  60. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
  61. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +703 -0
  62. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +90 -0
  63. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
  64. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +156 -0
  65. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +307 -0
  66. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
  67. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
  68. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
  69. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
  70. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +24 -8
  71. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/appLayout.tsx +7 -0
  72. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/CustomerWebClientChat.tsx +74 -0
  73. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
  74. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
  75. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
  76. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
  77. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/index.ts +6 -0
  78. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +49 -16
  79. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +21 -293
  80. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +22 -7
  81. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/TestChatPage.tsx +26 -0
  82. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +11 -5
  83. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/types/chat.ts +36 -0
  84. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
  85. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
  86. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
  87. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls +133 -0
  88. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig.cls-meta.xml +5 -0
  89. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls +101 -0
  90. package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleSocialLoginConfig_Test.cls-meta.xml +5 -0
  91. package/lib/templates/project/reactexternalapp/_p_/_m_/package.xml +4 -0
  92. package/lib/templates/project/reactexternalapp/package.json +8 -2
  93. package/lib/templates/project/reactexternalapp/scripts/org-setup-config-schema.mjs +120 -0
  94. package/lib/templates/project/reactexternalapp/scripts/org-setup-dev.mjs +87 -0
  95. package/lib/templates/project/reactexternalapp/scripts/org-setup-utils.mjs +569 -0
  96. package/lib/templates/project/reactexternalapp/scripts/org-setup-xml.mjs +182 -0
  97. package/lib/templates/project/reactexternalapp/scripts/org-setup.config.json +6 -2
  98. package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +1509 -328
  99. package/lib/templates/project/reactexternalapp/scripts/validate-org-setup-config.mjs +38 -0
  100. package/lib/templates/project/reactinternalapp/CHANGELOG.md +1127 -0
  101. package/lib/templates/project/reactinternalapp/PRD.md +35 -0
  102. package/lib/templates/project/reactinternalapp/README.md +14 -10
  103. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +509 -505
  104. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +9 -8
  105. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/distinctValuesService.ts +84 -0
  106. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/api/searchService.ts +71 -0
  107. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/MergedSearchResults.tsx +277 -0
  108. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/Search.tsx +185 -0
  109. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SearchResults.tsx +77 -0
  110. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/SourceSection.tsx +182 -0
  111. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/PaginationControls.tsx +185 -0
  112. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/ScopeSelector.tsx +55 -0
  113. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SearchBar.tsx +55 -0
  114. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/controls/SortControl.tsx +62 -0
  115. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/ActiveFilters.tsx +61 -0
  116. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/DefaultFilterPanel.tsx +125 -0
  117. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_/components → search/components/filters}/FilterContext.tsx +7 -20
  118. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/BooleanFilter.tsx +50 -0
  119. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/DateRangeFilter.tsx +160 -0
  120. package/lib/templates/project/{reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters → reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs}/FilterFieldWrapper.tsx +11 -6
  121. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/MultiSelectFilter.tsx +89 -0
  122. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/NumericRangeFilter.tsx +108 -0
  123. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/SelectFilter.tsx +46 -0
  124. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/filters/inputs/TextFilter.tsx +52 -0
  125. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/components/results/DefaultResultRow.tsx +109 -0
  126. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/config.json +83 -0
  127. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/{_os_ → search}/hooks/useAsyncData.ts +3 -10
  128. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useDistinctValues.ts +59 -0
  129. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/hooks/useSearch.ts +703 -0
  130. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/index.ts +90 -0
  131. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/loadConfig.ts +14 -0
  132. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/queryBuilder.ts +156 -0
  133. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/types.ts +307 -0
  134. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/debounce.ts +17 -0
  135. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/fieldUtils.ts +14 -0
  136. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/filterUtils.ts +285 -0
  137. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/search/utils/sortUtils.ts +34 -0
  138. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.tsx +16 -132
  139. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/components → components}/ObjectBreadcrumb.tsx +7 -2
  140. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/button.tsx +1 -1
  141. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/datePicker.tsx +2 -2
  142. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/table.tsx +9 -2
  143. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +49 -16
  144. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +21 -293
  145. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/Home.tsx +21 -6
  146. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +4 -4
  147. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/{_f_/_os_/utils/fieldUtils.ts → utils/accountFields.ts} +9 -0
  148. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +2 -2
  149. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.tsbuildinfo +1 -0
  150. package/lib/templates/project/reactinternalapp/package.json +4 -2
  151. package/lib/templates/project/reactinternalapp/scripts/org-setup-config-schema.mjs +24 -0
  152. package/lib/templates/project/reactinternalapp/scripts/org-setup-dev.mjs +87 -0
  153. package/lib/templates/project/reactinternalapp/scripts/org-setup-utils.mjs +569 -0
  154. package/lib/templates/project/reactinternalapp/scripts/org-setup-xml.mjs +182 -0
  155. package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +1162 -214
  156. package/lib/templates/uiBundles/angularbasic/angular.json +1 -1
  157. package/lib/templates/uiBundles/angularbasic/eslint-plugin-angular-graphql.js +148 -0
  158. package/lib/templates/uiBundles/angularbasic/eslint.config.js +9 -1
  159. package/lib/templates/uiBundles/angularbasic/package-lock.json +421 -394
  160. package/lib/templates/uiBundles/angularbasic/package.json +2 -2
  161. package/lib/templates/uiBundles/reactbasic/package-lock.json +445 -418
  162. package/lib/utils/template-placeholders.js +7 -0
  163. package/lib/utils/template-placeholders.js.map +1 -1
  164. package/lib/utils/uiBundleTemplateUtils.d.ts +3 -1
  165. package/lib/utils/uiBundleTemplateUtils.js +4 -1
  166. package/lib/utils/uiBundleTemplateUtils.js.map +1 -1
  167. package/package.json +7 -7
  168. package/lib/templates/project/reactexternalapp/AGENT.md +0 -252
  169. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
  170. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
  171. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
  172. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
  173. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
  174. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
  175. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
  176. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
  177. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
  178. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
  179. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
  180. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
  181. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
  182. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
  183. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
  184. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
  185. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
  186. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
  187. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
  188. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
  189. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
  190. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
  191. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
  192. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
  193. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
  194. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
  195. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
  196. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
  197. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
  198. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
  199. package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
  200. package/lib/templates/project/reactexternalapp/scripts/prepare-import-unique-fields.js +0 -122
  201. package/lib/templates/project/reactinternalapp/AGENT.md +0 -252
  202. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/accountSearchService.ts +0 -46
  203. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountIndustries.graphql +0 -19
  204. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/distinctAccountTypes.graphql +0 -19
  205. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/getAccountDetail.graphql +0 -121
  206. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/api/query/searchAccounts.graphql +0 -51
  207. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +0 -355
  208. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +0 -304
  209. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/Home.tsx +0 -34
  210. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +0 -82
  211. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/ActiveFilters.tsx +0 -89
  212. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/PaginationControls.tsx +0 -109
  213. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SearchBar.tsx +0 -41
  214. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/SortControl.tsx +0 -143
  215. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/BooleanFilter.tsx +0 -78
  216. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateFilter.tsx +0 -128
  217. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/DateRangeFilter.tsx +0 -70
  218. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/MultiSelectFilter.tsx +0 -97
  219. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +0 -167
  220. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +0 -52
  221. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SelectFilter.tsx +0 -97
  222. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +0 -93
  223. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useDebouncedCallback.ts +0 -34
  224. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useObjectSearchParams.ts +0 -254
  225. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/debounce.ts +0 -25
  226. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/filterUtils.ts +0 -404
  227. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/utils/sortUtils.ts +0 -38
  228. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/accountSearchService.ts +0 -46
  229. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountIndustries.graphql +0 -19
  230. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/distinctAccountTypes.graphql +0 -19
  231. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/account/query/searchAccounts.graphql +0 -51
  232. package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/graphql-operations-types.ts +0 -11260
  233. package/lib/templates/project/reactinternalapp/scripts/prepare-import-unique-fields.js +0 -122
  234. /package/lib/templates/project/angularextapp/_p_/_m_/{permissionsets → _ps_}/angularextapp_Guest_User_Api_Access.permissionset-meta.xml +0 -0
  235. /package/lib/templates/project/angularintapp/_p_/_m_/{permissionsets → _ps_}/angularintapp_Access.permissionset-meta.xml +0 -0
  236. /package/lib/templates/project/reactinternalapp/_p_/_m_/{permissionsets → _ps_}/reactinternalapp_Access.permissionset-meta.xml +0 -0
@@ -0,0 +1,89 @@
1
+ import { ChevronDown } from "lucide-react";
2
+ import { Button } from "../../../../../components/ui/button";
3
+ import { Checkbox } from "../../../../../components/ui/checkbox";
4
+ import { Label } from "../../../../../components/ui/label";
5
+ import {
6
+ Popover,
7
+ PopoverContent,
8
+ PopoverTrigger,
9
+ } from "../../../../../components/ui/popover";
10
+ import { useFilterField } from "../FilterContext";
11
+ import { FilterFieldWrapper } from "./FilterFieldWrapper";
12
+
13
+ interface MultiSelectFilterProps {
14
+ field: string;
15
+ label: string;
16
+ options: Array<{ value: string; label: string }>;
17
+ helpText?: string;
18
+ }
19
+
20
+ export function MultiSelectFilter({ field, label, options, helpText }: MultiSelectFilterProps) {
21
+ const { value, onChange } = useFilterField(field);
22
+ const selected = new Set(value?.value ? value.value.split(",").filter(Boolean) : []);
23
+
24
+ const toggle = (v: string, on: boolean) => {
25
+ const next = new Set(selected);
26
+ if (on) next.add(v);
27
+ else next.delete(v);
28
+ const joined = Array.from(next).join(",");
29
+ if (joined === "") onChange(undefined);
30
+ else onChange({ field, label, type: "multipicklist", value: joined });
31
+ };
32
+
33
+ // Summarize the selection on the collapsed trigger: the sole label when one is
34
+ // picked, a count when several are, and a placeholder when none.
35
+ const summary =
36
+ selected.size === 0
37
+ ? `Select ${label.toLowerCase()}`
38
+ : selected.size === 1
39
+ ? (options.find((o) => selected.has(o.value))?.label ?? `${selected.size} selected`)
40
+ : `${selected.size} selected`;
41
+ const triggerId = `filter-${field}`;
42
+
43
+ return (
44
+ <FilterFieldWrapper label={label} htmlFor={triggerId} helpText={helpText}>
45
+ <Popover>
46
+ <PopoverTrigger asChild>
47
+ <Button
48
+ id={triggerId}
49
+ variant="outline"
50
+ className="w-full justify-between font-normal"
51
+ aria-label={`${label}: ${summary}`}
52
+ >
53
+ <span className={`truncate ${selected.size === 0 ? "text-foreground/70" : ""}`}>
54
+ {summary}
55
+ </span>
56
+ <ChevronDown className="ml-2 size-4 shrink-0 opacity-50" aria-hidden="true" />
57
+ </Button>
58
+ </PopoverTrigger>
59
+ <PopoverContent
60
+ align="start"
61
+ aria-label={label}
62
+ className="w-(--radix-popover-trigger-width) max-h-72 overflow-y-auto"
63
+ >
64
+ <div role="group" className="flex flex-col gap-1">
65
+ {options.length === 0 ? (
66
+ <p className="px-1 py-2 text-sm text-muted-foreground">No options available.</p>
67
+ ) : (
68
+ options.map((opt) => {
69
+ const id = `filter-${field}-${opt.value}`;
70
+ return (
71
+ <div key={opt.value} className="flex items-center gap-2">
72
+ <Checkbox
73
+ id={id}
74
+ checked={selected.has(opt.value)}
75
+ onCheckedChange={(checked) => toggle(opt.value, checked === true)}
76
+ />
77
+ <Label htmlFor={id} className="text-sm font-normal cursor-pointer">
78
+ {opt.label}
79
+ </Label>
80
+ </div>
81
+ );
82
+ })
83
+ )}
84
+ </div>
85
+ </PopoverContent>
86
+ </Popover>
87
+ </FilterFieldWrapper>
88
+ );
89
+ }
@@ -0,0 +1,108 @@
1
+ import { useEffect, useId, useRef, useState } from "react";
2
+ import { Input } from "../../../../../components/ui/input";
3
+ import { useFilterField } from "../FilterContext";
4
+ import { FilterFieldWrapper } from "./FilterFieldWrapper";
5
+ import { debounce, FILTER_DEBOUNCE_MS } from "../../../utils/debounce";
6
+
7
+ interface NumericRangeFilterProps {
8
+ field: string;
9
+ label: string;
10
+ helpText?: string;
11
+ min?: number;
12
+ max?: number;
13
+ }
14
+
15
+ export function NumericRangeFilter({ field, label, helpText, min, max }: NumericRangeFilterProps) {
16
+ const { value, onChange } = useFilterField(field);
17
+ const errorId = useId();
18
+ const [draftMin, setDraftMin] = useState(value?.min ?? "");
19
+ const [draftMax, setDraftMax] = useState(value?.max ?? "");
20
+
21
+ const onChangeRef = useRef(onChange);
22
+ useEffect(() => {
23
+ onChangeRef.current = onChange;
24
+ });
25
+ // Build the debounced caller in a mount effect, not during render: creating it
26
+ // inline would read onChangeRef.current at render time (react-hooks/refs). It
27
+ // stays a single stable instance and always calls the latest onChange via the ref.
28
+ const debouncedRef = useRef<((nextMin: string, nextMax: string) => void) | undefined>(undefined);
29
+ useEffect(() => {
30
+ debouncedRef.current = debounce((nextMin: string, nextMax: string) => {
31
+ if (!nextMin && !nextMax) onChangeRef.current(undefined);
32
+ else
33
+ onChangeRef.current({
34
+ field,
35
+ label,
36
+ type: "numeric",
37
+ min: nextMin || undefined,
38
+ max: nextMax || undefined,
39
+ });
40
+ }, FILTER_DEBOUNCE_MS);
41
+ }, [field, label]);
42
+
43
+ useEffect(() => {
44
+ setDraftMin(value?.min ?? "");
45
+ setDraftMax(value?.max ?? "");
46
+ }, [value?.min, value?.max]);
47
+
48
+ const isOutOfBounds = (v: string) => {
49
+ if (v === "") return false;
50
+ const n = Number(v);
51
+ return (min != null && n < min) || (max != null && n > max);
52
+ };
53
+ const minOutOfBounds = isOutOfBounds(draftMin);
54
+ const maxOutOfBounds = isOutOfBounds(draftMax);
55
+ const isRangeInverted = draftMin !== "" && draftMax !== "" && Number(draftMin) > Number(draftMax);
56
+ const hasError = minOutOfBounds || maxOutOfBounds || isRangeInverted;
57
+
58
+ const boundsLabel =
59
+ min != null && max != null
60
+ ? `${min}–${max}`
61
+ : min != null
62
+ ? `${min} or more`
63
+ : max != null
64
+ ? `${max} or less`
65
+ : null;
66
+
67
+ const errorMessage = isRangeInverted
68
+ ? "Min must not exceed max"
69
+ : (minOutOfBounds || maxOutOfBounds) && boundsLabel
70
+ ? `Value must be between ${boundsLabel}`
71
+ : undefined;
72
+
73
+ return (
74
+ <FilterFieldWrapper label={label} helpText={helpText} error={errorMessage} errorId={errorId}>
75
+ <div className="flex items-center gap-2">
76
+ <Input
77
+ type="number"
78
+ placeholder="Min"
79
+ min={min}
80
+ max={max}
81
+ value={draftMin}
82
+ onChange={(e) => {
83
+ setDraftMin(e.target.value);
84
+ debouncedRef.current?.(e.target.value, draftMax);
85
+ }}
86
+ aria-label={`${label} minimum`}
87
+ aria-invalid={hasError || undefined}
88
+ aria-describedby={hasError ? errorId : undefined}
89
+ />
90
+ <span className="text-sm text-muted-foreground">–</span>
91
+ <Input
92
+ type="number"
93
+ placeholder="Max"
94
+ min={min}
95
+ max={max}
96
+ value={draftMax}
97
+ onChange={(e) => {
98
+ setDraftMax(e.target.value);
99
+ debouncedRef.current?.(draftMin, e.target.value);
100
+ }}
101
+ aria-label={`${label} maximum`}
102
+ aria-invalid={hasError || undefined}
103
+ aria-describedby={hasError ? errorId : undefined}
104
+ />
105
+ </div>
106
+ </FilterFieldWrapper>
107
+ );
108
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ Select,
3
+ SelectContent,
4
+ SelectItem,
5
+ SelectTrigger,
6
+ SelectValue,
7
+ } from "../../../../../components/ui/select";
8
+ import { useFilterField } from "../FilterContext";
9
+ import { FilterFieldWrapper } from "./FilterFieldWrapper";
10
+
11
+ const ALL_VALUE = "__all__";
12
+
13
+ interface SelectFilterProps {
14
+ field: string;
15
+ label: string;
16
+ options: Array<{ value: string; label: string }>;
17
+ helpText?: string;
18
+ }
19
+
20
+ export function SelectFilter({ field, label, options, helpText }: SelectFilterProps) {
21
+ const { value, onChange } = useFilterField(field);
22
+ const id = `filter-${field}`;
23
+ return (
24
+ <FilterFieldWrapper label={label} htmlFor={id} helpText={helpText}>
25
+ <Select
26
+ value={value?.value ?? ALL_VALUE}
27
+ onValueChange={(v) => {
28
+ if (v === ALL_VALUE) onChange(undefined);
29
+ else onChange({ field, label, type: "picklist", value: v });
30
+ }}
31
+ >
32
+ <SelectTrigger id={id} className="w-full">
33
+ <SelectValue placeholder={`Select ${label.toLowerCase()}`} />
34
+ </SelectTrigger>
35
+ <SelectContent>
36
+ <SelectItem value={ALL_VALUE}>All</SelectItem>
37
+ {options.map((opt) => (
38
+ <SelectItem key={opt.value} value={opt.value}>
39
+ {opt.label}
40
+ </SelectItem>
41
+ ))}
42
+ </SelectContent>
43
+ </Select>
44
+ </FilterFieldWrapper>
45
+ );
46
+ }
@@ -0,0 +1,52 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { Input } from "../../../../../components/ui/input";
3
+ import { useFilterField } from "../FilterContext";
4
+ import { FilterFieldWrapper } from "./FilterFieldWrapper";
5
+ import { debounce, FILTER_DEBOUNCE_MS } from "../../../utils/debounce";
6
+
7
+ interface TextFilterProps {
8
+ field: string;
9
+ label: string;
10
+ placeholder?: string;
11
+ helpText?: string;
12
+ }
13
+
14
+ export function TextFilter({ field, label, placeholder, helpText }: TextFilterProps) {
15
+ const { value, onChange } = useFilterField(field);
16
+ const [draft, setDraft] = useState(value?.value ?? "");
17
+
18
+ const onChangeRef = useRef(onChange);
19
+ useEffect(() => {
20
+ onChangeRef.current = onChange;
21
+ });
22
+ // Build the debounced caller in a mount effect, not during render: creating it
23
+ // inline would read onChangeRef.current at render time (react-hooks/refs). It
24
+ // stays a single stable instance and always calls the latest onChange via the ref.
25
+ const debouncedRef = useRef<((next: string) => void) | undefined>(undefined);
26
+ useEffect(() => {
27
+ debouncedRef.current = debounce((next: string) => {
28
+ if (next === "") onChangeRef.current(undefined);
29
+ else onChangeRef.current({ field, label, type: "text", value: next });
30
+ }, FILTER_DEBOUNCE_MS);
31
+ }, [field, label]);
32
+
33
+ useEffect(() => {
34
+ setDraft(value?.value ?? "");
35
+ }, [value?.value]);
36
+
37
+ const id = `filter-${field}`;
38
+ return (
39
+ <FilterFieldWrapper label={label} htmlFor={id} helpText={helpText}>
40
+ <Input
41
+ id={id}
42
+ type="text"
43
+ value={draft}
44
+ placeholder={placeholder}
45
+ onChange={(e) => {
46
+ setDraft(e.target.value);
47
+ debouncedRef.current?.(e.target.value);
48
+ }}
49
+ />
50
+ </FilterFieldWrapper>
51
+ );
52
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Default per-node row used when no `renderResult` is supplied for a source.
3
+ *
4
+ * Layout:
5
+ * - Title: first entry in `displayFields` (preferring displayValue).
6
+ * - Subtitle: remaining display fields joined with " · ".
7
+ * - When `routePattern` is configured on the source, the row is wrapped in
8
+ * a react-router `<Link>` whose `to` is built by substituting `:fieldName`
9
+ * tokens with the corresponding field values (special token `:id`
10
+ * resolves to the configured `idField`, default "Id").
11
+ *
12
+ * Applications can still pass `renderResult[sourceKey]` to override.
13
+ */
14
+
15
+ import type { ReactNode } from "react";
16
+ import { Link } from "react-router";
17
+ import { fieldValue } from "../../utils/fieldUtils";
18
+ import type { DisplayField, SObjectSourceConfig } from "../../types";
19
+
20
+ const ROUTE_TOKEN = /:([A-Za-z_][A-Za-z0-9_]*)/g;
21
+
22
+ function fieldName(f: DisplayField): string {
23
+ return typeof f === "string" ? f : f.name;
24
+ }
25
+
26
+ /** Resolves a single dot-path against a node, returning a string or null. */
27
+ function readPath(node: unknown, path: string): string | null {
28
+ if (!node || typeof node !== "object") return null;
29
+ const parts = path.split(".");
30
+ let cursor: unknown = node;
31
+ for (let i = 0; i < parts.length - 1; i++) {
32
+ if (!cursor || typeof cursor !== "object") return null;
33
+ cursor = (cursor as Record<string, unknown>)[parts[i]];
34
+ }
35
+ const leaf = (cursor as Record<string, unknown> | null)?.[parts[parts.length - 1]];
36
+ if (leaf == null) return null;
37
+ if (typeof leaf === "object") {
38
+ return fieldValue(leaf as { value?: unknown; displayValue?: string | null });
39
+ }
40
+ return String(leaf);
41
+ }
42
+
43
+ /** Reads the user-facing string for a top-level display field. */
44
+ function readDisplayField(node: unknown, field: DisplayField): string | null {
45
+ if (typeof field === "string") return readPath(node, field);
46
+ if ("raw" in field) return readPath(node, field.name);
47
+ // Relationship: prefer the first scalar subfield's display value.
48
+ const sub = field.subfields[0];
49
+ if (!sub) return null;
50
+ const subName = fieldName(sub);
51
+ return readPath(node, `${field.name}.${subName}`);
52
+ }
53
+
54
+ /** Substitutes `:fieldName` tokens in routePattern with node values. */
55
+ function resolveRoute(pattern: string, node: unknown, idField: string): string | null {
56
+ let failed = false;
57
+ const out = pattern.replace(ROUTE_TOKEN, (_, token) => {
58
+ const path = token === "id" ? idField : token;
59
+ const value = readPath(node, path);
60
+ if (value == null) {
61
+ failed = true;
62
+ return "";
63
+ }
64
+ return encodeURIComponent(value);
65
+ });
66
+ return failed ? null : out;
67
+ }
68
+
69
+ interface DefaultResultRowProps {
70
+ node: unknown;
71
+ source: SObjectSourceConfig;
72
+ }
73
+
74
+ export function DefaultResultRow({ node, source }: DefaultResultRowProps) {
75
+ const idField = source.idField ?? "Id";
76
+ const fields = source.displayFields;
77
+ const titleField = fields[0];
78
+ const title = titleField ? readDisplayField(node, titleField) : null;
79
+
80
+ const subtitleParts = fields
81
+ .slice(1)
82
+ .map((f) => readDisplayField(node, f))
83
+ .filter((s): s is string => s != null && s !== "");
84
+
85
+ const content: ReactNode = (
86
+ <>
87
+ <span className="font-medium">{title ?? "—"}</span>
88
+ {subtitleParts.length > 0 && (
89
+ <p className="text-sm text-muted-foreground">{subtitleParts.join(" · ")}</p>
90
+ )}
91
+ </>
92
+ );
93
+
94
+ if (source.routePattern) {
95
+ const href = resolveRoute(source.routePattern, node, idField);
96
+ if (href) {
97
+ return (
98
+ <Link
99
+ to={href}
100
+ className="block hover:bg-accent rounded-md px-3 -mx-3 py-1 transition-colors"
101
+ >
102
+ {content}
103
+ </Link>
104
+ );
105
+ }
106
+ }
107
+
108
+ return <div className="px-3 -mx-3 py-1">{content}</div>;
109
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "pagination": {
3
+ "pageSize": 10,
4
+ "pageSizeOptions": [5, 10, 20]
5
+ },
6
+ "sources": [
7
+ {
8
+ "kind": "sobject",
9
+ "key": "accounts",
10
+ "objectName": "Account",
11
+ "label": "Accounts",
12
+ "labelSingular": "Account",
13
+ "routePattern": "/accounts/:id",
14
+ "searchableFields": ["Name", "Phone", "Industry"],
15
+ "displayFields": [
16
+ "Name",
17
+ "Industry",
18
+ "Type",
19
+ "Phone",
20
+ "AnnualRevenue",
21
+ { "name": "Owner", "subfields": ["Name"] }
22
+ ],
23
+ "filterBy": [
24
+ { "field": "Industry", "label": "Industry", "type": "picklist" },
25
+ { "field": "Type", "label": "Type", "type": "picklist" },
26
+ { "field": "AnnualRevenue", "label": "Annual Revenue", "type": "numeric" }
27
+ ],
28
+ "sortBy": [
29
+ { "field": "Name", "label": "Name" },
30
+ { "field": "Industry", "label": "Industry" },
31
+ { "field": "AnnualRevenue", "label": "Annual Revenue" },
32
+ { "field": "CreatedDate", "label": "Created Date" }
33
+ ]
34
+ },
35
+ {
36
+ "kind": "sobject",
37
+ "key": "contacts",
38
+ "objectName": "Contact",
39
+ "label": "Contacts",
40
+ "labelSingular": "Contact",
41
+ "routePattern": "/contacts/:id",
42
+ "searchableFields": ["Name", "Email", "Phone"],
43
+ "displayFields": [
44
+ "Name",
45
+ "Title",
46
+ "Email",
47
+ "Phone",
48
+ { "name": "Account", "subfields": ["Name"] }
49
+ ],
50
+ "sortBy": [
51
+ { "field": "Name", "label": "Name" },
52
+ { "field": "CreatedDate", "label": "Created Date" }
53
+ ]
54
+ },
55
+ {
56
+ "kind": "sobject",
57
+ "key": "opportunities",
58
+ "objectName": "Opportunity",
59
+ "label": "Opportunities",
60
+ "labelSingular": "Opportunity",
61
+ "routePattern": "/opportunities/:id",
62
+ "searchableFields": ["Name"],
63
+ "displayFields": [
64
+ "Name",
65
+ "StageName",
66
+ "Amount",
67
+ "CloseDate",
68
+ { "name": "Account", "subfields": ["Name"] }
69
+ ],
70
+ "filterBy": [
71
+ { "field": "StageName", "label": "Stage", "type": "picklist" },
72
+ { "field": "Amount", "label": "Amount", "type": "numeric" },
73
+ { "field": "CloseDate", "label": "Close Date", "type": "daterange" }
74
+ ],
75
+ "sortBy": [
76
+ { "field": "CloseDate", "label": "Close Date" },
77
+ { "field": "Amount", "label": "Amount" },
78
+ { "field": "Name", "label": "Name" }
79
+ ],
80
+ "defaultSort": { "field": "CloseDate", "direction": "DESC" }
81
+ }
82
+ ]
83
+ }
@@ -7,13 +7,9 @@ interface UseAsyncDataResult<T> {
7
7
  }
8
8
 
9
9
  /**
10
- * Runs an async fetcher on mount and whenever `deps` change.
11
- * Returns the loading/error/data state. Does not cache — every call
12
- * to the fetcher hits the source directly.
13
- *
14
- * A cleanup flag prevents state updates if the component unmounts
15
- * or deps change before the fetch completes (avoids React warnings
16
- * and stale updates from out-of-order responses).
10
+ * Runs an async fetcher on mount and whenever `deps` change. Stale responses
11
+ * (deps changed during a fetch, or component unmounted) are dropped via a
12
+ * cancellation flag.
17
13
  */
18
14
  export function useAsyncData<T>(
19
15
  fetcher: () => Promise<T>,
@@ -29,7 +25,6 @@ export function useAsyncData<T>(
29
25
  fetcherRef.current = fetcher;
30
26
  });
31
27
 
32
- // Detect dep changes during render to reset loading state and bump generation
33
28
  const [prevDeps, setPrevDeps] = useState(deps);
34
29
  if (deps.length !== prevDeps.length || deps.some((d, i) => d !== prevDeps[i])) {
35
30
  setPrevDeps(deps);
@@ -40,7 +35,6 @@ export function useAsyncData<T>(
40
35
 
41
36
  useEffect(() => {
42
37
  let cancelled = false;
43
-
44
38
  fetcherRef
45
39
  .current()
46
40
  .then((result) => {
@@ -53,7 +47,6 @@ export function useAsyncData<T>(
53
47
  .finally(() => {
54
48
  if (!cancelled) setLoading(false);
55
49
  });
56
-
57
50
  return () => {
58
51
  cancelled = true;
59
52
  };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Caches and serves distinct picklist values fetched via the GraphQL
3
+ * aggregate API. The cache key is `${objectName}.${fieldName}`; entries
4
+ * persist for the lifetime of the page so repeated mounts of the same
5
+ * picklist filter share the same response.
6
+ *
7
+ * Pass `null` to short-circuit the fetch (e.g. when inline options are
8
+ * available on the filter config).
9
+ */
10
+
11
+ import { useEffect, useState } from "react";
12
+ import { fetchDistinctValues, type PicklistOption } from "../api/distinctValuesService";
13
+
14
+ const cache = new Map<string, PicklistOption[]>();
15
+ const inflight = new Map<string, Promise<PicklistOption[]>>();
16
+
17
+ interface DistinctValuesQuery {
18
+ objectName: string;
19
+ fieldName: string;
20
+ }
21
+
22
+ export function useDistinctValues(query: DistinctValuesQuery | null): PicklistOption[] | null {
23
+ const cacheKey = query ? `${query.objectName}.${query.fieldName}` : null;
24
+ const [options, setOptions] = useState<PicklistOption[] | null>(() =>
25
+ cacheKey ? (cache.get(cacheKey) ?? null) : null,
26
+ );
27
+
28
+ useEffect(() => {
29
+ if (!query || !cacheKey) return;
30
+ const cached = cache.get(cacheKey);
31
+ if (cached) {
32
+ setOptions(cached);
33
+ return;
34
+ }
35
+ let cancelled = false;
36
+ const existing = inflight.get(cacheKey);
37
+ const promise =
38
+ existing ??
39
+ fetchDistinctValues(query.objectName, query.fieldName).then((result) => {
40
+ cache.set(cacheKey, result);
41
+ inflight.delete(cacheKey);
42
+ return result;
43
+ });
44
+ if (!existing) inflight.set(cacheKey, promise);
45
+ promise
46
+ .then((result) => {
47
+ if (!cancelled) setOptions(result);
48
+ })
49
+ .catch((err: unknown) => {
50
+ inflight.delete(cacheKey);
51
+ console.error(`Failed to fetch distinct values for ${cacheKey}:`, err);
52
+ });
53
+ return () => {
54
+ cancelled = true;
55
+ };
56
+ }, [cacheKey, query]);
57
+
58
+ return options;
59
+ }