Skip to content

Commit f9334fd

Browse files
authored
feat(sagemakerunifiedstudio): Add job definition detail page and edit page (aws#2181)
## Problem - Need job definition detail page - Need job definition edit page ## Solution - Create new job definition detail page - Create new job definition edit page - Create common breadcrumb component for page navigation #### Job definition detail page <img width="1356" height="1310" alt="Screenshot 2025-07-29 at 12 27 42 PM" src="https://github.com/user-attachments/assets/69b85254-d318-42cb-8326-b3a0b65b44d9" /> #### Job definition edit page <img width="1356" height="1310" alt="Screenshot 2025-07-29 at 12 27 53 PM" src="https://github.com/user-attachments/assets/7cbc9229-bf0b-44de-804d-16f27427a857" /> --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 081c9b3 commit f9334fd

File tree

13 files changed

+849
-184
lines changed

13 files changed

+849
-184
lines changed

packages/core/src/sagemakerunifiedstudio/notebookScheduling/utils/constants.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,40 @@
55

66
export interface Page {
77
name: string
8-
metadata: CreateJobPageMetadata | ViewJobsPageMetadata | JobDetailPageMetadata
8+
metadata:
9+
| CreateJobPageMetadata
10+
| ViewJobsPageMetadata
11+
| JobDetailPageMetadata
12+
| JobDefinitionDetailPageMetadata
13+
| EditJobDefinitionPageMetadata
914
}
1015

1116
export interface CreateJobPageMetadata {}
1217

1318
export interface ViewJobsPageMetadata {
1419
newJob?: string
1520
newJobDefinition?: string
21+
showJobDefinitions?: boolean
1622
}
1723

1824
export interface JobDetailPageMetadata {
1925
jobId: string
2026
}
2127

28+
export interface JobDefinitionDetailPageMetadata {
29+
jobDefinitionId: string
30+
}
31+
32+
export interface EditJobDefinitionPageMetadata {
33+
jobDefinitionId: string
34+
}
35+
2236
export const createJobPage: string = 'createJob'
2337

2438
export const viewJobsPage: string = 'viewJobs'
2539

2640
export const jobDetailPage: string = 'jobDetailPage'
41+
42+
export const jobDefinitionDetailPage: string = 'jobDefinitionDetailPage'
43+
44+
export const editJobDefinitionPage: string = 'editJobDefinitionPage'

packages/core/src/sagemakerunifiedstudio/notebookScheduling/vue/app.vue

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@ import TkFixedLayout from '../../shared/ux/tkFixedLayout.vue'
99
import CreateJobPage from './views/createJobPage.vue'
1010
import ViewJobsPage from './views/viewJobsPage.vue'
1111
import JobDetailPage from './views/jobDetailPage.vue'
12+
import JobDefinitionDetailPage from './views/jobDefinitionDetailPage.vue'
13+
import EditJobDefinitionPage from './views/editJobDefinitionPage.vue'
1214
import { client } from './composables/useClient'
13-
import { createJobPage, viewJobsPage, jobDetailPage, Page } from '../utils/constants'
15+
import {
16+
createJobPage,
17+
viewJobsPage,
18+
jobDetailPage,
19+
jobDefinitionDetailPage,
20+
editJobDefinitionPage,
21+
Page,
22+
} from '../utils/constants'
1423
1524
import '../../shared/ux/styles.css'
1625
@@ -37,7 +46,7 @@ onBeforeMount(async () => {
3746
</script>
3847

3948
<template>
40-
<tk-fixed-layout v-if="state.page?.name === createJobPage" :width="628">
49+
<tk-fixed-layout v-if="state.page?.name === createJobPage" :width="628" :max-width="700" :center="false">
4150
<create-job-page />
4251
</tk-fixed-layout>
4352

@@ -49,5 +58,23 @@ onBeforeMount(async () => {
4958
<job-detail-page />
5059
</tk-fixed-layout>
5160

61+
<tk-fixed-layout
62+
v-else-if="state.page?.name === jobDefinitionDetailPage"
63+
:width="800"
64+
:max-width="900"
65+
:center="false"
66+
>
67+
<job-definition-detail-page />
68+
</tk-fixed-layout>
69+
70+
<tk-fixed-layout
71+
v-else-if="state.page?.name === editJobDefinitionPage"
72+
:width="628"
73+
:max-width="700"
74+
:center="false"
75+
>
76+
<edit-job-definition-page />
77+
</tk-fixed-layout>
78+
5279
<div v-else>Loading...</div>
5380
</template>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script setup lang="ts">
2+
/*!
3+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import TkSpaceBetween from '../../../shared/ux/tkSpaceBetween.vue'
8+
import { client } from '../composables/useClient'
9+
import { ViewJobsPageMetadata, JobDefinitionDetailPageMetadata } from '../../utils/constants'
10+
11+
//-------------------------------------------------------------------------------------------------
12+
// Props
13+
//-------------------------------------------------------------------------------------------------
14+
export interface BreadcrumbItem {
15+
text: string
16+
page?: string
17+
metadata?: ViewJobsPageMetadata | JobDefinitionDetailPageMetadata
18+
}
19+
20+
interface Props {
21+
items: BreadcrumbItem[]
22+
}
23+
24+
const props = withDefaults(defineProps<Props>(), {})
25+
26+
//-------------------------------------------------------------------------------------------------
27+
// Variables & Methods
28+
//-------------------------------------------------------------------------------------------------
29+
async function onNavigate(item: BreadcrumbItem) {
30+
if (item.page && item.metadata) {
31+
await client.setCurrentPage({ name: item.page, metadata: item.metadata })
32+
}
33+
}
34+
</script>
35+
36+
<template>
37+
<tk-space-between class="breadcrumb" direction="horizontal" size="xs">
38+
<template v-for="(item, index) in props.items" :key="index">
39+
<span v-if="index < props.items.length - 1"
40+
><a @click="onNavigate(item)">{{ item.text }}</a></span
41+
>
42+
<span v-else>{{ item.text }}</span>
43+
44+
<span v-if="index < props.items.length - 1">/</span>
45+
</template>
46+
</tk-space-between>
47+
</template>
48+
49+
<style scoped>
50+
.breadcrumb a {
51+
cursor: pointer;
52+
}
53+
</style>

packages/core/src/sagemakerunifiedstudio/notebookScheduling/vue/components/jobsDefinitions.vue

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66
7-
import { reactive, computed, onBeforeMount } from 'vue'
7+
import { reactive, computed, watch } from 'vue'
88
import TkSpaceBetween from '../../../shared/ux/tkSpaceBetween.vue'
99
import TkBox from '../../../shared/ux/tkBox.vue'
1010
import TkTable from '../../../shared/ux/tkTable.vue'
@@ -13,9 +13,10 @@ import TkBanner from '../../../shared/ux/tkBanner.vue'
1313
import PlayIcon from '../../../shared/ux/icons/playIcon.vue'
1414
import PauseIcon from '../../../shared/ux/icons/pauseIcon.vue'
1515
import CloseIcon from '../../../shared/ux/icons/closeIcon.vue'
16-
import { jobDefinitions } from '../composables/useJobs'
16+
import { jobDefinitions, JobDefinition } from '../composables/useJobs'
17+
import { newJobDefinition } from '../composables/useViewJobs'
1718
import { client } from '../composables/useClient'
18-
import { ViewJobsPageMetadata } from '../../utils/constants'
19+
import { jobDefinitionDetailPage, JobDefinitionDetailPageMetadata } from '../../utils/constants'
1920
2021
//-------------------------------------------------------------------------------------------------
2122
// State
@@ -59,14 +60,12 @@ const bannerMessage = computed(() => {
5960
})
6061
6162
//-------------------------------------------------------------------------------------------------
62-
// Lifecycle Hooks
63+
// Watchers
6364
//-------------------------------------------------------------------------------------------------
64-
onBeforeMount(async () => {
65-
const page = await client.getCurrentPage()
66-
const metadata = page.metadata as ViewJobsPageMetadata
67-
68-
if (metadata.newJobDefinition) {
69-
state.newJobDefinition = metadata.newJobDefinition
65+
watch(newJobDefinition, (newVal, _oldVal) => {
66+
if (newVal) {
67+
state.newJobDefinition = newVal
68+
newJobDefinition.value = undefined
7069
}
7170
})
7271
@@ -76,6 +75,14 @@ onBeforeMount(async () => {
7675
const itemsPerTablePage = 10
7776
const tableColumns = ['Job definition name', 'Input filename', 'Created at', 'Schedule', 'Status', 'Actions']
7877
78+
async function onJobDefinition(jobDefinition: JobDefinition): Promise<void> {
79+
const metadata: JobDefinitionDetailPageMetadata = {
80+
jobDefinitionId: jobDefinition.id,
81+
}
82+
83+
await client.setCurrentPage({ name: jobDefinitionDetailPage, metadata })
84+
}
85+
7986
function onPagination(page: number) {
8087
state.paginatedPage = page
8188
}
@@ -150,7 +157,7 @@ function resetJobDefinitionToDelete(): void {
150157
<template v-slot:body>
151158
<tr v-for="(jobDefinition, index) in jobsDefinitionsPerPage" :key="index">
152159
<td>
153-
<a>
160+
<a class="anchor-link" @click="onJobDefinition(jobDefinition)">
154161
{{ jobDefinition.name }}
155162
</a>
156163
</td>
@@ -189,7 +196,8 @@ function resetJobDefinitionToDelete(): void {
189196
</template>
190197

191198
<style scoped>
192-
.jobs-definitions {
199+
.jobs-definitions .anchor-link {
200+
cursor: pointer;
193201
}
194202
195203
.jobs-definitions .delete-confirm {

packages/core/src/sagemakerunifiedstudio/notebookScheduling/vue/components/jobsList.vue

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66
7-
import { computed, reactive, onBeforeMount } from 'vue'
7+
import { computed, reactive, watch } from 'vue'
88
import TkSpaceBetween from '../../../shared/ux/tkSpaceBetween.vue'
99
import TkBox from '../../../shared/ux/tkBox.vue'
1010
import TkBanner from '../../../shared/ux/tkBanner.vue'
@@ -14,7 +14,21 @@ import DownloadIcon from '../../../shared/ux/icons/downloadIcon.vue'
1414
import CloseIcon from '../../../shared/ux/icons/closeIcon.vue'
1515
import { jobs, Job } from '../composables/useJobs'
1616
import { client } from '../composables/useClient'
17-
import { jobDetailPage, JobDetailPageMetadata, ViewJobsPageMetadata } from '../../utils/constants'
17+
import { newJob } from '../composables/useViewJobs'
18+
import { jobDetailPage, JobDetailPageMetadata } from '../../utils/constants'
19+
20+
//-------------------------------------------------------------------------------------------------
21+
// Props
22+
//-------------------------------------------------------------------------------------------------
23+
interface Props {
24+
jobDefinitionId?: string
25+
hideHeading?: boolean
26+
}
27+
28+
const props = withDefaults(defineProps<Props>(), {
29+
jobDefinitionId: undefined,
30+
hideHeading: false,
31+
})
1832
1933
//-------------------------------------------------------------------------------------------------
2034
// State
@@ -34,18 +48,27 @@ const state: State = reactive({
3448
//-------------------------------------------------------------------------------------------------
3549
// Computed Properties
3650
//-------------------------------------------------------------------------------------------------
51+
const allowedJobs = computed(() => {
52+
if (props.jobDefinitionId) {
53+
const jobsForDefinition = jobs.value.filter((job) => job.jobDefinitionId === props.jobDefinitionId)
54+
return jobsForDefinition
55+
} else {
56+
return [...jobs.value]
57+
}
58+
})
59+
3760
const jobsPerPage = computed(() => {
3861
const items = []
3962
4063
const startIndex = state.paginatedPage * itemsPerTablePage
4164
let endIndex = startIndex + itemsPerTablePage
4265
43-
if (endIndex > jobs.value.length) {
44-
endIndex = jobs.value.length
66+
if (endIndex > allowedJobs.value.length) {
67+
endIndex = allowedJobs.value.length
4568
}
4669
4770
for (let index = startIndex; index < endIndex; index++) {
48-
items.push(jobs.value[index])
71+
items.push(allowedJobs.value[index])
4972
}
5073
5174
return items
@@ -58,14 +81,12 @@ const bannerMessage = computed(() => {
5881
})
5982
6083
//-------------------------------------------------------------------------------------------------
61-
// Lifecycle Hooks
84+
// Watchers
6285
//-------------------------------------------------------------------------------------------------
63-
onBeforeMount(async () => {
64-
const page = await client.getCurrentPage()
65-
const metadata = page.metadata as ViewJobsPageMetadata
66-
67-
if (metadata.newJob) {
68-
state.newJob = metadata.newJob
86+
watch(newJob, (newVal, _oldVal) => {
87+
if (newVal) {
88+
state.newJob = newVal
89+
newJob.value = undefined
6990
}
7091
})
7192
@@ -100,8 +121,8 @@ function onDelete(index: number): void {
100121
101122
const jobIndex = state.paginatedPage * itemsPerTablePage + index
102123
103-
if (jobIndex < jobs.value.length) {
104-
jobs.value[jobIndex].delete = true
124+
if (jobIndex < allowedJobs.value.length) {
125+
allowedJobs.value[jobIndex].delete = true
105126
state.jobToDeleteIndex = jobIndex
106127
}
107128
}
@@ -115,8 +136,8 @@ function onDownload(index: number): void {
115136
}
116137
117138
function resetJobToDelete(): void {
118-
if (state.jobToDeleteIndex !== undefined && state.jobToDeleteIndex < jobs.value.length) {
119-
jobs.value[state.jobToDeleteIndex].delete = false
139+
if (state.jobToDeleteIndex !== undefined && state.jobToDeleteIndex < allowedJobs.value.length) {
140+
allowedJobs.value[state.jobToDeleteIndex].delete = false
120141
state.jobToDeleteIndex = undefined
121142
}
122143
}
@@ -125,23 +146,26 @@ function resetJobToDelete(): void {
125146
<template>
126147
<div class="jobs-list">
127148
<tk-space-between>
128-
<h1>Notebook Jobs</h1>
149+
<h1 v-if="!props.hideHeading">Notebook Jobs</h1>
129150

130151
<tk-banner v-if="state.newJob" :content="bannerMessage" @dismiss="onBannerDismiss" />
131152

132153
<tk-box float="right">
133154
<button class="tk-button" @click="onReload">Reload</button>
134155
</tk-box>
135156

136-
<div v-if="jobs.length === 0">
157+
<div v-if="props.jobDefinitionId && allowedJobs.length === 0">
158+
No notebook jobs associated with this job definition.
159+
</div>
160+
<div v-else-if="allowedJobs.length === 0">
137161
There are no notebook jobs. Notebook jobs run files in the background, immediately or on a schedule. To
138162
create a notebook job, right-click on a notebook in the file browser and select "Create Notebook Job".
139163
</div>
140164

141165
<tk-table
142-
v-if="jobs.length > 0"
166+
v-if="allowedJobs.length > 0"
143167
:items-per-page="itemsPerTablePage"
144-
:total-items="jobs.length"
168+
:total-items="allowedJobs.length"
145169
@pagination="onPagination"
146170
>
147171
<template v-slot:head>

0 commit comments

Comments
 (0)