Moved the job action bar to a shred component
This commit is contained in:
@@ -9,6 +9,8 @@ import { startJob, stopJob } from '@/utils/jobs';
|
|||||||
import SampleImages from '@/components/SampleImages';
|
import SampleImages from '@/components/SampleImages';
|
||||||
import JobOverview from '@/components/JobOverview';
|
import JobOverview from '@/components/JobOverview';
|
||||||
import { JobConfig } from '@/types';
|
import { JobConfig } from '@/types';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
import JobActionBar from '@/components/JobActionBar';
|
||||||
|
|
||||||
type PageKey = 'overview' | 'samples';
|
type PageKey = 'overview' | 'samples';
|
||||||
|
|
||||||
@@ -28,21 +30,12 @@ export default function JobPage({ params }: { params: { jobID: string } }) {
|
|||||||
const { job, status, refreshJob } = useJob(jobID, 5000);
|
const { job, status, refreshJob } = useJob(jobID, 5000);
|
||||||
const [pageKey, setPageKey] = useState<PageKey>('overview');
|
const [pageKey, setPageKey] = useState<PageKey>('overview');
|
||||||
|
|
||||||
const numSamples = useMemo(() => {
|
|
||||||
if (job?.job_config) {
|
|
||||||
const jobConfig = JSON.parse(job.job_config) as JobConfig;
|
|
||||||
const sampleConfig = jobConfig.config.process[0].sample;
|
|
||||||
return sampleConfig.prompts.length;
|
|
||||||
}
|
|
||||||
return 10;
|
|
||||||
}, [job]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Fixed top bar */}
|
{/* Fixed top bar */}
|
||||||
<TopBar>
|
<TopBar>
|
||||||
<div>
|
<div>
|
||||||
<Button className="text-gray-500 dark:text-gray-300 px-3 mt-1" onClick={() => history.back()}>
|
<Button className="text-gray-500 dark:text-gray-300 px-3 mt-1" onClick={() => redirect('/jobs')}>
|
||||||
<FaChevronLeft />
|
<FaChevronLeft />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,27 +43,15 @@ export default function JobPage({ params }: { params: { jobID: string } }) {
|
|||||||
<h1 className="text-lg">Job: {job?.name}</h1>
|
<h1 className="text-lg">Job: {job?.name}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1"></div>
|
<div className="flex-1"></div>
|
||||||
{job?.status === 'running' && (
|
{job && (
|
||||||
<Button
|
<JobActionBar
|
||||||
onClick={async () => {
|
job={job}
|
||||||
await stopJob(jobID);
|
onRefresh={refreshJob}
|
||||||
refreshJob();
|
hideView
|
||||||
|
afterDelete={() => {
|
||||||
|
redirect('/jobs');
|
||||||
}}
|
}}
|
||||||
className="bg-red-500 text-white px-4 py-1 rounded-sm"
|
/>
|
||||||
>
|
|
||||||
Stop
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{(job?.status === 'stopped' || job?.status === 'error') && (
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
await startJob(jobID);
|
|
||||||
refreshJob();
|
|
||||||
}}
|
|
||||||
className="bg-green-800 text-white px-4 py-1 rounded-sm"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<MainContent className="pt-24">
|
<MainContent className="pt-24">
|
||||||
|
|||||||
87
ui/src/components/JobActionBar.tsx
Normal file
87
ui/src/components/JobActionBar.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { Eye, Trash2, Pen, Play, Pause } from 'lucide-react';
|
||||||
|
import { Button } from '@headlessui/react';
|
||||||
|
import { openConfirm } from '@/components/ConfirmModal';
|
||||||
|
import { Job } from '@prisma/client';
|
||||||
|
import { startJob, stopJob, deleteJob, getAvaliableJobActions } from '@/utils/jobs';
|
||||||
|
|
||||||
|
interface JobActionBarProps {
|
||||||
|
job: Job;
|
||||||
|
onRefresh?: () => void;
|
||||||
|
afterDelete?: () => void;
|
||||||
|
hideView?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function JobActionBar({ job, onRefresh, afterDelete, className, hideView }: JobActionBarProps) {
|
||||||
|
const { canStart, canStop, canDelete, canEdit } = getAvaliableJobActions(job);
|
||||||
|
|
||||||
|
if (!afterDelete) afterDelete = onRefresh;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${className}`}>
|
||||||
|
{canStart && (
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
if (!canStart) return;
|
||||||
|
await startJob(job.id);
|
||||||
|
if (onRefresh) onRefresh();
|
||||||
|
}}
|
||||||
|
className={`ml-2 opacity-100`}
|
||||||
|
>
|
||||||
|
<Play />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{canStop && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!canStop) return;
|
||||||
|
openConfirm({
|
||||||
|
title: 'Stop Job',
|
||||||
|
message: `Are you sure you want to stop the job "${job.name}"? You CAN resume later.`,
|
||||||
|
type: 'info',
|
||||||
|
confirmText: 'Stop',
|
||||||
|
onConfirm: async () => {
|
||||||
|
await stopJob(job.id);
|
||||||
|
if (onRefresh) onRefresh();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className={`ml-2 opacity-100`}
|
||||||
|
>
|
||||||
|
<Pause />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!hideView && (
|
||||||
|
<Link href={`/jobs/${job.id}`} className="ml-2 text-gray-200 hover:text-gray-100 inline-block">
|
||||||
|
<Eye />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{canEdit && (
|
||||||
|
<Link href={`/jobs/new?id=${job.id}`} className="ml-2 hover:text-gray-100 inline-block">
|
||||||
|
<Pen />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{canDelete && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!canDelete) return;
|
||||||
|
openConfirm({
|
||||||
|
title: 'Delete Job',
|
||||||
|
message: `Are you sure you want to delete the job "${job.name}"? This will also permanently remove it from your disk.`,
|
||||||
|
type: 'warning',
|
||||||
|
confirmText: 'Delete',
|
||||||
|
onConfirm: async () => {
|
||||||
|
await deleteJob(job.id);
|
||||||
|
if (afterDelete) afterDelete();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className={`ml-2 opacity-100`}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Eye, Trash2, Pen, Play, Pause } from 'lucide-react';
|
|||||||
import { Button } from '@headlessui/react';
|
import { Button } from '@headlessui/react';
|
||||||
import { openConfirm } from '@/components/ConfirmModal';
|
import { openConfirm } from '@/components/ConfirmModal';
|
||||||
import { startJob, stopJob, deleteJob, getAvaliableJobActions } from '@/utils/jobs';
|
import { startJob, stopJob, deleteJob, getAvaliableJobActions } from '@/utils/jobs';
|
||||||
|
import JobActionBar from './JobActionBar';
|
||||||
|
|
||||||
interface JobsTableProps {}
|
interface JobsTableProps {}
|
||||||
|
|
||||||
@@ -71,71 +72,7 @@ export default function JobsTable(props: JobsTableProps) {
|
|||||||
key: 'actions',
|
key: 'actions',
|
||||||
className: 'text-right',
|
className: 'text-right',
|
||||||
render: row => {
|
render: row => {
|
||||||
const { canDelete, canEdit, canStop, canStart } = getAvaliableJobActions(row);
|
return <JobActionBar job={row} onRefresh={refreshJobs} />;
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{canStart && (
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
if (!canStart) return;
|
|
||||||
await startJob(row.id);
|
|
||||||
refreshJobs();
|
|
||||||
}}
|
|
||||||
className={`ml-2 opacity-100`}
|
|
||||||
>
|
|
||||||
<Play />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{canStop && (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (!canStop) return;
|
|
||||||
openConfirm({
|
|
||||||
title: 'Stop Job',
|
|
||||||
message: `Are you sure you want to stop the job "${row.name}"? You CAN resume later.`,
|
|
||||||
type: 'info',
|
|
||||||
confirmText: 'Stop',
|
|
||||||
onConfirm: async () => {
|
|
||||||
await stopJob(row.id);
|
|
||||||
refreshJobs();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className={`ml-2 opacity-100`}
|
|
||||||
>
|
|
||||||
<Pause />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Link href={`/jobs/${row.id}`} className="ml-2 text-gray-200 hover:text-gray-100 inline-block">
|
|
||||||
<Eye />
|
|
||||||
</Link>
|
|
||||||
{canEdit && (
|
|
||||||
<Link href={`/jobs/new?id=${row.id}`} className="ml-2 hover:text-gray-100 inline-block">
|
|
||||||
<Pen />
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{canDelete && (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (!canDelete) return;
|
|
||||||
openConfirm({
|
|
||||||
title: 'Delete Job',
|
|
||||||
message: `Are you sure you want to delete the job "${row.name}"? This will also permanently remove it from your disk.`,
|
|
||||||
type: 'warning',
|
|
||||||
confirmText: 'Delete',
|
|
||||||
onConfirm: async () => {
|
|
||||||
await deleteJob(row.id);
|
|
||||||
refreshJobs();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className={`ml-2 opacity-100`}
|
|
||||||
>
|
|
||||||
<Trash2 />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -63,3 +63,13 @@ export const getAvaliableJobActions = (job: Job) => {
|
|||||||
}
|
}
|
||||||
return { canDelete, canEdit, canStop, canStart };
|
return { canDelete, canEdit, canStop, canStart };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getNumberOfSamples = (job: Job) => {
|
||||||
|
const jobConfig = getJobConfig(job);
|
||||||
|
return jobConfig.config.process[0].sample?.prompts?.length || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTotalSteps = (job: Job) => {
|
||||||
|
const jobConfig = getJobConfig(job);
|
||||||
|
return jobConfig.config.process[0].train.steps;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user