@salesforce/webapp-template-app-react-template-b2x-experimental 1.109.4 → 1.109.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 (122) hide show
  1. package/dist/CHANGELOG.md +16 -0
  2. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/package.json +6 -5
  3. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/api/graphql-operations-types.ts +11260 -0
  4. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/components/ui/sonner.tsx +20 -0
  5. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/api/accountSearchService.ts +46 -0
  6. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +19 -0
  7. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +19 -0
  8. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/api/query/getAccountDetail.graphql +121 -0
  9. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/api/query/searchAccounts.graphql +51 -0
  10. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +357 -0
  11. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/pages/AccountSearch.tsx +275 -0
  12. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/__examples__/pages/Home.tsx +34 -0
  13. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/api/objectSearchService.ts +84 -0
  14. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/ActiveFilters.tsx +89 -0
  15. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/FilterPanel.tsx +127 -0
  16. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/ObjectBreadcrumb.tsx +66 -0
  17. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/PaginationControls.tsx +151 -0
  18. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/SearchBar.tsx +41 -0
  19. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/SortControl.tsx +143 -0
  20. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/BooleanFilter.tsx +94 -0
  21. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/DateFilter.tsx +138 -0
  22. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/DateRangeFilter.tsx +78 -0
  23. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/MultiSelectFilter.tsx +106 -0
  24. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/NumericRangeFilter.tsx +102 -0
  25. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/SearchFilter.tsx +40 -0
  26. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/SelectFilter.tsx +97 -0
  27. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/components/filters/TextFilter.tsx +77 -0
  28. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/hooks/useAsyncData.ts +53 -0
  29. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/hooks/useCachedAsyncData.ts +183 -0
  30. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/hooks/useObjectSearchParams.ts +225 -0
  31. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/utils/debounce.ts +22 -0
  32. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/utils/fieldUtils.ts +29 -0
  33. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/utils/filterUtils.ts +372 -0
  34. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/object-search/utils/sortUtils.ts +38 -0
  35. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/pages/Home.tsx +10 -11
  36. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/routes.tsx +8 -20
  37. package/dist/package-lock.json +2 -2
  38. package/dist/package.json +1 -1
  39. package/package.json +1 -1
  40. package/dist/.a4drules/skills/designing-webapp-ui-ux/SKILL.md +0 -271
  41. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/charts.csv +0 -26
  42. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/colors.csv +0 -97
  43. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/icons.csv +0 -101
  44. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/landing.csv +0 -31
  45. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/products.csv +0 -97
  46. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/react-performance.csv +0 -45
  47. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/stacks/html-tailwind.csv +0 -56
  48. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/stacks/react.csv +0 -54
  49. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/stacks/shadcn.csv +0 -61
  50. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/styles.csv +0 -68
  51. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/typography.csv +0 -58
  52. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/ui-reasoning.csv +0 -101
  53. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/ux-guidelines.csv +0 -100
  54. package/dist/.a4drules/skills/designing-webapp-ui-ux/data/web-interface.csv +0 -31
  55. package/dist/.a4drules/skills/designing-webapp-ui-ux/scripts/core.js +0 -255
  56. package/dist/.a4drules/skills/designing-webapp-ui-ux/scripts/design_system.js +0 -861
  57. package/dist/.a4drules/skills/designing-webapp-ui-ux/scripts/search.js +0 -98
  58. package/dist/.a4drules/skills/integrating-unsplash-images/SKILL.md +0 -71
  59. package/dist/.a4drules/skills/integrating-unsplash-images/implementation/usage.md +0 -159
  60. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectDetailService.ts +0 -102
  61. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoGraphQLService.ts +0 -137
  62. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoService.ts +0 -95
  63. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/recordListGraphQLService.ts +0 -364
  64. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailFields.tsx +0 -55
  65. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailForm.tsx +0 -146
  66. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailHeader.tsx +0 -34
  67. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/DetailLayoutSections.tsx +0 -80
  68. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/Section.tsx +0 -108
  69. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/SectionRow.tsx +0 -20
  70. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/UiApiDetailForm.tsx +0 -140
  71. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FieldValueDisplay.tsx +0 -73
  72. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedAddress.tsx +0 -29
  73. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedEmail.tsx +0 -17
  74. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedPhone.tsx +0 -24
  75. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedText.tsx +0 -11
  76. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/formatted/FormattedUrl.tsx +0 -29
  77. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterField.tsx +0 -54
  78. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterInput.tsx +0 -55
  79. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FilterSelect.tsx +0 -72
  80. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/filters/FiltersPanel.tsx +0 -380
  81. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/forms/filters-form.tsx +0 -114
  82. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/forms/submit-button.tsx +0 -47
  83. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/GlobalSearchInput.tsx +0 -114
  84. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/ResultCardFields.tsx +0 -71
  85. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchHeader.tsx +0 -31
  86. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchPagination.tsx +0 -144
  87. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchResultCard.tsx +0 -138
  88. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/search/SearchResultsPanel.tsx +0 -197
  89. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/shared/LoadingFallback.tsx +0 -61
  90. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/constants.ts +0 -39
  91. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/filters/FilterInput.tsx +0 -55
  92. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/filters/FilterSelect.tsx +0 -72
  93. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/form.tsx +0 -209
  94. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useObjectInfoBatch.ts +0 -72
  95. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useObjectSearchData.ts +0 -174
  96. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useRecordDetailLayout.ts +0 -137
  97. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useRecordListGraphQL.ts +0 -135
  98. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/pages/DetailPage.tsx +0 -109
  99. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/pages/GlobalSearch.tsx +0 -235
  100. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/filters/filters.ts +0 -121
  101. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/filters/picklist.ts +0 -6
  102. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/objectInfo/objectInfo.ts +0 -49
  103. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/recordDetail/recordDetail.ts +0 -61
  104. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/schema.d.ts +0 -200
  105. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/search/searchResults.ts +0 -229
  106. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/apiUtils.ts +0 -59
  107. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/cacheUtils.ts +0 -76
  108. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/debounce.ts +0 -90
  109. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/fieldUtils.ts +0 -354
  110. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/fieldValueExtractor.ts +0 -67
  111. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/filterUtils.ts +0 -32
  112. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/formDataTransformUtils.ts +0 -260
  113. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/formUtils.ts +0 -142
  114. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLNodeFieldUtils.ts +0 -186
  115. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLObjectInfoAdapter.ts +0 -77
  116. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLRecordAdapter.ts +0 -90
  117. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/layoutTransformUtils.ts +0 -236
  118. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/linkUtils.ts +0 -14
  119. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/paginationUtils.ts +0 -49
  120. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/recordUtils.ts +0 -159
  121. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/sanitizationUtils.ts +0 -50
  122. package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/index.ts +0 -120
