@schandlergarcia/sf-web-components 2.3.17 → 2.5.0
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.
- package/.a4drules/skills/command-center-builder/SKILL.md +3 -2
- package/.a4drules/skills/component-library/SKILL.md +50 -4
- package/.a4drules/skills/component-library/card-components.md +88 -0
- package/.a4drules/skills/component-library/when-to-use.md +1 -0
- package/CHANGELOG.md +40 -0
- package/CLAUDE.md +12 -13
- package/README.md +0 -15
- package/dist/components/library/cards/KanbanBoard.js +313 -0
- package/dist/components/library/cards/KanbanBoard.js.map +1 -0
- package/dist/components/library/index.js +60 -57
- package/dist/components/library/index.js.map +1 -1
- package/dist/components/workspace/ComponentRegistry.js +5 -2
- package/dist/components/workspace/ComponentRegistry.js.map +1 -1
- package/dist/index.js +84 -82
- package/dist/index.js.map +1 -1
- package/dist/styles/global.css +44 -57
- package/package.json +7 -2
- package/scripts/apply-brand.mjs +47 -30
- package/scripts/postinstall.mjs +1 -11
- package/src/components/library/cards/KanbanBoard.jsx +507 -0
- package/src/components/library/index.jsx +1 -0
- package/src/styles/global.css +44 -57
- package/brands/engine/PARTNER_HUB_PRD.md +0 -584
- package/brands/engine/agentApiConfig.ts +0 -36
- package/brands/engine/app/api/graphql-operations-types.ts +0 -11260
- package/brands/engine/app/api/graphqlClient.ts +0 -25
- package/brands/engine/app/api/partnerQueries.ts +0 -212
- package/brands/engine/app/appLayout.tsx +0 -5
- package/brands/engine/app/components/AgentPanel.tsx +0 -541
- package/brands/engine/app/components/AgentforceConversationClient.tsx +0 -201
- package/brands/engine/app/components/Data360Widget.tsx +0 -301
- package/brands/engine/app/components/__inherit_AgentforceConversationClient.tsx +0 -3
- package/brands/engine/app/components/alerts/status-alert.tsx +0 -49
- package/brands/engine/app/components/layouts/card-layout.tsx +0 -29
- package/brands/engine/app/components/workspace/CommandCenter.tsx +0 -16
- package/brands/engine/app/config/agentApi.ts +0 -36
- package/brands/engine/app/data/partner-hub-sample-data.js +0 -297
- package/brands/engine/app/features/object-search/__examples__/api/accountSearchService.ts +0 -46
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +0 -19
- package/brands/engine/app/features/object-search/__examples__/api/query/getAccountDetail.graphql +0 -121
- package/brands/engine/app/features/object-search/__examples__/api/query/searchAccounts.graphql +0 -51
- package/brands/engine/app/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +0 -357
- package/brands/engine/app/features/object-search/__examples__/pages/AccountSearch.tsx +0 -312
- package/brands/engine/app/features/object-search/__examples__/pages/Home.tsx +0 -34
- package/brands/engine/app/features/object-search/api/objectSearchService.ts +0 -84
- package/brands/engine/app/features/object-search/components/ActiveFilters.tsx +0 -89
- package/brands/engine/app/features/object-search/components/FilterContext.tsx +0 -83
- package/brands/engine/app/features/object-search/components/ObjectBreadcrumb.tsx +0 -66
- package/brands/engine/app/features/object-search/components/PaginationControls.tsx +0 -109
- package/brands/engine/app/features/object-search/components/SearchBar.tsx +0 -41
- package/brands/engine/app/features/object-search/components/SortControl.tsx +0 -143
- package/brands/engine/app/features/object-search/components/filters/BooleanFilter.tsx +0 -78
- package/brands/engine/app/features/object-search/components/filters/DateFilter.tsx +0 -128
- package/brands/engine/app/features/object-search/components/filters/DateRangeFilter.tsx +0 -70
- package/brands/engine/app/features/object-search/components/filters/FilterFieldWrapper.tsx +0 -33
- package/brands/engine/app/features/object-search/components/filters/MultiSelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/NumericRangeFilter.tsx +0 -163
- package/brands/engine/app/features/object-search/components/filters/SearchFilter.tsx +0 -50
- package/brands/engine/app/features/object-search/components/filters/SelectFilter.tsx +0 -97
- package/brands/engine/app/features/object-search/components/filters/TextFilter.tsx +0 -91
- package/brands/engine/app/features/object-search/hooks/useAsyncData.ts +0 -54
- package/brands/engine/app/features/object-search/hooks/useCachedAsyncData.ts +0 -184
- package/brands/engine/app/features/object-search/hooks/useDebouncedCallback.ts +0 -34
- package/brands/engine/app/features/object-search/hooks/useObjectSearchParams.ts +0 -252
- package/brands/engine/app/features/object-search/utils/debounce.ts +0 -25
- package/brands/engine/app/features/object-search/utils/fieldUtils.ts +0 -29
- package/brands/engine/app/features/object-search/utils/filterUtils.ts +0 -404
- package/brands/engine/app/features/object-search/utils/sortUtils.ts +0 -38
- package/brands/engine/app/hooks/useEngineLiveData.ts +0 -49
- package/brands/engine/app/hooks/useEvaAgent.ts +0 -288
- package/brands/engine/app/hooks/usePartnerDashboardData.ts +0 -141
- package/brands/engine/app/navigationMenu.tsx +0 -80
- package/brands/engine/app/pages/AccountObjectDetailPage.tsx +0 -361
- package/brands/engine/app/pages/AccountSearch.tsx +0 -305
- package/brands/engine/app/pages/BlankDashboard.tsx +0 -15
- package/brands/engine/app/pages/DataTest.tsx +0 -78
- package/brands/engine/app/pages/Home.tsx +0 -5
- package/brands/engine/app/pages/NotFound.tsx +0 -19
- package/brands/engine/app/pages/PartnerHubDashboard.tsx +0 -2760
- package/brands/engine/app/pages/Search.tsx +0 -13
- package/brands/engine/app/router-utils.tsx +0 -35
- package/brands/engine/app/routes.tsx +0 -39
- package/brands/engine/app/styles/global.css +0 -269
- package/brands/engine/brand.css +0 -40
- package/brands/engine/engine-command-center-prd.md +0 -575
- package/brands/engine/engine-live-data.js +0 -135
- package/brands/engine/engine-sample-data.js +0 -378
- package/brands/engine/engine_logo.png +0 -0
- package/brands/engine/global.css +0 -269
- package/brands/engine/partner-hub-sample-data.js +0 -281
- package/brands/engine/schema.graphql +0 -292
- package/brands/engine/useEngineLiveData.ts +0 -49
- package/brands/engine/useEvaAgent.ts +0 -288
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { useSearchParams } from "react-router";
|
|
3
|
-
import type { FilterFieldConfig, ActiveFilterValue } from "../utils/filterUtils";
|
|
4
|
-
import type { SortFieldConfig, SortState } from "../utils/sortUtils";
|
|
5
|
-
import { filtersToSearchParams, searchParamsToFilters, buildFilter } from "../utils/filterUtils";
|
|
6
|
-
import { buildOrderBy } from "../utils/sortUtils";
|
|
7
|
-
import { debounce } from "../utils/debounce";
|
|
8
|
-
|
|
9
|
-
/** How long to wait before flushing local state changes to the URL. */
|
|
10
|
-
const URL_SYNC_DEBOUNCE_MS = 300;
|
|
11
|
-
|
|
12
|
-
export interface PaginationConfig {
|
|
13
|
-
defaultPageSize: number;
|
|
14
|
-
validPageSizes: number[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface UseObjectSearchParamsReturn<TFilter, TOrderBy> {
|
|
18
|
-
filters: {
|
|
19
|
-
active: ActiveFilterValue[];
|
|
20
|
-
set: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
21
|
-
remove: (field: string) => void;
|
|
22
|
-
};
|
|
23
|
-
sort: {
|
|
24
|
-
current: SortState | null;
|
|
25
|
-
set: (sort: SortState | null) => void;
|
|
26
|
-
};
|
|
27
|
-
query: { where: TFilter; orderBy: TOrderBy };
|
|
28
|
-
pagination: {
|
|
29
|
-
pageSize: number;
|
|
30
|
-
pageIndex: number;
|
|
31
|
-
afterCursor: string | undefined;
|
|
32
|
-
setPageSize: (size: number) => void;
|
|
33
|
-
goToNextPage: (cursor: string) => void;
|
|
34
|
-
goToPreviousPage: () => void;
|
|
35
|
-
};
|
|
36
|
-
resetAll: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Manages filter, sort, and cursor-based pagination state for an object search page.
|
|
41
|
-
*
|
|
42
|
-
* ## State model
|
|
43
|
-
* Local React state is the primary driver for instant UI updates.
|
|
44
|
-
* URL search params act as the durable source of truth so that a page
|
|
45
|
-
* refresh or shared link restores the same view. Changes are synced to
|
|
46
|
-
* the URL via a debounced write (300 ms) to avoid excessive history entries.
|
|
47
|
-
*
|
|
48
|
-
* ## Return shape
|
|
49
|
-
* Returns memoized groups so each group's reference is stable unless its
|
|
50
|
-
* contents change — safe to pass directly as props to `React.memo` children.
|
|
51
|
-
*
|
|
52
|
-
* - `filters` — active filter values + set/remove callbacks
|
|
53
|
-
* - `sort` — current sort state + set callback
|
|
54
|
-
* - `query` — derived `where` / `orderBy` objects ready for the API
|
|
55
|
-
* - `pagination` — page size, page index, cursor, and navigation callbacks
|
|
56
|
-
* - `resetAll` — clears all filters, sort, and pagination in one call
|
|
57
|
-
*/
|
|
58
|
-
export function useObjectSearchParams<TFilter, TOrderBy>(
|
|
59
|
-
filterConfigs: FilterFieldConfig[],
|
|
60
|
-
_sortConfigs?: SortFieldConfig[],
|
|
61
|
-
paginationConfig?: PaginationConfig,
|
|
62
|
-
) {
|
|
63
|
-
const defaultPageSize = paginationConfig?.defaultPageSize ?? 10;
|
|
64
|
-
const validPageSizes = useMemo(
|
|
65
|
-
() => paginationConfig?.validPageSizes ?? [defaultPageSize],
|
|
66
|
-
[paginationConfig?.validPageSizes, defaultPageSize],
|
|
67
|
-
);
|
|
68
|
-
const [searchParams, setSearchParams] = useSearchParams();
|
|
69
|
-
|
|
70
|
-
// Seed local state from URL on initial load
|
|
71
|
-
const initial = useMemo(
|
|
72
|
-
() => searchParamsToFilters(searchParams, filterConfigs),
|
|
73
|
-
// Only run on mount — local state takes over after that, no deps needed
|
|
74
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
-
[],
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const [filters, setFilters] = useState<ActiveFilterValue[]>(initial.filters);
|
|
79
|
-
const [sort, setLocalSort] = useState<SortState | null>(initial.sort);
|
|
80
|
-
|
|
81
|
-
// Pagination — cursor-based with a stack to support "previous page" navigation.
|
|
82
|
-
const getValidPageSize = useCallback(
|
|
83
|
-
(size: number) => (validPageSizes.includes(size) ? size : defaultPageSize),
|
|
84
|
-
[validPageSizes, defaultPageSize],
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const [pageSize, setPageSizeState] = useState<number>(
|
|
88
|
-
getValidPageSize(initial.pageSize ?? defaultPageSize),
|
|
89
|
-
);
|
|
90
|
-
const [pageIndex, setPageIndex] = useState(initial.pageIndex);
|
|
91
|
-
const [afterCursor, setAfterCursor] = useState<string | undefined>(undefined);
|
|
92
|
-
const cursorStackRef = useRef<string[]>([]);
|
|
93
|
-
|
|
94
|
-
// Debounced URL sync — keeps URL in sync without blocking the UI
|
|
95
|
-
const syncToUrl = useCallback(
|
|
96
|
-
(
|
|
97
|
-
nextFilters: ActiveFilterValue[],
|
|
98
|
-
nextSort: SortState | null,
|
|
99
|
-
nextPageSize?: number,
|
|
100
|
-
nextPageIndex?: number,
|
|
101
|
-
) => {
|
|
102
|
-
const params = filtersToSearchParams(nextFilters, nextSort, nextPageSize, nextPageIndex);
|
|
103
|
-
setSearchParams(params, { replace: true });
|
|
104
|
-
},
|
|
105
|
-
[setSearchParams],
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const debouncedSyncRef = useRef(debounce(syncToUrl, URL_SYNC_DEBOUNCE_MS));
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
debouncedSyncRef.current = debounce(syncToUrl, URL_SYNC_DEBOUNCE_MS);
|
|
111
|
-
}, [syncToUrl]);
|
|
112
|
-
|
|
113
|
-
// Snapshot ref — lets callbacks read the latest state without being
|
|
114
|
-
// recreated on every render (avoids infinite useCallback chains).
|
|
115
|
-
const stateRef = useRef({ filters, sort, pageSize, pageIndex });
|
|
116
|
-
stateRef.current = { filters, sort, pageSize, pageIndex };
|
|
117
|
-
|
|
118
|
-
// Any filter/sort change resets pagination to the first page.
|
|
119
|
-
const resetPagination = useCallback(() => {
|
|
120
|
-
setPageIndex(0);
|
|
121
|
-
setAfterCursor(undefined);
|
|
122
|
-
cursorStackRef.current = [];
|
|
123
|
-
}, []);
|
|
124
|
-
|
|
125
|
-
// -- Filter callbacks -------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
const setFilter = useCallback(
|
|
128
|
-
(field: string, value: ActiveFilterValue | undefined) => {
|
|
129
|
-
const { sort: s, pageSize: ps } = stateRef.current;
|
|
130
|
-
setFilters((prev) => {
|
|
131
|
-
const next = prev.filter((f) => f.field !== field);
|
|
132
|
-
if (value) next.push(value);
|
|
133
|
-
debouncedSyncRef.current(next, s, ps);
|
|
134
|
-
return next;
|
|
135
|
-
});
|
|
136
|
-
resetPagination();
|
|
137
|
-
},
|
|
138
|
-
[resetPagination],
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const removeFilter = useCallback(
|
|
142
|
-
(field: string) => {
|
|
143
|
-
const { sort: s, pageSize: ps } = stateRef.current;
|
|
144
|
-
setFilters((prev) => {
|
|
145
|
-
const next = prev.filter((f) => f.field !== field);
|
|
146
|
-
debouncedSyncRef.current(next, s, ps);
|
|
147
|
-
return next;
|
|
148
|
-
});
|
|
149
|
-
resetPagination();
|
|
150
|
-
},
|
|
151
|
-
[resetPagination],
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
// -- Sort callback ----------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
const setSort = useCallback(
|
|
157
|
-
(nextSort: SortState | null) => {
|
|
158
|
-
const { filters: f, pageSize: ps } = stateRef.current;
|
|
159
|
-
setLocalSort(nextSort);
|
|
160
|
-
debouncedSyncRef.current(f, nextSort, ps);
|
|
161
|
-
resetPagination();
|
|
162
|
-
},
|
|
163
|
-
[resetPagination],
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
// -- Reset ------------------------------------------------------------------
|
|
167
|
-
|
|
168
|
-
const resetAll = useCallback(() => {
|
|
169
|
-
setFilters([]);
|
|
170
|
-
setLocalSort(null);
|
|
171
|
-
resetPagination();
|
|
172
|
-
syncToUrl([], null, defaultPageSize, 0);
|
|
173
|
-
setPageSizeState(defaultPageSize);
|
|
174
|
-
}, [syncToUrl, resetPagination, defaultPageSize]);
|
|
175
|
-
|
|
176
|
-
// -- Pagination callbacks ---------------------------------------------------
|
|
177
|
-
// Uses a cursor stack to track visited pages. "Next" pushes the current
|
|
178
|
-
// endCursor onto the stack; "Previous" pops it to restore the prior cursor.
|
|
179
|
-
|
|
180
|
-
const goToNextPage = useCallback((endCursor: string) => {
|
|
181
|
-
cursorStackRef.current = [...cursorStackRef.current, endCursor];
|
|
182
|
-
setAfterCursor(endCursor);
|
|
183
|
-
setPageIndex((prev) => {
|
|
184
|
-
const nextIndex = prev + 1;
|
|
185
|
-
const { filters: f, sort: s, pageSize: ps } = stateRef.current;
|
|
186
|
-
debouncedSyncRef.current(f, s, ps, nextIndex);
|
|
187
|
-
return nextIndex;
|
|
188
|
-
});
|
|
189
|
-
}, []);
|
|
190
|
-
|
|
191
|
-
const goToPreviousPage = useCallback(() => {
|
|
192
|
-
const stack = cursorStackRef.current;
|
|
193
|
-
const next = stack.slice(0, -1);
|
|
194
|
-
cursorStackRef.current = next;
|
|
195
|
-
setAfterCursor(next.length > 0 ? next[next.length - 1] : undefined);
|
|
196
|
-
setPageIndex((prev) => {
|
|
197
|
-
const nextIndex = Math.max(0, prev - 1);
|
|
198
|
-
const { filters: f, sort: s, pageSize: ps } = stateRef.current;
|
|
199
|
-
debouncedSyncRef.current(f, s, ps, nextIndex);
|
|
200
|
-
return nextIndex;
|
|
201
|
-
});
|
|
202
|
-
}, []);
|
|
203
|
-
|
|
204
|
-
const setPageSize = useCallback(
|
|
205
|
-
(newSize: number) => {
|
|
206
|
-
const validated = getValidPageSize(newSize);
|
|
207
|
-
const { filters: f, sort: s } = stateRef.current;
|
|
208
|
-
setPageSizeState(validated);
|
|
209
|
-
resetPagination();
|
|
210
|
-
debouncedSyncRef.current(f, s, validated);
|
|
211
|
-
},
|
|
212
|
-
[resetPagination, getValidPageSize],
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// -- Derived query objects ---------------------------------------------------
|
|
216
|
-
// Translate local filter/sort state into API-ready `where` and `orderBy`.
|
|
217
|
-
|
|
218
|
-
const where = useMemo(
|
|
219
|
-
() => buildFilter<TFilter>(filters, filterConfigs),
|
|
220
|
-
[filters, filterConfigs],
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
const orderBy = useMemo(() => buildOrderBy<TOrderBy>(sort), [sort]);
|
|
224
|
-
|
|
225
|
-
// -- Memoized return groups -------------------------------------------------
|
|
226
|
-
// Each group is individually memoized so its object reference stays stable
|
|
227
|
-
// unless the contained values change. This makes it safe to pass a group
|
|
228
|
-
// (e.g. `pagination`) directly as props to a React.memo child without
|
|
229
|
-
// causing unnecessary re-renders.
|
|
230
|
-
|
|
231
|
-
const filtersGroup = useMemo(
|
|
232
|
-
() => ({ active: filters, set: setFilter, remove: removeFilter }),
|
|
233
|
-
[filters, setFilter, removeFilter],
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
const sortGroup = useMemo(() => ({ current: sort, set: setSort }), [sort, setSort]);
|
|
237
|
-
|
|
238
|
-
const query = useMemo(() => ({ where, orderBy }), [where, orderBy]);
|
|
239
|
-
|
|
240
|
-
const pagination = useMemo(
|
|
241
|
-
() => ({ pageSize, pageIndex, afterCursor, setPageSize, goToNextPage, goToPreviousPage }),
|
|
242
|
-
[pageSize, pageIndex, afterCursor, setPageSize, goToNextPage, goToPreviousPage],
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
filters: filtersGroup,
|
|
247
|
-
sort: sortGroup,
|
|
248
|
-
query,
|
|
249
|
-
pagination,
|
|
250
|
-
resetAll,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/** Default debounce delay for keystroke-driven filter inputs (search, text, numeric). */
|
|
2
|
-
export const FILTER_DEBOUNCE_MS = 300;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Creates a debounced version of the provided function.
|
|
6
|
-
*
|
|
7
|
-
* Each call to the returned function resets the internal timer. The wrapped
|
|
8
|
-
* function is only invoked once the timer expires without being reset. This
|
|
9
|
-
* makes it ideal for rate-limiting high-frequency events like input changes.
|
|
10
|
-
*
|
|
11
|
-
* @typeParam T - The function signature to debounce.
|
|
12
|
-
* @param fn - The function to debounce.
|
|
13
|
-
* @param ms - The debounce delay in milliseconds.
|
|
14
|
-
* @returns A new function with the same signature that delays execution.
|
|
15
|
-
*/
|
|
16
|
-
export function debounce<T extends (...args: any[]) => void>(
|
|
17
|
-
fn: T,
|
|
18
|
-
ms: number,
|
|
19
|
-
): (...args: Parameters<T>) => void {
|
|
20
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
21
|
-
return (...args: Parameters<T>) => {
|
|
22
|
-
clearTimeout(timer);
|
|
23
|
-
timer = setTimeout(() => fn(...args), ms);
|
|
24
|
-
};
|
|
25
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export function fieldValue(
|
|
2
|
-
field: { displayValue?: string | null; value?: unknown } | null | undefined,
|
|
3
|
-
): string | null {
|
|
4
|
-
if (field?.displayValue != null) return field.displayValue;
|
|
5
|
-
if (field?.value != null) return String(field.value);
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getAddressFieldLines(address: {
|
|
10
|
-
street?: string | null;
|
|
11
|
-
city?: string | null;
|
|
12
|
-
state?: string | null;
|
|
13
|
-
postalCode?: string | null;
|
|
14
|
-
country?: string | null;
|
|
15
|
-
}) {
|
|
16
|
-
const cityStateZip = [address.city, address.state].filter(Boolean).join(", ");
|
|
17
|
-
const cityStateZipLine = [cityStateZip, address.postalCode].filter(Boolean).join(" ");
|
|
18
|
-
const lines = [address.street, cityStateZipLine, address.country].filter(Boolean);
|
|
19
|
-
if (lines.length === 0) return null;
|
|
20
|
-
return lines;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function formatDateTimeField(
|
|
24
|
-
value?: string | null,
|
|
25
|
-
...args: Parameters<Date["toLocaleString"]>
|
|
26
|
-
) {
|
|
27
|
-
if (!value) return null;
|
|
28
|
-
return new Date(value).toLocaleString(...args);
|
|
29
|
-
}
|