@flowuent-org/diagramming-core 1.2.3 → 1.2.5
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/apps/diagramming/src/AutomationDiagramData.ts +14 -86
- package/package.json +1 -1
- package/packages/diagrams/src/lib/components/automation/AutomationGoogleServicesNode.tsx +205 -487
- package/packages/diagrams/src/lib/types/automation-node-data-types.ts +9 -0
- package/packages/diagrams/src/lib/types/node-types.ts +1 -1
|
@@ -534,20 +534,23 @@ export const automationDefaultNodes = [
|
|
|
534
534
|
position: { x: 100, y: 450 },
|
|
535
535
|
data: {
|
|
536
536
|
label: 'Create Report',
|
|
537
|
-
description: 'Google Docs document
|
|
537
|
+
description: 'Create a Google Docs document',
|
|
538
538
|
serviceType: 'docs',
|
|
539
539
|
status: 'idle',
|
|
540
|
+
isFirstGoogleNode: true, // Only first Google node shows Connect button
|
|
540
541
|
parameters: {
|
|
541
542
|
documentTitle: 'Automation Report',
|
|
542
543
|
operation: 'create',
|
|
543
544
|
content: 'Report content will be generated here...',
|
|
544
545
|
},
|
|
545
546
|
googleAuth: {
|
|
546
|
-
clientId: '',
|
|
547
|
+
clientId: '',
|
|
547
548
|
scopes: [
|
|
548
549
|
'https://www.googleapis.com/auth/documents',
|
|
549
550
|
'https://www.googleapis.com/auth/drive',
|
|
550
551
|
],
|
|
552
|
+
isAuthenticated: false,
|
|
553
|
+
isLoading: false,
|
|
551
554
|
},
|
|
552
555
|
formData: {
|
|
553
556
|
nodeId: 'google-docs-node',
|
|
@@ -566,9 +569,10 @@ export const automationDefaultNodes = [
|
|
|
566
569
|
position: { x: 450, y: 450 },
|
|
567
570
|
data: {
|
|
568
571
|
label: 'Send Email',
|
|
569
|
-
description: '
|
|
572
|
+
description: 'Send email via Gmail',
|
|
570
573
|
serviceType: 'gmail',
|
|
571
574
|
status: 'idle',
|
|
575
|
+
isFirstGoogleNode: false, // Not first, no Connect button
|
|
572
576
|
parameters: {
|
|
573
577
|
to: ['recipient@example.com'],
|
|
574
578
|
subject: 'Automation Report',
|
|
@@ -580,6 +584,8 @@ export const automationDefaultNodes = [
|
|
|
580
584
|
'https://www.googleapis.com/auth/gmail.send',
|
|
581
585
|
'https://www.googleapis.com/auth/gmail.compose',
|
|
582
586
|
],
|
|
587
|
+
isAuthenticated: false,
|
|
588
|
+
isLoading: false,
|
|
583
589
|
},
|
|
584
590
|
formData: {
|
|
585
591
|
nodeId: 'gmail-node',
|
|
@@ -598,9 +604,10 @@ export const automationDefaultNodes = [
|
|
|
598
604
|
position: { x: 800, y: 450 },
|
|
599
605
|
data: {
|
|
600
606
|
label: 'Create Meeting',
|
|
601
|
-
description: 'Google Meet link
|
|
607
|
+
description: 'Create a Google Meet link',
|
|
602
608
|
serviceType: 'meet',
|
|
603
609
|
status: 'idle',
|
|
610
|
+
isFirstGoogleNode: false, // Not first, no Connect button
|
|
604
611
|
parameters: {
|
|
605
612
|
meetingTitle: 'Project Discussion',
|
|
606
613
|
attendees: ['team@example.com'],
|
|
@@ -608,6 +615,8 @@ export const automationDefaultNodes = [
|
|
|
608
615
|
googleAuth: {
|
|
609
616
|
clientId: '',
|
|
610
617
|
scopes: ['https://www.googleapis.com/auth/meetings.space.created'],
|
|
618
|
+
isAuthenticated: false,
|
|
619
|
+
isLoading: false,
|
|
611
620
|
},
|
|
612
621
|
formData: {
|
|
613
622
|
nodeId: 'google-meet-node',
|
|
@@ -620,68 +629,6 @@ export const automationDefaultNodes = [
|
|
|
620
629
|
height: 200,
|
|
621
630
|
measured: { width: 300, height: 200 },
|
|
622
631
|
},
|
|
623
|
-
{
|
|
624
|
-
id: 'google-slides-node',
|
|
625
|
-
type: 'AutomationGoogleSlidesNode',
|
|
626
|
-
position: { x: 100, y: 700 },
|
|
627
|
-
data: {
|
|
628
|
-
label: 'Create Presentation',
|
|
629
|
-
description: 'Google Slides presentation එකක් create කරන්න',
|
|
630
|
-
serviceType: 'slides',
|
|
631
|
-
status: 'idle',
|
|
632
|
-
parameters: {
|
|
633
|
-
presentationTitle: 'Monthly Report',
|
|
634
|
-
slideContent: 'Slide content here...',
|
|
635
|
-
},
|
|
636
|
-
googleAuth: {
|
|
637
|
-
clientId: '',
|
|
638
|
-
scopes: [
|
|
639
|
-
'https://www.googleapis.com/auth/presentations',
|
|
640
|
-
'https://www.googleapis.com/auth/drive',
|
|
641
|
-
],
|
|
642
|
-
},
|
|
643
|
-
formData: {
|
|
644
|
-
nodeId: 'google-slides-node',
|
|
645
|
-
title: 'Create Presentation',
|
|
646
|
-
type: 'navigation',
|
|
647
|
-
serviceType: 'slides',
|
|
648
|
-
},
|
|
649
|
-
},
|
|
650
|
-
width: 300,
|
|
651
|
-
height: 200,
|
|
652
|
-
measured: { width: 300, height: 200 },
|
|
653
|
-
},
|
|
654
|
-
{
|
|
655
|
-
id: 'google-calendar-node',
|
|
656
|
-
type: 'AutomationGoogleCalendarNode',
|
|
657
|
-
position: { x: 450, y: 700 },
|
|
658
|
-
data: {
|
|
659
|
-
label: 'Create Event',
|
|
660
|
-
description: 'Google Calendar event එකක් create කරන්න',
|
|
661
|
-
serviceType: 'calendar',
|
|
662
|
-
status: 'idle',
|
|
663
|
-
parameters: {
|
|
664
|
-
calendarId: 'primary',
|
|
665
|
-
eventTitle: 'Team Meeting',
|
|
666
|
-
eventDescription: 'Weekly sync up',
|
|
667
|
-
startTime: new Date().toISOString(),
|
|
668
|
-
endTime: new Date(Date.now() + 3600000).toISOString(),
|
|
669
|
-
},
|
|
670
|
-
googleAuth: {
|
|
671
|
-
clientId: '',
|
|
672
|
-
scopes: ['https://www.googleapis.com/auth/calendar'],
|
|
673
|
-
},
|
|
674
|
-
formData: {
|
|
675
|
-
nodeId: 'google-calendar-node',
|
|
676
|
-
title: 'Create Event',
|
|
677
|
-
type: 'navigation',
|
|
678
|
-
serviceType: 'calendar',
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
width: 300,
|
|
682
|
-
height: 200,
|
|
683
|
-
measured: { width: 300, height: 200 },
|
|
684
|
-
},
|
|
685
632
|
];
|
|
686
633
|
|
|
687
634
|
// Default edges for the automation diagram
|
|
@@ -810,7 +757,7 @@ export const automationDefaultEdges = [
|
|
|
810
757
|
sourceHandle: 'right',
|
|
811
758
|
targetHandle: 'left',
|
|
812
759
|
data: {
|
|
813
|
-
label: 'Schedule
|
|
760
|
+
label: 'Schedule Meeting',
|
|
814
761
|
type: 'flow',
|
|
815
762
|
},
|
|
816
763
|
style: {
|
|
@@ -822,23 +769,4 @@ export const automationDefaultEdges = [
|
|
|
822
769
|
color: '#EA4335',
|
|
823
770
|
},
|
|
824
771
|
},
|
|
825
|
-
{
|
|
826
|
-
id: 'edge-slides-to-calendar',
|
|
827
|
-
source: 'google-slides-node',
|
|
828
|
-
target: 'google-calendar-node',
|
|
829
|
-
sourceHandle: 'right',
|
|
830
|
-
targetHandle: 'left',
|
|
831
|
-
data: {
|
|
832
|
-
label: 'Add to Calendar',
|
|
833
|
-
type: 'flow',
|
|
834
|
-
},
|
|
835
|
-
style: {
|
|
836
|
-
stroke: '#FBBC04',
|
|
837
|
-
strokeWidth: 2,
|
|
838
|
-
},
|
|
839
|
-
markerEnd: {
|
|
840
|
-
type: MarkerType.ArrowClosed,
|
|
841
|
-
color: '#FBBC04',
|
|
842
|
-
},
|
|
843
|
-
},
|
|
844
772
|
];
|
package/package.json
CHANGED
|
@@ -66,11 +66,17 @@ export interface AutomationGoogleServicesNodeData {
|
|
|
66
66
|
status?: NodeStatus;
|
|
67
67
|
iconName?: string;
|
|
68
68
|
|
|
69
|
+
// Flag to indicate if this is the first Google node (show auth button)
|
|
70
|
+
isFirstGoogleNode?: boolean;
|
|
71
|
+
|
|
69
72
|
// Google Auth config
|
|
70
73
|
googleAuth?: {
|
|
71
74
|
clientId?: string;
|
|
72
75
|
scopes?: string[];
|
|
73
76
|
tokens?: GoogleTokenData;
|
|
77
|
+
isAuthenticated?: boolean;
|
|
78
|
+
isLoading?: boolean;
|
|
79
|
+
userEmail?: string;
|
|
74
80
|
};
|
|
75
81
|
|
|
76
82
|
// Service-specific parameters
|
|
@@ -131,10 +137,9 @@ export interface AutomationGoogleServicesNodeData {
|
|
|
131
137
|
executionTime?: number;
|
|
132
138
|
};
|
|
133
139
|
|
|
134
|
-
// Callbacks
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
onAuthError?: (error: Error) => void;
|
|
140
|
+
// Callbacks - passed from parent workflow
|
|
141
|
+
onGoogleLogin?: () => void;
|
|
142
|
+
onGoogleDisconnect?: () => void;
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
export interface AutomationGoogleServicesNodeProps {
|
|
@@ -164,7 +169,7 @@ const SERVICE_CONFIG: Record<
|
|
|
164
169
|
'https://www.googleapis.com/auth/documents',
|
|
165
170
|
'https://www.googleapis.com/auth/drive',
|
|
166
171
|
],
|
|
167
|
-
description: '
|
|
172
|
+
description: 'Create or edit a document',
|
|
168
173
|
},
|
|
169
174
|
slides: {
|
|
170
175
|
icon: SlidesIcon,
|
|
@@ -174,14 +179,14 @@ const SERVICE_CONFIG: Record<
|
|
|
174
179
|
'https://www.googleapis.com/auth/presentations',
|
|
175
180
|
'https://www.googleapis.com/auth/drive',
|
|
176
181
|
],
|
|
177
|
-
description: '
|
|
182
|
+
description: 'Create or edit a presentation',
|
|
178
183
|
},
|
|
179
184
|
meet: {
|
|
180
185
|
icon: MeetIcon,
|
|
181
186
|
label: 'Google Meet',
|
|
182
187
|
color: '#34A853',
|
|
183
188
|
scopes: ['https://www.googleapis.com/auth/meetings.space.created'],
|
|
184
|
-
description: '
|
|
189
|
+
description: 'Create a meeting link',
|
|
185
190
|
},
|
|
186
191
|
gmail: {
|
|
187
192
|
icon: GmailIcon,
|
|
@@ -191,21 +196,21 @@ const SERVICE_CONFIG: Record<
|
|
|
191
196
|
'https://www.googleapis.com/auth/gmail.send',
|
|
192
197
|
'https://www.googleapis.com/auth/gmail.compose',
|
|
193
198
|
],
|
|
194
|
-
description: '
|
|
199
|
+
description: 'Send an email',
|
|
195
200
|
},
|
|
196
201
|
sheets: {
|
|
197
202
|
icon: SheetsIcon,
|
|
198
203
|
label: 'Google Sheets',
|
|
199
204
|
color: '#0F9D58',
|
|
200
205
|
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
|
|
201
|
-
description: '
|
|
206
|
+
description: 'Read or write spreadsheet data',
|
|
202
207
|
},
|
|
203
208
|
calendar: {
|
|
204
209
|
icon: CalendarIcon,
|
|
205
210
|
label: 'Google Calendar',
|
|
206
211
|
color: '#4285F4',
|
|
207
212
|
scopes: ['https://www.googleapis.com/auth/calendar'],
|
|
208
|
-
description: '
|
|
213
|
+
description: 'Create a calendar event',
|
|
209
214
|
},
|
|
210
215
|
};
|
|
211
216
|
|
|
@@ -222,7 +227,7 @@ const isTokenExpired = (expiresAt?: number): boolean => {
|
|
|
222
227
|
const getTimeUntilExpiry = (expiresAt?: number): string => {
|
|
223
228
|
if (!expiresAt) return 'N/A';
|
|
224
229
|
const remaining = expiresAt - Date.now();
|
|
225
|
-
if (remaining <= 0) return '
|
|
230
|
+
if (remaining <= 0) return 'Expired';
|
|
226
231
|
const minutes = Math.floor(remaining / 60000);
|
|
227
232
|
if (minutes < 60) return `${minutes} min`;
|
|
228
233
|
const hours = Math.floor(minutes / 60);
|
|
@@ -247,203 +252,23 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
247
252
|
// State
|
|
248
253
|
// ========================
|
|
249
254
|
const [status, setStatus] = useState<NodeStatus>(data.status || 'idle');
|
|
250
|
-
const [tokens, setTokens] = useState<GoogleTokenData | null>(null);
|
|
251
255
|
const [isExecuting, setIsExecuting] = useState(false);
|
|
252
256
|
const [executionProgress, setExecutionProgress] = useState(0);
|
|
253
257
|
const [error, setError] = useState<string | null>(null);
|
|
254
258
|
const [result, setResult] = useState<any>(null);
|
|
255
259
|
|
|
256
|
-
const googleClientRef = useRef<any>(null);
|
|
257
260
|
const serviceType = data.serviceType || 'docs';
|
|
258
261
|
const serviceConfig = SERVICE_CONFIG[serviceType];
|
|
259
262
|
const ServiceIcon = serviceConfig.icon;
|
|
260
263
|
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const savedTokens: GoogleTokenData = {
|
|
267
|
-
accessToken: data.parameters.googleAccessToken,
|
|
268
|
-
expiresAt: data.parameters.googleTokenExpiresAt || 0,
|
|
269
|
-
email: data.parameters.googleUserEmail,
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
if (!isTokenExpired(savedTokens.expiresAt)) {
|
|
273
|
-
setTokens(savedTokens);
|
|
274
|
-
setStatus('authenticated');
|
|
275
|
-
} else {
|
|
276
|
-
setStatus('idle');
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}, [data.parameters?.googleAccessToken]);
|
|
280
|
-
|
|
281
|
-
// ========================
|
|
282
|
-
// Google OAuth Login
|
|
283
|
-
// ========================
|
|
284
|
-
const handleGoogleLogin = useCallback(async () => {
|
|
285
|
-
setStatus('authenticating');
|
|
286
|
-
setError(null);
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
// Load Google Identity Services if not loaded
|
|
290
|
-
if (!(window as any).google?.accounts?.oauth2) {
|
|
291
|
-
await new Promise<void>((resolve, reject) => {
|
|
292
|
-
const script = document.createElement('script');
|
|
293
|
-
script.src = 'https://accounts.google.com/gsi/client';
|
|
294
|
-
script.async = true;
|
|
295
|
-
script.defer = true;
|
|
296
|
-
script.onload = () => resolve();
|
|
297
|
-
script.onerror = () => reject(new Error('Failed to load Google Identity Services'));
|
|
298
|
-
document.head.appendChild(script);
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Initialize OAuth client
|
|
303
|
-
const clientId = data.googleAuth?.clientId || process.env.REACT_APP_GOOGLE_CLIENT_ID;
|
|
304
|
-
|
|
305
|
-
if (!clientId) {
|
|
306
|
-
throw new Error('Google Client ID not configured');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const tokenClient = (window as any).google.accounts.oauth2.initTokenClient({
|
|
310
|
-
client_id: clientId,
|
|
311
|
-
scope: serviceConfig.scopes.join(' '),
|
|
312
|
-
callback: (response: any) => {
|
|
313
|
-
if (response.error) {
|
|
314
|
-
setError(response.error_description || 'Authentication failed');
|
|
315
|
-
setStatus('error');
|
|
316
|
-
data.onAuthError?.(new Error(response.error_description));
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const newTokens: GoogleTokenData = {
|
|
321
|
-
accessToken: response.access_token,
|
|
322
|
-
expiresAt: Date.now() + response.expires_in * 1000,
|
|
323
|
-
scope: response.scope,
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// Get user email
|
|
327
|
-
fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
|
|
328
|
-
headers: { Authorization: `Bearer ${response.access_token}` },
|
|
329
|
-
})
|
|
330
|
-
.then((res) => res.json())
|
|
331
|
-
.then((userInfo) => {
|
|
332
|
-
newTokens.email = userInfo.email;
|
|
333
|
-
setTokens(newTokens);
|
|
334
|
-
setStatus('authenticated');
|
|
335
|
-
data.onAuthSuccess?.(newTokens);
|
|
336
|
-
|
|
337
|
-
// Save tokens to node data
|
|
338
|
-
updateNodeData({
|
|
339
|
-
parameters: {
|
|
340
|
-
...data.parameters,
|
|
341
|
-
useUserAccount: true,
|
|
342
|
-
googleAccessToken: newTokens.accessToken,
|
|
343
|
-
googleTokenExpiresAt: newTokens.expiresAt,
|
|
344
|
-
googleUserEmail: newTokens.email,
|
|
345
|
-
},
|
|
346
|
-
});
|
|
347
|
-
})
|
|
348
|
-
.catch(() => {
|
|
349
|
-
setTokens(newTokens);
|
|
350
|
-
setStatus('authenticated');
|
|
351
|
-
data.onAuthSuccess?.(newTokens);
|
|
352
|
-
});
|
|
353
|
-
},
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
googleClientRef.current = tokenClient;
|
|
357
|
-
tokenClient.requestAccessToken();
|
|
358
|
-
} catch (err: any) {
|
|
359
|
-
setError(err.message || 'Authentication failed');
|
|
360
|
-
setStatus('error');
|
|
361
|
-
data.onAuthError?.(err);
|
|
362
|
-
}
|
|
363
|
-
}, [data, serviceConfig.scopes]);
|
|
264
|
+
// Check if authenticated from data
|
|
265
|
+
const isAuthenticated = data.googleAuth?.isAuthenticated ||
|
|
266
|
+
(data.parameters?.googleAccessToken && !isTokenExpired(data.parameters?.googleTokenExpiresAt));
|
|
267
|
+
const isAuthLoading = data.googleAuth?.isLoading || false;
|
|
268
|
+
const userEmail = data.googleAuth?.userEmail || data.parameters?.googleUserEmail;
|
|
364
269
|
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
// ========================
|
|
368
|
-
const handleDisconnect = useCallback(() => {
|
|
369
|
-
setTokens(null);
|
|
370
|
-
setStatus('idle');
|
|
371
|
-
setResult(null);
|
|
372
|
-
setError(null);
|
|
373
|
-
|
|
374
|
-
// Clear tokens from node data
|
|
375
|
-
updateNodeData({
|
|
376
|
-
parameters: {
|
|
377
|
-
...data.parameters,
|
|
378
|
-
useUserAccount: false,
|
|
379
|
-
googleAccessToken: undefined,
|
|
380
|
-
googleTokenExpiresAt: undefined,
|
|
381
|
-
googleUserEmail: undefined,
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
}, [data.parameters]);
|
|
385
|
-
|
|
386
|
-
// ========================
|
|
387
|
-
// Execute Node
|
|
388
|
-
// ========================
|
|
389
|
-
const handleExecute = useCallback(async () => {
|
|
390
|
-
// Check if authenticated
|
|
391
|
-
if (!tokens || isTokenExpired(tokens.expiresAt)) {
|
|
392
|
-
// Need to authenticate first
|
|
393
|
-
handleGoogleLogin();
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
setIsExecuting(true);
|
|
398
|
-
setStatus('running');
|
|
399
|
-
setExecutionProgress(0);
|
|
400
|
-
setError(null);
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
// Simulate progress
|
|
404
|
-
const progressInterval = setInterval(() => {
|
|
405
|
-
setExecutionProgress((prev) => Math.min(prev + 10, 90));
|
|
406
|
-
}, 200);
|
|
407
|
-
|
|
408
|
-
// Execute the actual operation
|
|
409
|
-
let executionResult: any;
|
|
410
|
-
|
|
411
|
-
if (data.onExecute) {
|
|
412
|
-
executionResult = await data.onExecute(tokens);
|
|
413
|
-
} else {
|
|
414
|
-
// Default execution based on service type
|
|
415
|
-
executionResult = await executeGoogleService(serviceType, tokens, data.parameters);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
clearInterval(progressInterval);
|
|
419
|
-
setExecutionProgress(100);
|
|
420
|
-
setResult(executionResult);
|
|
421
|
-
setStatus('completed');
|
|
422
|
-
|
|
423
|
-
// Update node data with result
|
|
424
|
-
updateNodeData({
|
|
425
|
-
executionResult: {
|
|
426
|
-
success: true,
|
|
427
|
-
data: executionResult,
|
|
428
|
-
timestamp: new Date().toISOString(),
|
|
429
|
-
},
|
|
430
|
-
});
|
|
431
|
-
} catch (err: any) {
|
|
432
|
-
setError(err.message || 'Execution failed');
|
|
433
|
-
setStatus('error');
|
|
434
|
-
|
|
435
|
-
// Update node data with error
|
|
436
|
-
updateNodeData({
|
|
437
|
-
executionResult: {
|
|
438
|
-
success: false,
|
|
439
|
-
error: err.message,
|
|
440
|
-
timestamp: new Date().toISOString(),
|
|
441
|
-
},
|
|
442
|
-
});
|
|
443
|
-
} finally {
|
|
444
|
-
setIsExecuting(false);
|
|
445
|
-
}
|
|
446
|
-
}, [tokens, data, serviceType, handleGoogleLogin]);
|
|
270
|
+
// Show Google Auth button only on first Google node
|
|
271
|
+
const showGoogleAuthButton = data.isFirstGoogleNode === true;
|
|
447
272
|
|
|
448
273
|
// ========================
|
|
449
274
|
// Update Node Data
|
|
@@ -470,157 +295,6 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
470
295
|
[nodeId, nodes, setNodes]
|
|
471
296
|
);
|
|
472
297
|
|
|
473
|
-
// ========================
|
|
474
|
-
// Execute Google Service
|
|
475
|
-
// ========================
|
|
476
|
-
const executeGoogleService = async (
|
|
477
|
-
service: GoogleServiceType,
|
|
478
|
-
tokenData: GoogleTokenData,
|
|
479
|
-
params?: any
|
|
480
|
-
): Promise<any> => {
|
|
481
|
-
const headers = {
|
|
482
|
-
Authorization: `Bearer ${tokenData.accessToken}`,
|
|
483
|
-
'Content-Type': 'application/json',
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
switch (service) {
|
|
487
|
-
case 'docs': {
|
|
488
|
-
// Create or update Google Doc
|
|
489
|
-
const docTitle = params?.documentTitle || 'New Document';
|
|
490
|
-
const response = await fetch(
|
|
491
|
-
'https://docs.googleapis.com/v1/documents',
|
|
492
|
-
{
|
|
493
|
-
method: 'POST',
|
|
494
|
-
headers,
|
|
495
|
-
body: JSON.stringify({ title: docTitle }),
|
|
496
|
-
}
|
|
497
|
-
);
|
|
498
|
-
return response.json();
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
case 'slides': {
|
|
502
|
-
// Create Google Slides presentation
|
|
503
|
-
const slideTitle = params?.presentationTitle || 'New Presentation';
|
|
504
|
-
const response = await fetch(
|
|
505
|
-
'https://slides.googleapis.com/v1/presentations',
|
|
506
|
-
{
|
|
507
|
-
method: 'POST',
|
|
508
|
-
headers,
|
|
509
|
-
body: JSON.stringify({ title: slideTitle }),
|
|
510
|
-
}
|
|
511
|
-
);
|
|
512
|
-
return response.json();
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
case 'meet': {
|
|
516
|
-
// Create Google Meet link
|
|
517
|
-
const response = await fetch(
|
|
518
|
-
'https://meet.googleapis.com/v2/spaces',
|
|
519
|
-
{
|
|
520
|
-
method: 'POST',
|
|
521
|
-
headers,
|
|
522
|
-
body: JSON.stringify({
|
|
523
|
-
config: {
|
|
524
|
-
accessType: 'OPEN',
|
|
525
|
-
entryPointAccess: 'ALL',
|
|
526
|
-
},
|
|
527
|
-
}),
|
|
528
|
-
}
|
|
529
|
-
);
|
|
530
|
-
return response.json();
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
case 'gmail': {
|
|
534
|
-
// Send Gmail
|
|
535
|
-
const to = params?.to?.join(', ') || '';
|
|
536
|
-
const subject = params?.subject || 'No Subject';
|
|
537
|
-
const body = params?.body || '';
|
|
538
|
-
|
|
539
|
-
const email = [
|
|
540
|
-
`To: ${to}`,
|
|
541
|
-
`Subject: ${subject}`,
|
|
542
|
-
'',
|
|
543
|
-
body,
|
|
544
|
-
].join('\n');
|
|
545
|
-
|
|
546
|
-
const encodedEmail = btoa(unescape(encodeURIComponent(email)))
|
|
547
|
-
.replace(/\+/g, '-')
|
|
548
|
-
.replace(/\//g, '_')
|
|
549
|
-
.replace(/=+$/, '');
|
|
550
|
-
|
|
551
|
-
const response = await fetch(
|
|
552
|
-
'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
|
|
553
|
-
{
|
|
554
|
-
method: 'POST',
|
|
555
|
-
headers,
|
|
556
|
-
body: JSON.stringify({ raw: encodedEmail }),
|
|
557
|
-
}
|
|
558
|
-
);
|
|
559
|
-
return response.json();
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
case 'sheets': {
|
|
563
|
-
// Read/Write Google Sheets
|
|
564
|
-
const spreadsheetId = params?.spreadsheetId;
|
|
565
|
-
const range = params?.range || 'Sheet1!A1:Z100';
|
|
566
|
-
|
|
567
|
-
if (!spreadsheetId) {
|
|
568
|
-
// Create new spreadsheet
|
|
569
|
-
const response = await fetch(
|
|
570
|
-
'https://sheets.googleapis.com/v4/spreadsheets',
|
|
571
|
-
{
|
|
572
|
-
method: 'POST',
|
|
573
|
-
headers,
|
|
574
|
-
body: JSON.stringify({
|
|
575
|
-
properties: {
|
|
576
|
-
title: params?.sheetName || 'New Spreadsheet',
|
|
577
|
-
},
|
|
578
|
-
}),
|
|
579
|
-
}
|
|
580
|
-
);
|
|
581
|
-
return response.json();
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// Read existing spreadsheet
|
|
585
|
-
const response = await fetch(
|
|
586
|
-
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`,
|
|
587
|
-
{ headers }
|
|
588
|
-
);
|
|
589
|
-
return response.json();
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
case 'calendar': {
|
|
593
|
-
// Create calendar event
|
|
594
|
-
const calendarId = params?.calendarId || 'primary';
|
|
595
|
-
const response = await fetch(
|
|
596
|
-
`https://www.googleapis.com/calendar/v3/calendars/${calendarId}/events`,
|
|
597
|
-
{
|
|
598
|
-
method: 'POST',
|
|
599
|
-
headers,
|
|
600
|
-
body: JSON.stringify({
|
|
601
|
-
summary: params?.eventTitle || 'New Event',
|
|
602
|
-
description: params?.eventDescription,
|
|
603
|
-
start: {
|
|
604
|
-
dateTime: params?.startTime || new Date().toISOString(),
|
|
605
|
-
timeZone: 'UTC',
|
|
606
|
-
},
|
|
607
|
-
end: {
|
|
608
|
-
dateTime:
|
|
609
|
-
params?.endTime ||
|
|
610
|
-
new Date(Date.now() + 3600000).toISOString(),
|
|
611
|
-
timeZone: 'UTC',
|
|
612
|
-
},
|
|
613
|
-
}),
|
|
614
|
-
}
|
|
615
|
-
);
|
|
616
|
-
return response.json();
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
default:
|
|
620
|
-
throw new Error(`Unsupported service: ${service}`);
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
|
|
624
298
|
// ========================
|
|
625
299
|
// Render Status Badge
|
|
626
300
|
// ========================
|
|
@@ -632,41 +306,42 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
632
306
|
idle: {
|
|
633
307
|
color: '#6B7280',
|
|
634
308
|
bgColor: 'rgba(107, 114, 128, 0.1)',
|
|
635
|
-
label: '
|
|
309
|
+
label: 'Ready',
|
|
636
310
|
},
|
|
637
311
|
authenticating: {
|
|
638
312
|
color: '#F59E0B',
|
|
639
313
|
bgColor: 'rgba(245, 158, 11, 0.1)',
|
|
640
|
-
label: '
|
|
314
|
+
label: 'Authenticating...',
|
|
641
315
|
icon: <CircularProgress size={12} color="inherit" />,
|
|
642
316
|
},
|
|
643
317
|
authenticated: {
|
|
644
318
|
color: '#10B981',
|
|
645
319
|
bgColor: 'rgba(16, 185, 129, 0.1)',
|
|
646
|
-
label: '
|
|
320
|
+
label: 'Connected',
|
|
647
321
|
icon: <CheckCircleIcon sx={{ fontSize: 12 }} />,
|
|
648
322
|
},
|
|
649
323
|
running: {
|
|
650
324
|
color: '#3B82F6',
|
|
651
325
|
bgColor: 'rgba(59, 130, 246, 0.1)',
|
|
652
|
-
label: '
|
|
326
|
+
label: 'Running...',
|
|
653
327
|
icon: <CircularProgress size={12} color="inherit" />,
|
|
654
328
|
},
|
|
655
329
|
completed: {
|
|
656
330
|
color: '#10B981',
|
|
657
331
|
bgColor: 'rgba(16, 185, 129, 0.1)',
|
|
658
|
-
label: '
|
|
332
|
+
label: 'Success',
|
|
659
333
|
icon: <CheckCircleIcon sx={{ fontSize: 12 }} />,
|
|
660
334
|
},
|
|
661
335
|
error: {
|
|
662
336
|
color: '#EF4444',
|
|
663
337
|
bgColor: 'rgba(239, 68, 68, 0.1)',
|
|
664
|
-
label: '
|
|
338
|
+
label: 'Error',
|
|
665
339
|
icon: <ErrorIcon sx={{ fontSize: 12 }} />,
|
|
666
340
|
},
|
|
667
341
|
};
|
|
668
342
|
|
|
669
|
-
const
|
|
343
|
+
const currentStatus = isAuthenticated ? 'authenticated' : status;
|
|
344
|
+
const config = statusConfig[currentStatus];
|
|
670
345
|
|
|
671
346
|
return (
|
|
672
347
|
<Chip
|
|
@@ -690,6 +365,81 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
690
365
|
);
|
|
691
366
|
};
|
|
692
367
|
|
|
368
|
+
// ========================
|
|
369
|
+
// Render Google Auth Button
|
|
370
|
+
// ========================
|
|
371
|
+
const renderGoogleAuthButton = () => {
|
|
372
|
+
if (!showGoogleAuthButton) return null;
|
|
373
|
+
|
|
374
|
+
if (isAuthenticated) {
|
|
375
|
+
return (
|
|
376
|
+
<Button
|
|
377
|
+
onClick={data.onGoogleDisconnect}
|
|
378
|
+
disabled={isAuthLoading}
|
|
379
|
+
fullWidth
|
|
380
|
+
sx={{
|
|
381
|
+
backgroundColor: '#10B981',
|
|
382
|
+
border: '1px solid #10B981',
|
|
383
|
+
borderRadius: '8px',
|
|
384
|
+
padding: '8px 12px',
|
|
385
|
+
gap: '8px',
|
|
386
|
+
color: '#FFFFFF',
|
|
387
|
+
textTransform: 'none',
|
|
388
|
+
fontSize: '13px',
|
|
389
|
+
fontWeight: 500,
|
|
390
|
+
'&:hover': {
|
|
391
|
+
backgroundColor: '#059669',
|
|
392
|
+
},
|
|
393
|
+
'&:disabled': {
|
|
394
|
+
opacity: 0.6,
|
|
395
|
+
},
|
|
396
|
+
}}
|
|
397
|
+
>
|
|
398
|
+
<FcGoogle size={18} />
|
|
399
|
+
{userEmail ? userEmail.split('@')[0] : 'Connected'}
|
|
400
|
+
</Button>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<Button
|
|
406
|
+
onClick={data.onGoogleLogin}
|
|
407
|
+
disabled={isAuthLoading}
|
|
408
|
+
fullWidth
|
|
409
|
+
sx={{
|
|
410
|
+
backgroundColor: '#171C29',
|
|
411
|
+
border: '1px solid #FFFFFF1A',
|
|
412
|
+
borderRadius: '8px',
|
|
413
|
+
padding: '8px 12px',
|
|
414
|
+
gap: '8px',
|
|
415
|
+
color: '#B2BCD8',
|
|
416
|
+
textTransform: 'none',
|
|
417
|
+
fontSize: '13px',
|
|
418
|
+
fontWeight: 400,
|
|
419
|
+
'&:hover': {
|
|
420
|
+
backgroundColor: '#1e293b',
|
|
421
|
+
borderColor: '#3B82F6',
|
|
422
|
+
},
|
|
423
|
+
'&:disabled': {
|
|
424
|
+
opacity: 0.6,
|
|
425
|
+
},
|
|
426
|
+
}}
|
|
427
|
+
>
|
|
428
|
+
{isAuthLoading ? (
|
|
429
|
+
<>
|
|
430
|
+
<CircularProgress size={16} sx={{ color: '#B2BCD8' }} />
|
|
431
|
+
Connecting...
|
|
432
|
+
</>
|
|
433
|
+
) : (
|
|
434
|
+
<>
|
|
435
|
+
<FcGoogle size={18} />
|
|
436
|
+
Connect Google
|
|
437
|
+
</>
|
|
438
|
+
)}
|
|
439
|
+
</Button>
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
|
|
693
443
|
// ========================
|
|
694
444
|
// Render
|
|
695
445
|
// ========================
|
|
@@ -757,7 +507,7 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
757
507
|
variant="caption"
|
|
758
508
|
sx={{ color: '#9CA3AF', fontSize: 10 }}
|
|
759
509
|
>
|
|
760
|
-
{serviceConfig.description}
|
|
510
|
+
{data.description || serviceConfig.description}
|
|
761
511
|
</Typography>
|
|
762
512
|
</Box>
|
|
763
513
|
</Box>
|
|
@@ -766,92 +516,63 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
766
516
|
|
|
767
517
|
{/* Content */}
|
|
768
518
|
<Box sx={{ px: 2, py: 1.5 }}>
|
|
769
|
-
{/*
|
|
770
|
-
|
|
771
|
-
sx={{
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
519
|
+
{/* Google Auth Button - Only on first Google node */}
|
|
520
|
+
{showGoogleAuthButton && (
|
|
521
|
+
<Box sx={{ mb: 1.5 }}>
|
|
522
|
+
{renderGoogleAuthButton()}
|
|
523
|
+
{!isAuthenticated && (
|
|
524
|
+
<Typography
|
|
525
|
+
variant="caption"
|
|
526
|
+
sx={{
|
|
527
|
+
color: '#6B7280',
|
|
528
|
+
fontSize: 10,
|
|
529
|
+
display: 'block',
|
|
530
|
+
textAlign: 'center',
|
|
531
|
+
mt: 0.5,
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
Click to authenticate with Google
|
|
535
|
+
</Typography>
|
|
536
|
+
)}
|
|
537
|
+
</Box>
|
|
538
|
+
)}
|
|
539
|
+
|
|
540
|
+
{/* Authentication Status for non-first nodes */}
|
|
541
|
+
{!showGoogleAuthButton && (
|
|
542
|
+
<Box
|
|
543
|
+
sx={{
|
|
544
|
+
bgcolor: isAuthenticated ? 'rgba(16, 185, 129, 0.1)' : 'rgba(107, 114, 128, 0.1)',
|
|
545
|
+
borderRadius: 1,
|
|
546
|
+
p: 1.5,
|
|
547
|
+
mb: 1.5,
|
|
548
|
+
}}
|
|
549
|
+
>
|
|
550
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
551
|
+
<FcGoogle size={18} style={{ opacity: isAuthenticated ? 1 : 0.5 }} />
|
|
552
|
+
<Box>
|
|
553
|
+
<Typography
|
|
554
|
+
variant="caption"
|
|
555
|
+
sx={{
|
|
556
|
+
color: isAuthenticated ? '#10B981' : '#6B7280',
|
|
557
|
+
display: 'block',
|
|
558
|
+
fontWeight: 600,
|
|
559
|
+
fontSize: 11,
|
|
560
|
+
}}
|
|
561
|
+
>
|
|
562
|
+
{isAuthenticated ? 'Google Connected' : 'Google Not Connected'}
|
|
563
|
+
</Typography>
|
|
564
|
+
{isAuthenticated && userEmail && (
|
|
789
565
|
<Typography
|
|
790
566
|
variant="caption"
|
|
791
567
|
sx={{ color: '#9CA3AF', fontSize: 10 }}
|
|
792
568
|
>
|
|
793
|
-
{
|
|
569
|
+
{userEmail}
|
|
794
570
|
</Typography>
|
|
795
|
-
|
|
571
|
+
)}
|
|
796
572
|
</Box>
|
|
797
|
-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
798
|
-
<Tooltip title={`කල් ඉකුත් වන්නේ: ${getTimeUntilExpiry(tokens.expiresAt)}`}>
|
|
799
|
-
<Typography
|
|
800
|
-
variant="caption"
|
|
801
|
-
sx={{
|
|
802
|
-
color: isTokenExpired(tokens.expiresAt) ? '#EF4444' : '#9CA3AF',
|
|
803
|
-
fontSize: 10,
|
|
804
|
-
}}
|
|
805
|
-
>
|
|
806
|
-
{getTimeUntilExpiry(tokens.expiresAt)}
|
|
807
|
-
</Typography>
|
|
808
|
-
</Tooltip>
|
|
809
|
-
<IconButton
|
|
810
|
-
size="small"
|
|
811
|
-
onClick={handleDisconnect}
|
|
812
|
-
sx={{ color: '#EF4444', p: 0.5 }}
|
|
813
|
-
>
|
|
814
|
-
<LinkOffIcon sx={{ fontSize: 14 }} />
|
|
815
|
-
</IconButton>
|
|
816
|
-
</Box>
|
|
817
|
-
</Box>
|
|
818
|
-
) : (
|
|
819
|
-
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
820
|
-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
821
|
-
<FcGoogle size={20} style={{ opacity: 0.5 }} />
|
|
822
|
-
<Typography variant="caption" sx={{ color: '#6B7280' }}>
|
|
823
|
-
Google ගිණුම සම්බන්ධ කර නැත
|
|
824
|
-
</Typography>
|
|
825
|
-
</Box>
|
|
826
|
-
<Button
|
|
827
|
-
size="small"
|
|
828
|
-
variant="outlined"
|
|
829
|
-
onClick={handleGoogleLogin}
|
|
830
|
-
disabled={status === 'authenticating'}
|
|
831
|
-
startIcon={
|
|
832
|
-
status === 'authenticating' ? (
|
|
833
|
-
<CircularProgress size={12} color="inherit" />
|
|
834
|
-
) : (
|
|
835
|
-
<LinkIcon sx={{ fontSize: 14 }} />
|
|
836
|
-
)
|
|
837
|
-
}
|
|
838
|
-
sx={{
|
|
839
|
-
fontSize: 10,
|
|
840
|
-
py: 0.25,
|
|
841
|
-
px: 1,
|
|
842
|
-
borderColor: '#374151',
|
|
843
|
-
color: '#9CA3AF',
|
|
844
|
-
'&:hover': {
|
|
845
|
-
borderColor: serviceConfig.color,
|
|
846
|
-
color: serviceConfig.color,
|
|
847
|
-
},
|
|
848
|
-
}}
|
|
849
|
-
>
|
|
850
|
-
සම්බන්ධ කරන්න
|
|
851
|
-
</Button>
|
|
852
573
|
</Box>
|
|
853
|
-
|
|
854
|
-
|
|
574
|
+
</Box>
|
|
575
|
+
)}
|
|
855
576
|
|
|
856
577
|
{/* Progress Bar (when running) */}
|
|
857
578
|
{status === 'running' && (
|
|
@@ -872,7 +593,7 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
872
593
|
variant="caption"
|
|
873
594
|
sx={{ color: '#9CA3AF', fontSize: 10, mt: 0.5, display: 'block' }}
|
|
874
595
|
>
|
|
875
|
-
|
|
596
|
+
Executing... {executionProgress}%
|
|
876
597
|
</Typography>
|
|
877
598
|
</Box>
|
|
878
599
|
)}
|
|
@@ -912,7 +633,7 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
912
633
|
variant="caption"
|
|
913
634
|
sx={{ color: '#10B981', fontSize: 10, display: 'block', fontWeight: 600 }}
|
|
914
635
|
>
|
|
915
|
-
✓
|
|
636
|
+
✓ Executed successfully
|
|
916
637
|
</Typography>
|
|
917
638
|
{result.documentId && (
|
|
918
639
|
<Typography
|
|
@@ -941,58 +662,55 @@ export const AutomationGoogleServicesNode: React.FC<
|
|
|
941
662
|
</Box>
|
|
942
663
|
)}
|
|
943
664
|
|
|
944
|
-
{/*
|
|
945
|
-
<
|
|
946
|
-
fullWidth
|
|
947
|
-
variant="contained"
|
|
948
|
-
onClick={handleExecute}
|
|
949
|
-
disabled={isExecuting}
|
|
950
|
-
startIcon={
|
|
951
|
-
isExecuting ? (
|
|
952
|
-
<CircularProgress size={16} color="inherit" />
|
|
953
|
-
) : tokens ? (
|
|
954
|
-
<PlayIcon />
|
|
955
|
-
) : (
|
|
956
|
-
<FcGoogle size={16} />
|
|
957
|
-
)
|
|
958
|
-
}
|
|
665
|
+
{/* Service-specific details */}
|
|
666
|
+
<Box
|
|
959
667
|
sx={{
|
|
960
|
-
bgcolor:
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
fontSize: 12,
|
|
964
|
-
py: 1,
|
|
965
|
-
'&:hover': {
|
|
966
|
-
bgcolor: tokens ? `${serviceConfig.color}CC` : '#4B5563',
|
|
967
|
-
},
|
|
968
|
-
'&:disabled': {
|
|
969
|
-
bgcolor: '#374151',
|
|
970
|
-
color: '#6B7280',
|
|
971
|
-
},
|
|
668
|
+
bgcolor: 'rgba(255, 255, 255, 0.03)',
|
|
669
|
+
borderRadius: 1,
|
|
670
|
+
p: 1,
|
|
972
671
|
}}
|
|
973
672
|
>
|
|
974
|
-
{
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
sx={{
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
673
|
+
{serviceType === 'docs' && data.parameters?.documentTitle && (
|
|
674
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
675
|
+
Document: {data.parameters.documentTitle}
|
|
676
|
+
</Typography>
|
|
677
|
+
)}
|
|
678
|
+
{serviceType === 'gmail' && data.parameters?.subject && (
|
|
679
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
680
|
+
Subject: {data.parameters.subject}
|
|
681
|
+
</Typography>
|
|
682
|
+
)}
|
|
683
|
+
{serviceType === 'meet' && data.parameters?.meetingTitle && (
|
|
684
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
685
|
+
Meeting: {data.parameters.meetingTitle}
|
|
686
|
+
</Typography>
|
|
687
|
+
)}
|
|
688
|
+
{serviceType === 'slides' && data.parameters?.presentationTitle && (
|
|
689
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
690
|
+
Presentation: {data.parameters.presentationTitle}
|
|
691
|
+
</Typography>
|
|
692
|
+
)}
|
|
693
|
+
{serviceType === 'calendar' && data.parameters?.eventTitle && (
|
|
694
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
695
|
+
Event: {data.parameters.eventTitle}
|
|
696
|
+
</Typography>
|
|
697
|
+
)}
|
|
698
|
+
{serviceType === 'sheets' && data.parameters?.sheetName && (
|
|
699
|
+
<Typography variant="caption" sx={{ color: '#9CA3AF', fontSize: 10 }}>
|
|
700
|
+
Sheet: {data.parameters.sheetName}
|
|
701
|
+
</Typography>
|
|
702
|
+
)}
|
|
703
|
+
{!data.parameters?.documentTitle &&
|
|
704
|
+
!data.parameters?.subject &&
|
|
705
|
+
!data.parameters?.meetingTitle &&
|
|
706
|
+
!data.parameters?.presentationTitle &&
|
|
707
|
+
!data.parameters?.eventTitle &&
|
|
708
|
+
!data.parameters?.sheetName && (
|
|
709
|
+
<Typography variant="caption" sx={{ color: '#6B7280', fontSize: 10, fontStyle: 'italic' }}>
|
|
710
|
+
Configure node parameters
|
|
711
|
+
</Typography>
|
|
712
|
+
)}
|
|
713
|
+
</Box>
|
|
996
714
|
</Box>
|
|
997
715
|
|
|
998
716
|
{/* Node Action Buttons */}
|
|
@@ -277,6 +277,9 @@ export interface AutomationGoogleServicesNodeForm extends AutomationNodeFormData
|
|
|
277
277
|
type: 'start' | 'api' | 'formatting' | 'sheets' | 'end' | 'navigation';
|
|
278
278
|
serviceType: GoogleServiceType;
|
|
279
279
|
|
|
280
|
+
// Flag to show Google auth button only on first node
|
|
281
|
+
isFirstGoogleNode?: boolean;
|
|
282
|
+
|
|
280
283
|
// Google Auth Configuration
|
|
281
284
|
googleAuth?: {
|
|
282
285
|
clientId?: string;
|
|
@@ -284,6 +287,8 @@ export interface AutomationGoogleServicesNodeForm extends AutomationNodeFormData
|
|
|
284
287
|
accessToken?: string;
|
|
285
288
|
expiresAt?: number;
|
|
286
289
|
userEmail?: string;
|
|
290
|
+
isAuthenticated?: boolean;
|
|
291
|
+
isLoading?: boolean;
|
|
287
292
|
};
|
|
288
293
|
|
|
289
294
|
// Service-specific Parameters
|
|
@@ -325,6 +330,10 @@ export interface AutomationGoogleServicesNodeForm extends AutomationNodeFormData
|
|
|
325
330
|
|
|
326
331
|
// Output
|
|
327
332
|
outputVariable?: string;
|
|
333
|
+
|
|
334
|
+
// Callbacks
|
|
335
|
+
onGoogleLogin?: () => void;
|
|
336
|
+
onGoogleDisconnect?: () => void;
|
|
328
337
|
}
|
|
329
338
|
|
|
330
339
|
export type AutomationNodeForm =
|
|
@@ -29,7 +29,7 @@ export default {
|
|
|
29
29
|
AutomationNoteNode,
|
|
30
30
|
AutomationNavigationNode,
|
|
31
31
|
AutomationAISuggestionNode,
|
|
32
|
-
// Google Services Nodes
|
|
32
|
+
// Google Services Nodes
|
|
33
33
|
AutomationGoogleServicesNode,
|
|
34
34
|
AutomationGoogleDocsNode: AutomationGoogleServicesNode,
|
|
35
35
|
AutomationGoogleSlidesNode: AutomationGoogleServicesNode,
|