@schandlergarcia/sf-web-components 2.3.17 → 2.4.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/CHANGELOG.md +16 -0
- package/CLAUDE.md +12 -13
- package/README.md +0 -15
- package/dist/styles/global.css +44 -57
- package/package.json +1 -2
- package/scripts/apply-brand.mjs +47 -30
- package/scripts/postinstall.mjs +1 -11
- 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,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useEvaAgent — React hook for the Agentforce Agent REST API
|
|
3
|
-
*
|
|
4
|
-
* Handles the full lifecycle:
|
|
5
|
-
* 1. OAuth client-credentials → access_token
|
|
6
|
-
* 2. POST …/sessions → sessionId + message href
|
|
7
|
-
* 3. POST …/messages → agent response text
|
|
8
|
-
* 4. DELETE …/sessions → cleanup on unmount
|
|
9
|
-
*
|
|
10
|
-
* All HTTP calls go through the Vite proxy (see vite.config.ts):
|
|
11
|
-
* /sf-oauth/* → myDomainUrl
|
|
12
|
-
* /sf-agent/* → agentApiBaseUrl
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
16
|
-
import { AGENT_API_CONFIG } from "@/config/agentApi";
|
|
17
|
-
|
|
18
|
-
export interface AgentMessage {
|
|
19
|
-
role: "user" | "agent";
|
|
20
|
-
text: string;
|
|
21
|
-
id: string;
|
|
22
|
-
timestamp: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface SessionState {
|
|
26
|
-
accessToken: string;
|
|
27
|
-
sessionId: string;
|
|
28
|
-
messagesHref: string;
|
|
29
|
-
endHref: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default function useEvaAgent() {
|
|
33
|
-
const [messages, setMessages] = useState<AgentMessage[]>([]);
|
|
34
|
-
const [isConnecting, setIsConnecting] = useState(false);
|
|
35
|
-
const [isReady, setIsReady] = useState(false);
|
|
36
|
-
const [isSending, setIsSending] = useState(false);
|
|
37
|
-
const [error, setError] = useState<string | null>(null);
|
|
38
|
-
|
|
39
|
-
const sessionRef = useRef<SessionState | null>(null);
|
|
40
|
-
const sequenceRef = useRef(1);
|
|
41
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
42
|
-
|
|
43
|
-
/* ── Step 1: OAuth token ──────────────────────────────── */
|
|
44
|
-
async function authenticate(signal: AbortSignal): Promise<string> {
|
|
45
|
-
const { clientId, clientSecret } = AGENT_API_CONFIG;
|
|
46
|
-
|
|
47
|
-
const body = new URLSearchParams({
|
|
48
|
-
grant_type: "client_credentials",
|
|
49
|
-
client_id: clientId,
|
|
50
|
-
client_secret: clientSecret,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const res = await fetch("/sf-oauth/services/oauth2/token", {
|
|
54
|
-
method: "POST",
|
|
55
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
56
|
-
body,
|
|
57
|
-
signal,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
if (!res.ok) {
|
|
61
|
-
const text = await res.text();
|
|
62
|
-
throw new Error(`OAuth failed (${res.status}): ${text}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const data = await res.json();
|
|
66
|
-
return data.access_token;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* ── Step 2: Create session ───────────────────────────── */
|
|
70
|
-
async function createSession(
|
|
71
|
-
accessToken: string,
|
|
72
|
-
signal: AbortSignal
|
|
73
|
-
): Promise<Omit<SessionState, "accessToken">> {
|
|
74
|
-
const { agentId, bypassUser, demoTraveler, myDomainUrl } =
|
|
75
|
-
AGENT_API_CONFIG;
|
|
76
|
-
|
|
77
|
-
const payload = {
|
|
78
|
-
externalSessionKey: crypto.randomUUID(),
|
|
79
|
-
instanceConfig: { endpoint: myDomainUrl },
|
|
80
|
-
streamingCapabilities: { chunkTypes: ["Text"] },
|
|
81
|
-
bypassUser,
|
|
82
|
-
variables: [
|
|
83
|
-
{
|
|
84
|
-
name: "$Context.EndUserId",
|
|
85
|
-
type: "Text",
|
|
86
|
-
value: demoTraveler.contactId,
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
name: "$Context.EndUserEmail",
|
|
90
|
-
type: "Text",
|
|
91
|
-
value: demoTraveler.email,
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: "$Context.EndUserFirstName",
|
|
95
|
-
type: "Text",
|
|
96
|
-
value: demoTraveler.firstName,
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: "$Context.EndUserLastName",
|
|
100
|
-
type: "Text",
|
|
101
|
-
value: demoTraveler.lastName,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const res = await fetch(
|
|
107
|
-
`/sf-agent/einstein/ai-agent/v1/agents/${agentId}/sessions`,
|
|
108
|
-
{
|
|
109
|
-
method: "POST",
|
|
110
|
-
headers: {
|
|
111
|
-
Authorization: `Bearer ${accessToken}`,
|
|
112
|
-
"Content-Type": "application/json",
|
|
113
|
-
},
|
|
114
|
-
body: JSON.stringify(payload),
|
|
115
|
-
signal,
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
if (!res.ok) {
|
|
120
|
-
const text = await res.text();
|
|
121
|
-
throw new Error(`Session creation failed (${res.status}): ${text}`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const data = await res.json();
|
|
125
|
-
return {
|
|
126
|
-
sessionId: data.sessionId,
|
|
127
|
-
messagesHref: data._links?.messages?.href ?? "",
|
|
128
|
-
endHref: data._links?.end?.href ?? "",
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/* ── Step 3: Send message ─────────────────────────────── */
|
|
133
|
-
const sendMessage = useCallback(async (text: string) => {
|
|
134
|
-
const session = sessionRef.current;
|
|
135
|
-
if (!session) return;
|
|
136
|
-
|
|
137
|
-
const userMsg: AgentMessage = {
|
|
138
|
-
role: "user",
|
|
139
|
-
text,
|
|
140
|
-
id: `user-${Date.now()}`,
|
|
141
|
-
timestamp: new Date().toISOString(),
|
|
142
|
-
};
|
|
143
|
-
setMessages((prev) => [...prev, userMsg]);
|
|
144
|
-
setIsSending(true);
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const seqId = sequenceRef.current++;
|
|
148
|
-
|
|
149
|
-
const messagesUrl = session.messagesHref.startsWith("http")
|
|
150
|
-
? `/sf-agent${new URL(session.messagesHref).pathname}`
|
|
151
|
-
: `/sf-agent${session.messagesHref}`;
|
|
152
|
-
|
|
153
|
-
const res = await fetch(messagesUrl, {
|
|
154
|
-
method: "POST",
|
|
155
|
-
headers: {
|
|
156
|
-
Authorization: `Bearer ${session.accessToken}`,
|
|
157
|
-
"Content-Type": "application/json",
|
|
158
|
-
Accept: "application/json",
|
|
159
|
-
},
|
|
160
|
-
body: JSON.stringify({
|
|
161
|
-
message: { sequenceId: seqId, type: "Text", text },
|
|
162
|
-
}),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (!res.ok) {
|
|
166
|
-
const errText = await res.text();
|
|
167
|
-
throw new Error(`Agent message failed (${res.status}): ${errText}`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const data = await res.json();
|
|
171
|
-
|
|
172
|
-
const agentTexts: string[] = [];
|
|
173
|
-
if (Array.isArray(data.messages)) {
|
|
174
|
-
for (const m of data.messages) {
|
|
175
|
-
if (m.type === "Inform" && m.message) {
|
|
176
|
-
agentTexts.push(m.message);
|
|
177
|
-
} else if (m.type === "Text" && m.message) {
|
|
178
|
-
agentTexts.push(m.message);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const responseText =
|
|
184
|
-
agentTexts.length > 0
|
|
185
|
-
? agentTexts.join("\n\n")
|
|
186
|
-
: data.message ?? "No response from agent.";
|
|
187
|
-
|
|
188
|
-
const agentMsg: AgentMessage = {
|
|
189
|
-
role: "agent",
|
|
190
|
-
text: responseText,
|
|
191
|
-
id: `agent-${Date.now()}`,
|
|
192
|
-
timestamp: new Date().toISOString(),
|
|
193
|
-
};
|
|
194
|
-
setMessages((prev) => [...prev, agentMsg]);
|
|
195
|
-
} catch (err: unknown) {
|
|
196
|
-
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
197
|
-
setError(msg);
|
|
198
|
-
const errMsg: AgentMessage = {
|
|
199
|
-
role: "agent",
|
|
200
|
-
text: `Error: ${msg}`,
|
|
201
|
-
id: `error-${Date.now()}`,
|
|
202
|
-
timestamp: new Date().toISOString(),
|
|
203
|
-
};
|
|
204
|
-
setMessages((prev) => [...prev, errMsg]);
|
|
205
|
-
} finally {
|
|
206
|
-
setIsSending(false);
|
|
207
|
-
}
|
|
208
|
-
}, []);
|
|
209
|
-
|
|
210
|
-
/* ── Step 4: End session ──────────────────────────────── */
|
|
211
|
-
const endSession = useCallback(async () => {
|
|
212
|
-
const session = sessionRef.current;
|
|
213
|
-
if (!session) return;
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
const endUrl = session.endHref.startsWith("http")
|
|
217
|
-
? `/sf-agent${new URL(session.endHref).pathname}`
|
|
218
|
-
: `/sf-agent${session.endHref}`;
|
|
219
|
-
|
|
220
|
-
await fetch(endUrl, {
|
|
221
|
-
method: "DELETE",
|
|
222
|
-
headers: { Authorization: `Bearer ${session.accessToken}` },
|
|
223
|
-
});
|
|
224
|
-
} catch {
|
|
225
|
-
/* best-effort cleanup */
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
sessionRef.current = null;
|
|
229
|
-
setIsReady(false);
|
|
230
|
-
}, []);
|
|
231
|
-
|
|
232
|
-
/* ── Init: authenticate + create session on mount ─────── */
|
|
233
|
-
const connect = useCallback(async () => {
|
|
234
|
-
if (sessionRef.current) return;
|
|
235
|
-
|
|
236
|
-
abortRef.current = new AbortController();
|
|
237
|
-
const { signal } = abortRef.current;
|
|
238
|
-
|
|
239
|
-
setIsConnecting(true);
|
|
240
|
-
setError(null);
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
const accessToken = await authenticate(signal);
|
|
244
|
-
const session = await createSession(accessToken, signal);
|
|
245
|
-
|
|
246
|
-
sessionRef.current = { accessToken, ...session };
|
|
247
|
-
sequenceRef.current = 1;
|
|
248
|
-
setIsReady(true);
|
|
249
|
-
} catch (err: unknown) {
|
|
250
|
-
if ((err as Error).name !== "AbortError") {
|
|
251
|
-
const msg = err instanceof Error ? err.message : "Connection failed";
|
|
252
|
-
setError(msg);
|
|
253
|
-
}
|
|
254
|
-
} finally {
|
|
255
|
-
setIsConnecting(false);
|
|
256
|
-
}
|
|
257
|
-
}, []);
|
|
258
|
-
|
|
259
|
-
/* ── Cleanup on unmount ───────────────────────────────── */
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
return () => {
|
|
262
|
-
abortRef.current?.abort();
|
|
263
|
-
if (sessionRef.current) {
|
|
264
|
-
const session = sessionRef.current;
|
|
265
|
-
const endUrl = session.endHref.startsWith("http")
|
|
266
|
-
? `/sf-agent${new URL(session.endHref).pathname}`
|
|
267
|
-
: `/sf-agent${session.endHref}`;
|
|
268
|
-
|
|
269
|
-
fetch(endUrl, {
|
|
270
|
-
method: "DELETE",
|
|
271
|
-
headers: { Authorization: `Bearer ${session.accessToken}` },
|
|
272
|
-
}).catch(() => {});
|
|
273
|
-
sessionRef.current = null;
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
}, []);
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
messages,
|
|
280
|
-
isConnecting,
|
|
281
|
-
isReady,
|
|
282
|
-
isSending,
|
|
283
|
-
error,
|
|
284
|
-
connect,
|
|
285
|
-
sendMessage,
|
|
286
|
-
endSession,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
3
|
-
import {
|
|
4
|
-
GET_CURRENT_USER,
|
|
5
|
-
GET_PARTNER_ACCOUNT,
|
|
6
|
-
FIND_PARTNER_ACCOUNT,
|
|
7
|
-
GET_CONTRACTS,
|
|
8
|
-
GET_INVOICES,
|
|
9
|
-
GET_PENALTIES,
|
|
10
|
-
GET_DISPUTES,
|
|
11
|
-
GET_PROPERTIES,
|
|
12
|
-
} from "@/api/partnerQueries";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Hook to fetch live partner dashboard data from Salesforce
|
|
16
|
-
*
|
|
17
|
-
* Queries:
|
|
18
|
-
* - Current partner Account data
|
|
19
|
-
* - Partner's invoices, contracts, penalties, disputes
|
|
20
|
-
*
|
|
21
|
-
* Uses GraphQL via Data SDK for all queries
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
export function usePartnerDashboardData(partnerId?: string | null) {
|
|
25
|
-
const [data, setData] = useState<any>(null);
|
|
26
|
-
const [loading, setLoading] = useState(true);
|
|
27
|
-
const [error, setError] = useState<Error | null>(null);
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
async function fetchData() {
|
|
31
|
-
try {
|
|
32
|
-
const sdk = await createDataSDK();
|
|
33
|
-
|
|
34
|
-
// Get current user
|
|
35
|
-
const userResponse = await sdk.graphql?.(GET_CURRENT_USER);
|
|
36
|
-
console.log("Current User:", userResponse?.data?.uiapi?.currentUser);
|
|
37
|
-
|
|
38
|
-
if (userResponse?.errors?.length) {
|
|
39
|
-
console.error("User query errors:", userResponse.errors);
|
|
40
|
-
throw new Error(userResponse.errors.map((e: any) => e.message).join("; "));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const user = userResponse?.data?.uiapi?.currentUser;
|
|
44
|
-
|
|
45
|
-
// Use partnerId if provided, otherwise query for first partner account
|
|
46
|
-
let accountId = partnerId;
|
|
47
|
-
|
|
48
|
-
if (!accountId) {
|
|
49
|
-
// Query for partner accounts
|
|
50
|
-
const partnerAccountResponse = await sdk.graphql?.(FIND_PARTNER_ACCOUNT);
|
|
51
|
-
const firstPartner = partnerAccountResponse?.data?.uiapi?.query?.Account?.edges?.[0]?.node;
|
|
52
|
-
|
|
53
|
-
if (firstPartner) {
|
|
54
|
-
accountId = firstPartner.Id;
|
|
55
|
-
console.log("Using first Partner Account:", firstPartner.Name?.value, accountId);
|
|
56
|
-
} else {
|
|
57
|
-
console.warn("No Partner accounts found. Please create an Account with Type='Partner'");
|
|
58
|
-
setLoading(false);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
console.log("Fetching data for Account:", accountId);
|
|
64
|
-
|
|
65
|
-
// Fetch all partner data in parallel
|
|
66
|
-
const [
|
|
67
|
-
partnerResponse,
|
|
68
|
-
contractsResponse,
|
|
69
|
-
invoicesResponse,
|
|
70
|
-
penaltiesResponse,
|
|
71
|
-
disputesResponse,
|
|
72
|
-
propertiesResponse,
|
|
73
|
-
] = await Promise.all([
|
|
74
|
-
sdk.graphql?.(GET_PARTNER_ACCOUNT, { accountId }),
|
|
75
|
-
sdk.graphql?.(GET_CONTRACTS, { accountId }),
|
|
76
|
-
sdk.graphql?.(GET_INVOICES, { accountId }),
|
|
77
|
-
sdk.graphql?.(GET_PENALTIES, { accountId }),
|
|
78
|
-
sdk.graphql?.(GET_DISPUTES, { accountId }),
|
|
79
|
-
sdk.graphql?.(GET_PROPERTIES, { accountId }),
|
|
80
|
-
]);
|
|
81
|
-
|
|
82
|
-
// Check for errors
|
|
83
|
-
if (partnerResponse?.errors?.length) {
|
|
84
|
-
console.error("Partner query errors:", partnerResponse.errors);
|
|
85
|
-
}
|
|
86
|
-
if (contractsResponse?.errors?.length) {
|
|
87
|
-
console.error("Contracts query errors:", contractsResponse.errors);
|
|
88
|
-
}
|
|
89
|
-
if (invoicesResponse?.errors?.length) {
|
|
90
|
-
console.error("Invoices query errors:", invoicesResponse.errors);
|
|
91
|
-
}
|
|
92
|
-
if (penaltiesResponse?.errors?.length) {
|
|
93
|
-
console.error("Penalties query errors:", penaltiesResponse.errors);
|
|
94
|
-
}
|
|
95
|
-
if (disputesResponse?.errors?.length) {
|
|
96
|
-
console.error("Disputes query errors:", disputesResponse.errors);
|
|
97
|
-
}
|
|
98
|
-
if (propertiesResponse?.errors?.length) {
|
|
99
|
-
console.error("Properties query errors:", propertiesResponse.errors);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Extract data
|
|
103
|
-
const partner = partnerResponse?.data?.uiapi?.query?.Account?.edges?.[0]?.node || null;
|
|
104
|
-
const contracts = contractsResponse?.data?.uiapi?.query?.Contract__c?.edges?.map((e: any) => e.node) ?? [];
|
|
105
|
-
const invoices = invoicesResponse?.data?.uiapi?.query?.Invoice__c?.edges?.map((e: any) => e.node) ?? [];
|
|
106
|
-
const penalties = penaltiesResponse?.data?.uiapi?.query?.Attrition_Penalty__c?.edges?.map((e: any) => e.node) ?? [];
|
|
107
|
-
const disputes = disputesResponse?.data?.uiapi?.query?.Case?.edges?.map((e: any) => e.node) ?? [];
|
|
108
|
-
const properties = propertiesResponse?.data?.uiapi?.query?.Property__c?.edges?.map((e: any) => e.node) ?? [];
|
|
109
|
-
|
|
110
|
-
console.log("Loaded partner data:", {
|
|
111
|
-
partner,
|
|
112
|
-
contracts: contracts.length,
|
|
113
|
-
invoices: invoices.length,
|
|
114
|
-
penalties: penalties.length,
|
|
115
|
-
disputes: disputes.length,
|
|
116
|
-
properties: properties.length,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
setData({
|
|
120
|
-
user,
|
|
121
|
-
partner,
|
|
122
|
-
contracts,
|
|
123
|
-
invoices,
|
|
124
|
-
penalties,
|
|
125
|
-
disputes,
|
|
126
|
-
properties,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
setLoading(false);
|
|
130
|
-
} catch (err) {
|
|
131
|
-
console.error("Data fetch error:", err);
|
|
132
|
-
setError(err as Error);
|
|
133
|
-
setLoading(false);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
fetchData();
|
|
138
|
-
}, [partnerId]);
|
|
139
|
-
|
|
140
|
-
return { data, loading, error };
|
|
141
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Link, useLocation } from 'react-router';
|
|
2
|
-
import { getAllRoutes } from './router-utils';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
|
|
5
|
-
export default function NavigationMenu() {
|
|
6
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
-
const location = useLocation();
|
|
8
|
-
|
|
9
|
-
const isActive = (path: string) => location.pathname === path;
|
|
10
|
-
|
|
11
|
-
const toggleMenu = () => setIsOpen(!isOpen);
|
|
12
|
-
|
|
13
|
-
const navigationRoutes: { path: string; label: string }[] = getAllRoutes()
|
|
14
|
-
.filter(
|
|
15
|
-
route =>
|
|
16
|
-
route.handle?.showInNavigation === true &&
|
|
17
|
-
route.fullPath !== undefined &&
|
|
18
|
-
route.handle?.label !== undefined
|
|
19
|
-
)
|
|
20
|
-
.map(
|
|
21
|
-
route =>
|
|
22
|
-
({
|
|
23
|
-
path: route.fullPath,
|
|
24
|
-
label: route.handle?.label,
|
|
25
|
-
}) as { path: string; label: string }
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<nav className="bg-white border-b border-gray-200">
|
|
30
|
-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
31
|
-
<div className="flex justify-between items-center h-16">
|
|
32
|
-
<Link to="/" className="text-xl font-semibold text-gray-900">
|
|
33
|
-
React App
|
|
34
|
-
</Link>
|
|
35
|
-
<button
|
|
36
|
-
onClick={toggleMenu}
|
|
37
|
-
className="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
38
|
-
aria-label="Toggle menu"
|
|
39
|
-
>
|
|
40
|
-
<div className="w-6 h-6 flex flex-col justify-center space-y-1.5">
|
|
41
|
-
<span
|
|
42
|
-
className={`block h-0.5 w-6 bg-current transition-all ${
|
|
43
|
-
isOpen ? 'rotate-45 translate-y-2' : ''
|
|
44
|
-
}`}
|
|
45
|
-
/>
|
|
46
|
-
<span
|
|
47
|
-
className={`block h-0.5 w-6 bg-current transition-all ${isOpen ? 'opacity-0' : ''}`}
|
|
48
|
-
/>
|
|
49
|
-
<span
|
|
50
|
-
className={`block h-0.5 w-6 bg-current transition-all ${
|
|
51
|
-
isOpen ? '-rotate-45 -translate-y-2' : ''
|
|
52
|
-
}`}
|
|
53
|
-
/>
|
|
54
|
-
</div>
|
|
55
|
-
</button>
|
|
56
|
-
</div>
|
|
57
|
-
{isOpen && (
|
|
58
|
-
<div className="pb-4">
|
|
59
|
-
<div className="flex flex-col space-y-2">
|
|
60
|
-
{navigationRoutes.map(item => (
|
|
61
|
-
<Link
|
|
62
|
-
key={item.path}
|
|
63
|
-
to={item.path}
|
|
64
|
-
onClick={() => setIsOpen(false)}
|
|
65
|
-
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
66
|
-
isActive(item.path)
|
|
67
|
-
? 'bg-blue-100 text-blue-700'
|
|
68
|
-
: 'text-gray-700 hover:bg-gray-100'
|
|
69
|
-
}`}
|
|
70
|
-
>
|
|
71
|
-
{item.label}
|
|
72
|
-
</Link>
|
|
73
|
-
))}
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
</div>
|
|
78
|
-
</nav>
|
|
79
|
-
);
|
|
80
|
-
}
|