@@ -0,0 +1,20 @@
1
+ import { Toaster as Sonner } from 'sonner';
2
+
3
+ /**
4
+ * Renders the toast container. Use with `toast()` from this module for messages
5
+ * with title, description, actions, and variants (success, error, warning).
6
+ *
7
+ * @example
8
+ * toast("Event has been created", {
9
+ * description: "Sunday, December 03, 2023 at 9:00 AM",
10
+ * action: { label: "Undo", onClick: () => {} },
11
+ * });
12
+ * toast.success("Saved!");
13
+ * toast.error("Something went wrong");
14
+ * toast.warning("Please review");
15
+ */
16
+ export function Toaster() {
17
+ return <Sonner position="top-right" richColors />;
18
+ }
19
+
20
+ export { toast } from 'sonner';
@@ -0,0 +1,46 @@
1
+ import SEARCH_ACCOUNTS_QUERY from "./query/searchAccounts.graphql?raw";
2
+ import DISTINCT_INDUSTRIES_QUERY from "./query/distinctAccountIndustries.graphql?raw";
3
+ import DISTINCT_TYPES_QUERY from "./query/distinctAccountTypes.graphql?raw";
4
+ import {
5
+ searchObjects,
6
+ fetchDistinctValues,
7
+ type ObjectSearchOptions,
8
+ type PicklistOption,
9
+ } from "../../api/objectSearchService";
10
+ import type {
11
+ SearchAccountsQuery,
12
+ SearchAccountsQueryVariables,
13
+ DistinctAccountIndustriesQuery,
14
+ DistinctAccountTypesQuery,
15
+ } from "../../../../api/graphql-operations-types";
16
+
17
+ export type AccountSearchResult = NonNullable<SearchAccountsQuery["uiapi"]["query"]["Account"]>;
18
+
19
+ export type AccountSearchOptions = ObjectSearchOptions<
20
+ SearchAccountsQueryVariables["where"],
21
+ SearchAccountsQueryVariables["orderBy"]
22
+ >;
23
+
24
+ export type { PicklistOption };
25
+
26
+ export async function searchAccounts(
27
+ options: AccountSearchOptions = {},
28
+ ): Promise<AccountSearchResult> {
29
+ return searchObjects<AccountSearchResult, SearchAccountsQuery, SearchAccountsQueryVariables>(
30
+ SEARCH_ACCOUNTS_QUERY,
31
+ "Account",
32
+ options,
33
+ );
34
+ }
35
+
36
+ export async function fetchDistinctIndustries(): Promise<PicklistOption[]> {
37
+ return fetchDistinctValues<DistinctAccountIndustriesQuery>(
38
+ DISTINCT_INDUSTRIES_QUERY,
39
+ "Account",
40
+ "Industry",
41
+ );
42
+ }
43
+
44
+ export async function fetchDistinctTypes(): Promise<PicklistOption[]> {
45
+ return fetchDistinctValues<DistinctAccountTypesQuery>(DISTINCT_TYPES_QUERY, "Account", "Type");
46
+ }
@@ -0,0 +1,19 @@
1
+ query DistinctAccountIndustries {
2
+ uiapi {
3
+ aggregate {
4
+ Account(groupBy: { Industry: { group: true } }) {
5
+ edges {
6
+ node {
7
+ aggregate @optional {
8
+ Industry @optional {
9
+ value
10
+ displayValue
11
+ label
12
+ }
13
+ }
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ query DistinctAccountTypes {
2
+ uiapi {
3
+ aggregate {
4
+ Account(groupBy: { Type: { group: true } }) {
5
+ edges {
6
+ node {
7
+ aggregate @optional {
8
+ Type @optional {
9
+ value
10
+ displayValue
11
+ label
12
+ }
13
+ }
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,121 @@
1
+ query GetAccountDetail($id: ID!) {
2
+ uiapi {
3
+ query {
4
+ Account(where: { Id: { eq: $id } }) {
5
+ edges {
6
+ node {
7
+ Id
8
+ Name @optional {
9
+ value
10
+ displayValue
11
+ }
12
+ Owner @optional {
13
+ Name @optional {
14
+ value
15
+ displayValue
16
+ }
17
+ }
18
+ Phone @optional {
19
+ value
20
+ displayValue
21
+ }
22
+ Fax @optional {
23
+ value
24
+ displayValue
25
+ }
26
+ Parent @optional {
27
+ Name @optional {
28
+ value
29
+ displayValue
30
+ }
31
+ }
32
+ Website @optional {
33
+ value
34
+ displayValue
35
+ }
36
+ Type @optional {
37
+ value
38
+ displayValue
39
+ }
40
+ NumberOfEmployees @optional {
41
+ value
42
+ displayValue
43
+ }
44
+ Industry @optional {
45
+ value
46
+ displayValue
47
+ }
48
+ AnnualRevenue @optional {
49
+ value
50
+ displayValue
51
+ }
52
+ Description @optional {
53
+ value
54
+ displayValue
55
+ }
56
+ BillingStreet @optional {
57
+ value
58
+ displayValue
59
+ }
60
+ BillingCity @optional {
61
+ value
62
+ displayValue
63
+ }
64
+ BillingState @optional {
65
+ value
66
+ displayValue
67
+ }
68
+ BillingPostalCode @optional {
69
+ value
70
+ displayValue
71
+ }
72
+ BillingCountry @optional {
73
+ value
74
+ displayValue
75
+ }
76
+ ShippingStreet @optional {
77
+ value
78
+ displayValue
79
+ }
80
+ ShippingCity @optional {
81
+ value
82
+ displayValue
83
+ }
84
+ ShippingState @optional {
85
+ value
86
+ displayValue
87
+ }
88
+ ShippingPostalCode @optional {
89
+ value
90
+ displayValue
91
+ }
92
+ ShippingCountry @optional {
93
+ value
94
+ displayValue
95
+ }
96
+ CreatedBy @optional {
97
+ Name @optional {
98
+ value
99
+ displayValue
100
+ }
101
+ }
102
+ CreatedDate @optional {
103
+ value
104
+ displayValue
105
+ }
106
+ LastModifiedBy @optional {
107
+ Name @optional {
108
+ value
109
+ displayValue
110
+ }
111
+ }
112
+ LastModifiedDate @optional {
113
+ value
114
+ displayValue
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,51 @@
1
+ query SearchAccounts(
2
+ $first: Int
3
+ $after: String
4
+ $where: Account_Filter
5
+ $orderBy: Account_OrderBy
6
+ ) {
7
+ uiapi {
8
+ query {
9
+ Account(first: $first, after: $after, where: $where, orderBy: $orderBy) {
10
+ edges {
11
+ node {
12
+ Id
13
+ Name @optional {
14
+ value
15
+ displayValue
16
+ }
17
+ Industry @optional {
18
+ value
19
+ displayValue
20
+ }
21
+ Type @optional {
22
+ value
23
+ displayValue
24
+ }
25
+ Phone @optional {
26
+ value
27
+ displayValue
28
+ }
29
+ Owner @optional {
30
+ Name @optional {
31
+ value
32
+ displayValue
33
+ }
34
+ }
35
+ AnnualRevenue @optional {
36
+ value
37
+ displayValue
38
+ }
39
+ }
40
+ }
41
+ pageInfo {
42
+ hasNextPage
43
+ hasPreviousPage
44
+ endCursor
45
+ startCursor
46
+ }
47
+ totalCount
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,357 @@
1
+ import { useState } from "react";
2
+ import { useParams, useNavigate } from "react-router";
3
+ import { createDataSDK } from "@salesforce/sdk-data";
4
+ import { AlertCircle, ChevronDown, ChevronRight, FileQuestion } from "lucide-react";
5
+ import GET_ACCOUNT_DETAIL from "../api/query/getAccountDetail.graphql?raw";
6
+ import type {
7
+ GetAccountDetailQuery,
8
+ GetAccountDetailQueryVariables,
9
+ } from "../../../../api/graphql-operations-types";
10
+ import { Alert, AlertTitle, AlertDescription } from "../../../../components/ui/alert";
11
+ import { Button } from "../../../../components/ui/button";
12
+ import { Card, CardContent } from "../../../../components/ui/card";
13
+ import { fieldValue, getAddressFieldLines, formatDateTimeField } from "../../utils/fieldUtils";
14
+ import {
15
+ Collapsible,
16
+ CollapsibleTrigger,
17
+ CollapsibleContent,
18
+ } from "../../../../components/ui/collapsible";
19
+ import { Separator } from "../../../../components/ui/separator";
20
+ import { Skeleton } from "../../../../components/ui/skeleton";
21
+ import { useCachedAsyncData } from "../../hooks/useCachedAsyncData";
22
+ import { ObjectBreadcrumb } from "../../components/ObjectBreadcrumb";
23
+
24
+ type AccountNode = NonNullable<
25
+ NonNullable<
26
+ NonNullable<NonNullable<GetAccountDetailQuery["uiapi"]["query"]["Account"]>["edges"]>[number]
27
+ >["node"]
28
+ >;
29
+
30
+ async function fetchAccountDetail(recordId: string): Promise<AccountNode | null | undefined> {
31
+ const data = await createDataSDK();
32
+ const response = await data.graphql?.<GetAccountDetailQuery, GetAccountDetailQueryVariables>(
33
+ GET_ACCOUNT_DETAIL,
34
+ { id: recordId },
35
+ );
36
+
37
+ if (response?.errors?.length) {
38
+ throw new Error(response.errors.map((e) => e.message).join("; "));
39
+ }
40
+
41
+ return response?.data?.uiapi?.query?.Account?.edges?.[0]?.node;
42
+ }
43
+
44
+ export default function AccountObjectDetail() {
45
+ const { recordId } = useParams();
46
+ const navigate = useNavigate();
47
+
48
+ const {
49
+ data: account,
50
+ loading,
51
+ error,
52
+ } = useCachedAsyncData(() => fetchAccountDetail(recordId!), [recordId], {
53
+ key: `account:${recordId}`,
54
+ });
55
+
56
+ return (
57
+ <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
58
+ <ObjectBreadcrumb
59
+ listPath="/accounts"
60
+ listLabel="Accounts"
61
+ loading={loading}
62
+ recordName={
63
+ account
64
+ ? (fieldValue(account.Name) ?? "")
65
+ : error
66
+ ? "Error"
67
+ : loading
68
+ ? undefined
69
+ : "Not Found"
70
+ }
71
+ />
72
+
73
+ {/* Loading state */}
74
+ {loading && <AccountDetailSkeleton />}
75
+
76
+ {/* Error state */}
77
+ {error && <AccountDetailError onBack={() => navigate(-1)} />}
78
+
79
+ {/* Not found state */}
80
+ {!loading && !error && !account && <AccountDetailNotFound onBack={() => navigate(-1)} />}
81
+
82
+ {/* Content */}
83
+ {account && <AccountDetailContent account={account} />}
84
+ </div>
85
+ );
86
+ }
87
+
88
+ function AccountDetailContent({ account }: { account: AccountNode }) {
89
+ const billingAddress = getAddressFieldLines({
90
+ street: fieldValue(account.BillingStreet),
91
+ city: fieldValue(account.BillingCity),
92
+ state: fieldValue(account.BillingState),
93
+ postalCode: fieldValue(account.BillingPostalCode),
94
+ country: fieldValue(account.BillingCountry),
95
+ });
96
+
97
+ const shippingAddress = getAddressFieldLines({
98
+ street: fieldValue(account.ShippingStreet),
99
+ city: fieldValue(account.ShippingCity),
100
+ state: fieldValue(account.ShippingState),
101
+ postalCode: fieldValue(account.ShippingPostalCode),
102
+ country: fieldValue(account.ShippingCountry),
103
+ });
104
+
105
+ const dateTimeOptions = { dateStyle: "medium", timeStyle: "short" } as const;
106
+ const createdDate = formatDateTimeField(
107
+ fieldValue(account.CreatedDate),
108
+ undefined,
109
+ dateTimeOptions,
110
+ );
111
+ const lastModifiedDate = formatDateTimeField(
112
+ fieldValue(account.LastModifiedDate),
113
+ undefined,
114
+ dateTimeOptions,
115
+ );
116
+
117
+ return (
118
+ <>
119
+ <h1 className="text-2xl font-bold mb-4">Account: {fieldValue(account.Name)}</h1>
120
+
121
+ <Card>
122
+ <CardContent className="space-y-8 pt-6">
123
+ {/* Top section */}
124
+ <div>
125
+ <div className="space-y-4">
126
+ <FieldRow>
127
+ <FieldItem label="Account Owner">{fieldValue(account.Owner?.Name)}</FieldItem>
128
+ <FieldItem label="Phone">
129
+ <TelephoneField value={fieldValue(account.Phone)} />
130
+ </FieldItem>
131
+ </FieldRow>
132
+ <FieldRow>
133
+ <FieldItem label="Account Name">{fieldValue(account.Name)}</FieldItem>
134
+ <FieldItem label="Fax">
135
+ <TelephoneField value={fieldValue(account.Fax)} />
136
+ </FieldItem>
137
+ </FieldRow>
138
+ <FieldRow>
139
+ <FieldItem label="Parent Account">{fieldValue(account.Parent?.Name)}</FieldItem>
140
+ <FieldItem label="Website">{fieldValue(account.Website)}</FieldItem>
141
+ </FieldRow>
142
+ </div>
143
+ </div>
144
+
145
+ <Separator />
146
+
147
+ {/* Additional Information */}
148
+ <Section title="Additional Information">
149
+ <FieldRow>
150
+ <FieldItem label="Type">{fieldValue(account.Type)}</FieldItem>
151
+ <FieldItem label="Employees">{fieldValue(account.NumberOfEmployees)}</FieldItem>
152
+ </FieldRow>
153
+ <FieldRow>
154
+ <FieldItem label="Industry">{fieldValue(account.Industry)}</FieldItem>
155
+ <FieldItem label="Annual Revenue">{fieldValue(account.AnnualRevenue)}</FieldItem>
156
+ </FieldRow>
157
+ <FieldItem label="Description">{fieldValue(account.Description)}</FieldItem>
158
+ </Section>
159
+
160
+ <Separator />
161
+
162
+ {/* Address Information */}
163
+ <Section title="Address Information">
164
+ <FieldRow>
165
+ <FieldItem label="Billing Address">
166
+ {billingAddress ? billingAddress.map((line, i) => <div key={i}>{line}</div>) : null}
167
+ </FieldItem>
168
+ <FieldItem label="Shipping Address">
169
+ {shippingAddress
170
+ ? shippingAddress.map((line, i) => <div key={i}>{line}</div>)
171
+ : null}
172
+ </FieldItem>
173
+ </FieldRow>
174
+ </Section>
175
+
176
+ <Separator />
177
+
178
+ {/* System Information */}
179
+ <Section title="System Information">
180
+ <FieldRow>
181
+ <FieldItem label="Created By">
182
+ {[fieldValue(account.CreatedBy?.Name), createdDate].filter(Boolean).join(" ") ||
183
+ null}
184
+ </FieldItem>
185
+ <FieldItem label="Last Modified By">
186
+ {[fieldValue(account.LastModifiedBy?.Name), lastModifiedDate]
187
+ .filter(Boolean)
188
+ .join(" ") || null}
189
+ </FieldItem>
190
+ </FieldRow>
191
+ </Section>
192
+ </CardContent>
193
+ </Card>
194
+ </>
195
+ );
196
+ }
197
+
198
+ function TelephoneField({ value }: { value?: string | null }) {
199
+ if (!value) return null;
200
+ return (
201
+ <a href={`tel:${value}`} className="underline">
202
+ {value}
203
+ </a>
204
+ );
205
+ }
206
+
207
+ function FieldItem({ label, children }: { label: string; children: React.ReactNode }) {
208
+ return (
209
+ <div>
210
+ <dt className="text-sm text-muted-foreground">{label}</dt>
211
+ <dd className="mt-0.5">{children ?? "—"}</dd>
212
+ </div>
213
+ );
214
+ }
215
+
216
+ function FieldRow({ children }: { children: React.ReactNode }) {
217
+ return <div className="grid grid-cols-2 gap-x-8 gap-y-4">{children}</div>;
218
+ }
219
+
220
+ function Section({ title, children }: { title: string; children: React.ReactNode }) {
221
+ const [open, setOpen] = useState(true);
222
+ return (
223
+ <Collapsible open={open} onOpenChange={setOpen}>
224
+ <CollapsibleTrigger className="flex items-center gap-2 cursor-pointer text-lg font-semibold py-2">
225
+ {open ? <ChevronDown className="size-5" /> : <ChevronRight className="size-5" />}
226
+ {title}
227
+ </CollapsibleTrigger>
228
+ <CollapsibleContent>
229
+ <div className="mt-2 space-y-4">{children}</div>
230
+ </CollapsibleContent>
231
+ </Collapsible>
232
+ );
233
+ }
234
+
235
+ function AccountDetailError({ onBack }: { onBack: () => void }) {
236
+ return (
237
+ <>
238
+ <Alert variant="destructive" role="alert">
239
+ <AlertCircle />
240
+ <AlertTitle>
241
+ <h2>Failed to load account</h2>
242
+ </AlertTitle>
243
+ <AlertDescription>
244
+ Something went wrong while loading this account. Please try again later.
245
+ </AlertDescription>
246
+ </Alert>
247
+ <div className="mt-4 flex gap-3">
248
+ <Button variant="outline" onClick={onBack}>
249
+ ← Back
250
+ </Button>
251
+ <Button variant="outline" onClick={() => window.location.reload()}>
252
+ Retry
253
+ </Button>
254
+ </div>
255
+ </>
256
+ );
257
+ }
258
+
259
+ function AccountDetailNotFound({ onBack }: { onBack: () => void }) {
260
+ return (
261
+ <Card>
262
+ <CardContent className="flex flex-col items-center justify-center py-16 text-center">
263
+ <FileQuestion className="size-12 text-muted-foreground mb-4" />
264
+ <h2 className="text-lg font-semibold mb-1">Account not found</h2>
265
+ <p className="text-sm text-muted-foreground mb-6">
266
+ The account you're looking for doesn't exist or may have been deleted.
267
+ </p>
268
+ <Button variant="outline" onClick={onBack}>
269
+ ← Go back
270
+ </Button>
271
+ </CardContent>
272
+ </Card>
273
+ );
274
+ }
275
+
276
+ function AccountDetailSkeleton() {
277
+ return (
278
+ <>
279
+ <Skeleton className="h-8 w-56 mb-4" />
280
+
281
+ <Card>
282
+ <CardContent className="space-y-8 pt-6">
283
+ {/* Top section: field rows */}
284
+ <div>
285
+ <div className="space-y-4">
286
+ <SkeletonFieldRow />
287
+ <SkeletonFieldRow />
288
+ <SkeletonFieldRow />
289
+ </div>
290
+ </div>
291
+
292
+ <Separator />
293
+
294
+ {/* Additional Information */}
295
+ <SkeletonSection />
296
+
297
+ <Separator />
298
+
299
+ {/* Address Information */}
300
+ <div className="space-y-4">
301
+ <Skeleton className="h-7 w-48 py-2" />
302
+ <div className="grid grid-cols-2 gap-x-8 gap-y-4">
303
+ <div>
304
+ <Skeleton className="h-4 w-28 mb-1.5" />
305
+ <Skeleton className="h-5 w-44 mb-1" />
306
+ <Skeleton className="h-5 w-36 mb-1" />
307
+ <Skeleton className="h-5 w-28" />
308
+ </div>
309
+ <div>
310
+ <Skeleton className="h-4 w-32 mb-1.5" />
311
+ <Skeleton className="h-5 w-44 mb-1" />
312
+ <Skeleton className="h-5 w-36 mb-1" />
313
+ <Skeleton className="h-5 w-28" />
314
+ </div>
315
+ </div>
316
+ </div>
317
+
318
+ <Separator />
319
+
320
+ {/* System Information */}
321
+ <div className="space-y-4">
322
+ <Skeleton className="h-7 w-48 py-2" />
323
+ <SkeletonFieldRow />
324
+ </div>
325
+ </CardContent>
326
+ </Card>
327
+ </>
328
+ );
329
+ }
330
+
331
+ function SkeletonField() {
332
+ return (
333
+ <div>
334
+ <Skeleton className="h-4 w-24 mb-1.5" />
335
+ <Skeleton className="h-5 w-40" />
336
+ </div>
337
+ );
338
+ }
339
+
340
+ function SkeletonFieldRow() {
341
+ return (
342
+ <div className="grid grid-cols-2 gap-x-8 gap-y-4">
343
+ <SkeletonField />
344
+ <SkeletonField />
345
+ </div>
346
+ );
347
+ }
348
+
349
+ function SkeletonSection() {
350
+ return (
351
+ <div className="space-y-4">
352
+ <Skeleton className="h-7 w-48 py-2" />
353
+ <SkeletonFieldRow />
354
+ <SkeletonFieldRow />
355
+ </div>
356
+ );
357
+ }