@orsetra/shared-ui 1.1.9 → 1.1.11
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/components/extends/ArrayItemGroup/index.tsx +58 -0
- package/components/extends/CPUNumber/index.tsx +33 -0
- package/components/extends/ClassStorageSelect/index.tsx +11 -0
- package/components/extends/ClusterSelect/index.tsx +11 -0
- package/components/extends/ComponentSelect/index.tsx +114 -0
- package/components/extends/DiskNumber/index.tsx +34 -0
- package/components/extends/EnvSelect/index.tsx +11 -0
- package/components/extends/Group/index.tsx +152 -0
- package/components/extends/Ignore/index.tsx +11 -0
- package/components/extends/ImageInput/index.less +49 -0
- package/components/extends/ImageInput/index.tsx +195 -0
- package/components/extends/ImageSecretSelect/index.tsx +170 -0
- package/components/extends/KV/index.tsx +23 -0
- package/components/extends/MemoryNumber/index.tsx +34 -0
- package/components/extends/Numbers/index.tsx +23 -0
- package/components/extends/PVCSelect/index.tsx +12 -0
- package/components/extends/PolicySelect/index.tsx +55 -0
- package/components/extends/SecretKeySelect/index.tsx +97 -0
- package/components/extends/SecretSelect/index.tsx +78 -0
- package/components/extends/StepSelect/index.tsx +76 -0
- package/components/extends/Strings/index.tsx +23 -0
- package/components/extends/Switch/index.tsx +23 -0
- package/components/extends/index.ts +21 -0
- package/components/ui/combobox.tsx +137 -0
- package/components/ui/index.ts +3 -0
- package/components/ui/kv-input.tsx +117 -0
- package/components/ui/multi-select.tsx +151 -0
- package/components/ui/numbers-input.tsx +87 -0
- package/components/ui/strings-input.tsx +83 -0
- package/index.ts +12 -0
- package/package.json +2 -2
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from 'react';
|
|
4
|
+
import { Check, ChevronsUpDown, X, Loader2 } from 'lucide-react';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
|
7
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '@/components/ui/command';
|
|
8
|
+
import { Badge } from '@/components/ui/badge';
|
|
9
|
+
import { cn } from '@/lib/utils';
|
|
10
|
+
import i18n from '@/i18n';
|
|
11
|
+
import type { GetImageReposResponse, ImageRegistry } from '@/api/repository';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
value?: string[];
|
|
16
|
+
onChange: (value: string[]) => void;
|
|
17
|
+
id: string;
|
|
18
|
+
disabled: boolean;
|
|
19
|
+
project?: string;
|
|
20
|
+
getImageRepos: (params: { project: string }) => Promise<GetImageReposResponse>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const ImageSecretSelect: React.FC<Props> = ({
|
|
24
|
+
value = [],
|
|
25
|
+
id,
|
|
26
|
+
onChange,
|
|
27
|
+
disabled,
|
|
28
|
+
project,
|
|
29
|
+
getImageRepos,
|
|
30
|
+
}) => {
|
|
31
|
+
const [open, setOpen] = useState(false);
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const [registries, setRegistries] = useState<ImageRegistry[]>([]);
|
|
34
|
+
const [searchValue, setSearchValue] = useState('');
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (project) {
|
|
38
|
+
setLoading(true);
|
|
39
|
+
getImageRepos({ project })
|
|
40
|
+
.then((res) => {
|
|
41
|
+
if (res) {
|
|
42
|
+
setRegistries(res.registries || []);
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.finally(() => {
|
|
46
|
+
setLoading(false);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}, [project]);
|
|
50
|
+
|
|
51
|
+
const convertImageRegistryOptions = (data: ImageRegistry[]): Array<{ label: string; value: string }> => {
|
|
52
|
+
return (data || []).map((item: ImageRegistry) => {
|
|
53
|
+
let label = item.secretName;
|
|
54
|
+
if (item.domain) {
|
|
55
|
+
label = `${item.secretName} (${item.domain})`;
|
|
56
|
+
}
|
|
57
|
+
return { label, value: item.secretName };
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleSelect = (selectedValue: string) => {
|
|
62
|
+
const newValue = value.includes(selectedValue)
|
|
63
|
+
? value.filter((v) => v !== selectedValue)
|
|
64
|
+
: [...value, selectedValue];
|
|
65
|
+
onChange(newValue);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleRemove = (valueToRemove: string) => {
|
|
69
|
+
onChange(value.filter((v) => v !== valueToRemove));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const dataSource = [...registries];
|
|
73
|
+
if (searchValue && !dataSource.some(r => r.secretName === searchValue)) {
|
|
74
|
+
dataSource.unshift({ secretName: searchValue, name: searchValue });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const options = convertImageRegistryOptions(dataSource);
|
|
78
|
+
const selectedLabels = options
|
|
79
|
+
.filter((opt) => value.includes(opt.value))
|
|
80
|
+
.map((opt) => opt.label);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="w-full">
|
|
84
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
85
|
+
<PopoverTrigger asChild>
|
|
86
|
+
<Button
|
|
87
|
+
id={id}
|
|
88
|
+
variant="secondary"
|
|
89
|
+
role="combobox"
|
|
90
|
+
aria-expanded={open}
|
|
91
|
+
disabled={disabled || loading}
|
|
92
|
+
className="w-full justify-between h-auto min-h-[40px] px-3 py-2"
|
|
93
|
+
style={{ borderRadius: 0 }}
|
|
94
|
+
>
|
|
95
|
+
<div className="flex flex-wrap gap-1 flex-1">
|
|
96
|
+
{loading ? (
|
|
97
|
+
<div className="flex items-center gap-2">
|
|
98
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
99
|
+
<span className="text-ibm-gray-50">Loading...</span>
|
|
100
|
+
</div>
|
|
101
|
+
) : value.length === 0 ? (
|
|
102
|
+
<span className="text-ibm-gray-50">
|
|
103
|
+
{i18n.t('Please select or input your owner image registry secret')}
|
|
104
|
+
</span>
|
|
105
|
+
) : (
|
|
106
|
+
selectedLabels.map((label, index) => (
|
|
107
|
+
<Badge
|
|
108
|
+
key={value[index]}
|
|
109
|
+
variant="secondary"
|
|
110
|
+
className="mr-1"
|
|
111
|
+
style={{ borderRadius: 0 }}
|
|
112
|
+
>
|
|
113
|
+
{label}
|
|
114
|
+
<button
|
|
115
|
+
className="ml-1 hover:text-ibm-red-60"
|
|
116
|
+
onClick={(e) => {
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
handleRemove(value[index]);
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<X className="h-3 w-3" />
|
|
122
|
+
</button>
|
|
123
|
+
</Badge>
|
|
124
|
+
))
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
128
|
+
</Button>
|
|
129
|
+
</PopoverTrigger>
|
|
130
|
+
<PopoverContent className="w-full p-0" style={{ borderRadius: 0 }}>
|
|
131
|
+
<Command>
|
|
132
|
+
<CommandInput
|
|
133
|
+
placeholder="Search or enter custom secret name..."
|
|
134
|
+
value={searchValue}
|
|
135
|
+
onValueChange={setSearchValue}
|
|
136
|
+
/>
|
|
137
|
+
<CommandEmpty>
|
|
138
|
+
{searchValue ? (
|
|
139
|
+
<div className="p-2 text-sm">
|
|
140
|
+
Press Enter to add "{searchValue}"
|
|
141
|
+
</div>
|
|
142
|
+
) : (
|
|
143
|
+
'No registry found.'
|
|
144
|
+
)}
|
|
145
|
+
</CommandEmpty>
|
|
146
|
+
<CommandGroup className="max-h-64 overflow-auto">
|
|
147
|
+
{options.map((option) => (
|
|
148
|
+
<CommandItem
|
|
149
|
+
key={option.value}
|
|
150
|
+
value={option.value}
|
|
151
|
+
onSelect={() => handleSelect(option.value)}
|
|
152
|
+
>
|
|
153
|
+
<Check
|
|
154
|
+
className={cn(
|
|
155
|
+
"mr-2 h-4 w-4",
|
|
156
|
+
value.includes(option.value) ? "opacity-100" : "opacity-0"
|
|
157
|
+
)}
|
|
158
|
+
/>
|
|
159
|
+
{option.label}
|
|
160
|
+
</CommandItem>
|
|
161
|
+
))}
|
|
162
|
+
</CommandGroup>
|
|
163
|
+
</Command>
|
|
164
|
+
</PopoverContent>
|
|
165
|
+
</Popover>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export default ImageSecretSelect;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { KVInput } from '../../ui/kv-input';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
value?: Record<string, string>;
|
|
6
|
+
onChange?: (value: Record<string, string>) => void;
|
|
7
|
+
id: string;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const KV: React.FC<Props> = ({ value, onChange, disabled }) => {
|
|
12
|
+
return (
|
|
13
|
+
<KVInput
|
|
14
|
+
value={value}
|
|
15
|
+
onChange={onChange}
|
|
16
|
+
disabled={disabled}
|
|
17
|
+
keyPlaceholder="Key"
|
|
18
|
+
valuePlaceholder="Value"
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default KV;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Input } from '../../ui/input';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
id: string;
|
|
6
|
+
onChange: (value: any) => void;
|
|
7
|
+
value?: any;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const MemoryNumber: React.FC<Props> = ({ value, id, onChange, disabled }) => {
|
|
12
|
+
const initValue = value ? parseInt(value.replace('Mi', ''), 10) : undefined;
|
|
13
|
+
|
|
14
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
15
|
+
onChange(e.target.value + 'Mi');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="flex items-center gap-2">
|
|
20
|
+
<Input
|
|
21
|
+
id={id}
|
|
22
|
+
type="number"
|
|
23
|
+
min="0"
|
|
24
|
+
disabled={disabled}
|
|
25
|
+
onChange={handleChange}
|
|
26
|
+
value={initValue}
|
|
27
|
+
className="flex-1"
|
|
28
|
+
/>
|
|
29
|
+
<span className="text-sm text-ibm-gray-70">Mi</span>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default MemoryNumber;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NumbersInput } from '@/components/ui/numbers-input';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
label?: string;
|
|
6
|
+
value?: number[];
|
|
7
|
+
id: string;
|
|
8
|
+
onChange: (value: number[]) => void;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Numbers: React.FC<Props> = ({ value, onChange, disabled }) => {
|
|
13
|
+
return (
|
|
14
|
+
<NumbersInput
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={onChange}
|
|
17
|
+
disabled={disabled}
|
|
18
|
+
placeholder="Enter number"
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default Numbers;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from 'react';
|
|
4
|
+
import { MultiSelect } from '../../ui/multi-select';
|
|
5
|
+
import type { ApplicationPolicyBase } from '@/api';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
onChange: (value: any) => void;
|
|
9
|
+
value?: any;
|
|
10
|
+
id: string;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
appName: string;
|
|
13
|
+
getPolicyList: (params: { appName: string }) => Promise<{ policies: ApplicationPolicyBase[] }>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const PolicySelect: React.FC<Props> = ({ value, id, disabled, onChange, appName, getPolicyList }) => {
|
|
17
|
+
const [policySelectDataSource, setPolicySelectDataSource] = useState<Array<{ label: string; value: string }>>([]);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const fetchPolicyList = async () => {
|
|
21
|
+
if (appName) {
|
|
22
|
+
try {
|
|
23
|
+
const res = await getPolicyList({ appName });
|
|
24
|
+
if (res && res.policies) {
|
|
25
|
+
const policyListData = (res.policies || []).map((item: ApplicationPolicyBase) => ({
|
|
26
|
+
label: `${item.name}(${item.type})`,
|
|
27
|
+
value: item.name,
|
|
28
|
+
}));
|
|
29
|
+
setPolicySelectDataSource(policyListData);
|
|
30
|
+
} else {
|
|
31
|
+
setPolicySelectDataSource([]);
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
setPolicySelectDataSource([]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
fetchPolicyList();
|
|
40
|
+
}, [appName, getPolicyList]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<MultiSelect
|
|
44
|
+
placeholder="Please select"
|
|
45
|
+
onChange={onChange}
|
|
46
|
+
id={id}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
defaultValue={value || []}
|
|
49
|
+
value={value || []}
|
|
50
|
+
dataSource={policySelectDataSource}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default PolicySelect;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { Check, ChevronsUpDown } from 'lucide-react';
|
|
5
|
+
import { Button } from '../../ui/button';
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover';
|
|
7
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../../ui/command';
|
|
8
|
+
import { cn } from '../../../lib/utils';
|
|
9
|
+
import i18n from '@/i18n';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
onChange: (value: any) => void;
|
|
13
|
+
secretKeys?: string[];
|
|
14
|
+
value?: any;
|
|
15
|
+
id: string;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SecretKeySelect: React.FC<Props> = ({ onChange, value, secretKeys, id, disabled }) => {
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const [searchValue, setSearchValue] = useState('');
|
|
22
|
+
|
|
23
|
+
const dataSource = [...(secretKeys || [])];
|
|
24
|
+
if (searchValue && !dataSource.includes(searchValue)) {
|
|
25
|
+
dataSource.unshift(searchValue);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const options = dataSource.map((item) => ({
|
|
29
|
+
label: item,
|
|
30
|
+
value: item,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const handleSelect = (selectedValue: string) => {
|
|
34
|
+
onChange(selectedValue);
|
|
35
|
+
setOpen(false);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const selectedLabel = options.find((opt) => opt.value === value)?.label || value;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
42
|
+
<PopoverTrigger asChild>
|
|
43
|
+
<Button
|
|
44
|
+
id={id}
|
|
45
|
+
variant="secondary"
|
|
46
|
+
role="combobox"
|
|
47
|
+
aria-expanded={open}
|
|
48
|
+
disabled={disabled}
|
|
49
|
+
className="w-full justify-between h-[40px] px-3"
|
|
50
|
+
style={{ borderRadius: 0 }}
|
|
51
|
+
>
|
|
52
|
+
<span className={cn(!value && "text-ibm-gray-50")}>
|
|
53
|
+
{value ? selectedLabel : i18n.t('Please select or input a secret key')}
|
|
54
|
+
</span>
|
|
55
|
+
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
56
|
+
</Button>
|
|
57
|
+
</PopoverTrigger>
|
|
58
|
+
<PopoverContent className="w-full p-0" style={{ borderRadius: 0 }}>
|
|
59
|
+
<Command>
|
|
60
|
+
<CommandInput
|
|
61
|
+
placeholder="Search or enter custom secret key..."
|
|
62
|
+
value={searchValue}
|
|
63
|
+
onValueChange={setSearchValue}
|
|
64
|
+
/>
|
|
65
|
+
<CommandEmpty>
|
|
66
|
+
{searchValue ? (
|
|
67
|
+
<div className="p-2 text-sm">
|
|
68
|
+
Press Enter to add "{searchValue}"
|
|
69
|
+
</div>
|
|
70
|
+
) : (
|
|
71
|
+
'No secret key found.'
|
|
72
|
+
)}
|
|
73
|
+
</CommandEmpty>
|
|
74
|
+
<CommandGroup className="max-h-64 overflow-auto">
|
|
75
|
+
{options.map((option) => (
|
|
76
|
+
<CommandItem
|
|
77
|
+
key={option.value}
|
|
78
|
+
value={option.value}
|
|
79
|
+
onSelect={() => handleSelect(option.value)}
|
|
80
|
+
>
|
|
81
|
+
<Check
|
|
82
|
+
className={cn(
|
|
83
|
+
"mr-2 h-4 w-4",
|
|
84
|
+
value === option.value ? "opacity-100" : "opacity-0"
|
|
85
|
+
)}
|
|
86
|
+
/>
|
|
87
|
+
{option.label}
|
|
88
|
+
</CommandItem>
|
|
89
|
+
))}
|
|
90
|
+
</CommandGroup>
|
|
91
|
+
</Command>
|
|
92
|
+
</PopoverContent>
|
|
93
|
+
</Popover>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default SecretKeySelect;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { Combobox } from '../../ui/combobox';
|
|
5
|
+
|
|
6
|
+
import { locale } from '@/utils/locale';
|
|
7
|
+
import type { Secret } from '@/api';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
onChange: (value: any) => void;
|
|
11
|
+
setKeys: (keys: string[]) => void;
|
|
12
|
+
value?: any;
|
|
13
|
+
id: string;
|
|
14
|
+
appNamespace?: string;
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
listCloudResourceSecrets: (params: { appNs: string }) => Promise<{ secrets: Secret[] }>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SecretSelect: React.FC<Props> = ({ value, id, disabled, appNamespace, onChange, setKeys, listCloudResourceSecrets }) => {
|
|
20
|
+
const [secrets, setSecrets] = useState<Secret[]>([]);
|
|
21
|
+
|
|
22
|
+
const getSecretKeys = useCallback((name: string) => {
|
|
23
|
+
let keys: string[] = [];
|
|
24
|
+
secrets?.forEach((secret) => {
|
|
25
|
+
if (secret.metadata.labels['app.oam.dev/sync-alias'] === name && 'data' in secret) {
|
|
26
|
+
keys = Object.keys(secret.data);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return keys;
|
|
30
|
+
}, [secrets]);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const loadSecrets = async () => {
|
|
34
|
+
if (appNamespace) {
|
|
35
|
+
try {
|
|
36
|
+
const res = await listCloudResourceSecrets({ appNs: appNamespace });
|
|
37
|
+
if (res) {
|
|
38
|
+
setSecrets(res.secrets);
|
|
39
|
+
const keys = getSecretKeys(value);
|
|
40
|
+
setKeys(keys);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
setSecrets([]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
loadSecrets();
|
|
49
|
+
}, [appNamespace, value, setKeys, getSecretKeys, listCloudResourceSecrets]);
|
|
50
|
+
|
|
51
|
+
const handleChange = (selectedValue: string) => {
|
|
52
|
+
const keys = getSecretKeys(selectedValue);
|
|
53
|
+
onChange(selectedValue);
|
|
54
|
+
setKeys(keys);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const filters = secrets?.filter((secret) => secret.metadata.labels['app.oam.dev/sync-alias']);
|
|
58
|
+
const dataSource =
|
|
59
|
+
filters?.map((secret) => ({
|
|
60
|
+
label: secret.metadata.labels['app.oam.dev/sync-alias'],
|
|
61
|
+
value: secret.metadata.labels['app.oam.dev/sync-alias'],
|
|
62
|
+
})) || [];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Combobox
|
|
66
|
+
locale={locale().Select}
|
|
67
|
+
onChange={handleChange}
|
|
68
|
+
value={value}
|
|
69
|
+
id={id}
|
|
70
|
+
disabled={disabled}
|
|
71
|
+
placeholder="Please select or input a secret name"
|
|
72
|
+
enableInput={true}
|
|
73
|
+
dataSource={dataSource}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default SecretSelect;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
import { MultiSelect } from '@/components/ui/multi-select';
|
|
6
|
+
import { WorkflowContext, WorkflowEditContext } from '@/context';
|
|
7
|
+
import i18n from '@/i18n';
|
|
8
|
+
import type { WorkflowStep, WorkflowStepBase } from '@/api';
|
|
9
|
+
import { showAlias } from '@/utils/common';
|
|
10
|
+
import { locale } from '@/utils/locale';
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
value?: string[];
|
|
14
|
+
id: string;
|
|
15
|
+
onChange: (value: string[]) => void;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const StepSelect = (props: Props) => {
|
|
20
|
+
const { value, id, disabled } = props;
|
|
21
|
+
const { stepName, steps } = useContext(WorkflowEditContext);
|
|
22
|
+
const { workflow } = useContext(WorkflowContext);
|
|
23
|
+
const stepOptions: Array<{ label: string; value: string }> = [];
|
|
24
|
+
let inGroup = false;
|
|
25
|
+
let groupStep: WorkflowStep | undefined;
|
|
26
|
+
steps?.map((step) => {
|
|
27
|
+
step.subSteps?.map((subStep) => {
|
|
28
|
+
if (subStep.name === stepName) {
|
|
29
|
+
inGroup = true;
|
|
30
|
+
groupStep = step;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
if (workflow?.mode === 'DAG' && (workflow.subMode === 'DAG' || (workflow.subMode === 'StepByStep' && !inGroup))) {
|
|
35
|
+
steps
|
|
36
|
+
?.filter((s) => s.name !== stepName)
|
|
37
|
+
.map((step: WorkflowStep) => {
|
|
38
|
+
stepOptions.push({
|
|
39
|
+
label: showAlias(step.name, step.alias),
|
|
40
|
+
value: step.name,
|
|
41
|
+
});
|
|
42
|
+
step.subSteps
|
|
43
|
+
?.filter((s) => s.name !== stepName)
|
|
44
|
+
.map((b: WorkflowStepBase) => {
|
|
45
|
+
stepOptions.push({
|
|
46
|
+
label: `${showAlias(step.name, step.alias)}/${showAlias(b.name, b.alias)}`,
|
|
47
|
+
value: b.name,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (workflow?.mode === 'StepByStep' && workflow.subMode === 'DAG' && inGroup && groupStep) {
|
|
54
|
+
groupStep.subSteps
|
|
55
|
+
?.filter((s) => s.name !== stepName)
|
|
56
|
+
.map((step: WorkflowStep) => {
|
|
57
|
+
stepOptions.push({
|
|
58
|
+
label: showAlias(step.name, step.alias),
|
|
59
|
+
value: step.name,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<MultiSelect
|
|
66
|
+
placeholder={i18n.t('Please select the steps')}
|
|
67
|
+
onChange={props.onChange}
|
|
68
|
+
id={id}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
defaultValue={value || []}
|
|
71
|
+
value={value || []}
|
|
72
|
+
dataSource={stepOptions}
|
|
73
|
+
locale={locale().Select}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StringsInput } from '@/components/ui/strings-input';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
label?: string;
|
|
6
|
+
value?: string[];
|
|
7
|
+
id: string;
|
|
8
|
+
onChange: (value: string[]) => void;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const Strings: React.FC<Props> = ({ value, onChange, disabled }) => {
|
|
13
|
+
return (
|
|
14
|
+
<StringsInput
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={onChange}
|
|
17
|
+
disabled={disabled}
|
|
18
|
+
placeholder="Enter value"
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default Strings;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Switch } from '@/components/ui/switch';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
value: boolean;
|
|
6
|
+
id: string;
|
|
7
|
+
onChange: (value: any) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const SwitchComponent: React.FC<Props> = ({ value, id, onChange }) => {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex items-center gap-2">
|
|
13
|
+
<Switch
|
|
14
|
+
id={id}
|
|
15
|
+
checked={value}
|
|
16
|
+
onCheckedChange={onChange}
|
|
17
|
+
/>
|
|
18
|
+
<span className="text-xs text-ibm-gray-70">{value ? 'on' : 'off'}</span>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default SwitchComponent;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Extended UI Components for Dynamic Forms
|
|
2
|
+
export { default as CPUNumber } from './CPUNumber'
|
|
3
|
+
export { default as ClassStorageSelect } from './ClassStorageSelect'
|
|
4
|
+
export { default as ClusterSelect } from './ClusterSelect'
|
|
5
|
+
export { default as ComponentSelect } from './ComponentSelect'
|
|
6
|
+
export { default as DiskNumber } from './DiskNumber'
|
|
7
|
+
export { default as EnvSelect } from './EnvSelect'
|
|
8
|
+
export { default as Group } from './Group'
|
|
9
|
+
export { default as Ignore } from './Ignore'
|
|
10
|
+
export { default as ImageInput } from './ImageInput'
|
|
11
|
+
export { default as ImageSecretSelect } from './ImageSecretSelect'
|
|
12
|
+
export { default as KV } from './KV'
|
|
13
|
+
export { default as MemoryNumber } from './MemoryNumber'
|
|
14
|
+
export { default as Numbers } from './Numbers'
|
|
15
|
+
export { default as PVCSelect } from './PVCSelect'
|
|
16
|
+
export { default as PolicySelect } from './PolicySelect'
|
|
17
|
+
export { default as SecretKeySelect } from './SecretKeySelect'
|
|
18
|
+
export { default as SecretSelect } from './SecretSelect'
|
|
19
|
+
export { StepSelect } from './StepSelect'
|
|
20
|
+
export { default as Strings } from './Strings'
|
|
21
|
+
export { default as FormSwitch } from './Switch'
|