Skip to content

Commit

Permalink
NEOS-316: tesing new layout for job flow (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
evisdrenova authored Nov 15, 2023
1 parent 2140133 commit 2c982cd
Show file tree
Hide file tree
Showing 42 changed files with 1,263 additions and 1,141 deletions.
2 changes: 1 addition & 1 deletion frontend/app/connections/[id]/components/MysqlForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export default function MysqlForm(props: Props) {
/>

<TestConnectionResult resp={checkResp} />
<div className="flex flex-row gap-3 justify-end">
<div className="flex flex-row gap-3 justify-between">
<Button
onClick={async () => {
try {
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/connections/[id]/components/PostgresForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export default function PostgresForm(props: Props) {
)}
/>
<TestConnectionResult resp={checkResp} />
<div className="flex flex-row gap-3 justify-end">
<div className="flex flex-row gap-3 justify-between">
<Button
onClick={async () => {
try {
Expand Down
7 changes: 5 additions & 2 deletions frontend/app/connections/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function ConnectionPage({ params }: PageProps) {
}
if (isLoading) {
return (
<div className="mt-10">
<div className="mt-10 mx-24">
<SkeletonForm />
</div>
);
Expand Down Expand Up @@ -49,7 +49,10 @@ export default function ConnectionPage({ params }: PageProps) {
),
});
return (
<OverviewContainer Header={connectionComponent.header}>
<OverviewContainer
Header={connectionComponent.header}
containerClassName="mx-64"
>
<div className="connection-details-container">
<div>
<div className="flex flex-col">
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/connections/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function Connections(): ReactElement {
extraHeading={<NewConnectionButton />}
/>
}
containerClassName="connections-page"
containerClassName="connections-page mx-24"
>
<ConnectionTable />
</OverviewContainer>
Expand Down
110 changes: 110 additions & 0 deletions frontend/app/jobs/[id]/components/JobPauseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client';
import ButtonText from '@/components/ButtonText';
import Spinner from '@/components/Spinner';
import { Button } from '@/components/ui/button';
import { useToast } from '@/components/ui/use-toast';
import {
JobStatus,
PauseJobRequest,
PauseJobResponse,
} from '@/neosync-api-client/mgmt/v1alpha1/job_pb';
import { getErrorMessage } from '@/util/util';
import { PauseIcon, PlayIcon } from '@radix-ui/react-icons';
import { ReactElement, useEffect, useState } from 'react';

interface Props {
jobId: string;
status?: JobStatus;
mutate: () => void;
}

export default function JobPauseButton({
status,
mutate,
jobId,
}: Props): ReactElement {
const { toast } = useToast();
const [buttonText, setButtonText] = useState(
status === JobStatus.PAUSED ? 'Resume Job' : 'Pause Job'
);

const [buttonIcon, setButtonIcon] = useState<JSX.Element>(
status === JobStatus.PAUSED ? <PlayIcon /> : <PauseIcon />
);
const [isTrying, setIsTrying] = useState<boolean>(false);

useEffect(() => {
setButtonText(status === JobStatus.PAUSED ? 'Resume Job' : 'Pause Job');
setButtonIcon(status === JobStatus.PAUSED ? <PlayIcon /> : <PauseIcon />);
}, [status]);

async function updateJobStatus(isPaused: boolean) {
setIsTrying(true);
try {
await pauseJob(jobId, isPaused);
toast({
title: `Successfully ${isPaused ? 'paused' : 'unpaused'} job!`,
variant: 'default',
});
mutate();
setIsTrying(false);
setButtonText((val) => (val == 'Pause Job' ? 'Resume Job' : 'Pause Job'));
setButtonIcon(handleIcon());
} catch (err) {
console.error(err);
toast({
title: 'Unable to pause',
description: getErrorMessage(err),
variant: 'destructive',
});
setIsTrying(false);
}
}

const handleIcon = () => {
if (isTrying) {
return <Spinner />;
} else if (!isTrying && buttonText == 'Resume Job') {
return <PlayIcon />;
} else {
return <PauseIcon />;
}
};

return (
<div className="max-w-[300px]">
<Button
variant="outline"
onClick={async () => {
const isCurrentlyPaused = status === JobStatus.PAUSED;
updateJobStatus(!isCurrentlyPaused);
}}
>
<ButtonText leftIcon={buttonIcon} text={buttonText} />
</Button>
</div>
);
}

async function pauseJob(
jobId: string,
isPaused: boolean
): Promise<PauseJobResponse> {
const res = await fetch(`/api/jobs/${jobId}/pause`, {
method: 'PUT',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(
new PauseJobRequest({
id: jobId,
pause: isPaused,
})
),
});
if (!res.ok) {
const body = await res.json();
throw new Error(body.message);
}
return PauseJobResponse.fromJson(await res.json());
}
2 changes: 1 addition & 1 deletion frontend/app/jobs/[id]/components/NextRuns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function JobNextRuns({ jobId, status }: Props): ReactElement {
}

return (
<Card className="p-2">
<Card>
{!data?.nextRuns || error ? (
<Alert variant="destructive">
<AlertTitle>{`Error: Unable to retrieve recent runs`}</AlertTitle>
Expand Down
78 changes: 0 additions & 78 deletions frontend/app/jobs/[id]/components/PauseSwitch.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/app/jobs/[id]/components/ScheduleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default function JobScheduleCard({ job, mutate }: Props): ReactElement {
: '';

return (
<Card>
<Card className="overflow-hidden">
<CardHeader>
<CardTitle>Schedule</CardTitle>
</CardHeader>
Expand Down
83 changes: 50 additions & 33 deletions frontend/app/jobs/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import { Alert, AlertTitle } from '@/components/ui/alert';
import { Button, buttonVariants } from '@/components/ui/button';
import { toast } from '@/components/ui/use-toast';
import { useGetJob } from '@/libs/hooks/useGetJob';
import { useGetJobStatus } from '@/libs/hooks/useGetJobStatus';
import { cn } from '@/libs/utils';
import { getErrorMessage } from '@/util/util';
import { TrashIcon } from '@radix-ui/react-icons';
import { LightningBoltIcon, TrashIcon } from '@radix-ui/react-icons';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import JobPauseButton from './components/JobPauseButton';

export default function JobIdLayout({ children, params }: LayoutProps) {
const id = params?.id ?? '';
const basePath = `/jobs/${params?.id}`;
const { data, isLoading } = useGetJob(id);
const { data, isLoading, mutate } = useGetJob(id);
const router = useRouter();
const { data: jobStatus } = useGetJobStatus(id);

async function onTriggerJobRun(): Promise<void> {
try {
Expand Down Expand Up @@ -96,38 +99,52 @@ export default function JobIdLayout({ children, params }: LayoutProps) {
}

return (
<OverviewContainer
Header={
<div>
<h2 className="text-2xl font-bold tracking-tight">Job Overview</h2>
<PageHeader
pageHeaderContainerClassName="gap-4"
header={data?.job?.name || ''}
description={data?.job?.id || ''}
extraHeading={
<div className="flex flex-row space-x-4">
<DeleteConfirmationDialog
trigger={
<Button variant="destructive">
<ButtonText leftIcon={<TrashIcon />} text="Delete Job" />
</Button>
}
headerText="Are you sure you want to delete this job?"
description="Deleting this job will also delete all job runs."
onConfirm={async () => onDelete()}
/>
<Button onClick={() => onTriggerJobRun()}>Trigger Run</Button>
</div>
}
/>
<div className="mx-24">
<OverviewContainer
Header={
<div>
<PageHeader
pageHeaderContainerClassName="gap-2"
header={data?.job?.name || ''}
description={data?.job?.id || ''}
extraHeading={
<div className="flex flex-row space-x-4">
<DeleteConfirmationDialog
trigger={
<Button variant="destructive">
<ButtonText
leftIcon={<TrashIcon />}
text="Delete Job"
/>
</Button>
}
headerText="Are you sure you want to delete this job?"
description="Deleting this job will also delete all job runs."
onConfirm={async () => onDelete()}
/>
<JobPauseButton
jobId={id}
status={jobStatus?.status}
mutate={mutate}
/>
<Button onClick={() => onTriggerJobRun()}>
<ButtonText
leftIcon={<LightningBoltIcon />}
text="Trigger Run"
/>
</Button>
</div>
}
/>
</div>
}
>
<div className="flex flex-col gap-6">
<SubNav items={sidebarNavItems} />
<div className="mt-10">{children}</div>
</div>
}
>
<div className="flex flex-col gap-6">
<SubNav items={sidebarNavItems} />
<div>{children}</div>
</div>
</OverviewContainer>
</OverviewContainer>
</div>
);
}

Expand Down
2 changes: 0 additions & 2 deletions frontend/app/jobs/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Alert, AlertTitle } from '@/components/ui/alert';
import { useGetJobStatus } from '@/libs/hooks/useGetJobStatus';
import { ReactElement } from 'react';
import JobNextRuns from './components/NextRuns';
import JobPauseSwitch from './components/PauseSwitch';
import JobRecentRuns from './components/RecentRuns';
import JobScheduleCard from './components/ScheduleCard';

Expand Down Expand Up @@ -37,7 +36,6 @@ export default function Page({ params }: PageProps): ReactElement {
return (
<div className="job-details-container">
<div className="flex flex-col gap-5">
<JobPauseSwitch jobId={id} status={jobStatus?.status} mutate={mutate} />
<JobScheduleCard job={data?.job} mutate={mutate} />
<JobRecentRuns jobId={id} />
<div className="flex">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export default function SourceConnectionCard({ jobId }: Props): ReactElement {
render={({ field }) => (
<FormItem>
<FormLabel>Source</FormLabel>
<FormDescription>
The location of the source data set.
</FormDescription>
<FormControl>
<Select
value={field.value}
Expand Down Expand Up @@ -200,9 +203,7 @@ export default function SourceConnectionCard({ jobId }: Props): ReactElement {
</SelectContent>
</Select>
</FormControl>
<FormDescription>
The location of the source data set.
</FormDescription>

<FormMessage />
</FormItem>
)}
Expand Down
Loading

0 comments on commit 2c982cd

Please sign in to comment.