@salesforce/ui-bundle-template-app-angular-template-b2x 11.35.5 → 11.36.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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { Injectable, inject } from "@angular/core";
7
+ import { DataClient } from "../../../api/data-client.service";
8
+
9
+ /** Search args */
10
+ export interface ObjectSearchOptions<TWhere, TOrderBy> {
11
+ where?: TWhere;
12
+ orderBy?: TOrderBy;
13
+ first?: number;
14
+ after?: string;
15
+ }
16
+
17
+ /** Picklist option shape shared with filter components. */
18
+ export type PicklistOption = { value: string; label: string };
19
+
20
+ /**
21
+ * Generic object-search data service
22
+ *
23
+ * Provides reusable, object-agnostic methods for:
24
+ * - Searching objects via GraphQL `uiapi.query.<ObjectName>`
25
+ * - Fetching distinct picklist values via `uiapi.aggregate.<ObjectName>`
26
+ *
27
+ * Domain-specific services (e.g. `AccountSearchService`) delegate here with
28
+ * typed parameters rather than reimplementing the query/extraction logic.
29
+ */
30
+ @Injectable({ providedIn: "root" })
31
+ export class ObjectSearchService {
32
+ private readonly client = inject(DataClient);
33
+
34
+ /**
35
+ * Executes a GraphQL search query and extracts the result for the given
36
+ * object name from the standard `uiapi.query.<ObjectName>` response shape.
37
+ */
38
+ async searchObjects<TResult, TQuery, TVariables>(
39
+ query: string,
40
+ objectName: string,
41
+ options: ObjectSearchOptions<unknown, unknown> = {},
42
+ ): Promise<TResult> {
43
+ const { where, orderBy, first = 20, after } = options;
44
+ const data = await this.client.execute<TQuery, TVariables>(query, {
45
+ first,
46
+ after,
47
+ where,
48
+ orderBy,
49
+ } as TVariables);
50
+
51
+ const uiapi = (data as Record<string, unknown>)?.["uiapi"] as
52
+ | Record<string, unknown>
53
+ | undefined;
54
+ const queryResult = (uiapi?.["query"] as Record<string, unknown> | undefined)?.[objectName] as
55
+ | TResult
56
+ | undefined;
57
+
58
+ if (!queryResult) {
59
+ throw new Error(`No ${objectName} data returned`);
60
+ }
61
+ return queryResult;
62
+ }
63
+
64
+ /**
65
+ * Executes a GraphQL aggregate/groupBy query and extracts picklist options
66
+ * from the standard `uiapi.aggregate.<ObjectName>` response shape.
67
+ */
68
+ async fetchDistinctValues<TQuery>(
69
+ query: string,
70
+ objectName: string,
71
+ fieldName: string,
72
+ ): Promise<PicklistOption[]> {
73
+ const data = await this.client.execute<TQuery>(query);
74
+
75
+ const uiapi = (data as Record<string, unknown>)?.["uiapi"] as
76
+ | Record<string, unknown>
77
+ | undefined;
78
+ const aggregate = (uiapi?.["aggregate"] as Record<string, unknown> | undefined)?.[
79
+ objectName
80
+ ] as { edges?: Array<{ node?: { aggregate?: Record<string, unknown> } }> } | undefined;
81
+
82
+ const edges = aggregate?.edges ?? [];
83
+ return edges
84
+ .map((edge) => {
85
+ const field = edge?.node?.aggregate?.[fieldName] as
86
+ | { value?: string | null; displayValue?: string | null; label?: string | null }
87
+ | undefined;
88
+ const value = field?.value;
89
+ if (!value) return null;
90
+ return { value, label: field.label ?? field.displayValue ?? value };
91
+ })
92
+ .filter((opt): opt is PicklistOption => opt !== null);
93
+ }
94
+ }
@@ -4,14 +4,7 @@
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
6
 
7
- // NOTE: The React reference imports `ResultOrder` / `NullOrder` from generated
8
- // GraphQL types (graphql-operations-types.ts). Codegen is parked for the
9
- // Angular port, so the enum string values are inlined here. When codegen lands,
10
- // swap these constants for the generated enums.
11
- /** GraphQL `ResultOrder` values. */
12
- const RESULT_ORDER = { asc: "ASC", desc: "DESC" } as const;
13
- /** GraphQL `NullOrder` values. */
14
- const NULL_ORDER = { first: "FIRST", last: "LAST" } as const;
7
+ import { ResultOrder, NullOrder } from "../../../api/graphql-operations-types";
15
8
 
