Skip to content

Commit 5fb7d1f

Browse files
authored
Display success message on list view after creating a job (#297)
1 parent 44ba6c6 commit 5fb7d1f

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

src/components/job-row.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ type DownloadFilesButtonProps = {
7474

7575
function DownloadFilesButton(props: DownloadFilesButtonProps) {
7676
const [downloading, setDownloading] = useState(false);
77+
const trans = useTranslator('jupyterlab');
7778

7879
return (
7980
<IconButton
8081
aria-label="download"
81-
title="Download Job Files"
82+
title={trans.__('Download Job Files')}
8283
disabled={downloading}
8384
onClick={async () => {
8485
setDownloading(true);
@@ -109,11 +110,12 @@ export function buildJobRow(
109110
const inputFile = job.job_files.find(
110111
jobFile => jobFile.file_format === 'input' && jobFile.file_path
111112
);
113+
const trans = useTranslator('jupyterlab');
112114

113115
const cellContents: React.ReactNode[] = [
114116
<Link
115117
onClick={() => showDetailView(job.job_id)}
116-
title={`Open detail view for "${job.name}"`}
118+
title={trans.__('Open detail view for "%1"', job.name)}
117119
>
118120
{job.name}
119121
</Link>,

src/mainviews/create-job.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ export interface ICreateJobProps {
4646
model: ICreateJobModel;
4747
handleModelChange: (model: ICreateJobModel) => void;
4848
showListView: (
49-
list: JobsView.ListJobs | JobsView.ListJobDefinitions
49+
list: JobsView.ListJobs | JobsView.ListJobDefinitions,
50+
newlyCreatedId?: string,
51+
newlyCreatedName?: string
5052
) => unknown;
5153
// Extension point: optional additional component
5254
advancedOptions: React.FunctionComponent<SchedulerTokens.IAdvancedOptionsProps>;
@@ -334,7 +336,7 @@ export function CreateJob(props: ICreateJobProps): JSX.Element {
334336
.createJob(jobOptions)
335337
.then(response => {
336338
// Switch to the list view with "Job List" active
337-
props.showListView(JobsView.ListJobs);
339+
props.showListView(JobsView.ListJobs, response.job_id, jobOptions.name);
338340
})
339341
.catch((error: Error) => {
340342
props.handleModelChange({
@@ -381,7 +383,11 @@ export function CreateJob(props: ICreateJobProps): JSX.Element {
381383
.createJobDefinition(jobDefinitionOptions)
382384
.then(response => {
383385
// Switch to the list view with "Job Definition List" active
384-
props.showListView(JobsView.ListJobDefinitions);
386+
props.showListView(
387+
JobsView.ListJobDefinitions,
388+
response.job_definition_id,
389+
jobDefinitionOptions.name
390+
);
385391
})
386392
.catch((error: Error) => {
387393
props.handleModelChange({

src/mainviews/list-jobs.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function ListJobsTable(props: IListJobsTableProps): JSX.Element {
3939
const [deletedRows, setDeletedRows] = useState<
4040
Set<Scheduler.IDescribeJob['job_id']>
4141
>(new Set());
42+
4243
const trans = useTranslator('jupyterlab');
4344

4445
// Cache environment list — we need this for the output formats.
@@ -185,7 +186,10 @@ function ListJobDefinitionsTable(props: ListJobDefinitionsTableProps) {
185186
const [deletedRows, setDeletedRows] = useState<
186187
Set<Scheduler.IDescribeJobDefinition['job_definition_id']>
187188
>(new Set());
188-
const [displayError, setDisplayError] = useState<string | null>(null);
189+
190+
const [displayError, setDisplayError] = useState<React.ReactNode | null>(
191+
null
192+
);
189193

190194
const api = useMemo(() => new SchedulerService({}), []);
191195

@@ -264,7 +268,11 @@ function ListJobDefinitionsTable(props: ListJobDefinitionsTableProps) {
264268

265269
return (
266270
<>
267-
{displayError && <Alert severity="error">{displayError}</Alert>}
271+
{displayError && (
272+
<Alert severity="error" onClose={() => setDisplayError(null)}>
273+
{displayError}
274+
</Alert>
275+
)}
268276
{reloadButton}
269277
<AdvancedTable
270278
query={jobDefsQuery}
@@ -289,6 +297,8 @@ export interface IListJobsProps {
289297
showCreateJob: (newModel: ICreateJobModel) => void;
290298
showJobDetail: (jobId: string) => void;
291299
showJobDefinitionDetail: (jobDefId: string) => void;
300+
newlyCreatedId?: string;
301+
newlyCreatedName?: string;
292302
}
293303

294304
export function NotebookJobsList(props: IListJobsProps): JSX.Element {
@@ -300,6 +310,26 @@ export function NotebookJobsList(props: IListJobsProps): JSX.Element {
300310
[trans]
301311
);
302312

313+
// Display creation message
314+
const successMessage =
315+
props.newlyCreatedId !== undefined && props.newlyCreatedName !== undefined
316+
? props.listView === JobsView.ListJobs
317+
? trans.__(
318+
'Your job "%1" has been created. ' +
319+
'If you do not see it in the list below, please reload the list in a few seconds.',
320+
props.newlyCreatedName
321+
)
322+
: trans.__(
323+
'Your job definition "%1" has been created. ' +
324+
'If you do not see it in the list below, please reload the list in a few seconds.',
325+
props.newlyCreatedName
326+
)
327+
: null;
328+
329+
const [displayInfo, setDisplayInfo] = useState<React.ReactNode | null>(
330+
successMessage
331+
);
332+
303333
// Retrieve the initial jobs list
304334
return (
305335
<Box sx={{ p: 4 }} style={{ height: '100%', boxSizing: 'border-box' }}>
@@ -317,6 +347,11 @@ export function NotebookJobsList(props: IListJobsProps): JSX.Element {
317347
value={JobsView.ListJobDefinitions}
318348
/>
319349
</Tabs>
350+
{displayInfo && (
351+
<Alert severity="info" onClose={() => setDisplayInfo(null)}>
352+
{displayInfo}
353+
</Alert>
354+
)}
320355
{props.listView === JobsView.ListJobs && (
321356
<>
322357
<Heading level={1}>{jobsHeader}</Heading>

src/notebook-jobs-panel.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export class NotebookJobsPanel extends VDomRenderer<JobsModel> {
3434
readonly _translator: ITranslator;
3535
readonly _trans: TranslationBundle;
3636
readonly _advancedOptions: React.FunctionComponent<Scheduler.IAdvancedOptionsProps>;
37+
private _newlyCreatedId: string | undefined;
38+
private _newlyCreatedName: string | undefined;
3739

3840
constructor(options: NotebookJobsPanel.IOptions) {
3941
super(
@@ -62,7 +64,13 @@ export class NotebookJobsPanel extends VDomRenderer<JobsModel> {
6264
this.node.setAttribute('aria-label', trans.__('Notebook Jobs'));
6365
}
6466

65-
showListView(view: JobsView.ListJobs | JobsView.ListJobDefinitions): void {
67+
showListView(
68+
view: JobsView.ListJobs | JobsView.ListJobDefinitions,
69+
newlyCreatedId?: string,
70+
newlyCreatedName?: string
71+
): void {
72+
this._newlyCreatedId = newlyCreatedId;
73+
this._newlyCreatedName = newlyCreatedName;
6674
this.model.jobsView = view;
6775
}
6876

@@ -139,6 +147,8 @@ export class NotebookJobsPanel extends VDomRenderer<JobsModel> {
139147
showJobDefinitionDetail={this.showJobDefinitionDetail.bind(
140148
this
141149
)}
150+
newlyCreatedId={this._newlyCreatedId}
151+
newlyCreatedName={this._newlyCreatedName}
142152
/>
143153
)}
144154
{(this.model.jobsView === JobsView.JobDetail ||

0 commit comments

Comments
 (0)