Skip to content

Commit 9c89813

Browse files
committed
feat(canvas): integrate job spec update api
1 parent 7d316a8 commit 9c89813

File tree

7 files changed

+104
-27
lines changed

7 files changed

+104
-27
lines changed

canvas/app/dashboard/project/id/[id]/view/hooks/useParsePathRecordToArray.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type TPathRecord = {
1111
name: string;
1212
type: string;
1313
value: string;
14+
raw: TSpecPathRecord;
1415
};
1516

1617
export type TUseParsePathRecordToArrayReturn = TPathRecord[];
@@ -36,6 +37,7 @@ export function useParsePathRecordToArray(
3637
name,
3738
type,
3839
value: path || projectWorkspaceURI,
40+
raw: record,
3941
});
4042
}
4143

canvas/app/dashboard/project/id/[id]/view/jobs/job-input-edit.tsx

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,94 @@
1-
import React from "react";
1+
import React, { useMemo } from "react";
22
import { ActionButton } from "@/components/custom/action-button";
3-
import { Check, XIcon } from "lucide-react";
4-
import { JobInputUploadFile } from "./job-input-upload-file";
3+
import { Check, Upload, XIcon } from "lucide-react";
4+
// import { JobInputUploadFile } from "./job-input-upload-file";
55
import { ProjectJobInputEditWarningModal } from "./job-input-edit-warning-modal";
66
import { PathFieldWithAction } from "@/components/custom/path-filed-with-actions";
7+
import { useToast } from "@/components/ui/use-toast";
8+
import { TJob, useUpdateJob } from "@/services/job";
9+
import { FeatureNotImplementedModal } from "@/components/custom/feature-not-implemented-modal";
710

811
export type TProjectJobInputProps = {
12+
job: TJob;
913
inputPath: string;
1014
inputName: string;
1115
onInputPathChange: (value: string) => void;
1216
onRequestView: () => void;
1317
};
1418