16
9
  export interface SortFieldConfig<TFieldName extends string = string> {
17
10
  field: TFieldName;
@@ -39,8 +32,8 @@ export function buildOrderBy<TOrderBy>(sort: SortState | null): TOrderBy | undef
39
32
  if (!sort) return undefined;
40
33
  return {
41
34
  [sort.field]: {
42
- order: sort.direction === "ASC" ? RESULT_ORDER.asc : RESULT_ORDER.desc,
43
- nulls: NULL_ORDER.last,
35
+ order: sort.direction === "ASC" ? ResultOrder.Asc : ResultOrder.Desc,
36
+ nulls: NullOrder.Last,
44
37
  },
45
38
  } as TOrderBy;
46
39
  }
@@ -31,7 +31,11 @@ import { createAsyncData } from "../../utils/async-data";
31
31
  import { fieldValue } from "../../features/object-search/utils/field-utils";
32
32
  import type { FilterFieldConfig } from "../../features/object-search/utils/filter-utils";
33
33
  import type { SortFieldConfig } from "../../features/object-search/utils/sort-utils";
34
- import { AccountSearchService, type AccountNode } from "../../api/account/account-search.service";
34
+ import {
35
+ AccountSearchService,
36
+ type AccountSearchNode,
37
+ type AccountSearchOptions,
38
+ } from "../../api/account/account-search.service";
35
39
 
36
40
  /** Cursor-pagination config for the account search (mirrors the React reference). */
37
41
  const PAGINATION_CONFIG: PaginationConfig = {
@@ -124,7 +128,10 @@ export class AccountSearchPageComponent {
124
128
  protected readonly filtersOpen = signal<boolean>(true);
125
129
 
126
130
  /** Derived, API-ready `where` / `orderBy` signals from the shared state. */
127
- private readonly query = this.state.init<unknown, unknown>(FILTER_CONFIGS, PAGINATION_CONFIG);
131
+ private readonly query = this.state.init<
132
+ AccountSearchOptions["where"],
133
+ AccountSearchOptions["orderBy"]
134
+ >(FILTER_CONFIGS, PAGINATION_CONFIG);
128
135
 
129
136
  /** Live page-size / cursor signals from the shared state. */
130
137
  protected readonly pageSize = this.state.pageSize;
@@ -171,9 +178,9 @@ export class AccountSearchPageComponent {
171
178
  protected readonly endCursor = computed(() => this.pageInfo()?.endCursor ?? undefined);
172
179
 
173
180
  /** Non-null account nodes from the current page's edges. */
174
- protected readonly nodes = computed<AccountNode[]>(() => {
181
+ protected readonly nodes = computed<AccountSearchNode[]>(() => {
175
182
  const edges = this.result.data()?.edges ?? [];
176
- const out: AccountNode[] = [];
183
+ const out: AccountSearchNode[] = [];
177
184
  for (const edge of edges) {
178
185
  if (edge?.node) out.push(edge.node);
179
186
  }
@@ -199,19 +206,19 @@ export class AccountSearchPageComponent {
199
206
  });
200
207
 
201
208
  /** Display helpers for the results list (mirrors `fieldValue` in the template). */
202
- protected name(node: AccountNode): string {
209
+ protected name(node: AccountSearchNode): string {
203
210
  return fieldValue(node.Name) ?? "—";
204
211
  }
205
212
 
206
- protected secondaryLine(node: AccountNode): string {
213
+ protected secondaryLine(node: AccountSearchNode): string {
207
214
  return [fieldValue(node.Industry), fieldValue(node.Type)].filter(Boolean).join(" · ") || "—";
208
215
  }
209
216
 
210
- protected phone(node: AccountNode): string {
217
+ protected phone(node: AccountSearchNode): string {
211
218
  return fieldValue(node.Phone) ?? "";
212
219
  }
213
220
 
214
- protected ownerName(node: AccountNode): string {
221
+ protected ownerName(node: AccountSearchNode): string {
215
222
  return fieldValue(node.Owner?.Name) ?? "";
216
223
  }
217
224
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "11.35.5",
3
+ "version": "11.36.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
9
- "version": "11.35.5",
9
+ "version": "11.36.0",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "dependencies": {
12
12
  "fast-xml-parser": "^5.9.3",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "11.35.5",
3
+ "version": "11.36.0",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-app-angular-template-b2x",
3
- "version": "11.35.5",
3
+ "version": "11.36.0",
4
4
  "description": "Salesforce Angular external app template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",