@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
@@ -1,55 +0,0 @@
1
- /**
2
- * FilterInput Component
3
- *
4
- * Renders a text input field for filter values.
5
- * Used for filters that don't have a picklist (affordance !== 'select').
6
- *
7
- * @param filter - Filter definition containing field path, label, and attributes
8
- * @param value - Current filter input value
9
- * @param onChange - Callback when input value changes
10
- *
11
- * @remarks
12
- * - Displays filter label or field path as the label
13
- * - Shows placeholder text from filter attributes or generates default
14
- * - Displays help message if available
15
- *
16
- * @example
17
- * ```tsx
18
- * <FilterInput
19
- * filter={textFilter}
20
- * value={filterValue}
21
- * onChange={(value) => setFilterValue(value)}
22
- * />
23
- * ```
24
- */
25
- import { Input } from "../../../components/ui/input";
26
- import { Field, FieldLabel, FieldDescription } from "../../../components/ui/field";
27
- import type { Filter } from "../types/filters/filters";
28
-
29
- interface FilterInputProps {
30
- filter: Filter;
31
- value: string;
32
- onChange: (value: string) => void;
33
- }
34
-
35
- export default function FilterInput({ filter, value, onChange }: FilterInputProps) {
36
- return (
37
- <Field>
38
- <FieldLabel htmlFor={filter.targetFieldPath}>
39
- {filter.label || filter.targetFieldPath}
40
- </FieldLabel>
41
- <Input
42
- id={filter.targetFieldPath}
43
- type="text"
44
- value={value}
45
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
46
- placeholder={
47
- filter.attributes?.placeholder ||
48
- `Enter ${(filter.label || filter.targetFieldPath).toLowerCase()}`
49
- }
50
- aria-label={filter.label || filter.targetFieldPath}
51
- />
52
- {filter.helpMessage && <FieldDescription>{filter.helpMessage}</FieldDescription>}
53
- </Field>
54
- );
55
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * FilterSelect Component
3
- *
4
- * Renders a dropdown select field for filter values with picklist options.
5
- * Used for filters with affordance === 'select'.
6
- *
7
- * @param filter - Filter definition containing field path, label, and attributes
8
- * @param value - Currently selected filter value
9
- * @param options - Array of picklist values to display as options
10
- * @param onChange - Callback when selection changes
11
- *
12
- * @remarks
13
- * - Filters out invalid options (null/undefined values)
14
- * - Displays option label if available, otherwise uses value
15
- * - Shows placeholder from filter attributes or default "Select..."
16
- *
17
- * @example
18
- * ```tsx
19
- * <FilterSelect
20
- * filter={selectFilter}
21
- * value={selectedValue}
22
- * options={picklistOptions}
23
- * onChange={(value) => setSelectedValue(value)}
24
- * />
25
- * ```
26
- */
27
- import {
28
- Select,
29
- SelectContent,
30
- SelectItem,
31
- SelectTrigger,
32
- SelectValue,
33
- } from "../../../components/ui/select";
34
- import { Field, FieldLabel, FieldDescription } from "../../../components/ui/field";
35
- import type { Filter } from "../types/filters/filters";
36
- import type { PicklistValue } from "../types/filters/picklist";
37
-
38
- interface FilterSelectProps {
39
- filter: Filter;
40
- value: string;
41
- options: PicklistValue[];
42
- onChange: (value: string) => void;
43
- }
44
-
45
- export default function FilterSelect({ filter, value, options, onChange }: FilterSelectProps) {
46
- return (
47
- <Field>
48
- <FieldLabel htmlFor={filter.targetFieldPath}>
49
- {filter.label || filter.targetFieldPath}
50
- </FieldLabel>
51
- <Select value={value} onValueChange={onChange}>
52
- <SelectTrigger
53
- id={filter.targetFieldPath}
54
- aria-label={filter.label || filter.targetFieldPath}
55
- >
56
- <SelectValue placeholder={filter.attributes?.placeholder || "Select..."} />
57
- </SelectTrigger>
58
- <SelectContent>
59
- {options.map((option) => {
60
- if (!option || !option.value) return null;
61
- return (
62
- <SelectItem key={option.value} value={option.value}>
63
- {option.label || option.value}
64
- </SelectItem>
65
- );
66
- })}
67
- </SelectContent>
68
- </Select>
69
- {filter.helpMessage && <FieldDescription>{filter.helpMessage}</FieldDescription>}
70
- </Field>
71
- );
72
- }
@@ -1,209 +0,0 @@
1
- import React, { useId } from "react";
2
- import { createFormHookContexts, createFormHook } from "@tanstack/react-form";
3
- import {
4
- Field,
5
- FieldDescription,
6
- FieldError,
7
- FieldLabel,
8
- } from "../../../components/ui/field";
9
- import { Input } from "../../../components/ui/input";
10
- import {
11
- Select,
12
- SelectContent,
13
- SelectItem,
14
- SelectTrigger,
15
- SelectValue,
16
- } from "../../../components/ui/select";
17
- import { cn } from "../../../lib/utils";
18
- import type { PicklistValue } from "../types/filters/picklist";
19
- import { getUniqueErrors } from "../utils/formUtils";
20
-
21
- export type { FormError } from "../utils/formUtils";
22
- export { validateRangeValues } from "../utils/formUtils";
23
-
24
- export const { fieldContext, formContext, useFieldContext, useFormContext } =
25
- createFormHookContexts();
26
-
27
- interface FilterTextFieldProps extends Omit<
28
- React.ComponentProps<typeof Input>,
29
- "name" | "value" | "onBlur" | "onChange" | "aria-invalid"
30
- > {
31
- label: string;
32
- description?: React.ReactNode;
33
- placeholder?: string;
34
- }
35
-
36
- function FilterTextField({
37
- label,
38
- id: providedId,
39
- description,
40
- placeholder,
41
- type = "text",
42
- ...props
43
- }: FilterTextFieldProps) {
44
- const field = useFieldContext<string>();
45
- const generatedId = useId();
46
- const id = providedId ?? generatedId;
47
- const descriptionId = `${id}-description`;
48
- const errorId = `${id}-error`;
49
- const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
50
-
51
- const uniqueErrors = getUniqueErrors(field.state.meta.errors);
52
-
53
- return (
54
- <Field data-invalid={isInvalid}>
55
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
56
- {description && <FieldDescription id={descriptionId}>{description}</FieldDescription>}
57
- <Input
58
- id={id}
59
- name={field.name as string}
60
- type={type}
61
- value={field.state.value ?? ""}
62
- onBlur={field.handleBlur}
63
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => field.handleChange(e.target.value)}
64
- placeholder={placeholder}
65
- aria-invalid={isInvalid}
66
- aria-describedby={cn(description && descriptionId, isInvalid && errorId)}
67
- {...props}
68
- />
69
- {isInvalid && uniqueErrors.length > 0 && <FieldError errors={uniqueErrors} />}
70
- </Field>
71
- );
72
- }
73
-
74
- interface FilterSelectFieldProps {
75
- label: string;
76
- id?: string;
77
- description?: React.ReactNode;
78
- placeholder?: string;
79
- options: PicklistValue[];
80
- }
81
-
82
- function FilterSelectField({
83
- label,
84
- id: providedId,
85
- description,
86
- placeholder = "Select...",
87
- options,
88
- }: FilterSelectFieldProps) {
89
- const field = useFieldContext<string>();
90
- const generatedId = useId();
91
- const id = providedId ?? generatedId;
92
- const descriptionId = `${id}-description`;
93
- const errorId = `${id}-error`;
94
- const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
95
-
96
- const uniqueErrors = getUniqueErrors(field.state.meta.errors);
97
-
98
- return (
99
- <Field data-invalid={isInvalid}>
100
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
101
- {description && <FieldDescription id={descriptionId}>{description}</FieldDescription>}
102
- <Select value={field.state.value ?? ""} onValueChange={(value) => field.handleChange(value)}>
103
- <SelectTrigger
104
- id={id}
105
- aria-invalid={isInvalid}
106
- aria-describedby={cn(description && descriptionId, isInvalid && errorId)}
107
- >
108
- <SelectValue placeholder={placeholder} />
109
- </SelectTrigger>
110
- <SelectContent>
111
- {options.map((option) => {
112
- if (!option || !option.value) return null;
113
- return (
114
- <SelectItem key={option.value} value={option.value}>
115
- {option.label || option.value}
116
- </SelectItem>
117
- );
118
- })}
119
- </SelectContent>
120
- </Select>
121
- {isInvalid && uniqueErrors.length > 0 && <FieldError errors={uniqueErrors} />}
122
- </Field>
123
- );
124
- }
125
-
126
- interface FilterRangeFieldProps extends Omit<
127
- React.ComponentProps<typeof Input>,
128
- "name" | "value" | "onBlur" | "onChange" | "aria-invalid"
129
- > {
130
- label?: string;
131
- description?: React.ReactNode;
132
- placeholder?: string;
133
- }
134
-
135
- function FilterRangeFieldBase({
136
- label,
137
- id: providedId,
138
- description,
139
- placeholder,
140
- type = "text",
141
- ...props
142
- }: FilterRangeFieldProps) {
143
- const field = useFieldContext<string>();
144
- const generatedId = useId();
145
- const id = providedId ?? generatedId;
146
- const descriptionId = `${id}-description`;
147
- const errorId = `${id}-error`;
148
- const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
149
-
150
- const uniqueErrors = getUniqueErrors(field.state.meta.errors);
151
-
152
- return (
153
- <div>
154
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
155
- {description && <FieldDescription id={descriptionId}>{description}</FieldDescription>}
156
- <Input
157
- id={id}
158
- name={field.name as string}
159
- type={type}
160
- value={field.state.value ?? ""}
161
- onBlur={field.handleBlur}
162
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => field.handleChange(e.target.value)}
163
- placeholder={placeholder}
164
- aria-invalid={isInvalid}
165
- aria-describedby={cn(description && descriptionId, isInvalid && errorId)}
166
- {...props}
167
- />
168
- {isInvalid && uniqueErrors.length > 0 && <FieldError errors={uniqueErrors} />}
169
- </div>
170
- );
171
- }
172
-
173
- interface FilterRangeMinFieldProps extends Omit<
174
- React.ComponentProps<typeof Input>,
175
- "name" | "value" | "onBlur" | "onChange" | "aria-invalid"
176
- > {
177
- label?: string;
178
- description?: React.ReactNode;
179
- placeholder?: string;
180
- }
181
-
182
- interface FilterRangeMaxFieldProps extends Omit<
183
- React.ComponentProps<typeof Input>,
184
- "name" | "value" | "onBlur" | "onChange" | "aria-invalid"
185
- > {
186
- label?: string;
187
- description?: React.ReactNode;
188
- placeholder?: string;
189
- }
190
-
191
- function FilterRangeMinField({ placeholder = "Min", ...props }: FilterRangeMinFieldProps) {
192
- return <FilterRangeFieldBase placeholder={placeholder} {...props} />;
193
- }
194
-
195
- function FilterRangeMaxField({ placeholder = "Max", ...props }: FilterRangeMaxFieldProps) {
196
- return <FilterRangeFieldBase placeholder={placeholder} {...props} />;
197
- }
198
-
199
- export const { useAppForm } = createFormHook({
200
- fieldContext,
201
- formContext,
202
- fieldComponents: {
203
- FilterTextField,
204
- FilterSelectField,
205
- FilterRangeMinField,
206
- FilterRangeMaxField,
207
- },
208
- formComponents: {},
209
- });
@@ -1,72 +0,0 @@
1
- /**
2
- * useObjectInfoBatch
3
- *
4
- * Fetches object metadata (label, labelPlural, fields, etc.) for the given object API names.
5
- * Uses the shared cache in objectInfoService so List, Home, and Detail views reuse one request.
6
- */
7
-
8
- import { useState, useEffect, useRef } from "react";
9
- import { objectInfoService } from "../api/objectInfoService";
10
- import type { ObjectInfoResult } from "../types/objectInfo/objectInfo";
11
-
12
- export interface UseObjectInfoBatchResult {
13
- /** Object metadata in the same order as the requested objectApiNames. */
14
- objectInfos: ObjectInfoResult[];
15
- loading: boolean;
16
- error: string | null;
17
- }
18
-
19
- /**
20
- * Fetches batch object info for the given object API names. Results are cached;
21
- * multiple callers (List, Home, Detail) share the same request.
22
- *
23
- * @param objectApiNames - Array of object API names (e.g. OBJECT_API_NAMES)
24
- * @returns objectInfos (same order as input), loading, error
25
- */
26
- export function useObjectInfoBatch(objectApiNames: string[]): UseObjectInfoBatchResult {
27
- const [state, setState] = useState<UseObjectInfoBatchResult>({
28
- objectInfos: [],
29
- loading: objectApiNames.length > 0,
30
- error: null,
31
- });
32
- const isCancelled = useRef(false);
33
- // Derive a stable primitive from the array so the effect dependency doesn't
34
- // change on every render. A new array reference (even with identical contents)
35
- // would otherwise trigger the effect on every render, causing an infinite loop.
36
- const namesKey = objectApiNames.filter(Boolean).join(",");
37
-
38
- useEffect(() => {
39
- isCancelled.current = false;
40
- // Re-derive the array inside the effect from the stable key rather than
41
- // closing over objectApiNames directly, which would require it in the dep
42
- // array and reintroduce the infinite loop.
43
- const names = namesKey ? namesKey.split(",") : [];
44
- if (names.length === 0) {
45
- queueMicrotask(() => setState({ objectInfos: [], loading: false, error: null }));
46
- return;
47
- }
48
- queueMicrotask(() => setState((s) => ({ ...s, loading: true, error: null })));
49
- objectInfoService
50
- .getObjectInfoBatch(namesKey)
51
- .then((res) => {
52
- if (isCancelled.current) return;
53
- const objectInfos = names
54
- .map((apiName) => res.results?.find((r) => r.result?.ApiName === apiName)?.result)
55
- .filter((r) => r != null) as ObjectInfoResult[];
56
- setState({ objectInfos, loading: false, error: null });
57
- })
58
- .catch((err) => {
59
- if (isCancelled.current) return;
60
- setState({
61
- objectInfos: [],
62
- loading: false,
63
- error: err instanceof Error ? err.message : (err as string),
64
- });
65
- });
66
- return () => {
67
- isCancelled.current = true;
68
- };
69
- }, [namesKey]);
70
-
71
- return state;
72
- }
@@ -1,174 +0,0 @@
1
- /**
2
- * Object Search Data Hooks
3
- *
4
- * - useObjectListMetadata: single source for list-view metadata (filters → columns + picklists). Use in list pages to avoid duplicate state and API calls.
5
- * - useObjectColumns / useObjectFilters: thin wrappers over useObjectListMetadata for backward compatibility.
6
- * - getSharedFilters: module-level deduplication for getObjectListFilters across hook instances.
7
- */
8
-
9
- import { useState, useEffect } from "react";
10
- import { objectInfoService } from "../api/objectInfoService";
11
- import type { Column } from "../types/search/searchResults";
12
- import type { Filter } from "../types/filters/filters";
13
- import type { PicklistValue } from "../types/filters/picklist";
14
-
15
- // --- Shared filters cache (deduplicates getObjectListFilters across useObjectColumns + useObjectFilters) ---
16
- const sharedFiltersCache = new Map<string, Filter[]>();
17
- const sharedFiltersInFlight = new Map<string, Promise<Filter[]>>();
18
-
19
- function getSharedFilters(objectApiName: string): Promise<Filter[]> {
20
- const cached = sharedFiltersCache.get(objectApiName);
21
- if (cached) return Promise.resolve(cached);
22
- const inFlight = sharedFiltersInFlight.get(objectApiName);
23
- if (inFlight) return inFlight;
24
- const promise = objectInfoService
25
- .getObjectListFilters(objectApiName)
26
- .then((filters) => {
27
- sharedFiltersCache.set(objectApiName, filters);
28
- sharedFiltersInFlight.delete(objectApiName);
29
- return filters;
30
- })
31
- .catch((err) => {
32
- sharedFiltersInFlight.delete(objectApiName);
33
- throw err;
34
- });
35
- sharedFiltersInFlight.set(objectApiName, promise);
36
- return promise;
37
- }
38
-
39
- // --- Shared Types ---
40
- export interface FiltersData {
41
- filters: Filter[];
42
- picklistValues: Record<string, PicklistValue[]>;
43
- loading: boolean;
44
- error: string | null;
45
- }
46
-
47
- /**
48
- * Derives column definitions from filter definitions for list/result UI.
49
- */
50
- function filtersToColumns(filters: Filter[]): Column[] {
51
- return filters.map((f) => ({
52
- fieldApiName: f.targetFieldPath,
53
- label: f.label,
54
- searchable: true,
55
- sortable: true,
56
- }));
57
- }
58
-
59
- export interface ObjectListMetadata {
60
- columns: Column[];
61
- filters: Filter[];
62
- picklistValues: Record<string, PicklistValue[]>;
63
- loading: boolean;
64
- error: string | null;
65
- }
66
-
67
- /**
68
- * Single hook for list-view metadata: filters (shared API), derived columns, and picklist values.
69
- * Use this in list/search pages to avoid duplicate useObjectColumns + useObjectFilters and duplicate state.
70
- */
71
- export function useObjectListMetadata(objectApiName: string | null): ObjectListMetadata {
72
- const [state, setState] = useState<{
73
- columns: Column[];
74
- filters: Filter[];
75
- picklistValues: Record<string, PicklistValue[]>;
76
- loading: boolean;
77
- error: string | null;
78
- }>({
79
- columns: [],
80
- filters: [],
81
- picklistValues: {},
82
- loading: true,
83
- error: null,
84
- });
85
-
86
- useEffect(() => {
87
- if (!objectApiName) {
88
- queueMicrotask(() => setState((s) => ({ ...s, loading: false, error: "Invalid object" })));
89
- return;
90
- }
91
-
92
- let isCancelled = false;
93
-
94
- const run = async () => {
95
- queueMicrotask(() => setState((s) => ({ ...s, loading: true, error: null })));
96
- try {
97
- const filters = await getSharedFilters(objectApiName!);
98
- if (isCancelled) return;
99
-
100
- const selectFilters = filters.filter((f) => f.affordance?.toLowerCase() === "select");
101
- const picklistPromises = selectFilters.map((f) =>
102
- objectInfoService
103
- .getPicklistValues(objectApiName!, f.targetFieldPath)
104
- .then((values) => ({ fieldPath: f.targetFieldPath, values }))
105
- .catch(() => ({ fieldPath: f.targetFieldPath, values: [] as PicklistValue[] })),
106
- );
107
- const picklistResults = await Promise.all(picklistPromises);
108
- if (isCancelled) return;
109
-
110
- const picklistValues: Record<string, PicklistValue[]> = {};
111
- picklistResults.forEach(({ fieldPath, values }) => {
112
- picklistValues[fieldPath] = values;
113
- });
114
-
115
- setState({
116
- columns: filtersToColumns(filters),
117
- filters,
118
- picklistValues,
119
- loading: false,
120
- error: null,
121
- });
122
- } catch {
123
- if (isCancelled) return;
124
- setState((s) => ({
125
- ...s,
126
- columns: [],
127
- filters: [],
128
- picklistValues: {},
129
- loading: false,
130
- error: "Failed to load list metadata",
131
- }));
132
- }
133
- };
134
-
135
- run();
136
- return () => {
137
- isCancelled = true;
138
- };
139
- }, [objectApiName]);
140
-
141
- return state;
142
- }
143
-
144
- /**
145
- * Hook: useObjectColumns
146
- * Thin wrapper over useObjectListMetadata for backward compatibility.
147
- */
148
- export function useObjectColumns(objectApiName: string | null) {
149
- const { columns, loading, error } = useObjectListMetadata(objectApiName);
150
- return {
151
- columns: objectApiName ? columns : [],
152
- columnsLoading: loading,
153
- columnsError: error,
154
- };
155
- }
156
-
157
- /**
158
- * Hook: useObjectFilters
159
- * Thin wrapper over useObjectListMetadata for backward compatibility.
160
- */
161
- export function useObjectFilters(objectApiName: string | null) {
162
- const { filters, picklistValues, loading, error } = useObjectListMetadata(objectApiName);
163
- const filtersData: Record<string, FiltersData> = objectApiName
164
- ? {
165
- [objectApiName]: {
166
- filters,
167
- picklistValues,
168
- loading,
169
- error,
170
- },
171
- }
172
- : {};
173
- return { filtersData };
174
- }