Skip to content

Commit e0b74bc

Browse files
authored
Native jobs plugins refactoring + edit running native jobs (#23)
* refactor: Secondary plugins into plugins * refactor: DeploymentType to use 'pluginType' for CAR/WAR * feat: Display Custom Parameters in JobConfigurations + small UX fixes * refactor: Split JobConfigurations into two seperate sections: deployment & plugins * feat: Display Pipeline section in JobDeploymentSection * feat: Enable updating custom params for * chore: Add a warning for when chainstoreResponse is set to true * fix: Update pipeline input type and URI in JobEditFormWrapper, fix chainstoreResponse in deeploy-utils * feat: Create job using plugins array initial implementation * fix: Native schema and NativeJobsCostRundown * fix: Schema fixes for plugins * feat: Edit running job plugins * style: Target nodes section restyling * fix: Possible undefined values when using JobKeyValueSection
1 parent d200513 commit e0b74bc

38 files changed

+786
-567
lines changed

src/components/account/profile/ImageUpload.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ import toast from 'react-hot-toast';
66
export default function ImageUpload({
77
onUpload,
88
setImageLoading,
9+
setImageError,
910
}: {
1011
onUpload: () => void;
1112
setImageLoading: (loading: boolean) => void;
13+
setImageError: (error: boolean) => void;
1214
}) {
1315
const inputRef = useRef<HTMLInputElement | null>(null);
1416

1517
const handleFileChange = useCallback(
1618
async (event: React.ChangeEvent<HTMLInputElement>) => {
19+
console.log('ImageUpload handleFileChange');
20+
1721
setImageLoading(true);
22+
setImageError(false);
1823

1924
const file = event.target.files?.[0];
2025

src/components/account/profile/ProfileSectionWrapper.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { BorderedCard } from '@shared/cards/BorderedCard';
22

33
export default function ProfileSectionWrapper({ children }: { children: React.ReactNode }) {
44
return (
5-
// TODO: Check rounding
65
<BorderedCard disableWrapper>
76
<div className="col gap-4 px-4 py-3 sm:px-5 sm:py-4">{children}</div>
87
</BorderedCard>

src/components/account/profile/PublicProfile.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default function PublicProfile() {
6767
try {
6868
const response = await getPublicProfileInfo(address as EthAddress);
6969

70-
if (!response) {
70+
if (!response || !response?.brands?.[0] || response?.brands?.[0]?.links === undefined) {
7171
console.log('Error fetching public profile info.');
7272
}
7373

@@ -135,9 +135,12 @@ export default function PublicProfile() {
135135
className={clsx('z-10 h-full w-full rounded-[37.5%] object-cover object-center', {
136136
'opacity-0': isImageLoading,
137137
})}
138-
onLoad={() => setImageLoading(false)}
138+
onLoad={() => {
139+
setImageLoading(false);
140+
setImageError(false);
141+
}}
139142
onError={() => {
140-
console.log('Error loading image', profileImageUrl);
143+
console.error('Error loading image');
141144
setImageLoading(false);
142145
setImageError(true);
143146
}}
@@ -155,6 +158,7 @@ export default function PublicProfile() {
155158
setImageRefreshToken(Date.now());
156159
}}
157160
setImageLoading={setImageLoading}
161+
setImageError={setImageError}
158162
/>
159163

160164
<div className="text-sm text-slate-500">The maximum file size allowed is 500 KB.</div>

src/components/create-job/JobFormWrapper.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import db from '@lib/storage/db';
1818
import { isValidProjectHash } from '@lib/utils';
1919
import { jobSchema } from '@schemas/index';
2020
import { DraftJob, JobType } from '@typedefs/deeploys';
21+
import { BasePluginType, PluginType } from '@typedefs/steps/deploymentStepTypes';
2122
import { useEffect } from 'react';
2223
import { FieldErrors, FormProvider, useForm } from 'react-hook-form';
2324
import toast from 'react-hot-toast';
@@ -69,7 +70,7 @@ function JobFormWrapper({ projectName, draftJobsCount }) {
6970
deployment: {
7071
...getBaseSchemaDefaults().deployment,
7172
deploymentType: {
72-
type: 'container',
73+
pluginType: PluginType.Container,
7374
containerImage: '',
7475
containerRegistry: 'docker.io',
7576
crVisibility: CR_VISIBILITY_OPTIONS[0],
@@ -89,12 +90,18 @@ function JobFormWrapper({ projectName, draftJobsCount }) {
8990
},
9091
deployment: {
9192
...getBaseSchemaDefaults().deployment,
92-
pluginSignature: PLUGIN_SIGNATURE_TYPES[0],
93-
customParams: [{ key: '', value: '', valueType: 'string' }],
93+
// Pipeline
9494
pipelineParams: [{ key: '', value: '' }],
9595
pipelineInputType: PIPELINE_INPUT_TYPES[0],
96+
plugins: [
97+
{
98+
basePluginType: BasePluginType.Native,
99+
pluginSignature: PLUGIN_SIGNATURE_TYPES[0],
100+
enableTunneling: BOOLEAN_TYPES[0],
101+
customParams: [],
102+
},
103+
],
96104
chainstoreResponse: BOOLEAN_TYPES[1],
97-
secondaryPlugins: [],
98105
},
99106
});
100107

src/components/create-job/secondary-plugins/CARInputsSection.tsx renamed to src/components/create-job/plugins/CARInputsSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import ContainerSection from '@shared/jobs/deployment-type/ContainerSection';
2-
import GenericSecondaryPluginSections from './GenericSecondaryPluginSections';
2+
import GenericPluginSections from './GenericPluginSections';
33

44
export default function CARInputsSection({ index }: { index: number }) {
5-
const name = `deployment.secondaryPlugins.${index}`;
5+
const name = `deployment.plugins.${index}`;
66

77
return (
88
<div className="col gap-4">
99
<ContainerSection baseName={name} />
10-
<GenericSecondaryPluginSections name={name} />
10+
<GenericPluginSections name={name} />
1111
</div>
1212
);
1313
}

src/components/create-job/secondary-plugins/GenericSecondaryPluginSections.tsx renamed to src/components/create-job/plugins/GenericPluginSections.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import AppParametersSection from '../sections/AppParametersSection';
77
import PluginEnvVariablesSection from '../sections/PluginEnvVariablesSection';
88
import PoliciesSection from '../sections/PoliciesSection';
99

10-
export default function GenericSecondaryPluginSections({ name }: { name: string }) {
10+
export default function GenericPluginSections({ name }: { name: string }) {
1111
return (
1212
<>
1313
<ConfigSectionTitle title="App Parameters" />

src/components/create-job/secondary-plugins/NativeInputsSection.tsx renamed to src/components/create-job/plugins/NativeInputsSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useFormContext } from 'react-hook-form';
77
import AppParametersSection from '../sections/AppParametersSection';
88

99
export default function NativeInputsSection({ index }: { index: number }) {
10-
const name = `deployment.secondaryPlugins.${index}`;
10+
const name = `deployment.plugins.${index}`;
1111

1212
const { watch } = useFormContext();
1313

src/components/create-job/secondary-plugins/SecondaryPluginsCard.tsx renamed to src/components/create-job/plugins/PluginsCard.tsx

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,16 @@ import { InteractionContextType, useInteractionContext } from '@lib/contexts/int
66
import ActionButton from '@shared/ActionButton';
77
import { SlateCard } from '@shared/cards/SlateCard';
88
import { SmallTag } from '@shared/SmallTag';
9-
import { GenericSecondaryPlugin, SecondaryPlugin, SecondaryPluginType } from '@typedefs/steps/deploymentStepTypes';
9+
import { BasePluginType, GenericPlugin, Plugin, PluginType } from '@typedefs/steps/deploymentStepTypes';
1010
import clsx from 'clsx';
11-
import { useEffect } from 'react';
1211
import { useFieldArray, useFormContext } from 'react-hook-form';
1312
import toast from 'react-hot-toast';
14-
import { RiAddLine, RiBox3Line, RiTerminalBoxLine } from 'react-icons/ri';
13+
import { RiAddLine, RiBox3Line, RiDeleteBin2Line, RiTerminalBoxLine } from 'react-icons/ri';
1514
import CARInputsSection from './CARInputsSection';
1615
import NativeInputsSection from './NativeInputsSection';
1716
import WARInputsSection from './WARInputsSection';
1817

19-
enum PluginType {
20-
Native = 'native',
21-
Container = 'container',
22-
Worker = 'worker',
23-
}
24-
25-
type SecondaryPluginWithId = SecondaryPlugin & {
18+
type PluginWithId = Plugin & {
2619
id: string;
2720
};
2821

@@ -48,7 +41,7 @@ const OPTIONS: {
4841
title: 'Native Plugin',
4942
icon: <RiTerminalBoxLine />,
5043
textColorClass: 'text-green-600',
51-
color: 'green',
44+
color: 'emerald',
5245
},
5346
{
5447
pluginType: PluginType.Container,
@@ -66,29 +59,27 @@ const OPTIONS: {
6659
},
6760
];
6861

69-
export default function SecondaryPluginsCard() {
62+
export default function PluginsCard() {
7063
const { confirm } = useInteractionContext() as InteractionContextType;
7164

72-
const { control } = useFormContext();
65+
const { control, formState } = useFormContext();
7366

7467
const { fields, append, remove } = useFieldArray({
7568
control,
76-
name: 'deployment.secondaryPlugins',
69+
name: 'deployment.plugins',
7770
});
7871

79-
const plugins = fields as SecondaryPluginWithId[];
72+
const plugins = fields as PluginWithId[];
8073

81-
useEffect(() => {
82-
console.log({ plugins });
83-
}, [plugins]);
74+
const rootError: string | undefined = (formState.errors.deployment as any)?.plugins?.root?.message as string | undefined;
8475

85-
const onAddPlugin = (type: PluginType) => {
86-
switch (type) {
76+
const onAddPlugin = (pluginType: PluginType) => {
77+
switch (pluginType) {
8778
case PluginType.Container:
8879
append({
89-
secondaryPluginType: SecondaryPluginType.Generic,
80+
basePluginType: BasePluginType.Generic,
9081
deploymentType: {
91-
type: 'container',
82+
pluginType,
9283
containerImage: '',
9384
containerRegistry: 'docker.io',
9485
crVisibility: CR_VISIBILITY_OPTIONS[0],
@@ -102,9 +93,9 @@ export default function SecondaryPluginsCard() {
10293

10394
case PluginType.Worker:
10495
append({
105-
secondaryPluginType: SecondaryPluginType.Generic,
96+
basePluginType: BasePluginType.Generic,
10697
deploymentType: {
107-
type: 'worker',
98+
pluginType,
10899
image: 'node:22',
109100
repositoryUrl: '',
110101
username: '',
@@ -122,32 +113,32 @@ export default function SecondaryPluginsCard() {
122113

123114
case PluginType.Native:
124115
append({
125-
secondaryPluginType: SecondaryPluginType.Native,
116+
basePluginType: BasePluginType.Native,
126117
pluginSignature: PLUGIN_SIGNATURE_TYPES[0],
127118
customParams: [{ key: '', value: '', valueType: 'string' }],
128119
...TUNNELING_DEFAULTS,
129120
});
130121
break;
131122

132123
default:
133-
console.error('Unknown plugin type:', type);
124+
console.error('Unknown plugin type:', pluginType);
134125
}
135126
};
136127

137128
// Aliases are used in the form so the user can identify the plugins easier
138-
const getSecondaryPluginAlias = (
139-
plugin: SecondaryPluginWithId,
129+
const getPluginAlias = (
130+
plugin: PluginWithId,
140131
index: number,
141132
): {
142133
title: string;
143134
element: React.ReactNode;
144135
} => {
145136
let option: (typeof OPTIONS)[number];
146137

147-
if (plugin.secondaryPluginType === SecondaryPluginType.Native) {
138+
if (plugin.basePluginType === BasePluginType.Native) {
148139
option = OPTIONS.find((option) => option.pluginType === PluginType.Native)!;
149140
} else {
150-
const pluginType = (plugin as GenericSecondaryPlugin).deploymentType.type as PluginType;
141+
const pluginType: PluginType = (plugin as GenericPlugin).deploymentType.pluginType;
151142
option = OPTIONS.find((option) => option.pluginType === pluginType)!;
152143
}
153144

@@ -158,7 +149,7 @@ export default function SecondaryPluginsCard() {
158149
element: (
159150
<SmallTag variant={option.color} isLarge>
160151
<div className="row gap-1.5">
161-
<div className={`text-xl ${option.textColorClass}`}>{option.icon}</div>
152+
<div className="text-xl">{option.icon}</div>
162153
<div>{title}</div>
163154
</div>
164155
</SmallTag>
@@ -167,10 +158,12 @@ export default function SecondaryPluginsCard() {
167158
};
168159

169160
return (
170-
<SlateCard title="Secondary Plugins">
161+
<SlateCard title="Plugins">
171162
<div className="col gap-6">
163+
{!!rootError && <div className="text-danger text-sm">{rootError}</div>}
164+
172165
{plugins.map((plugin, index) => {
173-
const { title, element } = getSecondaryPluginAlias(plugin, index);
166+
const { title, element } = getPluginAlias(plugin, index);
174167

175168
return (
176169
<div
@@ -180,9 +173,11 @@ export default function SecondaryPluginsCard() {
180173
})}
181174
>
182175
{/* Plugin Header */}
183-
<div className="row justify-between">
176+
<div className="row gap-3">
184177
{element}
185178

179+
<div className="flex-1 border-b-2 border-slate-200"></div>
180+
186181
<div
187182
className="compact cursor-pointer text-red-600 hover:opacity-50"
188183
onClick={async () => {
@@ -205,13 +200,16 @@ export default function SecondaryPluginsCard() {
205200
}
206201
}}
207202
>
208-
Remove plugin
203+
<div className="row gap-1">
204+
<RiDeleteBin2Line className="text-lg" />
205+
<div className="font-medium">Remove plugin</div>
206+
</div>
209207
</div>
210208
</div>
211209

212-
{plugin.secondaryPluginType === SecondaryPluginType.Generic ? (
210+
{plugin.basePluginType === BasePluginType.Generic ? (
213211
<>
214-
{(plugin as GenericSecondaryPlugin).deploymentType.type === 'container' ? (
212+
{(plugin as GenericPlugin).deploymentType.pluginType === PluginType.Container ? (
215213
<CARInputsSection index={index} />
216214
) : (
217215
<WARInputsSection index={index} />
@@ -237,18 +235,18 @@ export default function SecondaryPluginsCard() {
237235
</div>
238236

239237
<div className="row gap-2">
240-
{OPTIONS.map((plugin) => (
238+
{OPTIONS.map((option) => (
241239
<ActionButton
242-
key={plugin.pluginType}
240+
key={option.pluginType}
243241
className="bg-slate-200 hover:opacity-70!"
244242
color="default"
245243
onPress={() => {
246-
onAddPlugin(plugin.pluginType);
244+
onAddPlugin(option.pluginType);
247245
}}
248246
>
249247
<div className="row gap-1.5">
250-
<div className={`text-xl ${plugin.textColorClass}`}>{plugin.icon}</div>
251-
<div className="text-sm">{plugin.title}</div>
248+
<div className={`text-xl ${option.textColorClass}`}>{option.icon}</div>
249+
<div className="text-sm">{option.title}</div>
252250
</div>
253251
</ActionButton>
254252
))}

src/components/create-job/secondary-plugins/WARInputsSection.tsx renamed to src/components/create-job/plugins/WARInputsSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import WorkerSection from '@shared/jobs/deployment-type/WorkerSection';
2-
import GenericSecondaryPluginSections from './GenericSecondaryPluginSections';
2+
import GenericPluginSections from './GenericPluginSections';
33

44
export default function WARInputsSection({ index }: { index: number }) {
5-
const name = `deployment.secondaryPlugins.${index}`;
5+
const name = `deployment.plugins.${index}`;
66

77
return (
88
<div className="col gap-4">
99
<WorkerSection baseName={name} />
10-
<GenericSecondaryPluginSections name={name} />
10+
<GenericPluginSections name={name} />
1111
</div>
1212
);
1313
}

0 commit comments

Comments
 (0)