@dev-fastn-ai/react-core 2.4.10 → 2.4.12
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/README.md +141 -100
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Also, make sure you install the required **peer dependencies**:
|
|
|
24
24
|
npm install react react-dom @tanstack/react-query
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
> ✅ Requires React 18
|
|
27
|
+
> ✅ Requires React 18 or 19
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -292,7 +292,15 @@ interface HideOptionConfig {
|
|
|
292
292
|
readonly enable: boolean;
|
|
293
293
|
readonly hideBasedOnValue: boolean;
|
|
294
294
|
readonly key: string;
|
|
295
|
-
readonly operation:
|
|
295
|
+
readonly operation:
|
|
296
|
+
| "!="
|
|
297
|
+
| "=="
|
|
298
|
+
| ">"
|
|
299
|
+
| "<"
|
|
300
|
+
| ">="
|
|
301
|
+
| "<="
|
|
302
|
+
| "contains"
|
|
303
|
+
| "not_contains";
|
|
296
304
|
readonly value: string;
|
|
297
305
|
}
|
|
298
306
|
```
|
|
@@ -315,14 +323,14 @@ const fieldConfig: ConnectorFieldConfig = {
|
|
|
315
323
|
fileTypes: [],
|
|
316
324
|
source: {
|
|
317
325
|
flowId: "",
|
|
318
|
-
isSameProject: false
|
|
326
|
+
isSameProject: false,
|
|
319
327
|
},
|
|
320
328
|
destination: {
|
|
321
329
|
flowId: "",
|
|
322
|
-
isSameProject: false
|
|
330
|
+
isSameProject: false,
|
|
323
331
|
},
|
|
324
332
|
isEditKeys: false,
|
|
325
|
-
isAddFields: false
|
|
333
|
+
isAddFields: false,
|
|
326
334
|
},
|
|
327
335
|
disable: false,
|
|
328
336
|
label: "Select Workspace",
|
|
@@ -331,8 +339,8 @@ const fieldConfig: ConnectorFieldConfig = {
|
|
|
331
339
|
hideBasedOnValue: false,
|
|
332
340
|
key: "",
|
|
333
341
|
operation: "!=",
|
|
334
|
-
value: ""
|
|
335
|
-
}
|
|
342
|
+
value: "",
|
|
343
|
+
},
|
|
336
344
|
};
|
|
337
345
|
|
|
338
346
|
// Example connector field with configuration
|
|
@@ -344,27 +352,23 @@ const connectorField: ConnectorField = {
|
|
|
344
352
|
required: true,
|
|
345
353
|
placeholder: "Select a workspace",
|
|
346
354
|
description: "Choose the workspace to connect to",
|
|
347
|
-
configs: fieldConfig
|
|
355
|
+
configs: fieldConfig,
|
|
348
356
|
};
|
|
349
357
|
|
|
350
358
|
// Using the field in a React component
|
|
351
359
|
function WorkspaceField({ field }: { field: ConnectorField }) {
|
|
352
360
|
const { configs } = field;
|
|
353
|
-
|
|
361
|
+
|
|
354
362
|
// Access configuration properties
|
|
355
363
|
const isSelectionEnabled = configs?.selection?.enable;
|
|
356
364
|
const flowId = configs?.selection?.flowId;
|
|
357
365
|
const isDisabled = configs?.disable;
|
|
358
|
-
|
|
366
|
+
|
|
359
367
|
return (
|
|
360
368
|
<div className="field-container">
|
|
361
369
|
<label>{field.label}</label>
|
|
362
|
-
{isSelectionEnabled &&
|
|
363
|
-
|
|
364
|
-
)}
|
|
365
|
-
<select disabled={isDisabled}>
|
|
366
|
-
{/* Field options */}
|
|
367
|
-
</select>
|
|
370
|
+
{isSelectionEnabled && <p>Selection enabled for flow: {flowId}</p>}
|
|
371
|
+
<select disabled={isDisabled}>{/* Field options */}</select>
|
|
368
372
|
</div>
|
|
369
373
|
);
|
|
370
374
|
}
|
|
@@ -399,13 +403,13 @@ function ConnectorList() {
|
|
|
399
403
|
<img src={connector.imageUri} alt={connector.name} />
|
|
400
404
|
<h3>{connector.name}</h3>
|
|
401
405
|
<p>{connector.description}</p>
|
|
402
|
-
|
|
406
|
+
|
|
403
407
|
{connector.status === "ACTIVE" && (
|
|
404
408
|
<span className="status-badge connected">Connected</span>
|
|
405
409
|
)}
|
|
406
|
-
|
|
410
|
+
|
|
407
411
|
{connector.actions?.map((action) => (
|
|
408
|
-
<button
|
|
412
|
+
<button
|
|
409
413
|
key={action.name}
|
|
410
414
|
onClick={action.onClick}
|
|
411
415
|
className={`action-btn ${action.actionType.toLowerCase()}`}
|
|
@@ -428,7 +432,11 @@ After a connector is activated, you can list its configurations:
|
|
|
428
432
|
import { useConfigurations } from "@fastn-ai/react-core";
|
|
429
433
|
|
|
430
434
|
function ConfigurationList({ configurationId }) {
|
|
431
|
-
const {
|
|
435
|
+
const {
|
|
436
|
+
data: configurations,
|
|
437
|
+
isLoading,
|
|
438
|
+
error,
|
|
439
|
+
} = useConfigurations({ configurationId });
|
|
432
440
|
const [selectedConfig, setSelectedConfig] = useState(null);
|
|
433
441
|
|
|
434
442
|
if (isLoading) return <div>Loading configurations...</div>;
|
|
@@ -446,21 +454,27 @@ function ConfigurationList({ configurationId }) {
|
|
|
446
454
|
<p>{config.description}</p>
|
|
447
455
|
</div>
|
|
448
456
|
</div>
|
|
449
|
-
|
|
457
|
+
|
|
450
458
|
<div className="config-actions">
|
|
451
459
|
{config.status === "ENABLED" && (
|
|
452
460
|
<span className="status-badge enabled">Active</span>
|
|
453
461
|
)}
|
|
454
|
-
|
|
462
|
+
|
|
455
463
|
{config.actions?.map((action) => (
|
|
456
464
|
<button
|
|
457
465
|
key={action.name}
|
|
458
466
|
onClick={async () => {
|
|
459
467
|
const result = await action.onClick();
|
|
460
|
-
if (
|
|
468
|
+
if (
|
|
469
|
+
action.actionType === "ENABLE" &&
|
|
470
|
+
result?.status === "SUCCESS"
|
|
471
|
+
) {
|
|
461
472
|
// Show configuration form for new setup
|
|
462
473
|
setSelectedConfig(config);
|
|
463
|
-
} else if (
|
|
474
|
+
} else if (
|
|
475
|
+
action.actionType === "UPDATE" &&
|
|
476
|
+
result?.status === "SUCCESS"
|
|
477
|
+
) {
|
|
464
478
|
// Show configuration form for editing
|
|
465
479
|
setSelectedConfig(config);
|
|
466
480
|
}
|
|
@@ -473,9 +487,9 @@ function ConfigurationList({ configurationId }) {
|
|
|
473
487
|
</div>
|
|
474
488
|
</div>
|
|
475
489
|
))}
|
|
476
|
-
|
|
490
|
+
|
|
477
491
|
{selectedConfig && (
|
|
478
|
-
<ConfigurationForm
|
|
492
|
+
<ConfigurationForm
|
|
479
493
|
configurationId={selectedConfig.id}
|
|
480
494
|
onClose={() => setSelectedConfig(null)}
|
|
481
495
|
/>
|
|
@@ -522,7 +536,7 @@ function ConfigurationForm({ configurationId, onClose }) {
|
|
|
522
536
|
const onSubmit = async (e) => {
|
|
523
537
|
e.preventDefault();
|
|
524
538
|
setIsSubmitting(true);
|
|
525
|
-
|
|
539
|
+
|
|
526
540
|
try {
|
|
527
541
|
await handleSubmit({ formData });
|
|
528
542
|
console.log("Configuration saved successfully!");
|
|
@@ -539,9 +553,9 @@ function ConfigurationForm({ configurationId, onClose }) {
|
|
|
539
553
|
<form onSubmit={onSubmit} className="configuration-form">
|
|
540
554
|
<h2>Configure {configurationForm.name}</h2>
|
|
541
555
|
<p>{configurationForm.description}</p>
|
|
542
|
-
|
|
556
|
+
|
|
543
557
|
{configurationForm.fields.map((field) => (
|
|
544
|
-
<FormField
|
|
558
|
+
<FormField
|
|
545
559
|
key={field.key}
|
|
546
560
|
field={field}
|
|
547
561
|
value={formData[field.key]}
|
|
@@ -550,9 +564,11 @@ function ConfigurationForm({ configurationId, onClose }) {
|
|
|
550
564
|
}
|
|
551
565
|
/>
|
|
552
566
|
))}
|
|
553
|
-
|
|
567
|
+
|
|
554
568
|
<div className="form-actions">
|
|
555
|
-
<button type="button" onClick={onClose}>
|
|
569
|
+
<button type="button" onClick={onClose}>
|
|
570
|
+
Cancel
|
|
571
|
+
</button>
|
|
556
572
|
<button type="submit" disabled={isSubmitting}>
|
|
557
573
|
{isSubmitting ? "Saving..." : "Save Configuration"}
|
|
558
574
|
</button>
|
|
@@ -678,27 +694,27 @@ The form fields handle different value types based on the field type:
|
|
|
678
694
|
const formData = {
|
|
679
695
|
// Select field - single object
|
|
680
696
|
channel: { label: "General", value: "C123456" },
|
|
681
|
-
|
|
697
|
+
|
|
682
698
|
// Multi-select field - array of objects
|
|
683
699
|
channels: [
|
|
684
700
|
{ label: "General", value: "C123456" },
|
|
685
|
-
{ label: "Random", value: "C789012" }
|
|
701
|
+
{ label: "Random", value: "C789012" },
|
|
686
702
|
],
|
|
687
|
-
|
|
703
|
+
|
|
688
704
|
// Google Drive picker - single object
|
|
689
705
|
folder: { label: "My Documents", value: "folder_id_123" },
|
|
690
|
-
|
|
706
|
+
|
|
691
707
|
// Google Drive picker multi - array of objects
|
|
692
708
|
files: [
|
|
693
709
|
{ label: "document1.pdf", value: "file_id_1" },
|
|
694
|
-
{ label: "document2.pdf", value: "file_id_2" }
|
|
710
|
+
{ label: "document2.pdf", value: "file_id_2" },
|
|
695
711
|
],
|
|
696
|
-
|
|
712
|
+
|
|
697
713
|
// Text field - primitive
|
|
698
714
|
webhookUrl: "https://hooks.slack.com/...",
|
|
699
|
-
|
|
715
|
+
|
|
700
716
|
// Boolean field - primitive
|
|
701
|
-
enableNotifications: true
|
|
717
|
+
enableNotifications: true,
|
|
702
718
|
};
|
|
703
719
|
```
|
|
704
720
|
|
|
@@ -713,7 +729,13 @@ For fields of type `select` or `multi-select`, use the `useFieldOptions` hook to
|
|
|
713
729
|
```tsx
|
|
714
730
|
import { useFieldOptions } from "@fastn-ai/react-core";
|
|
715
731
|
|
|
716
|
-
function SelectField({
|
|
732
|
+
function SelectField({
|
|
733
|
+
field,
|
|
734
|
+
value,
|
|
735
|
+
onChange,
|
|
736
|
+
isMulti = false,
|
|
737
|
+
context = {},
|
|
738
|
+
}) {
|
|
717
739
|
// context contains all form values and is used to fetch dependent options
|
|
718
740
|
const {
|
|
719
741
|
options,
|
|
@@ -737,17 +759,19 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
737
759
|
function handleSelectChange(selectedOptions) {
|
|
738
760
|
if (isMulti) {
|
|
739
761
|
// For multi-select, value should be an array of { label, value } objects
|
|
740
|
-
const selectedValues = selectedOptions.map(option => ({
|
|
762
|
+
const selectedValues = selectedOptions.map((option) => ({
|
|
741
763
|
label: option.label,
|
|
742
|
-
value: option.value
|
|
764
|
+
value: option.value,
|
|
743
765
|
}));
|
|
744
766
|
onChange(selectedValues);
|
|
745
767
|
} else {
|
|
746
768
|
// For single select, value should be a single { label, value } object
|
|
747
|
-
const selectedValue = selectedOptions[0]
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
769
|
+
const selectedValue = selectedOptions[0]
|
|
770
|
+
? {
|
|
771
|
+
label: selectedOptions[0].label,
|
|
772
|
+
value: selectedOptions[0].value,
|
|
773
|
+
}
|
|
774
|
+
: null;
|
|
751
775
|
onChange(selectedValue);
|
|
752
776
|
}
|
|
753
777
|
}
|
|
@@ -758,13 +782,13 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
758
782
|
{field.label}
|
|
759
783
|
{field.required && <span className="required"> *</span>}
|
|
760
784
|
</label>
|
|
761
|
-
|
|
785
|
+
|
|
762
786
|
{error && (
|
|
763
787
|
<div className="error-message">
|
|
764
788
|
Error loading options: {error.message}
|
|
765
789
|
</div>
|
|
766
790
|
)}
|
|
767
|
-
|
|
791
|
+
|
|
768
792
|
<input
|
|
769
793
|
type="text"
|
|
770
794
|
placeholder={field.placeholder || `Search ${field.label}`}
|
|
@@ -772,19 +796,23 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
772
796
|
disabled={loading}
|
|
773
797
|
className="search-input"
|
|
774
798
|
/>
|
|
775
|
-
|
|
799
|
+
|
|
776
800
|
<select
|
|
777
801
|
multiple={isMulti}
|
|
778
|
-
value={isMulti ? (value || []).map(v => v.value) :
|
|
802
|
+
value={isMulti ? (value || []).map((v) => v.value) : value?.value || ""}
|
|
779
803
|
onChange={(e) => {
|
|
780
804
|
if (isMulti) {
|
|
781
|
-
const selectedOptions = Array.from(e.target.selectedOptions).map(
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
805
|
+
const selectedOptions = Array.from(e.target.selectedOptions).map(
|
|
806
|
+
(option) => {
|
|
807
|
+
const opt = options.find((o) => o.value === option.value);
|
|
808
|
+
return { label: opt.label, value: opt.value };
|
|
809
|
+
}
|
|
810
|
+
);
|
|
785
811
|
handleSelectChange(selectedOptions);
|
|
786
812
|
} else {
|
|
787
|
-
const selectedOption = options.find(
|
|
813
|
+
const selectedOption = options.find(
|
|
814
|
+
(o) => o.value === e.target.value
|
|
815
|
+
);
|
|
788
816
|
handleSelectChange(selectedOption ? [selectedOption] : []);
|
|
789
817
|
}
|
|
790
818
|
}}
|
|
@@ -797,9 +825,9 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
797
825
|
</option>
|
|
798
826
|
))}
|
|
799
827
|
</select>
|
|
800
|
-
|
|
828
|
+
|
|
801
829
|
{loading && <div className="loading">Loading options...</div>}
|
|
802
|
-
|
|
830
|
+
|
|
803
831
|
{hasNext && !loadingMore && (
|
|
804
832
|
<button
|
|
805
833
|
type="button"
|
|
@@ -809,13 +837,13 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
809
837
|
Load More
|
|
810
838
|
</button>
|
|
811
839
|
)}
|
|
812
|
-
|
|
840
|
+
|
|
813
841
|
{loadingMore && <div className="loading">Loading more...</div>}
|
|
814
|
-
|
|
842
|
+
|
|
815
843
|
<div className="options-info">
|
|
816
844
|
Loaded {totalLoadedOptions} options{hasNext ? "" : " (all loaded)"}
|
|
817
845
|
</div>
|
|
818
|
-
|
|
846
|
+
|
|
819
847
|
{field.description && (
|
|
820
848
|
<div className="field-description">{field.description}</div>
|
|
821
849
|
)}
|
|
@@ -829,23 +857,29 @@ function SelectField({ field, value, onChange, isMulti = false, context = {} })
|
|
|
829
857
|
For Google Drive file picker fields, handle the file selection flow. These fields also work with `{ label, value }` objects and support file type filtering:
|
|
830
858
|
|
|
831
859
|
```tsx
|
|
832
|
-
function GoogleFilesPickerField({
|
|
860
|
+
function GoogleFilesPickerField({
|
|
861
|
+
field,
|
|
862
|
+
value,
|
|
863
|
+
onChange,
|
|
864
|
+
isMulti = false,
|
|
865
|
+
context = {},
|
|
866
|
+
}) {
|
|
833
867
|
async function handlePickFiles() {
|
|
834
868
|
if (field.optionsSource?.openGoogleFilesPicker) {
|
|
835
869
|
await field.optionsSource.openGoogleFilesPicker({
|
|
836
870
|
onComplete: async (files) => {
|
|
837
871
|
if (isMulti) {
|
|
838
872
|
// For multi-select, ensure we have an array of { label, value } objects
|
|
839
|
-
const formattedFiles = files.map(file => ({
|
|
873
|
+
const formattedFiles = files.map((file) => ({
|
|
840
874
|
label: file.label || file.name || file.value,
|
|
841
|
-
value: file.value || file.id
|
|
875
|
+
value: file.value || file.id,
|
|
842
876
|
}));
|
|
843
877
|
onChange(formattedFiles);
|
|
844
878
|
} else {
|
|
845
879
|
// For single select, ensure we have a single { label, value } object
|
|
846
880
|
const formattedFile = {
|
|
847
881
|
label: files[0]?.label || files[0]?.name || files[0]?.value,
|
|
848
|
-
value: files[0]?.value || files[0]?.id
|
|
882
|
+
value: files[0]?.value || files[0]?.id,
|
|
849
883
|
};
|
|
850
884
|
onChange(formattedFile);
|
|
851
885
|
}
|
|
@@ -865,15 +899,15 @@ function GoogleFilesPickerField({ field, value, onChange, isMulti = false, conte
|
|
|
865
899
|
{field.label}
|
|
866
900
|
{field.required && <span className="required"> *</span>}
|
|
867
901
|
</label>
|
|
868
|
-
|
|
869
|
-
<button
|
|
870
|
-
type="button"
|
|
902
|
+
|
|
903
|
+
<button
|
|
904
|
+
type="button"
|
|
871
905
|
onClick={handlePickFiles}
|
|
872
906
|
className="google-picker-btn"
|
|
873
907
|
>
|
|
874
908
|
Pick from Google Drive
|
|
875
909
|
</button>
|
|
876
|
-
|
|
910
|
+
|
|
877
911
|
{value && (
|
|
878
912
|
<div className="selected-files">
|
|
879
913
|
<strong>Selected file{isMulti ? "s" : ""}:</strong>
|
|
@@ -884,7 +918,7 @@ function GoogleFilesPickerField({ field, value, onChange, isMulti = false, conte
|
|
|
884
918
|
</ul>
|
|
885
919
|
</div>
|
|
886
920
|
)}
|
|
887
|
-
|
|
921
|
+
|
|
888
922
|
{field.description && (
|
|
889
923
|
<div className="field-description">{field.description}</div>
|
|
890
924
|
)}
|
|
@@ -923,7 +957,7 @@ function FormField({ field, value, onChange, context = {} }) {
|
|
|
923
957
|
)}
|
|
924
958
|
</div>
|
|
925
959
|
);
|
|
926
|
-
|
|
960
|
+
|
|
927
961
|
case "checkbox":
|
|
928
962
|
return (
|
|
929
963
|
<div className="field-container">
|
|
@@ -943,51 +977,51 @@ function FormField({ field, value, onChange, context = {} }) {
|
|
|
943
977
|
)}
|
|
944
978
|
</div>
|
|
945
979
|
);
|
|
946
|
-
|
|
980
|
+
|
|
947
981
|
case "select":
|
|
948
982
|
return (
|
|
949
|
-
<SelectField
|
|
950
|
-
field={field}
|
|
951
|
-
value={value}
|
|
952
|
-
onChange={onChange}
|
|
983
|
+
<SelectField
|
|
984
|
+
field={field}
|
|
985
|
+
value={value}
|
|
986
|
+
onChange={onChange}
|
|
953
987
|
isMulti={false}
|
|
954
988
|
context={context}
|
|
955
989
|
/>
|
|
956
990
|
);
|
|
957
|
-
|
|
991
|
+
|
|
958
992
|
case "multi-select":
|
|
959
993
|
return (
|
|
960
|
-
<SelectField
|
|
961
|
-
field={field}
|
|
962
|
-
value={value}
|
|
963
|
-
onChange={onChange}
|
|
994
|
+
<SelectField
|
|
995
|
+
field={field}
|
|
996
|
+
value={value}
|
|
997
|
+
onChange={onChange}
|
|
964
998
|
isMulti={true}
|
|
965
999
|
context={context}
|
|
966
1000
|
/>
|
|
967
1001
|
);
|
|
968
|
-
|
|
1002
|
+
|
|
969
1003
|
case "google-files-picker-select":
|
|
970
1004
|
return (
|
|
971
|
-
<GoogleFilesPickerField
|
|
972
|
-
field={field}
|
|
973
|
-
value={value}
|
|
974
|
-
onChange={onChange}
|
|
1005
|
+
<GoogleFilesPickerField
|
|
1006
|
+
field={field}
|
|
1007
|
+
value={value}
|
|
1008
|
+
onChange={onChange}
|
|
975
1009
|
isMulti={false}
|
|
976
1010
|
context={context}
|
|
977
1011
|
/>
|
|
978
1012
|
);
|
|
979
|
-
|
|
1013
|
+
|
|
980
1014
|
case "google-files-picker-multi-select":
|
|
981
1015
|
return (
|
|
982
|
-
<GoogleFilesPickerField
|
|
983
|
-
field={field}
|
|
984
|
-
value={value}
|
|
985
|
-
onChange={onChange}
|
|
1016
|
+
<GoogleFilesPickerField
|
|
1017
|
+
field={field}
|
|
1018
|
+
value={value}
|
|
1019
|
+
onChange={onChange}
|
|
986
1020
|
isMulti={true}
|
|
987
1021
|
context={context}
|
|
988
1022
|
/>
|
|
989
1023
|
);
|
|
990
|
-
|
|
1024
|
+
|
|
991
1025
|
default:
|
|
992
1026
|
return (
|
|
993
1027
|
<div className="field-container">
|
|
@@ -1022,17 +1056,18 @@ Here's how to implement context passing in your form components:
|
|
|
1022
1056
|
import { useConfigurationForm } from "@fastn-ai/react-core";
|
|
1023
1057
|
|
|
1024
1058
|
function ConfigurationForm({ configurationId, connectorId, configuration }) {
|
|
1025
|
-
const {
|
|
1059
|
+
const {
|
|
1060
|
+
data: configurationForm,
|
|
1061
|
+
isLoading,
|
|
1062
|
+
error,
|
|
1063
|
+
} = useConfigurationForm({
|
|
1026
1064
|
configurationId,
|
|
1027
1065
|
connectorId,
|
|
1028
1066
|
configuration,
|
|
1029
1067
|
});
|
|
1030
1068
|
|
|
1031
1069
|
return (
|
|
1032
|
-
<Formik
|
|
1033
|
-
initialValues={initialValues}
|
|
1034
|
-
onSubmit={handleSubmit}
|
|
1035
|
-
>
|
|
1070
|
+
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
|
1036
1071
|
{({ values, setFieldValue, setFieldTouched, errors, touched }) => (
|
|
1037
1072
|
<Form>
|
|
1038
1073
|
{configurationForm.fields
|
|
@@ -1156,7 +1191,9 @@ function MultiLevelSelectForm() {
|
|
|
1156
1191
|
<SelectField
|
|
1157
1192
|
field={workspaceField}
|
|
1158
1193
|
value={formValues.workspace}
|
|
1159
|
-
onChange={(value) =>
|
|
1194
|
+
onChange={(value) =>
|
|
1195
|
+
setFormValues((prev) => ({ ...prev, workspace: value }))
|
|
1196
|
+
}
|
|
1160
1197
|
context={formValues}
|
|
1161
1198
|
/>
|
|
1162
1199
|
|
|
@@ -1164,7 +1201,9 @@ function MultiLevelSelectForm() {
|
|
|
1164
1201
|
<SelectField
|
|
1165
1202
|
field={channelField}
|
|
1166
1203
|
value={formValues.channel}
|
|
1167
|
-
onChange={(value) =>
|
|
1204
|
+
onChange={(value) =>
|
|
1205
|
+
setFormValues((prev) => ({ ...prev, channel: value }))
|
|
1206
|
+
}
|
|
1168
1207
|
context={formValues} // Has access to workspace value
|
|
1169
1208
|
/>
|
|
1170
1209
|
|
|
@@ -1172,7 +1211,9 @@ function MultiLevelSelectForm() {
|
|
|
1172
1211
|
<SelectField
|
|
1173
1212
|
field={userField}
|
|
1174
1213
|
value={formValues.user}
|
|
1175
|
-
onChange={(value) =>
|
|
1214
|
+
onChange={(value) =>
|
|
1215
|
+
setFormValues((prev) => ({ ...prev, user: value }))
|
|
1216
|
+
}
|
|
1176
1217
|
context={formValues} // Has access to both workspace and channel values
|
|
1177
1218
|
/>
|
|
1178
1219
|
</div>
|
|
@@ -1225,7 +1266,6 @@ function ConnectorManager() {
|
|
|
1225
1266
|
}
|
|
1226
1267
|
```
|
|
1227
1268
|
|
|
1228
|
-
|
|
1229
1269
|
```tsx
|
|
1230
1270
|
function ConfigurationActions({ config }) {
|
|
1231
1271
|
const queryClient = useQueryClient();
|
|
@@ -1273,6 +1313,7 @@ function ConfigurationActions({ config }) {
|
|
|
1273
1313
|
```
|
|
1274
1314
|
|
|
1275
1315
|
---
|
|
1316
|
+
|
|
1276
1317
|
## 🚨 Troubleshooting
|
|
1277
1318
|
|
|
1278
1319
|
### **Common Issues**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-fastn-ai/react-core",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.12",
|
|
4
4
|
"description": "React hooks and components for integrating Fastn AI connector marketplace into your applications. Built on top of @fastn-ai/core with React Query for optimal performance.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"license": "MIT",
|
|
53
53
|
"repository": {
|
|
54
54
|
"type": "git",
|
|
55
|
-
"url": "https://github.com/fastn-ai/react-core.git"
|
|
55
|
+
"url": "https://github.com/dev-fastn-ai/react-core.git"
|
|
56
56
|
},
|
|
57
57
|
"bugs": {
|
|
58
|
-
"url": "https://github.com/fastn-ai/react-core/issues"
|
|
58
|
+
"url": "https://github.com/dev-fastn-ai/react-core/issues"
|
|
59
59
|
},
|
|
60
60
|
"homepage": "https://docs.fastn.ai",
|
|
61
61
|
"dependencies": {
|
|
@@ -66,15 +66,15 @@
|
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@tanstack/react-query": "^5.0.0",
|
|
69
|
-
"react": "^18.0.0",
|
|
70
|
-
"react-dom": "^18.0.0"
|
|
69
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
70
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@rollup/plugin-commonjs": "^28.0.1",
|
|
74
74
|
"@rollup/plugin-json": "^6.1.0",
|
|
75
75
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
76
76
|
"@types/node": "^20.11.24",
|
|
77
|
-
"@types/react": "^18.0.29",
|
|
77
|
+
"@types/react": "^18.0.29 || ^19.0.0",
|
|
78
78
|
"@types/react-dom": "^18.0.11",
|
|
79
79
|
"rimraf": "^5.0.5",
|
|
80
80
|
"rollup": "^4.12.0",
|