@salesforce/templates 66.7.13 → 66.9.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/lib/generators/lightningEmbeddingGenerator.d.ts +6 -0
- package/lib/generators/lightningEmbeddingGenerator.js +68 -0
- package/lib/generators/lightningEmbeddingGenerator.js.map +1 -0
- package/lib/i18n/i18n.d.ts +6 -0
- package/lib/i18n/i18n.js +6 -0
- package/lib/i18n/i18n.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/templates/lightningembedding/default/default.css +5 -0
- package/lib/templates/lightningembedding/default/default.html +7 -0
- package/lib/templates/lightningembedding/default/default.js +5 -0
- package/lib/templates/lightningembedding/default/default.js-meta.xml +12 -0
- package/lib/templates/project/reactexternalapp/AGENT.md +3 -3
- package/lib/templates/project/reactexternalapp/CHANGELOG.md +428 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package-lock.json +792 -2031
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/package.json +4 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +7 -9
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +7 -15
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +14 -19
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +9 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +5 -3
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +5 -3
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useAsyncData.ts +11 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/api/userProfileApi.ts +12 -11
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/authenticationConfig.ts +9 -9
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/context/AuthContext.tsx +21 -4
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/forms/auth-form.tsx +15 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/hooks/form.tsx +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/layouts/privateRouteLayout.tsx +2 -11
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/ChangePassword.tsx +21 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/ForgotPassword.tsx +20 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Login.tsx +20 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Profile.tsx +80 -43
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/Register.tsx +16 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/pages/ResetPassword.tsx +20 -5
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/utils/helpers.ts +15 -52
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +13 -13
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/alerts/status-alert.tsx +11 -8
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/components/ui/input.tsx +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/hooks/useAsyncData.ts +67 -0
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +7 -9
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +7 -15
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/routes.tsx +19 -25
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/tsconfig.json +4 -6
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/vite-env.d.ts +0 -3
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleAuthUtils.cls-meta.xml +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleChangePassword.cls-meta.xml +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleForgotPassword.cls-meta.xml +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleLogin.cls-meta.xml +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/classes/UIBundleRegistration.cls-meta.xml +1 -1
- package/lib/templates/project/reactexternalapp/_p_/_m_/package.xml +1 -1
- package/lib/templates/project/reactexternalapp/package.json +1 -1
- package/lib/templates/project/reactexternalapp/scripts/org-setup.config.json +0 -1
- package/lib/templates/project/reactexternalapp/scripts/org-setup.mjs +528 -44
- package/lib/templates/project/reactexternalapp/sfdx-project.json +1 -1
- package/lib/templates/project/reactinternalapp/AGENT.md +3 -3
- package/lib/templates/project/reactinternalapp/CHANGELOG.md +428 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package-lock.json +784 -2036
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/package.json +5 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/reactinternalapp.uibundle-meta.xml +1 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountObjectDetailPage.tsx +7 -9
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/_ex_/pages/AccountSearch.tsx +7 -15
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/api/objectSearchService.ts +14 -19
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/NumericRangeFilter.tsx +9 -5
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/SearchFilter.tsx +5 -3
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/components/filters/TextFilter.tsx +5 -3
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useAsyncData.ts +11 -4
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/api/graphqlClient.ts +13 -13
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/AgentforceConversationClient.tsx +40 -44
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/alerts/status-alert.tsx +11 -8
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/components/ui/input.tsx +1 -1
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/hooks/useAsyncData.ts +67 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountObjectDetailPage.tsx +7 -9
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/pages/AccountSearch.tsx +7 -15
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/types/conversation.ts +9 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/types/globals.d.ts +13 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/tsconfig.json +4 -6
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/vite-env.d.ts +0 -3
- package/lib/templates/project/reactinternalapp/_p_/_m_/applications/reactinternalapp.app-meta.xml +17 -0
- package/lib/templates/project/reactinternalapp/_p_/_m_/permissionsets/reactinternalapp_Access.permissionset-meta.xml +9 -0
- package/lib/templates/project/reactinternalapp/package.json +1 -1
- package/lib/templates/project/reactinternalapp/scripts/org-setup.config.json +6 -3
- package/lib/templates/project/reactinternalapp/scripts/org-setup.mjs +528 -44
- package/lib/templates/project/reactinternalapp/sfdx-project.json +1 -1
- package/lib/templates/uiBundles/reactbasic/package-lock.json +1040 -593
- package/lib/templates/uiBundles/reactbasic/package.json +3 -3
- package/lib/templates/uiBundles/reactbasic/src/api/graphqlClient.ts +13 -13
- package/lib/templates/uiBundles/reactbasic/src/components/alerts/status-alert.tsx +11 -8
- package/lib/templates/uiBundles/reactbasic/src/components/ui/input.tsx +1 -1
- package/lib/templates/uiBundles/reactbasic/src/hooks/useAsyncData.ts +67 -0
- package/lib/templates/uiBundles/reactbasic/tsconfig.json +4 -6
- package/lib/templates/uiBundles/reactbasic/vite-env.d.ts +0 -3
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/lightningEmbedding.d.ts +12 -0
- package/lib/utils/lightningEmbedding.js +50 -0
- package/lib/utils/lightningEmbedding.js.map +1 -0
- package/lib/utils/types.d.ts +15 -6
- package/lib/utils/types.js +8 -5
- package/lib/utils/types.js.map +1 -1
- package/package.json +6 -6
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useCachedAsyncData.ts +0 -188
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/layout/card-skeleton.tsx +0 -38
- package/lib/templates/project/reactexternalapp/_p_/_m_/_w_/_a_/src/_f_/authentication/layouts/authenticationRouteLayout.tsx +0 -21
- package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useCachedAsyncData.ts +0 -188
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@salesforce/
|
|
22
|
-
"@salesforce/
|
|
21
|
+
"@salesforce/agentforce-conversation-client": "file:../../../../../../../../../agentforceConversationClient",
|
|
22
|
+
"@salesforce/platform-sdk": "file:../../../../../../../../../sdk/platform-sdk",
|
|
23
|
+
"@salesforce/ui-bundle": "file:../../../../../../../../../ui-bundle",
|
|
23
24
|
"@tailwindcss/vite": "^4.1.17",
|
|
24
25
|
"class-variance-authority": "^0.7.1",
|
|
25
26
|
"clsx": "^2.1.1",
|
|
@@ -34,8 +35,7 @@
|
|
|
34
35
|
"sonner": "^1.7.0",
|
|
35
36
|
"tailwind-merge": "^3.5.0",
|
|
36
37
|
"tailwindcss": "^4.1.17",
|
|
37
|
-
"tw-animate-css": "^1.4.0"
|
|
38
|
-
"@salesforce/agentforce-conversation-client": "^1.116.9"
|
|
38
|
+
"tw-animate-css": "^1.4.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/js": "^9.39.1",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
46
46
|
"@graphql-tools/utils": "^11.0.0",
|
|
47
47
|
"@playwright/test": "^1.49.0",
|
|
48
|
-
"@salesforce/vite-plugin-ui-bundle": "
|
|
48
|
+
"@salesforce/vite-plugin-ui-bundle": "file:../../../../../../../../../vite-plugin-ui-bundle",
|
|
49
49
|
"@testing-library/jest-dom": "^6.6.3",
|
|
50
50
|
"@testing-library/react": "^16.1.0",
|
|
51
51
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { useParams, useNavigate } from "react-router";
|
|
3
|
-
import { createDataSDK } from "@salesforce/sdk
|
|
3
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
4
4
|
import { AlertCircle, ChevronDown, ChevronRight, FileQuestion } from "lucide-react";
|
|
5
5
|
import GET_ACCOUNT_DETAIL from "../api/query/getAccountDetail.graphql?raw";
|
|
6
6
|
import type {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "../../../../components/ui/collapsible";
|
|
19
19
|
import { Separator } from "../../../../components/ui/separator";
|
|
20
20
|
import { Skeleton } from "../../../../components/ui/skeleton";
|
|
21
|
-
import {
|
|
21
|
+
import { useAsyncData } from "../../hooks/useAsyncData";
|
|
22
22
|
import { ObjectBreadcrumb } from "../../components/ObjectBreadcrumb";
|
|
23
23
|
|
|
24
24
|
type AccountNode = NonNullable<
|
|
@@ -29,16 +29,16 @@ type AccountNode = NonNullable<
|
|
|
29
29
|
|
|
30
30
|
async function fetchAccountDetail(recordId: string): Promise<AccountNode | null | undefined> {
|
|
31
31
|
const data = await createDataSDK();
|
|
32
|
-
const
|
|
32
|
+
const result = await data.graphql!.query<GetAccountDetailQuery, GetAccountDetailQueryVariables>({
|
|
33
33
|
query: GET_ACCOUNT_DETAIL,
|
|
34
34
|
variables: { id: recordId },
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
if (
|
|
38
|
-
throw new Error(
|
|
37
|
+
if (result.errors?.length) {
|
|
38
|
+
throw new Error(result.errors.map((e) => e.message).join("; "));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
return
|
|
41
|
+
return result.data?.uiapi?.query?.Account?.edges?.[0]?.node;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export default function AccountObjectDetail() {
|
|
@@ -49,9 +49,7 @@ export default function AccountObjectDetail() {
|
|
|
49
49
|
data: account,
|
|
50
50
|
loading,
|
|
51
51
|
error,
|
|
52
|
-
} =
|
|
53
|
-
key: `account:${recordId}`,
|
|
54
|
-
});
|
|
52
|
+
} = useAsyncData(() => fetchAccountDetail(recordId!), [recordId]);
|
|
55
53
|
|
|
56
54
|
return (
|
|
57
55
|
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
fetchDistinctIndustries,
|
|
7
7
|
fetchDistinctTypes,
|
|
8
8
|
} from "../api/accountSearchService";
|
|
9
|
-
import {
|
|
9
|
+
import { useAsyncData } from "../../hooks/useAsyncData";
|
|
10
10
|
import { fieldValue } from "../../utils/fieldUtils";
|
|
11
11
|
import { useObjectSearchParams } from "../../hooks/useObjectSearchParams";
|
|
12
12
|
import { Alert, AlertTitle, AlertDescription } from "../../../../components/ui/alert";
|
|
@@ -42,8 +42,8 @@ import PaginationControls from "../../components/PaginationControls";
|
|
|
42
42
|
import type { PaginationConfig } from "../../hooks/useObjectSearchParams";
|
|
43
43
|
|
|
44
44
|
const PAGINATION_CONFIG: PaginationConfig = {
|
|
45
|
-
defaultPageSize:
|
|
46
|
-
validPageSizes: [
|
|
45
|
+
defaultPageSize: 7,
|
|
46
|
+
validPageSizes: [7, 14, 28, 42],
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
type AccountNode = NonNullable<
|
|
@@ -77,22 +77,15 @@ const ACCOUNT_SORT_CONFIGS: SortFieldConfig<keyof Account_OrderBy>[] = [
|
|
|
77
77
|
|
|
78
78
|
export default function AccountSearch() {
|
|
79
79
|
const [filtersOpen, setFiltersOpen] = useState(true);
|
|
80
|
-
const { data: industryOptions } =
|
|
81
|
-
|
|
82
|
-
ttl: 300_000,
|
|
83
|
-
});
|
|
84
|
-
const { data: typeOptions } = useCachedAsyncData(fetchDistinctTypes, [], {
|
|
85
|
-
key: "distinctTypes",
|
|
86
|
-
ttl: 300_000,
|
|
87
|
-
});
|
|
80
|
+
const { data: industryOptions } = useAsyncData(fetchDistinctIndustries, []);
|
|
81
|
+
const { data: typeOptions } = useAsyncData(fetchDistinctTypes, []);
|
|
88
82
|
|
|
89
83
|
const { filters, sort, query, pagination, resetAll } = useObjectSearchParams<
|
|
90
84
|
Account_Filter,
|
|
91
85
|
Account_OrderBy
|
|
92
86
|
>(FILTER_CONFIGS, ACCOUNT_SORT_CONFIGS, PAGINATION_CONFIG);
|
|
93
87
|
|
|
94
|
-
const
|
|
95
|
-
const { data, loading, error } = useCachedAsyncData(
|
|
88
|
+
const { data, loading, error } = useAsyncData(
|
|
96
89
|
() =>
|
|
97
90
|
searchAccounts({
|
|
98
91
|
where: query.where,
|
|
@@ -101,7 +94,6 @@ export default function AccountSearch() {
|
|
|
101
94
|
after: pagination.afterCursor,
|
|
102
95
|
}),
|
|
103
96
|
[query.where, query.orderBy, pagination.pageSize, pagination.afterCursor],
|
|
104
|
-
{ key: searchKey },
|
|
105
97
|
);
|
|
106
98
|
|
|
107
99
|
const pageInfo = data?.pageInfo;
|
|
@@ -196,7 +188,7 @@ export default function AccountSearch() {
|
|
|
196
188
|
<ActiveFilters filters={filters.active} onRemove={filters.remove} />
|
|
197
189
|
</div>
|
|
198
190
|
|
|
199
|
-
<div className="min-h-
|
|
191
|
+
<div className="min-h-132">
|
|
200
192
|
{/* Loading state */}
|
|
201
193
|
{loading && (
|
|
202
194
|
<>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDataSDK } from "@salesforce/sdk
|
|
1
|
+
import { createDataSDK } from "@salesforce/platform-sdk";
|
|
2
2
|
|
|
3
3
|
export interface ObjectSearchOptions<TWhere, TOrderBy> {
|
|
4
4
|
where?: TWhere;
|
|
@@ -21,24 +21,20 @@ export async function searchObjects<TResult, TQuery, TVariables>(
|
|
|
21
21
|
const { where, orderBy, first = 20, after } = options;
|
|
22
22
|
|
|
23
23
|
const data = await createDataSDK();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
after,
|
|
29
|
-
where,
|
|
30
|
-
orderBy,
|
|
31
|
-
} as TVariables,
|
|
24
|
+
const variables = { first, after, where, orderBy } as TVariables;
|
|
25
|
+
const result = await data.graphql!.query<TQuery, TVariables>({
|
|
26
|
+
query: query,
|
|
27
|
+
variables: variables,
|
|
32
28
|
});
|
|
33
29
|
|
|
34
|
-
if (
|
|
35
|
-
throw new Error(
|
|
30
|
+
if (result.errors?.length) {
|
|
31
|
+
throw new Error(result.errors.map((e) => e.message).join("; "));
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
const
|
|
34
|
+
const uiapi = (result.data as Record<string, unknown> | undefined)?.uiapi as
|
|
39
35
|
| Record<string, unknown>
|
|
40
36
|
| undefined;
|
|
41
|
-
const queryResult = (
|
|
37
|
+
const queryResult = (uiapi?.query as Record<string, unknown> | undefined)?.[objectName] as
|
|
42
38
|
| TResult
|
|
43
39
|
| undefined;
|
|
44
40
|
|
|
@@ -59,17 +55,16 @@ export async function fetchDistinctValues<TQuery>(
|
|
|
59
55
|
fieldName: string,
|
|
60
56
|
): Promise<PicklistOption[]> {
|
|
61
57
|
const data = await createDataSDK();
|
|
62
|
-
const
|
|
63
|
-
const errors = response?.errors;
|
|
58
|
+
const result = await data.graphql!.query<TQuery>({ query: query });
|
|
64
59
|
|
|
65
|
-
if (errors?.length) {
|
|
66
|
-
throw new Error(errors.map((e) => e.message).join("; "));
|
|
60
|
+
if (result.errors?.length) {
|
|
61
|
+
throw new Error(result.errors.map((e) => e.message).join("; "));
|
|
67
62
|
}
|
|
68
63
|
|
|
69
|
-
const
|
|
64
|
+
const uiapi = (result.data as Record<string, unknown> | undefined)?.uiapi as
|
|
70
65
|
| Record<string, unknown>
|
|
71
66
|
| undefined;
|
|
72
|
-
const aggregate = (
|
|
67
|
+
const aggregate = (uiapi?.aggregate as Record<string, unknown> | undefined)?.[objectName] as
|
|
73
68
|
| { edges?: Array<{ node?: { aggregate?: Record<string, unknown> } }> }
|
|
74
69
|
| undefined;
|
|
75
70
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { Input } from "../../../../components/ui/input";
|
|
3
3
|
|
|
4
4
|
import { useFilterField } from "../FilterContext";
|
|
@@ -67,12 +67,16 @@ export function NumericRangeFilterInputs({
|
|
|
67
67
|
|
|
68
68
|
const externalMin = value?.min ?? "";
|
|
69
69
|
const externalMax = value?.max ?? "";
|
|
70
|
-
|
|
70
|
+
const [prevExternalMin, setPrevExternalMin] = useState(externalMin);
|
|
71
|
+
const [prevExternalMax, setPrevExternalMax] = useState(externalMax);
|
|
72
|
+
if (prevExternalMin !== externalMin) {
|
|
73
|
+
setPrevExternalMin(externalMin);
|
|
71
74
|
setLocalMin(externalMin);
|
|
72
|
-
}
|
|
73
|
-
|
|
75
|
+
}
|
|
76
|
+
if (prevExternalMax !== externalMax) {
|
|
77
|
+
setPrevExternalMax(externalMax);
|
|
74
78
|
setLocalMax(externalMax);
|
|
75
|
-
}
|
|
79
|
+
}
|
|
76
80
|
|
|
77
81
|
const isOutOfBounds = (v: string) => {
|
|
78
82
|
if (v === "") return false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { SearchBar } from "../SearchBar";
|
|
4
4
|
import { useFilterField } from "../FilterContext";
|
|
@@ -22,9 +22,11 @@ export function SearchFilter({
|
|
|
22
22
|
const [localValue, setLocalValue] = useState(value?.value ?? "");
|
|
23
23
|
|
|
24
24
|
const externalValue = value?.value ?? "";
|
|
25
|
-
|
|
25
|
+
const [prevExternalValue, setPrevExternalValue] = useState(externalValue);
|
|
26
|
+
if (prevExternalValue !== externalValue) {
|
|
27
|
+
setPrevExternalValue(externalValue);
|
|
26
28
|
setLocalValue(externalValue);
|
|
27
|
-
}
|
|
29
|
+
}
|
|
28
30
|
|
|
29
31
|
const debouncedOnChange = useDebouncedCallback((v: string) => {
|
|
30
32
|
if (v) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { Input } from "../../../../components/ui/input";
|
|
3
3
|
import { cn } from "../../../../lib/utils";
|
|
4
4
|
import { useFilterField } from "../FilterContext";
|
|
@@ -62,9 +62,11 @@ export function TextFilterInput({
|
|
|
62
62
|
const [localValue, setLocalValue] = useState(value?.value ?? "");
|
|
63
63
|
|
|
64
64
|
const externalValue = value?.value ?? "";
|
|
65
|
-
|
|
65
|
+
const [prevExternalValue, setPrevExternalValue] = useState(externalValue);
|
|
66
|
+
if (prevExternalValue !== externalValue) {
|
|
67
|
+
setPrevExternalValue(externalValue);
|
|
66
68
|
setLocalValue(externalValue);
|
|
67
|
-
}
|
|
69
|
+
}
|
|
68
70
|
|
|
69
71
|
const debouncedOnChange = useDebouncedCallback((v: string) => {
|
|
70
72
|
if (v) {
|
package/lib/templates/project/reactinternalapp/_p_/_m_/_w_/_a_/src/_f_/_os_/hooks/useAsyncData.ts
CHANGED
|
@@ -22,16 +22,24 @@ export function useAsyncData<T>(
|
|
|
22
22
|
const [data, setData] = useState<T | null>(null);
|
|
23
23
|
const [loading, setLoading] = useState(true);
|
|
24
24
|
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
const [generation, setGeneration] = useState(0);
|
|
25
26
|
|
|
26
27
|
const fetcherRef = useRef(fetcher);
|
|
27
28
|
useEffect(() => {
|
|
28
29
|
fetcherRef.current = fetcher;
|
|
29
30
|
});
|
|
30
31
|
|
|
32
|
+
// Detect dep changes during render to reset loading state and bump generation
|
|
33
|
+
const [prevDeps, setPrevDeps] = useState(deps);
|
|
34
|
+
if (deps.length !== prevDeps.length || deps.some((d, i) => d !== prevDeps[i])) {
|
|
35
|
+
setPrevDeps(deps);
|
|
36
|
+
setGeneration((g) => g + 1);
|
|
37
|
+
if (!loading) setLoading(true);
|
|
38
|
+
if (error !== null) setError(null);
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
useEffect(() => {
|
|
32
42
|
let cancelled = false;
|
|
33
|
-
setLoading(true);
|
|
34
|
-
setError(null);
|
|
35
43
|
|
|
36
44
|
fetcherRef
|
|
37
45
|
.current()
|
|
@@ -49,8 +57,7 @@ export function useAsyncData<T>(
|
|
|
49
57
|
return () => {
|
|
50
58
|
cancelled = true;
|
|
51
59
|
};
|
|
52
|
-
|
|
53
|
-
}, deps);
|
|
60
|
+
}, [generation]);
|
|
54
61
|
|
|
55
62
|
return { data, loading, error };
|
|
56
63
|
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Thin GraphQL client: createDataSDK +
|
|
3
|
-
* Use with gql-tagged queries and generated operation types
|
|
2
|
+
* Thin GraphQL client: createDataSDK + sdk.graphql.query with centralized
|
|
3
|
+
* error handling. Use with gql-tagged queries and generated operation types
|
|
4
|
+
* for type-safe calls.
|
|
4
5
|
*/
|
|
5
|
-
import { createDataSDK } from '@salesforce/sdk
|
|
6
|
+
import { createDataSDK } from '@salesforce/platform-sdk';
|
|
6
7
|
|
|
7
8
|
export async function executeGraphQL<TData, TVariables>(
|
|
8
9
|
query: string,
|
|
9
10
|
variables?: TVariables
|
|
10
11
|
): Promise<TData> {
|
|
11
12
|
const data = await createDataSDK();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
variables,
|
|
13
|
+
const result = await data.graphql!.query<TData, TVariables>({
|
|
14
|
+
query: query,
|
|
15
|
+
variables: variables,
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (result.errors?.length) {
|
|
19
|
+
const msg = result.errors.map(e => e.message).join('; ');
|
|
20
|
+
throw new Error(`GraphQL Error: ${msg}`);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
throw new Error(`GraphQL Error: ${msg}`);
|
|
23
|
+
if (result.data == null) {
|
|
24
|
+
throw new Error('GraphQL response data is null');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
return
|
|
27
|
+
return result.data;
|
|
28
28
|
}
|
|
@@ -13,38 +13,6 @@ import type {
|
|
|
13
13
|
} from "../types/conversation";
|
|
14
14
|
|
|
15
15
|
const GLOBAL_HOST_ID = "agentforce-conversation-client-global-host";
|
|
16
|
-
|
|
17
|
-
const DEFAULT_STYLE_TOKENS: NonNullable<AgentforceClientConfig["styleTokens"]> = {
|
|
18
|
-
containerBackground: "#fafafa",
|
|
19
|
-
|
|
20
|
-
headerBlockBackground: "#372949",
|
|
21
|
-
headerBlockTextColor: "#ffffff",
|
|
22
|
-
headerBlockIconColor: "#ffffff",
|
|
23
|
-
headerBlockBorderBottomColor: "#3b0764",
|
|
24
|
-
headerBlockFocusBorder: "#c4b5fd",
|
|
25
|
-
|
|
26
|
-
messageBlockInboundBackgroundColor: "#ffffff",
|
|
27
|
-
messageBlockInboundTextColor: "#1f2937",
|
|
28
|
-
messageBlockInboundBorder: "1px solid #e5e7eb",
|
|
29
|
-
|
|
30
|
-
messageBlockOutboundBackgroundColor: "#ede9fe",
|
|
31
|
-
messageBlockOutboundTextColor: "#1f2937",
|
|
32
|
-
messageBlockOutboundBorder: "1px solid #d8b4fe",
|
|
33
|
-
|
|
34
|
-
messageInputTextColor: "#1f2937",
|
|
35
|
-
messageInputTextBackgroundColor: "#ffffff",
|
|
36
|
-
messageInputFooterBorderColor: "#d1d5db",
|
|
37
|
-
messageInputFooterBorderFocusColor: "#9ca3af",
|
|
38
|
-
messageInputFocusShadow: "0 0 0 3px rgba(156, 163, 175, 0.25)",
|
|
39
|
-
messageInputFooterPlaceholderText: "#6b7280",
|
|
40
|
-
|
|
41
|
-
messageInputFooterSendButton: "#7e22ce",
|
|
42
|
-
messageInputFooterSendButtonHoverColor: "#6b21a8",
|
|
43
|
-
messageInputSendButtonIconColor: "#ffffff",
|
|
44
|
-
messageInputSendButtonDisabledColor: "#e5e7eb",
|
|
45
|
-
messageInputActionButtonFocusBorder: "#a855f7",
|
|
46
|
-
errorBlockBackground: "#fafafa",
|
|
47
|
-
};
|
|
48
16
|
const SINGLETON_KEY = "__agentforceConversationClientSingleton";
|
|
49
17
|
|
|
50
18
|
interface AgentforceConversationClientSingleton {
|
|
@@ -56,6 +24,12 @@ interface WindowWithAgentforceSingleton extends Window {
|
|
|
56
24
|
[SINGLETON_KEY]?: AgentforceConversationClientSingleton;
|
|
57
25
|
}
|
|
58
26
|
|
|
27
|
+
type GlobalWithSfdcEnv = typeof globalThis & {
|
|
28
|
+
SFDC_ENV?: {
|
|
29
|
+
orgUrl?: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
59
33
|
function getSingleton(): AgentforceConversationClientSingleton {
|
|
60
34
|
const win = window as WindowWithAgentforceSingleton;
|
|
61
35
|
if (!win[SINGLETON_KEY]) {
|
|
@@ -77,7 +51,8 @@ function getOrCreateGlobalHost(): HTMLDivElement {
|
|
|
77
51
|
}
|
|
78
52
|
|
|
79
53
|
function getDefaultEmbedOptions(): ResolvedEmbedOptions {
|
|
80
|
-
|
|
54
|
+
const sfdcEnv = (globalThis as GlobalWithSfdcEnv).SFDC_ENV;
|
|
55
|
+
return { salesforceOrigin: sfdcEnv?.orgUrl };
|
|
81
56
|
}
|
|
82
57
|
|
|
83
58
|
/**
|
|
@@ -96,6 +71,8 @@ export function AgentforceConversationClient({
|
|
|
96
71
|
styleTokens,
|
|
97
72
|
salesforceOrigin,
|
|
98
73
|
frontdoorUrl,
|
|
74
|
+
onReady,
|
|
75
|
+
onError,
|
|
99
76
|
}: AgentforceConversationClientProps) {
|
|
100
77
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
101
78
|
const normalizedAgentforceClientConfig = useMemo<AgentforceClientConfig>(() => {
|
|
@@ -111,8 +88,9 @@ export function AgentforceConversationClient({
|
|
|
111
88
|
return {
|
|
112
89
|
...(agentId !== undefined && { agentId }),
|
|
113
90
|
...(agentLabel !== undefined && { agentLabel }),
|
|
114
|
-
styleTokens
|
|
91
|
+
...(styleTokens !== undefined && { styleTokens }),
|
|
115
92
|
renderingConfig,
|
|
93
|
+
channel: "Vibes",
|
|
116
94
|
};
|
|
117
95
|
}, [agentId, agentLabel, inlineProp, headerEnabled, showHeaderIcon, width, height, styleTokens]);
|
|
118
96
|
|
|
@@ -146,13 +124,31 @@ export function AgentforceConversationClient({
|
|
|
146
124
|
}
|
|
147
125
|
const host = inline ? containerRef.current! : getOrCreateGlobalHost();
|
|
148
126
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
127
|
+
try {
|
|
128
|
+
embedAgentforceClient({
|
|
129
|
+
container: host,
|
|
130
|
+
salesforceOrigin: salesforceOrigin ?? options.salesforceOrigin,
|
|
131
|
+
frontdoorUrl: frontdoorUrl ?? options.frontdoorUrl,
|
|
132
|
+
agentforceClientConfig: normalizedAgentforceClientConfig,
|
|
133
|
+
onReady,
|
|
134
|
+
onError,
|
|
135
|
+
});
|
|
136
|
+
singleton.initialized = true;
|
|
137
|
+
} catch (err) {
|
|
138
|
+
// Strip a partially-created LO element so the next mount can retry.
|
|
139
|
+
const partialEmbed = document.querySelector('lightning-out-application[data-lo="acc"]');
|
|
140
|
+
partialEmbed?.remove();
|
|
141
|
+
console.error("AgentforceConversationClient: initialization failed", err);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const safeInitialize = (options: ResolvedEmbedOptions) => {
|
|
146
|
+
try {
|
|
147
|
+
initialize(options);
|
|
148
|
+
} catch (initErr) {
|
|
149
|
+
console.error("AgentforceConversationClient: initialization failed", initErr);
|
|
150
|
+
singleton.initialized = false;
|
|
151
|
+
}
|
|
156
152
|
};
|
|
157
153
|
|
|
158
154
|
const shouldFetchFrontdoor = window.location.hostname === "localhost";
|
|
@@ -165,7 +161,7 @@ export function AgentforceConversationClient({
|
|
|
165
161
|
return;
|
|
166
162
|
}
|
|
167
163
|
const { frontdoorUrl: resolvedFrontdoorUrl } = await res.json();
|
|
168
|
-
|
|
164
|
+
safeInitialize({ frontdoorUrl: resolvedFrontdoorUrl });
|
|
169
165
|
})
|
|
170
166
|
.catch((err) => {
|
|
171
167
|
console.error("AgentforceConversationClient: failed to fetch frontdoor URL", err);
|
|
@@ -176,7 +172,7 @@ export function AgentforceConversationClient({
|
|
|
176
172
|
} else {
|
|
177
173
|
singleton.initPromise = Promise.resolve()
|
|
178
174
|
.then(() => {
|
|
179
|
-
|
|
175
|
+
safeInitialize(getDefaultEmbedOptions());
|
|
180
176
|
})
|
|
181
177
|
.catch((err) => {
|
|
182
178
|
console.error("AgentforceConversationClient: failed to embed Agentforce client", err);
|
|
@@ -190,7 +186,7 @@ export function AgentforceConversationClient({
|
|
|
190
186
|
// Intentionally no cleanup:
|
|
191
187
|
// This component guarantees a single LO initialization per window.
|
|
192
188
|
};
|
|
193
|
-
}, [salesforceOrigin, frontdoorUrl, normalizedAgentforceClientConfig, inline]);
|
|
189
|
+
}, [salesforceOrigin, frontdoorUrl, normalizedAgentforceClientConfig, inline, onReady, onError]);
|
|
194
190
|
|
|
195
191
|
if (!inline) {
|
|
196
192
|
return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
-
import { AlertCircleIcon, CheckCircle2Icon } from 'lucide-react';
|
|
2
|
+
import { AlertCircleIcon, CheckCircle2Icon, InfoIcon } from 'lucide-react';
|
|
3
3
|
import { Alert, AlertDescription } from '../../components/ui/alert';
|
|
4
4
|
import { useId } from 'react';
|
|
5
5
|
|
|
@@ -8,6 +8,7 @@ const statusAlertVariants = cva('', {
|
|
|
8
8
|
variant: {
|
|
9
9
|
error: '',
|
|
10
10
|
success: '',
|
|
11
|
+
info: 'text-blue-600 *:[svg]:text-current *:data-[slot=alert-description]:text-blue-600/90',
|
|
11
12
|
},
|
|
12
13
|
},
|
|
13
14
|
defaultVariants: {
|
|
@@ -18,11 +19,11 @@ const statusAlertVariants = cva('', {
|
|
|
18
19
|
interface StatusAlertProps extends VariantProps<typeof statusAlertVariants> {
|
|
19
20
|
children?: React.ReactNode;
|
|
20
21
|
/** Alert variant type. @default "error" */
|
|
21
|
-
variant?: 'error' | 'success';
|
|
22
|
+
variant?: 'error' | 'success' | 'info';
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
* Status alert component for displaying error or
|
|
26
|
+
* Status alert component for displaying error, success, or info messages.
|
|
26
27
|
* Returns null if no children are provided.
|
|
27
28
|
*/
|
|
28
29
|
export function StatusAlert({ children, variant = 'error' }: StatusAlertProps) {
|
|
@@ -31,6 +32,12 @@ export function StatusAlert({ children, variant = 'error' }: StatusAlertProps) {
|
|
|
31
32
|
|
|
32
33
|
const isError = variant === 'error';
|
|
33
34
|
|
|
35
|
+
const icon = {
|
|
36
|
+
error: <AlertCircleIcon aria-hidden="true" />,
|
|
37
|
+
success: <CheckCircle2Icon aria-hidden="true" />,
|
|
38
|
+
info: <InfoIcon aria-hidden="true" />,
|
|
39
|
+
}[variant];
|
|
40
|
+
|
|
34
41
|
return (
|
|
35
42
|
<Alert
|
|
36
43
|
variant={isError ? 'destructive' : 'default'}
|
|
@@ -38,11 +45,7 @@ export function StatusAlert({ children, variant = 'error' }: StatusAlertProps) {
|
|
|
38
45
|
aria-describedby={descriptionId}
|
|
39
46
|
role={isError ? 'alert' : 'status'}
|
|
40
47
|
>
|
|
41
|
-
{
|
|
42
|
-
<AlertCircleIcon aria-hidden="true" />
|
|
43
|
-
) : (
|
|
44
|
-
<CheckCircle2Icon aria-hidden="true" />
|
|
45
|
-
)}
|
|
48
|
+
{icon}
|
|
46
49
|
<AlertDescription id={descriptionId}>{children}</AlertDescription>
|
|
47
50
|
</Alert>
|
|
48
51
|
);
|
|
@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
|
8
8
|
type={type}
|
|
9
9
|
data-slot="input"
|
|
10
10
|
className={cn(
|
|
11
|
-
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
11
|
+
'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground/70 placeholder:italic w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
12
12
|
className
|
|
13
13
|
)}
|
|
14
14
|
{...props}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UseAsyncDataResult<T> {
|
|
4
|
+
data: T | null;
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: string | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Runs an async fetcher on mount and whenever `deps` change.
|
|
11
|
+
* Returns the loading/error/data state. Does not cache — every call
|
|
12
|
+
* to the fetcher hits the source directly.
|
|
13
|
+
*
|
|
14
|
+
* A cleanup flag prevents state updates if the component unmounts
|
|
15
|
+
* or deps change before the fetch completes (avoids React warnings
|
|
16
|
+
* and stale updates from out-of-order responses).
|
|
17
|
+
*/
|
|
18
|
+
export function useAsyncData<T>(
|
|
19
|
+
fetcher: () => Promise<T>,
|
|
20
|
+
deps: React.DependencyList
|
|
21
|
+
): UseAsyncDataResult<T> {
|
|
22
|
+
const [data, setData] = useState<T | null>(null);
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
const [generation, setGeneration] = useState(0);
|
|
26
|
+
|
|
27
|
+
const fetcherRef = useRef(fetcher);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
fetcherRef.current = fetcher;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Detect dep changes during render to reset loading state and bump generation
|
|
33
|
+
const [prevDeps, setPrevDeps] = useState(deps);
|
|
34
|
+
if (
|
|
35
|
+
deps.length !== prevDeps.length ||
|
|
36
|
+
deps.some((d, i) => d !== prevDeps[i])
|
|
37
|
+
) {
|
|
38
|
+
setPrevDeps(deps);
|
|
39
|
+
setGeneration(g => g + 1);
|
|
40
|
+
if (!loading) setLoading(true);
|
|
41
|
+
if (error !== null) setError(null);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let cancelled = false;
|
|
46
|
+
|
|
47
|
+
fetcherRef
|
|
48
|
+
.current()
|
|
49
|
+
.then(result => {
|
|
50
|
+
if (!cancelled) setData(result);
|
|
51
|
+
})
|
|
52
|
+
.catch(err => {
|
|
53
|
+
console.error(err);
|
|
54
|
+
if (!cancelled)
|
|
55
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
56
|
+
})
|
|
57
|
+
.finally(() => {
|
|
58
|
+
if (!cancelled) setLoading(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
cancelled = true;
|
|
63
|
+
};
|
|
64
|
+
}, [generation]);
|
|
65
|
+
|
|
66
|
+
return { data, loading, error };
|
|
67
|
+
}
|