@flowuent-org/diagramming-core 1.1.8 → 1.2.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/package.json +116 -116
- package/packages/diagrams/src/index.ts +1 -0
- package/packages/diagrams/src/lib/atoms/ConnectionPoints.tsx +149 -0
- package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +794 -650
- package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +606 -449
- package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +831 -687
- package/packages/diagrams/src/lib/components/automation/AutomationNoteNode.tsx +420 -275
- package/packages/diagrams/src/lib/components/automation/AutomationSheetsNode.tsx +1118 -974
- package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +509 -344
- package/packages/diagrams/src/lib/components/automation/NodeAIAssistantPopup.tsx +504 -0
- package/packages/diagrams/src/lib/components/automation/NodeActionButtons.tsx +146 -0
- package/packages/diagrams/src/lib/components/automation/index.ts +20 -11
- package/packages/diagrams/src/lib/molecules/SideHandles.tsx +177 -12
- package/packages/diagrams/src/lib/organisms/CustomEdge/custom-edge-generator.tsx +10 -5
- package/packages/diagrams/src/lib/styles.css +53 -0
- package/packages/diagrams/src/lib/templates/DiagramContainer.tsx +59 -0
- package/packages/diagrams/src/lib/templates/Diagramming.tsx +246 -204
- package/packages/diagrams/src/lib/types/edge-types.ts +17 -0
- package/packages/diagrams/src/lib/utils/generateEdgesFromNodeOrder.ts +113 -0
- package/packages/diagrams/src/lib/utils/nodeAIAssistantConfig.ts +54 -0
|
@@ -1,974 +1,1118 @@
|
|
|
1
|
-
import React, { useEffect, useState, useRef } from 'react';
|
|
2
|
-
import { createRoot } from 'react-dom/client';
|
|
3
|
-
import { Handle, Position, useNodeId } from '@xyflow/react';
|
|
4
|
-
import { Box, Typography, Chip, IconButton, Card, CardContent, Button } from '@mui/material';
|
|
5
|
-
import {
|
|
6
|
-
TableChart as TableChartIcon,
|
|
7
|
-
AccessTime as AccessTimeIcon,
|
|
8
|
-
Save as SaveIcon,
|
|
9
|
-
Send as SendIcon,
|
|
10
|
-
Settings as SettingsIcon,
|
|
11
|
-
TableRows as TableRowsIcon,
|
|
12
|
-
Email as EmailIcon,
|
|
13
|
-
Description as DescriptionIcon,
|
|
14
|
-
Drafts as DraftsIcon,
|
|
15
|
-
Add as AddIcon,
|
|
16
|
-
CheckCircle as CheckCircleIcon,
|
|
17
|
-
Settings as ConfigureIcon,
|
|
18
|
-
Error as ErrorIcon,
|
|
19
|
-
ArrowForwardIos as ArrowForwardIcon,
|
|
20
|
-
WhatsApp as WhatsAppIcon,
|
|
21
|
-
Phone as PhoneIcon,
|
|
22
|
-
Lightbulb as LightbulbIcon
|
|
23
|
-
} from '@mui/icons-material';
|
|
24
|
-
import { RiCloseLine } from 'react-icons/ri';
|
|
25
|
-
import ReactJson from 'react-json-view';
|
|
26
|
-
import { getIconByName } from '../../utils/iconMapper';
|
|
27
|
-
import { useTranslation } from 'react-i18next';
|
|
28
|
-
import { useDiagram } from '../../contexts/DiagramProvider';
|
|
29
|
-
import { AISuggestion } from './AISuggestionsModal';
|
|
30
|
-
import { AISuggestionsPanel } from './AISuggestionsPanel';
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
exportOptions.
|
|
131
|
-
exportOptions.
|
|
132
|
-
exportOptions.
|
|
133
|
-
);
|
|
134
|
-
const
|
|
135
|
-
exportOptions.
|
|
136
|
-
exportOptions.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
return '
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
return '
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return '
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
(
|
|
235
|
-
(
|
|
236
|
-
(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
'
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
'
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
'
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
'
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
'
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
'
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
</Box>
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
'
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
</Box>
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
{
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1
|
+
import React, { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { Handle, Position, useNodeId } from '@xyflow/react';
|
|
4
|
+
import { Box, Typography, Chip, IconButton, Card, CardContent, Button } from '@mui/material';
|
|
5
|
+
import {
|
|
6
|
+
TableChart as TableChartIcon,
|
|
7
|
+
AccessTime as AccessTimeIcon,
|
|
8
|
+
Save as SaveIcon,
|
|
9
|
+
Send as SendIcon,
|
|
10
|
+
Settings as SettingsIcon,
|
|
11
|
+
TableRows as TableRowsIcon,
|
|
12
|
+
Email as EmailIcon,
|
|
13
|
+
Description as DescriptionIcon,
|
|
14
|
+
Drafts as DraftsIcon,
|
|
15
|
+
Add as AddIcon,
|
|
16
|
+
CheckCircle as CheckCircleIcon,
|
|
17
|
+
Settings as ConfigureIcon,
|
|
18
|
+
Error as ErrorIcon,
|
|
19
|
+
ArrowForwardIos as ArrowForwardIcon,
|
|
20
|
+
WhatsApp as WhatsAppIcon,
|
|
21
|
+
Phone as PhoneIcon,
|
|
22
|
+
Lightbulb as LightbulbIcon
|
|
23
|
+
} from '@mui/icons-material';
|
|
24
|
+
import { RiCloseLine } from 'react-icons/ri';
|
|
25
|
+
import ReactJson from 'react-json-view';
|
|
26
|
+
import { getIconByName } from '../../utils/iconMapper';
|
|
27
|
+
import { useTranslation } from 'react-i18next';
|
|
28
|
+
import { useDiagram } from '../../contexts/DiagramProvider';
|
|
29
|
+
import { AISuggestion } from './AISuggestionsModal';
|
|
30
|
+
import { AISuggestionsPanel } from './AISuggestionsPanel';
|
|
31
|
+
import { NodeActionButtons } from './NodeActionButtons';
|
|
32
|
+
import { showNodeAIAssistantPopup } from './NodeAIAssistantPopup';
|
|
33
|
+
|
|
34
|
+
interface AutomationSheetsNodeProps {
|
|
35
|
+
data: {
|
|
36
|
+
label: string;
|
|
37
|
+
description: string;
|
|
38
|
+
status: 'Ready' | 'Running' | 'Completed' | 'Error';
|
|
39
|
+
inputVariable: string;
|
|
40
|
+
lastRun?: string;
|
|
41
|
+
iconName?: string;
|
|
42
|
+
sheetsConfig: {
|
|
43
|
+
spreadsheetId: string;
|
|
44
|
+
sheetName: string;
|
|
45
|
+
range: string;
|
|
46
|
+
credentials: {
|
|
47
|
+
type: 'service-account' | 'oauth' | 'api-key';
|
|
48
|
+
serviceAccountKey?: string;
|
|
49
|
+
oauthToken?: string;
|
|
50
|
+
apiKey?: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
dataMapping: {
|
|
54
|
+
headers: Array<{
|
|
55
|
+
column: string;
|
|
56
|
+
sourceField: string;
|
|
57
|
+
dataType: 'string' | 'number' | 'date' | 'boolean';
|
|
58
|
+
}>;
|
|
59
|
+
appendMode: boolean;
|
|
60
|
+
clearSheet: boolean;
|
|
61
|
+
};
|
|
62
|
+
exportOptions: {
|
|
63
|
+
emailRecipients: string[];
|
|
64
|
+
fileName: string;
|
|
65
|
+
exportFormat: 'sheets' | 'excel' | 'both';
|
|
66
|
+
includeHeaders: boolean;
|
|
67
|
+
sendEmailNotification: boolean;
|
|
68
|
+
// Optional fields for Gmail output configuration (may live under formData.exportOptions)
|
|
69
|
+
emailSendEnabled?: boolean;
|
|
70
|
+
emailSender?: string;
|
|
71
|
+
emailSubject?: string;
|
|
72
|
+
emailMessage?: string;
|
|
73
|
+
// WhatsApp configuration
|
|
74
|
+
whatsapp?: {
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
phoneNumber: string;
|
|
77
|
+
messageTemplate: string;
|
|
78
|
+
includeDataMode: 'json' | 'summary' | 'custom';
|
|
79
|
+
// Twilio configuration
|
|
80
|
+
twilio?: {
|
|
81
|
+
enabled: boolean;
|
|
82
|
+
accountSid: string;
|
|
83
|
+
authToken: string;
|
|
84
|
+
fromNumber: string;
|
|
85
|
+
isSandbox: boolean;
|
|
86
|
+
templateSid?: string;
|
|
87
|
+
templateVariables?: Record<string, string>;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
formData?: {
|
|
92
|
+
aiSuggestionsCount?: number; // Number of AI suggestions available
|
|
93
|
+
[key: string]: any;
|
|
94
|
+
};
|
|
95
|
+
// Optional per-output-method statuses supplied by engine
|
|
96
|
+
outputStatuses?: {
|
|
97
|
+
googleSheets?: 'not-set' | 'configured' | 'running' | 'connected' | 'failed';
|
|
98
|
+
gmail?: 'not-set' | 'configured' | 'running' | 'connected' | 'failed';
|
|
99
|
+
whatsapp?: 'not-set' | 'configured' | 'running' | 'connected' | 'failed';
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
selected?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const AutomationSheetsNode: React.FC<AutomationSheetsNodeProps> = ({ data, selected }) => {
|
|
106
|
+
const { t } = useTranslation();
|
|
107
|
+
const [isJsonOpen, setIsJsonOpen] = useState(false);
|
|
108
|
+
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
109
|
+
const rootRef = useRef<any>(null);
|
|
110
|
+
const portalRef = useRef<HTMLDivElement | null>(null);
|
|
111
|
+
const nodeRef = useRef<HTMLDivElement | null>(null);
|
|
112
|
+
const nodeId = useNodeId();
|
|
113
|
+
const setSelectedNode = useDiagram((state) => state.setSelectedNode);
|
|
114
|
+
const enableJson = useDiagram((state) => state.enableNodeJsonPopover ?? true);
|
|
115
|
+
const onNodesChange = useDiagram((state) => state.onNodesChange);
|
|
116
|
+
const nodes = useDiagram((state) => state.nodes);
|
|
117
|
+
const setNodes = useDiagram((state) => state.setNodes);
|
|
118
|
+
|
|
119
|
+
// Get the icon component based on the iconName
|
|
120
|
+
const IconComponent = getIconByName(data.iconName || 'TableChart');
|
|
121
|
+
|
|
122
|
+
// Resolve export options from formData or data
|
|
123
|
+
const exportOptions = (data.formData?.exportOptions || (data as any).exportOptions) || {};
|
|
124
|
+
const showSheets = Boolean(
|
|
125
|
+
exportOptions?.exportFormat === 'sheets' || exportOptions?.exportFormat === 'both'
|
|
126
|
+
);
|
|
127
|
+
const showGmail = Boolean(
|
|
128
|
+
exportOptions.emailSendEnabled &&
|
|
129
|
+
exportOptions.emailSender &&
|
|
130
|
+
(exportOptions.emailRecipients?.length || 0) > 0 &&
|
|
131
|
+
exportOptions.emailSubject &&
|
|
132
|
+
exportOptions.emailMessage
|
|
133
|
+
);
|
|
134
|
+
const showSlack = Boolean(
|
|
135
|
+
exportOptions.slack?.enabled &&
|
|
136
|
+
exportOptions.slack?.webhookUrl &&
|
|
137
|
+
exportOptions.slack?.channel
|
|
138
|
+
);
|
|
139
|
+
const showWhatsApp = Boolean(
|
|
140
|
+
exportOptions.whatsapp?.enabled &&
|
|
141
|
+
exportOptions.whatsapp?.phoneNumber
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Determine Google Sheets status from provided data and execution result
|
|
145
|
+
const determineGoogleSheetsStatus = (): 'not-set' | 'configured' | 'running' | 'connected' | 'failed' => {
|
|
146
|
+
// Prefer status provided by engine
|
|
147
|
+
if (data.outputStatuses?.googleSheets) return data.outputStatuses.googleSheets;
|
|
148
|
+
|
|
149
|
+
// Required config check (accepts either spreadsheetId or OAuth clientId that can create one)
|
|
150
|
+
const cfg = (data.formData?.sheetsConfig || data.sheetsConfig) || {};
|
|
151
|
+
const creds = cfg.credentials || {};
|
|
152
|
+
const hasRequired = Boolean((cfg.spreadsheetId || creds.clientId) && cfg.sheetName && creds.type);
|
|
153
|
+
if (!hasRequired) return 'not-set';
|
|
154
|
+
|
|
155
|
+
// Running inferred from node status
|
|
156
|
+
if (data.status === 'Running') return 'running';
|
|
157
|
+
|
|
158
|
+
// Execution result inference
|
|
159
|
+
const exec = data.formData?.executionResult || (data as any).executionResult;
|
|
160
|
+
if (exec?.success === true) return 'connected';
|
|
161
|
+
if (exec?.success === false || data.status === 'Error') return 'failed';
|
|
162
|
+
return 'configured';
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Determine Gmail status
|
|
166
|
+
const determineGmailStatus = (): 'not-set' | 'configured' | 'running' | 'connected' | 'failed' => {
|
|
167
|
+
// Prefer status provided by engine
|
|
168
|
+
if (data.outputStatuses?.gmail) return data.outputStatuses.gmail;
|
|
169
|
+
|
|
170
|
+
const ex = (data.formData?.exportOptions || data.exportOptions) || {};
|
|
171
|
+
const hasRequired = Boolean(
|
|
172
|
+
ex.emailSendEnabled && ex.emailSender && (ex.emailRecipients?.length || 0) > 0 && ex.emailSubject && ex.emailMessage
|
|
173
|
+
);
|
|
174
|
+
if (!hasRequired) return 'not-set';
|
|
175
|
+
|
|
176
|
+
if (data.status === 'Running') return 'running';
|
|
177
|
+
|
|
178
|
+
const exec = data.formData?.executionResult || (data as any).executionResult;
|
|
179
|
+
if (exec?.success === true) return 'connected';
|
|
180
|
+
if (exec?.success === false || data.status === 'Error') return 'failed';
|
|
181
|
+
return 'configured';
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Determine Slack status
|
|
185
|
+
const determineSlackStatus = (): 'not-set' | 'configured' | 'running' | 'connected' | 'failed' => {
|
|
186
|
+
if ((data as any).outputStatuses?.slack) return (data as any).outputStatuses.slack;
|
|
187
|
+
const slack = (data.formData?.exportOptions?.slack || (data as any).exportOptions?.slack) || {};
|
|
188
|
+
if (!slack.enabled || !slack.webhookUrl || !slack.channel) return 'not-set';
|
|
189
|
+
if (data.status === 'Running') return 'running';
|
|
190
|
+
const exec = data.formData?.executionResult || (data as any).executionResult;
|
|
191
|
+
if (exec?.success === true) return 'connected';
|
|
192
|
+
if (exec?.success === false || data.status === 'Error') return 'failed';
|
|
193
|
+
return 'configured';
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Determine WhatsApp status
|
|
197
|
+
const determineWhatsAppStatus = (): 'not-set' | 'configured' | 'running' | 'connected' | 'failed' => {
|
|
198
|
+
if (data.outputStatuses?.whatsapp) return data.outputStatuses.whatsapp;
|
|
199
|
+
|
|
200
|
+
const whatsapp = (data.formData?.exportOptions?.whatsapp || data.exportOptions?.whatsapp) || {};
|
|
201
|
+
|
|
202
|
+
// Check if Twilio is configured
|
|
203
|
+
if (whatsapp.twilio?.enabled) {
|
|
204
|
+
const twilioConfig = whatsapp.twilio;
|
|
205
|
+
const hasRequiredTwilio = Boolean(
|
|
206
|
+
twilioConfig.accountSid &&
|
|
207
|
+
twilioConfig.authToken &&
|
|
208
|
+
twilioConfig.fromNumber &&
|
|
209
|
+
whatsapp.phoneNumber
|
|
210
|
+
);
|
|
211
|
+
if (!hasRequiredTwilio) return 'not-set';
|
|
212
|
+
} else {
|
|
213
|
+
// Fallback to basic WhatsApp Web check
|
|
214
|
+
if (!whatsapp.enabled || !whatsapp.phoneNumber) return 'not-set';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (data.status === 'Running') return 'running';
|
|
218
|
+
|
|
219
|
+
const exec = data.formData?.executionResult || (data as any).executionResult;
|
|
220
|
+
if (exec?.success === true) return 'connected';
|
|
221
|
+
if (exec?.success === false || data.status === 'Error') return 'failed';
|
|
222
|
+
return 'configured';
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const getNodeStatus = (): string => {
|
|
226
|
+
const gs = determineGoogleSheetsStatus();
|
|
227
|
+
const gm = determineGmailStatus();
|
|
228
|
+
const sl = determineSlackStatus();
|
|
229
|
+
const wa = determineWhatsAppStatus();
|
|
230
|
+
if (gs === 'failed' || gm === 'failed' || sl === 'failed' || wa === 'failed') return 'Failed';
|
|
231
|
+
if (gs === 'running' || gm === 'running' || sl === 'running' || wa === 'running') return 'Running';
|
|
232
|
+
if (
|
|
233
|
+
(gs === 'connected' || gs === 'not-set') &&
|
|
234
|
+
(gm === 'connected' || gm === 'not-set') &&
|
|
235
|
+
(sl === 'connected' || sl === 'not-set') &&
|
|
236
|
+
(wa === 'connected' || wa === 'not-set')
|
|
237
|
+
) return 'Success';
|
|
238
|
+
if (
|
|
239
|
+
(gs === 'configured' || gs === 'not-set') &&
|
|
240
|
+
(gm === 'configured' || gm === 'not-set') &&
|
|
241
|
+
(sl === 'configured' || sl === 'not-set') &&
|
|
242
|
+
(wa === 'configured' || wa === 'not-set')
|
|
243
|
+
) return 'Ready';
|
|
244
|
+
return 'Mixed Status';
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Status badge (shared)
|
|
248
|
+
const StatusBadge = ({ status }: { status: 'not-set' | 'configured' | 'running' | 'connected' | 'failed' }) => {
|
|
249
|
+
const cfg = (
|
|
250
|
+
status === 'not-set' ? { icon: <ErrorIcon sx={{ fontSize: '12px' }} />, label: t('automation.sheetsNode.status.notSet'), bg: '#6b7280' } :
|
|
251
|
+
status === 'configured' ? { icon: <ConfigureIcon sx={{ fontSize: '12px' }} />, label: t('automation.sheetsNode.status.configured'), bg: '#3b82f6' } :
|
|
252
|
+
status === 'running' ? { icon: <SettingsIcon sx={{ fontSize: '12px' }} />, label: t('automation.sheetsNode.status.running'), bg: '#f59e0b' } :
|
|
253
|
+
status === 'connected' ? { icon: <CheckCircleIcon sx={{ fontSize: '12px' }} />, label: t('automation.sheetsNode.status.connected'), bg: '#10b981' } :
|
|
254
|
+
{ icon: <ErrorIcon sx={{ fontSize: '12px' }} />, label: t('automation.sheetsNode.status.failed'), bg: '#ef4444' }
|
|
255
|
+
);
|
|
256
|
+
return (
|
|
257
|
+
<Chip
|
|
258
|
+
icon={cfg.icon}
|
|
259
|
+
label={cfg.label}
|
|
260
|
+
size="small"
|
|
261
|
+
sx={{
|
|
262
|
+
backgroundColor: cfg.bg,
|
|
263
|
+
color: 'white',
|
|
264
|
+
fontSize: '11px',
|
|
265
|
+
height: '20px',
|
|
266
|
+
'& .MuiChip-icon': { fontSize: '12px' },
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// WhatsApp message sending function
|
|
273
|
+
const sendWhatsAppMessage = (phoneNumber: string, data: any) => {
|
|
274
|
+
const message = `
|
|
275
|
+
Automation Result:
|
|
276
|
+
------------------
|
|
277
|
+
Name: ${data.name || 'Automation Workflow'}
|
|
278
|
+
Score: ${data.score || 'N/A'}
|
|
279
|
+
Status: ${data.status || 'Completed'}
|
|
280
|
+
Timestamp: ${new Date().toLocaleString()}
|
|
281
|
+
Data: ${JSON.stringify(data, null, 2)}
|
|
282
|
+
`;
|
|
283
|
+
const encodedMsg = encodeURIComponent(message);
|
|
284
|
+
const url = `https://wa.me/${phoneNumber}?text=${encodedMsg}`;
|
|
285
|
+
window.open(url, '_blank');
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const handleJsonClick = () => {
|
|
289
|
+
if (nodeId) setSelectedNode(nodeId);
|
|
290
|
+
if (!enableJson) return;
|
|
291
|
+
setIsJsonOpen(!isJsonOpen);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const handleClose = () => {
|
|
295
|
+
setIsJsonOpen(false);
|
|
296
|
+
// Clean up portal
|
|
297
|
+
if (rootRef.current) {
|
|
298
|
+
rootRef.current.unmount();
|
|
299
|
+
rootRef.current = null;
|
|
300
|
+
}
|
|
301
|
+
if (portalRef.current) {
|
|
302
|
+
document.body.removeChild(portalRef.current);
|
|
303
|
+
portalRef.current = null;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
309
|
+
if (isJsonOpen && !(event.target as Element).closest('#automation-json-popover')) {
|
|
310
|
+
handleClose();
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
314
|
+
return () => {
|
|
315
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
316
|
+
};
|
|
317
|
+
}, [isJsonOpen]);
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
if (isJsonOpen) {
|
|
322
|
+
const portalRoot = document.createElement('div');
|
|
323
|
+
document.body.appendChild(portalRoot);
|
|
324
|
+
portalRef.current = portalRoot;
|
|
325
|
+
|
|
326
|
+
const root = createRoot(portalRoot);
|
|
327
|
+
rootRef.current = root;
|
|
328
|
+
|
|
329
|
+
root.render(
|
|
330
|
+
<Card
|
|
331
|
+
id="automation-json-popover"
|
|
332
|
+
sx={{
|
|
333
|
+
position: 'fixed',
|
|
334
|
+
top: 0,
|
|
335
|
+
right: 0,
|
|
336
|
+
zIndex: 9999,
|
|
337
|
+
width: '400px',
|
|
338
|
+
height: '100vh',
|
|
339
|
+
overflow: 'auto',
|
|
340
|
+
bgcolor: '#242424',
|
|
341
|
+
color: '#fff',
|
|
342
|
+
border: '1px solid #333',
|
|
343
|
+
'&::-webkit-scrollbar': {
|
|
344
|
+
width: '6px',
|
|
345
|
+
},
|
|
346
|
+
'&::-webkit-scrollbar-track': {
|
|
347
|
+
background: 'transparent',
|
|
348
|
+
},
|
|
349
|
+
'&::-webkit-scrollbar-thumb': {
|
|
350
|
+
background: '#444',
|
|
351
|
+
borderRadius: '3px',
|
|
352
|
+
'&:hover': {
|
|
353
|
+
background: '#666',
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
<CardContent sx={{ bgcolor: '#242424', color: '#fff' }}>
|
|
359
|
+
<IconButton
|
|
360
|
+
aria-label="close"
|
|
361
|
+
onClick={handleClose}
|
|
362
|
+
sx={{
|
|
363
|
+
color: '#999',
|
|
364
|
+
'&:hover': {
|
|
365
|
+
color: '#fff',
|
|
366
|
+
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
|
367
|
+
},
|
|
368
|
+
}}
|
|
369
|
+
>
|
|
370
|
+
<RiCloseLine />
|
|
371
|
+
</IconButton>
|
|
372
|
+
{/* Show execution result prominently if available */}
|
|
373
|
+
{data.formData?.executionResult && (
|
|
374
|
+
<Box sx={{ mb: 2 }}>
|
|
375
|
+
<Typography variant="h6" sx={{ color: '#fff', mb: 1 }}>
|
|
376
|
+
{t('automation.common.executionResult')}
|
|
377
|
+
</Typography>
|
|
378
|
+
<Box sx={{
|
|
379
|
+
bgcolor: data.formData.executionResult.success ? '#1e3a8a' : '#dc2626',
|
|
380
|
+
p: 1,
|
|
381
|
+
borderRadius: 1,
|
|
382
|
+
mb: 1
|
|
383
|
+
}}>
|
|
384
|
+
<Typography variant="body2" sx={{ color: '#fff' }}>
|
|
385
|
+
{t('automation.common.status')}: {data.formData.executionResult.success ? t('automation.common.success') : t('automation.common.failed')}
|
|
386
|
+
</Typography>
|
|
387
|
+
<Typography variant="body2" sx={{ color: '#fff' }}>
|
|
388
|
+
{t('automation.common.timestamp')}: {new Date(data.formData.executionResult.timestamp).toLocaleString()}
|
|
389
|
+
</Typography>
|
|
390
|
+
{data.formData.executionResult.error && (
|
|
391
|
+
<Typography variant="body2" sx={{ color: '#fff' }}>
|
|
392
|
+
{t('automation.common.error')}: {data.formData.executionResult.error}
|
|
393
|
+
</Typography>
|
|
394
|
+
)}
|
|
395
|
+
</Box>
|
|
396
|
+
<Typography variant="h6" sx={{ color: '#fff', mb: 1 }}>
|
|
397
|
+
Sheets Data
|
|
398
|
+
</Typography>
|
|
399
|
+
<ReactJson
|
|
400
|
+
theme={'monokai'}
|
|
401
|
+
src={data.formData.executionResult.data}
|
|
402
|
+
collapsed={false}
|
|
403
|
+
/>
|
|
404
|
+
</Box>
|
|
405
|
+
)}
|
|
406
|
+
|
|
407
|
+
{/* Show full node data */}
|
|
408
|
+
<Typography variant="h6" sx={{ color: '#fff', mb: 1 }}>
|
|
409
|
+
Full Node Data
|
|
410
|
+
</Typography>
|
|
411
|
+
<ReactJson theme={'monokai'} src={data.formData || data} collapsed={false} />
|
|
412
|
+
</CardContent>
|
|
413
|
+
</Card>
|
|
414
|
+
);
|
|
415
|
+
} else {
|
|
416
|
+
// Clean up when closing
|
|
417
|
+
if (rootRef.current) {
|
|
418
|
+
rootRef.current.unmount();
|
|
419
|
+
rootRef.current = null;
|
|
420
|
+
}
|
|
421
|
+
if (portalRef.current) {
|
|
422
|
+
document.body.removeChild(portalRef.current);
|
|
423
|
+
portalRef.current = null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}, [isJsonOpen, data]);
|
|
427
|
+
|
|
428
|
+
// Output Method Component for Google Sheets
|
|
429
|
+
const GoogleSheetsOutputMethod = () => (
|
|
430
|
+
<Box sx={{
|
|
431
|
+
display: 'flex',
|
|
432
|
+
alignItems: 'center',
|
|
433
|
+
justifyContent: 'space-between',
|
|
434
|
+
py: 2,
|
|
435
|
+
px: 2,
|
|
436
|
+
borderBottom: '1px solid #334155',
|
|
437
|
+
'&:hover': {
|
|
438
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
439
|
+
},
|
|
440
|
+
cursor: 'pointer',
|
|
441
|
+
}}>
|
|
442
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
443
|
+
{/* Google Sheets Icon */}
|
|
444
|
+
<Box sx={{
|
|
445
|
+
width: '32px',
|
|
446
|
+
height: '32px',
|
|
447
|
+
backgroundColor: '#10b981',
|
|
448
|
+
borderRadius: '6px',
|
|
449
|
+
display: 'flex',
|
|
450
|
+
alignItems: 'center',
|
|
451
|
+
justifyContent: 'center',
|
|
452
|
+
}}>
|
|
453
|
+
<TableChartIcon sx={{ color: 'white', fontSize: '18px' }} />
|
|
454
|
+
</Box>
|
|
455
|
+
|
|
456
|
+
<Box>
|
|
457
|
+
<Typography variant="body2" sx={{
|
|
458
|
+
color: '#ffffff',
|
|
459
|
+
fontSize: '14px',
|
|
460
|
+
fontWeight: 600,
|
|
461
|
+
mb: 0.5
|
|
462
|
+
}}>
|
|
463
|
+
{t('automation.sheetsNode.googleSheets')}
|
|
464
|
+
</Typography>
|
|
465
|
+
<Typography variant="body2" sx={{
|
|
466
|
+
color: '#94a3b8',
|
|
467
|
+
fontSize: '12px',
|
|
468
|
+
mb: 0.5
|
|
469
|
+
}}>
|
|
470
|
+
{t('automation.sheetsNode.readWriteRows')}
|
|
471
|
+
</Typography>
|
|
472
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
473
|
+
<DescriptionIcon sx={{ fontSize: '12px', color: '#94a3b8' }} />
|
|
474
|
+
<Typography variant="body2" sx={{
|
|
475
|
+
color: '#94a3b8',
|
|
476
|
+
fontSize: '11px'
|
|
477
|
+
}}>
|
|
478
|
+
{t('automation.sheetsNode.sheet')}: {data.sheetsConfig?.sheetName || data.formData?.sheetsConfig?.sheetName || 'Sheet'}
|
|
479
|
+
</Typography>
|
|
480
|
+
</Box>
|
|
481
|
+
</Box>
|
|
482
|
+
</Box>
|
|
483
|
+
|
|
484
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
485
|
+
{/* Status Badge */}
|
|
486
|
+
<StatusBadge status={determineGoogleSheetsStatus()} />
|
|
487
|
+
<ArrowForwardIcon sx={{ fontSize: '14px', color: '#94a3b8' }} />
|
|
488
|
+
</Box>
|
|
489
|
+
</Box>
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
// Output Method Component for Gmail
|
|
493
|
+
const GmailOutputMethod = () => (
|
|
494
|
+
<Box sx={{
|
|
495
|
+
display: 'flex',
|
|
496
|
+
alignItems: 'center',
|
|
497
|
+
justifyContent: 'space-between',
|
|
498
|
+
py: 2,
|
|
499
|
+
px: 2,
|
|
500
|
+
borderBottom: '1px solid #334155',
|
|
501
|
+
'&:hover': {
|
|
502
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
503
|
+
},
|
|
504
|
+
cursor: 'pointer',
|
|
505
|
+
}}>
|
|
506
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
507
|
+
{/* Gmail Icon */}
|
|
508
|
+
<Box sx={{
|
|
509
|
+
width: '32px',
|
|
510
|
+
height: '32px',
|
|
511
|
+
backgroundColor: '#dc2626',
|
|
512
|
+
borderRadius: '6px',
|
|
513
|
+
display: 'flex',
|
|
514
|
+
alignItems: 'center',
|
|
515
|
+
justifyContent: 'center',
|
|
516
|
+
}}>
|
|
517
|
+
<EmailIcon sx={{ color: 'white', fontSize: '18px' }} />
|
|
518
|
+
</Box>
|
|
519
|
+
|
|
520
|
+
<Box>
|
|
521
|
+
<Typography variant="body2" sx={{
|
|
522
|
+
color: '#ffffff',
|
|
523
|
+
fontSize: '14px',
|
|
524
|
+
fontWeight: 600,
|
|
525
|
+
mb: 0.5
|
|
526
|
+
}}>
|
|
527
|
+
{t('automation.sheetsNode.gmail')}
|
|
528
|
+
</Typography>
|
|
529
|
+
<Typography variant="body2" sx={{
|
|
530
|
+
color: '#94a3b8',
|
|
531
|
+
fontSize: '12px',
|
|
532
|
+
mb: 0.5
|
|
533
|
+
}}>
|
|
534
|
+
{t('automation.sheetsNode.sendEmail')}
|
|
535
|
+
</Typography>
|
|
536
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
537
|
+
<DraftsIcon sx={{ fontSize: '12px', color: '#94a3b8' }} />
|
|
538
|
+
<Typography variant="body2" sx={{
|
|
539
|
+
color: '#94a3b8',
|
|
540
|
+
fontSize: '11px'
|
|
541
|
+
}}>
|
|
542
|
+
{t('automation.sheetsNode.drafts')}: {data.exportOptions?.emailRecipients?.length || 0}
|
|
543
|
+
</Typography>
|
|
544
|
+
</Box>
|
|
545
|
+
</Box>
|
|
546
|
+
</Box>
|
|
547
|
+
|
|
548
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
549
|
+
{/* Status Badge */}
|
|
550
|
+
<StatusBadge status={determineGmailStatus()} />
|
|
551
|
+
<ArrowForwardIcon sx={{ fontSize: '14px', color: '#94a3b8' }} />
|
|
552
|
+
</Box>
|
|
553
|
+
</Box>
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
// Output Method Component for Slack
|
|
557
|
+
const SlackOutputMethod = () => {
|
|
558
|
+
const slack = (data.formData?.exportOptions?.slack || (data as any).exportOptions?.slack) || {};
|
|
559
|
+
return (
|
|
560
|
+
<Box sx={{
|
|
561
|
+
display: 'flex',
|
|
562
|
+
alignItems: 'center',
|
|
563
|
+
justifyContent: 'space-between',
|
|
564
|
+
py: 2,
|
|
565
|
+
px: 2,
|
|
566
|
+
borderBottom: '1px solid #334155',
|
|
567
|
+
'&:hover': {
|
|
568
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
569
|
+
},
|
|
570
|
+
cursor: 'pointer',
|
|
571
|
+
}}>
|
|
572
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
573
|
+
{/* Slack Icon-like box */}
|
|
574
|
+
<Box sx={{
|
|
575
|
+
width: '32px',
|
|
576
|
+
height: '32px',
|
|
577
|
+
backgroundColor: '#4A154B',
|
|
578
|
+
borderRadius: '6px',
|
|
579
|
+
display: 'flex',
|
|
580
|
+
alignItems: 'center',
|
|
581
|
+
justifyContent: 'center',
|
|
582
|
+
}}>
|
|
583
|
+
<SendIcon sx={{ color: 'white', fontSize: '18px' }} />
|
|
584
|
+
</Box>
|
|
585
|
+
|
|
586
|
+
<Box>
|
|
587
|
+
<Typography variant="body2" sx={{
|
|
588
|
+
color: '#ffffff',
|
|
589
|
+
fontSize: '14px',
|
|
590
|
+
fontWeight: 600,
|
|
591
|
+
mb: 0.5
|
|
592
|
+
}}>
|
|
593
|
+
{t('automation.sheetsNode.slack')}
|
|
594
|
+
</Typography>
|
|
595
|
+
<Typography variant="body2" sx={{
|
|
596
|
+
color: '#94a3b8',
|
|
597
|
+
fontSize: '12px',
|
|
598
|
+
mb: 0.5
|
|
599
|
+
}}>
|
|
600
|
+
{t('automation.sheetsNode.sendMessage')}
|
|
601
|
+
</Typography>
|
|
602
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
603
|
+
<TableRowsIcon sx={{ fontSize: '12px', color: '#94a3b8' }} />
|
|
604
|
+
<Typography variant="body2" sx={{
|
|
605
|
+
color: '#94a3b8',
|
|
606
|
+
fontSize: '11px'
|
|
607
|
+
}}>
|
|
608
|
+
{t('automation.sheetsNode.channel')}: {slack.channel ? `#${slack.channel}` : t('automation.sheetsNode.notSet')}
|
|
609
|
+
</Typography>
|
|
610
|
+
</Box>
|
|
611
|
+
</Box>
|
|
612
|
+
</Box>
|
|
613
|
+
|
|
614
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
615
|
+
<StatusBadge status={determineSlackStatus()} />
|
|
616
|
+
<ArrowForwardIcon sx={{ fontSize: '14px', color: '#94a3b8' }} />
|
|
617
|
+
</Box>
|
|
618
|
+
</Box>
|
|
619
|
+
);
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// Output Method Component for WhatsApp
|
|
623
|
+
const WhatsAppOutputMethod = () => {
|
|
624
|
+
const whatsapp = (data.formData?.exportOptions?.whatsapp || data.exportOptions?.whatsapp) || {};
|
|
625
|
+
const isTwilioEnabled = whatsapp.twilio?.enabled;
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<Box sx={{
|
|
629
|
+
display: 'flex',
|
|
630
|
+
alignItems: 'center',
|
|
631
|
+
justifyContent: 'space-between',
|
|
632
|
+
py: 2,
|
|
633
|
+
px: 2,
|
|
634
|
+
borderBottom: '1px solid #334155',
|
|
635
|
+
'&:hover': {
|
|
636
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
637
|
+
},
|
|
638
|
+
cursor: 'pointer',
|
|
639
|
+
}}
|
|
640
|
+
onClick={() => {
|
|
641
|
+
if (whatsapp.enabled && whatsapp.phoneNumber && !isTwilioEnabled) {
|
|
642
|
+
const executionData = data.formData?.executionResult || {};
|
|
643
|
+
sendWhatsAppMessage(whatsapp.phoneNumber, executionData);
|
|
644
|
+
}
|
|
645
|
+
}}>
|
|
646
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
647
|
+
{/* WhatsApp Icon */}
|
|
648
|
+
<Box sx={{
|
|
649
|
+
width: '32px',
|
|
650
|
+
height: '32px',
|
|
651
|
+
backgroundColor: '#25D366',
|
|
652
|
+
borderRadius: '6px',
|
|
653
|
+
display: 'flex',
|
|
654
|
+
alignItems: 'center',
|
|
655
|
+
justifyContent: 'center',
|
|
656
|
+
}}>
|
|
657
|
+
<WhatsAppIcon sx={{ color: 'white', fontSize: '18px' }} />
|
|
658
|
+
</Box>
|
|
659
|
+
|
|
660
|
+
<Box>
|
|
661
|
+
<Typography variant="body2" sx={{
|
|
662
|
+
color: '#ffffff',
|
|
663
|
+
fontSize: '14px',
|
|
664
|
+
fontWeight: 600,
|
|
665
|
+
mb: 0.5
|
|
666
|
+
}}>
|
|
667
|
+
{isTwilioEnabled ? t('automation.sheetsNode.whatsappTwilio') : t('automation.sheetsNode.whatsappWeb')}
|
|
668
|
+
</Typography>
|
|
669
|
+
<Typography variant="body2" sx={{
|
|
670
|
+
color: '#94a3b8',
|
|
671
|
+
fontSize: '12px',
|
|
672
|
+
mb: 0.5
|
|
673
|
+
}}>
|
|
674
|
+
{isTwilioEnabled ? t('automation.sheetsNode.apiSend') : t('automation.sheetsNode.sendMessage')}
|
|
675
|
+
</Typography>
|
|
676
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
677
|
+
<PhoneIcon sx={{ fontSize: '12px', color: '#94a3b8' }} />
|
|
678
|
+
<Typography variant="body2" sx={{
|
|
679
|
+
color: '#94a3b8',
|
|
680
|
+
fontSize: '11px'
|
|
681
|
+
}}>
|
|
682
|
+
{t('automation.sheetsNode.phone')}: {whatsapp.phoneNumber || t('automation.sheetsNode.notSet')}
|
|
683
|
+
</Typography>
|
|
684
|
+
</Box>
|
|
685
|
+
{isTwilioEnabled && (
|
|
686
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 0.5 }}>
|
|
687
|
+
<Typography variant="body2" sx={{
|
|
688
|
+
color: whatsapp.twilio?.isSandbox ? '#f59e0b' : '#10b981',
|
|
689
|
+
fontSize: '10px',
|
|
690
|
+
fontWeight: 500
|
|
691
|
+
}}>
|
|
692
|
+
{whatsapp.twilio?.isSandbox ? t('automation.sheetsNode.sandboxMode') : t('automation.sheetsNode.productionMode')}
|
|
693
|
+
</Typography>
|
|
694
|
+
</Box>
|
|
695
|
+
)}
|
|
696
|
+
</Box>
|
|
697
|
+
</Box>
|
|
698
|
+
|
|
699
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
700
|
+
<StatusBadge status={determineWhatsAppStatus()} />
|
|
701
|
+
<ArrowForwardIcon sx={{ fontSize: '14px', color: '#94a3b8' }} />
|
|
702
|
+
</Box>
|
|
703
|
+
</Box>
|
|
704
|
+
);
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
return (
|
|
708
|
+
<Box
|
|
709
|
+
sx={{
|
|
710
|
+
position: 'relative',
|
|
711
|
+
width: '380px',
|
|
712
|
+
overflow: 'visible',
|
|
713
|
+
}}
|
|
714
|
+
>
|
|
715
|
+
<Box
|
|
716
|
+
ref={nodeRef}
|
|
717
|
+
sx={{
|
|
718
|
+
width: '380px',
|
|
719
|
+
minHeight: '280px',
|
|
720
|
+
backgroundColor: '#181C25', // Darker background like in image
|
|
721
|
+
border: selected ? '2px solid #3b82f6' : '1px solid #1e293b',
|
|
722
|
+
borderRadius: '12px',
|
|
723
|
+
padding: '0',
|
|
724
|
+
color: '#ffffff',
|
|
725
|
+
position: 'relative',
|
|
726
|
+
boxShadow: selected ? '0 0 0 2px rgba(59, 130, 246, 0.5)' : '0 4px 8px rgba(0, 0, 0, 0.3)',
|
|
727
|
+
transition: 'all 0.2s ease',
|
|
728
|
+
cursor: 'pointer',
|
|
729
|
+
overflow: 'hidden',
|
|
730
|
+
...(data.status === 'Running' && {
|
|
731
|
+
animation: 'pulse-glow 2s ease-in-out infinite',
|
|
732
|
+
'@keyframes pulse-glow': {
|
|
733
|
+
'0%, 100%': {
|
|
734
|
+
boxShadow: '0 0 20px rgba(59, 130, 246, 0.4), 0 0 40px rgba(59, 130, 246, 0.2)',
|
|
735
|
+
borderColor: 'rgba(59, 130, 246, 0.6)',
|
|
736
|
+
},
|
|
737
|
+
'50%': {
|
|
738
|
+
boxShadow: '0 0 30px rgba(59, 130, 246, 0.6), 0 0 60px rgba(59, 130, 246, 0.3)',
|
|
739
|
+
borderColor: 'rgba(59, 130, 246, 0.9)',
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
}),
|
|
743
|
+
}}
|
|
744
|
+
onClick={handleJsonClick}
|
|
745
|
+
>
|
|
746
|
+
{/* Header */}
|
|
747
|
+
<Box sx={{
|
|
748
|
+
display: 'flex',
|
|
749
|
+
alignItems: 'center',
|
|
750
|
+
justifyContent: 'space-between',
|
|
751
|
+
p: 3,
|
|
752
|
+
borderBottom: '1px solid #334155'
|
|
753
|
+
}}>
|
|
754
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
|
755
|
+
<Box
|
|
756
|
+
sx={{
|
|
757
|
+
width: '32px',
|
|
758
|
+
height: '32px',
|
|
759
|
+
backgroundColor: '#3b82f6', // Blue color like in image
|
|
760
|
+
borderRadius: '6px',
|
|
761
|
+
display: 'flex',
|
|
762
|
+
alignItems: 'center',
|
|
763
|
+
justifyContent: 'center',
|
|
764
|
+
}}
|
|
765
|
+
>
|
|
766
|
+
<IconComponent sx={{ color: 'white', fontSize: '18px' }} />
|
|
767
|
+
</Box>
|
|
768
|
+
<Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px' }}>
|
|
769
|
+
{data.label}
|
|
770
|
+
</Typography>
|
|
771
|
+
</Box>
|
|
772
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
773
|
+
{/* Status Chip */}
|
|
774
|
+
<Chip
|
|
775
|
+
label={(() => {
|
|
776
|
+
// Map node status to standard status values
|
|
777
|
+
const nodeStatus = getNodeStatus();
|
|
778
|
+
if (nodeStatus === 'Failed' || data.status === 'Error') return 'Error';
|
|
779
|
+
if (nodeStatus === 'Running' || data.status === 'Running') return 'Running';
|
|
780
|
+
if (nodeStatus === 'Success' || data.status === 'Completed') return 'Completed';
|
|
781
|
+
return data.status || 'Ready';
|
|
782
|
+
})()}
|
|
783
|
+
size="small"
|
|
784
|
+
sx={{
|
|
785
|
+
backgroundColor: (() => {
|
|
786
|
+
const status = data.status || 'Ready';
|
|
787
|
+
return status === 'Completed'
|
|
788
|
+
? 'rgba(37, 99, 235, 0.1)'
|
|
789
|
+
: status === 'Running'
|
|
790
|
+
? 'rgba(251, 191, 36, 0.1)'
|
|
791
|
+
: status === 'Error'
|
|
792
|
+
? 'rgba(239, 68, 68, 0.1)'
|
|
793
|
+
: 'rgba(16, 185, 129, 0.1)';
|
|
794
|
+
})(),
|
|
795
|
+
color: (() => {
|
|
796
|
+
const status = data.status || 'Ready';
|
|
797
|
+
return status === 'Completed'
|
|
798
|
+
? '#93C5FD'
|
|
799
|
+
: status === 'Running'
|
|
800
|
+
? '#FCD34D'
|
|
801
|
+
: status === 'Error'
|
|
802
|
+
? '#FCA5A5'
|
|
803
|
+
: '#86EFAC';
|
|
804
|
+
})(),
|
|
805
|
+
fontWeight: 500,
|
|
806
|
+
fontSize: '12px',
|
|
807
|
+
height: '24px',
|
|
808
|
+
borderRadius: '12px',
|
|
809
|
+
}}
|
|
810
|
+
/>
|
|
811
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
|
812
|
+
<AccessTimeIcon sx={{ fontSize: '12px', color: '#94a3b8' }} />
|
|
813
|
+
<Typography variant="body2" sx={{ color: '#94a3b8', fontSize: '11px' }}>
|
|
814
|
+
{t('automation.common.lastRun')}: {data.lastRun || '2m ago'}
|
|
815
|
+
</Typography>
|
|
816
|
+
</Box>
|
|
817
|
+
</Box>
|
|
818
|
+
</Box>
|
|
819
|
+
|
|
820
|
+
{/* Output Methods */}
|
|
821
|
+
<Box>
|
|
822
|
+
{showSheets && <GoogleSheetsOutputMethod />}
|
|
823
|
+
{showGmail && <GmailOutputMethod />}
|
|
824
|
+
{showSlack && <SlackOutputMethod />}
|
|
825
|
+
{showWhatsApp && <WhatsAppOutputMethod />}
|
|
826
|
+
{!showGmail && !showSlack && !showWhatsApp && (
|
|
827
|
+
<Typography variant="body2" sx={{ color: '#94a3b8', p: 2 }}>
|
|
828
|
+
{t('automation.sheetsNode.noOutputMethodsConfigured')}
|
|
829
|
+
</Typography>
|
|
830
|
+
)}
|
|
831
|
+
</Box>
|
|
832
|
+
|
|
833
|
+
{/* Add Configuration Button */}
|
|
834
|
+
<Box sx={{
|
|
835
|
+
p: 2,
|
|
836
|
+
display: 'flex',
|
|
837
|
+
justifyContent: 'center',
|
|
838
|
+
borderTop: '1px solid #334155'
|
|
839
|
+
}}>
|
|
840
|
+
<Button
|
|
841
|
+
startIcon={<AddIcon sx={{ fontSize: '16px' }} />}
|
|
842
|
+
sx={{
|
|
843
|
+
color: '#94a3b8',
|
|
844
|
+
fontSize: '12px',
|
|
845
|
+
textTransform: 'none',
|
|
846
|
+
'&:hover': {
|
|
847
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
848
|
+
color: '#ffffff',
|
|
849
|
+
},
|
|
850
|
+
}}
|
|
851
|
+
>
|
|
852
|
+
{t('automation.sheetsNode.addConfiguration')}
|
|
853
|
+
</Button>
|
|
854
|
+
</Box>
|
|
855
|
+
|
|
856
|
+
{/* Connection Handles - Bidirectional (source + target at each position) */}
|
|
857
|
+
{/* Top - Source */}
|
|
858
|
+
<Handle
|
|
859
|
+
type="source"
|
|
860
|
+
position={Position.Top}
|
|
861
|
+
id="top-source"
|
|
862
|
+
className="connection-handle"
|
|
863
|
+
style={{
|
|
864
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
865
|
+
width: '14px',
|
|
866
|
+
height: '14px',
|
|
867
|
+
border: '3px solid #10B981',
|
|
868
|
+
top: '-8px',
|
|
869
|
+
opacity: selected ? 1 : 0,
|
|
870
|
+
transition: 'all 0.2s ease-in-out',
|
|
871
|
+
cursor: 'crosshair',
|
|
872
|
+
zIndex: 10,
|
|
873
|
+
}}
|
|
874
|
+
/>
|
|
875
|
+
{/* Top - Target (hidden but functional) */}
|
|
876
|
+
<Handle
|
|
877
|
+
type="target"
|
|
878
|
+
position={Position.Top}
|
|
879
|
+
id="top-target"
|
|
880
|
+
style={{
|
|
881
|
+
background: 'transparent',
|
|
882
|
+
width: '14px',
|
|
883
|
+
height: '14px',
|
|
884
|
+
border: 'none',
|
|
885
|
+
top: '-8px',
|
|
886
|
+
opacity: 0,
|
|
887
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
888
|
+
}}
|
|
889
|
+
/>
|
|
890
|
+
{/* Bottom - Source */}
|
|
891
|
+
<Handle
|
|
892
|
+
type="source"
|
|
893
|
+
position={Position.Bottom}
|
|
894
|
+
id="bottom-source"
|
|
895
|
+
className="connection-handle"
|
|
896
|
+
style={{
|
|
897
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
898
|
+
width: '14px',
|
|
899
|
+
height: '14px',
|
|
900
|
+
border: '3px solid #10B981',
|
|
901
|
+
bottom: '-8px',
|
|
902
|
+
opacity: selected ? 1 : 0,
|
|
903
|
+
transition: 'all 0.2s ease-in-out',
|
|
904
|
+
cursor: 'crosshair',
|
|
905
|
+
zIndex: 10,
|
|
906
|
+
}}
|
|
907
|
+
/>
|
|
908
|
+
{/* Bottom - Target (hidden but functional) */}
|
|
909
|
+
<Handle
|
|
910
|
+
type="target"
|
|
911
|
+
position={Position.Bottom}
|
|
912
|
+
id="bottom-target"
|
|
913
|
+
style={{
|
|
914
|
+
background: 'transparent',
|
|
915
|
+
width: '14px',
|
|
916
|
+
height: '14px',
|
|
917
|
+
border: 'none',
|
|
918
|
+
bottom: '-8px',
|
|
919
|
+
opacity: 0,
|
|
920
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
921
|
+
}}
|
|
922
|
+
/>
|
|
923
|
+
{/* Left - Source */}
|
|
924
|
+
<Handle
|
|
925
|
+
type="source"
|
|
926
|
+
position={Position.Left}
|
|
927
|
+
id="left-source"
|
|
928
|
+
className="connection-handle"
|
|
929
|
+
style={{
|
|
930
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
931
|
+
width: '14px',
|
|
932
|
+
height: '14px',
|
|
933
|
+
border: '3px solid #10B981',
|
|
934
|
+
left: '-8px',
|
|
935
|
+
opacity: selected ? 1 : 0,
|
|
936
|
+
transition: 'all 0.2s ease-in-out',
|
|
937
|
+
cursor: 'crosshair',
|
|
938
|
+
zIndex: 10,
|
|
939
|
+
}}
|
|
940
|
+
/>
|
|
941
|
+
{/* Left - Target (hidden but functional) */}
|
|
942
|
+
<Handle
|
|
943
|
+
type="target"
|
|
944
|
+
position={Position.Left}
|
|
945
|
+
id="left-target"
|
|
946
|
+
style={{
|
|
947
|
+
background: 'transparent',
|
|
948
|
+
width: '14px',
|
|
949
|
+
height: '14px',
|
|
950
|
+
border: 'none',
|
|
951
|
+
left: '-8px',
|
|
952
|
+
opacity: 0,
|
|
953
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
954
|
+
}}
|
|
955
|
+
/>
|
|
956
|
+
{/* Right - Source */}
|
|
957
|
+
<Handle
|
|
958
|
+
type="source"
|
|
959
|
+
position={Position.Right}
|
|
960
|
+
id="right-source"
|
|
961
|
+
className="connection-handle"
|
|
962
|
+
style={{
|
|
963
|
+
background: selected ? '#10B981' : '#1a1a2e',
|
|
964
|
+
width: '14px',
|
|
965
|
+
height: '14px',
|
|
966
|
+
border: '3px solid #10B981',
|
|
967
|
+
right: '-8px',
|
|
968
|
+
opacity: selected ? 1 : 0,
|
|
969
|
+
transition: 'all 0.2s ease-in-out',
|
|
970
|
+
cursor: 'crosshair',
|
|
971
|
+
zIndex: 10,
|
|
972
|
+
}}
|
|
973
|
+
/>
|
|
974
|
+
{/* Right - Target (hidden but functional) */}
|
|
975
|
+
<Handle
|
|
976
|
+
type="target"
|
|
977
|
+
position={Position.Right}
|
|
978
|
+
id="right-target"
|
|
979
|
+
style={{
|
|
980
|
+
background: 'transparent',
|
|
981
|
+
width: '14px',
|
|
982
|
+
height: '14px',
|
|
983
|
+
border: 'none',
|
|
984
|
+
right: '-8px',
|
|
985
|
+
opacity: 0,
|
|
986
|
+
pointerEvents: selected ? 'all' : 'none',
|
|
987
|
+
}}
|
|
988
|
+
/>
|
|
989
|
+
|
|
990
|
+
</Box>
|
|
991
|
+
|
|
992
|
+
{/* Node Action Buttons - Shows when selected */}
|
|
993
|
+
<NodeActionButtons
|
|
994
|
+
selected={selected}
|
|
995
|
+
onOpenAIAssistant={(buttonElement) => {
|
|
996
|
+
if (nodeId) {
|
|
997
|
+
showNodeAIAssistantPopup(nodeId, 'Sheets Node', buttonElement);
|
|
998
|
+
}
|
|
999
|
+
}}
|
|
1000
|
+
onDelete={() => {
|
|
1001
|
+
if (nodeId && onNodesChange) {
|
|
1002
|
+
onNodesChange([{ id: nodeId, type: 'remove' }]);
|
|
1003
|
+
}
|
|
1004
|
+
}}
|
|
1005
|
+
onDuplicate={() => {
|
|
1006
|
+
if (nodeId) {
|
|
1007
|
+
const currentNode = nodes.find(n => n.id === nodeId);
|
|
1008
|
+
if (currentNode) {
|
|
1009
|
+
const newNode = {
|
|
1010
|
+
...currentNode,
|
|
1011
|
+
id: `${currentNode.id}-copy-${Date.now()}`,
|
|
1012
|
+
position: {
|
|
1013
|
+
x: currentNode.position.x + 50,
|
|
1014
|
+
y: currentNode.position.y + 50,
|
|
1015
|
+
},
|
|
1016
|
+
selected: false,
|
|
1017
|
+
};
|
|
1018
|
+
setNodes([...nodes, newNode]);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}}
|
|
1022
|
+
/>
|
|
1023
|
+
|
|
1024
|
+
{/* AI Suggestions Button - Positioned below the node box */}
|
|
1025
|
+
{data.formData?.aiSuggestionsCount !== undefined && data.formData.aiSuggestionsCount > 0 && (
|
|
1026
|
+
<Box
|
|
1027
|
+
sx={{
|
|
1028
|
+
position: 'absolute',
|
|
1029
|
+
top: '100%',
|
|
1030
|
+
left: '50%',
|
|
1031
|
+
transform: 'translateX(-50%)',
|
|
1032
|
+
marginTop: '12px',
|
|
1033
|
+
zIndex: 10,
|
|
1034
|
+
whiteSpace: 'nowrap',
|
|
1035
|
+
}}
|
|
1036
|
+
onClick={(e) => {
|
|
1037
|
+
e.stopPropagation();
|
|
1038
|
+
// Toggle AI Suggestions panel
|
|
1039
|
+
setShowSuggestions(!showSuggestions);
|
|
1040
|
+
}}
|
|
1041
|
+
>
|
|
1042
|
+
<Button
|
|
1043
|
+
variant="contained"
|
|
1044
|
+
startIcon={<LightbulbIcon sx={{ fontSize: '12px' }} />}
|
|
1045
|
+
sx={{
|
|
1046
|
+
backgroundColor: '#2563EB',
|
|
1047
|
+
color: '#ffffff',
|
|
1048
|
+
borderRadius: '20px',
|
|
1049
|
+
textTransform: 'none',
|
|
1050
|
+
fontSize: '10px',
|
|
1051
|
+
fontWeight: 400,
|
|
1052
|
+
padding: '8px 16px',
|
|
1053
|
+
whiteSpace: 'nowrap',
|
|
1054
|
+
display: 'inline-flex',
|
|
1055
|
+
alignItems: 'center',
|
|
1056
|
+
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
|
1057
|
+
'&:hover': {
|
|
1058
|
+
backgroundColor: '#2563eb',
|
|
1059
|
+
},
|
|
1060
|
+
'& .MuiButton-startIcon': {
|
|
1061
|
+
marginRight: '8px',
|
|
1062
|
+
}
|
|
1063
|
+
}}
|
|
1064
|
+
>
|
|
1065
|
+
AI Suggestions
|
|
1066
|
+
<Box
|
|
1067
|
+
component="span"
|
|
1068
|
+
sx={{
|
|
1069
|
+
marginLeft: '8px',
|
|
1070
|
+
backgroundColor: '#FFFFFF26',
|
|
1071
|
+
color: '#ffffff',
|
|
1072
|
+
fontSize: '10px',
|
|
1073
|
+
fontWeight: 400,
|
|
1074
|
+
minWidth: '18px',
|
|
1075
|
+
height: '18px',
|
|
1076
|
+
borderRadius: '9px',
|
|
1077
|
+
display: 'inline-flex',
|
|
1078
|
+
alignItems: 'center',
|
|
1079
|
+
justifyContent: 'center',
|
|
1080
|
+
padding: '0 6px',
|
|
1081
|
+
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
1082
|
+
}}
|
|
1083
|
+
>
|
|
1084
|
+
{data.formData.aiSuggestionsCount}
|
|
1085
|
+
</Box>
|
|
1086
|
+
</Button>
|
|
1087
|
+
</Box>
|
|
1088
|
+
)}
|
|
1089
|
+
|
|
1090
|
+
{/* AI Suggestions Panel - Rendered on canvas below the button */}
|
|
1091
|
+
{showSuggestions && data.formData?.aiSuggestionsCount !== undefined && data.formData.aiSuggestionsCount > 0 && nodeId && (
|
|
1092
|
+
<AISuggestionsPanel
|
|
1093
|
+
suggestions={data.formData?.aiSuggestions || [
|
|
1094
|
+
{
|
|
1095
|
+
id: '1',
|
|
1096
|
+
title: 'Add Citation Extraction',
|
|
1097
|
+
description: 'Automatically extract and format citations from article content.',
|
|
1098
|
+
tags: ['classification', 'enhancement'],
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
id: '2',
|
|
1102
|
+
title: 'Generate Bullet Summary',
|
|
1103
|
+
description: 'Create a concise bullet-point summary of the article\'s main points.',
|
|
1104
|
+
tags: ['classification', 'enhancement'],
|
|
1105
|
+
},
|
|
1106
|
+
]}
|
|
1107
|
+
parentNodeId={nodeId}
|
|
1108
|
+
onSuggestionClick={(suggestion) => {
|
|
1109
|
+
console.log('Suggestion clicked:', suggestion);
|
|
1110
|
+
// Handle suggestion selection here
|
|
1111
|
+
}}
|
|
1112
|
+
onClose={() => setShowSuggestions(false)}
|
|
1113
|
+
/>
|
|
1114
|
+
)}
|
|
1115
|
+
</Box>
|
|
1116
|
+
);
|
|
1117
|
+
};
|
|
1118
|
+
|