1519
export function ProjectJobInputEdit(props: TProjectJobInputProps) {
16-
const { onRequestView, inputName, inputPath, onInputPathChange } = props;
20+
const { onRequestView, inputName, inputPath, job, onInputPathChange } = props;
1721
const [isWarningModalOpen, setIsWarningModalOpen] = React.useState(false);
1822
const oldValue = React.useRef(inputPath);
23+
const { toast } = useToast();
1924

2025
// To upload new file.
2126
const [inputFile, setInputFile] = React.useState<File | null>(null);
2227

23-
const handleOnCloseEditingModeClick = React.useCallback(() => {
28+
const handleOnErrorSavingChanges = React.useCallback(() => {
29+
toast({
30+
title: "Unable to save changes. Please try again later.",
31+
variant: "destructive",
32+
});
33+
}, [toast]);
34+
35+
const { isPending, mutate: updateJob } = useUpdateJob({
36+
onError: handleOnErrorSavingChanges,
37+
onSuccess: onRequestView,
38+
});
39+
40+
const hasAnyFieldUpdated = useMemo(() => {
2441
if (inputFile) {
25-
// If input file is not null, we have some files to upload.
26-
// Before closing edit mode, we need to show the warning.
27-
setIsWarningModalOpen(true);
28-
return;
42+
// If input file is not null, meaning we have updated input file.
43+
return true;
2944
}
3045

3146
if (oldValue.current !== inputPath) {
32-
// Input has been changed, so we need to show the warning,
33-
// before closing the edit mode.
47+
// Input has been changed.
48+
return true;
49+
}
50+
}, [inputFile, inputPath]);
51+
52+
const handleOnCloseEditingModeClick = React.useCallback(() => {
53+
if (hasAnyFieldUpdated) {
3454
setIsWarningModalOpen(true);
3555
return;
3656
}
3757

3858
onRequestView();
39-
}, [inputFile, onRequestView, inputPath]);
59+
}, [hasAnyFieldUpdated, onRequestView]);
4060

4161
const handleOnWarningModalPrimaryActionClick = React.useCallback(() => {
4262
setInputFile(null);
4363
onInputPathChange(oldValue.current);
4464
onRequestView();
4565
}, [onInputPathChange, onRequestView]);
4666

67+
const handleOnSaveChanges = React.useCallback(() => {
68+
if (!hasAnyFieldUpdated) {
69+
// if no filed has been updated, then we don't have any thing to
70+
// updated, hence closing the edit view.
71+
onRequestView();
72+
}
73+
74+
const specInputs = [...job.spec.inputs].map((input) => {
75+
if (input.name === inputName) {
76+
return { ...input, path: inputPath };
77+
}
78+
return input;
79+
});
80+
81+
updateJob({
82+
job: {
83+
...job,
84+
spec: {
85+
...job.spec,
86+
inputs: [...specInputs],
87+
},
88+
},
89+
});
90+
}, [hasAnyFieldUpdated, job, updateJob, onRequestView, inputName, inputPath]);
91+
4792
return (
4893
<>
4994
<PathFieldWithAction
@@ -61,10 +106,18 @@ export function ProjectJobInputEdit(props: TProjectJobInputProps) {
61106
{
62107
id: "upload-file",
63108
children: (
64-
<JobInputUploadFile
65-
inputFile={inputFile}
66-
updateInputFile={setInputFile}
67-
/>
109+
<FeatureNotImplementedModal featureName="Upload File">
110+
<ActionButton
111+
message="Upload File"
112+
icon={<Upload />}
113+
></ActionButton>
114+
{/* TODO: Implement Upload input file feature.
115+
<JobInputUploadFile
116+
inputFile={inputFile}
117+
updateInputFile={setInputFile}
118+
/>
119+
*/}
120+
</FeatureNotImplementedModal>
68121
),
69122
},
70123
{
@@ -82,7 +135,9 @@ export function ProjectJobInputEdit(props: TProjectJobInputProps) {
82135
id: "save-changes",
83136
children: (
84137
<ActionButton
85-
onClick={onRequestView}
138+
onClick={handleOnSaveChanges}
139+
isLoading={isPending}
140+
disabled={!hasAnyFieldUpdated}
86141
icon={<Check />}
87142
message="Save changes"
88143
key="save-changes"

canvas/app/dashboard/project/id/[id]/view/jobs/job-input.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import React from "react";
22
import { ProjectJobInputView } from "./job-input-view";
33
import { ProjectJobInputEdit } from "./job-input-edit";
44
import { TPathRecord } from "../hooks/useParsePathRecordToArray";
5+
import { TJob } from "@/services/job";
56

67
export type TProjectJobInputProps = {
78
input: TPathRecord;
9+
job: TJob;
810
};
911

1012
export function ProjectJobInput(props: TProjectJobInputProps) {
11-
const { input } = props;
13+
const { input, job } = props;
1214
const [isEditing, setIsEditing] = React.useState(false);
1315
const [inputPath, setInputPath] = React.useState(input.value);
1416

@@ -24,6 +26,7 @@ export function ProjectJobInput(props: TProjectJobInputProps) {
2426
<>
2527
{isEditing ? (
2628
<ProjectJobInputEdit
29+
job={job}
2730
inputName={input.name}
2831
inputPath={inputPath}
2932
onInputPathChange={setInputPath}

canvas/app/dashboard/project/id/[id]/view/jobs/job-inputs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function ProjectJobInputs(props: TProjectJobInputsProps) {
1919
)}
2020
<div className="space-y-2 my-2">
2121
{inputs.map((input) => (
22-
<ProjectJobInput input={input} key={input.id} />
22+
<ProjectJobInput job={job} input={input} key={input.id} />
2323
))}
2424
</div>
2525
</div>

canvas/components/custom/action-button.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ export const ActionButton = React.forwardRef<
3838
{...rest}
3939
>
4040
<span className={clsx(isLoading && "animate-spin")}>{_icon}</span>
41-
<span
42-
{...childSpan}
43-
className={clsx("md:hidden lg:inline", childSpan?.className)}
44-
>
45-
{children}
46-
</span>
41+
{children && (
42+
<span
43+
{...childSpan}
44+
className={clsx("md:hidden lg:inline", childSpan?.className)}
45+
>
46+
{children}
47+
</span>
48+
)}
4749
</ButtonWithTooltip>
4850
);
4951
});

canvas/components/custom/path-filed-with-actions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import clsx from "clsx";
55
import { WithTooltip } from "./with-tooltip";
66

77
export type TPathFiledWithActionsProps = {
8-
pathRecord: TPathRecord;
8+
pathRecord: Omit<TPathRecord, "raw">;
99
/**
1010
* If true then use span to show the path value,
1111
* else use input component.

canvas/services/job.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,23 @@ export type TUpdateJobOptions = {
9393
job: TJob;
9494
};
9595

96-
export function updateJob(options: TUpdateJobOptions) {
96+
export function updateJob(options: TUpdateJobOptions): Promise<TJob> {
9797
const { job } = options;
9898

9999
return api.put({ ...job }, "/job").json();
100100
}
101+
102+
export type TUseUpdateJobOptions = {
103+
onError: (error?: unknown) => void;
104+
onSuccess: (data: TJob) => void;
105+
};
106+
107+
export function useUpdateJob(options: TUseUpdateJobOptions) {
108+
const { onError, onSuccess } = options;
109+
110+
return useMutation({
111+
mutationFn: updateJob,
112+
onError,
113+
onSuccess,
114+
});
115+
}

0 commit comments

Comments
 (0)