Skip to content

Commit

Permalink
Overview of project jobs
Browse files Browse the repository at this point in the history
* Added pagination to openapi.yaml

* Used projectId from parameter

* Implemented fetching jobs

* Added pinia store to store projects

* Added pagination to jobs overview
  • Loading branch information
lorriborri authored Dec 18, 2024
1 parent 31eb09b commit cbe6c59
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 34 deletions.
8 changes: 6 additions & 2 deletions sechub-openapi-java/src/main/resources/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2019,13 +2019,17 @@ components:
title: SecHubJobInfoForUserListPage
type: object
properties:
jobs:
content:
type: array
items:
$ref: '#/components/schemas/SecHubJobInfoForUser'
projectId:
type: string

page:
type: int
totalPages:
type: int

TemplateType:
title: TemplateType
type: string
Expand Down
2 changes: 2 additions & 0 deletions sechub-web-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ To start the development server with hot-reload, run the following command. The
npm run dev
```

> If you receive an empty page, do a reload (sometimes it needs a little bit of time until everything is setup correctly)
> Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script.
#### Running in Development mode with sechub for testing
Expand Down
49 changes: 49 additions & 0 deletions sechub-web-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sechub-web-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@mdi/font": "7.4.47",
"@openapitools/openapi-generator-cli": "^2.15.3",
"core-js": "^3.37.1",
"pinia": "^2.3.0",
"roboto-fontface": "*",
"vue": "^3.4.31",
"vue-i18n": "^10.0.5",
Expand Down
5 changes: 3 additions & 2 deletions sechub-web-ui/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AppHeader: typeof import('./components/AppHeader.vue')['default']
Projects: typeof import('./components/Projects.vue')['default']
Pagination: typeof import('./components/Pagination.vue')['default']
Project: typeof import('./components/Project.vue')['default']
ProjectsList: typeof import('./components/ProjectsList.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SecHubDefault: typeof import('./components/SecHubDefault.vue')['default']
}
}
59 changes: 59 additions & 0 deletions sechub-web-ui/src/components/Pagination.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!-- SPDX-License-Identifier: MIT -->
<template>
<div class="text-center">
<v-container>
<v-row>
<v-col class="ma-0 pa-0">
<v-container class="ma-0 pa-0">
<v-pagination
v-model="localCurrentPage"
:length="totalPages"
:total-visible="7"
@update:model-value="onPageChange"
/>
</v-container>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, toRefs, watch } from 'vue'

interface Props {
currentPage: number
totalPages: number
}

export default defineComponent({
props: {
currentPage: {
type: Number,
required: true,
},
totalPages: {
type: Number,
required: true,
},
},
emits: ['pageChanged'],
setup (props: Props, { emit }) {
const { currentPage } = toRefs(props)
const localCurrentPage = ref(currentPage.value)

watch(currentPage, newVal => {
localCurrentPage.value = newVal
})

function onPageChange (page: number) {
// calls function from parent component (emit)
emit('pageChanged', page)
}

return {
localCurrentPage,
onPageChange,
}
},
})
</script>
143 changes: 143 additions & 0 deletions sechub-web-ui/src/components/Project.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<!-- SPDX-License-Identifier: MIT -->
<template>
<v-card class="mr-auto" color="background_paper" width="70%">
<v-toolbar color="background_paper" width="70%">
<v-toolbar-title>{{ projectId }}</v-toolbar-title>
<v-btn icon="mdi-refresh" @click="fetchProjectJobs(currentRequestParameters)" />
</v-toolbar>

<div v-if="jobs.length === 0 && !loading">
<v-list bg-color="background_paper" lines="two">
<v-list-item v-if="error" class="ma-5 background-color" rounded="lg">{{ $t('ERROR_FETCHING_DATA') }}</v-list-item>
<v-list-item v-else class="ma-5" rounded="lg">{{ $t('NO_JOBS_RUNNED') }}</v-list-item>
</v-list>
</div>

<div v-else>
<v-table
class="background-color"
fixed-header
height="90%"
>
<thead>
<tr>
<th class="background-color">{{ $t('HEADER_JOB_TABLE_CREATED') }}</th>
<th class="background-color">{{ $t('HEADER_JOB_TABLE_STATUS') }}</th>
<th class="background-color">{{ $t('HEADER_JOB_TABLE_RESULT') }}</th>
<th class="text-center background-color">{{ $t('HEADER_JOB_TABLE_TRAFFIC_LIGHT') }}</th>
<th class="text-center background-color">{{ $t('HEADER_JOB_TABLE_REPORT') }}</th>
<th class="background-color">{{ $t('JOB_TABLE_DOWNLOAD_JOBUUID') }}</th>
</tr>
</thead>
<tbody>
<tr
v-for="job in jobs"
:key="job.jobUUID"
class="background-color"
>
<td>{{ formatDate(job.created) }}</td>
<td>{{ job.executionState }}</td>
<td>{{ job.executionResult }}</td>
<td class="text-center"><v-icon :class="getTrafficLightClass(job.trafficLight)" icon="mdi-circle" /></td>
<td class="text-center"><span v-if="job.executionResult === 'OK'">
<v-btn class="ma-2">{{ $t('JOB_TABLE_DOWNLOAD_REPORT') }}
<v-icon end icon="mdi-arrow-down" />
</v-btn>
</span>
</td>
<td>{{ job.jobUUID }}</td>
</tr>
</tbody>
</v-table>
</div>
<Pagination
:current-page="jobsObject.page + 1"
:total-pages="jobsObject.totalPages"
@page-changed="onPageChange"
/>
</v-card>
</template>

<script>
import { onMounted, ref } from 'vue'
import defaultClient from '@/services/defaultClient'
import { useRoute } from 'vue-router'
import { formatDate, getTrafficLightClass } from '@/utils/projectUtils'

export default {
name: 'ProjectComponent',

setup () {
// loads projectId from route
const route = useRoute()
const projectId = route.params.id

const currentRequestParameters = {
projectId,
size: 10,
page: 0,
}

const jobsObject = ref({})
const jobs = ref([])
const loading = ref(true)
const error = ref(null)

async function fetchProjectJobs (requestParameters) {
try {
jobsObject.value = await defaultClient.withOtherApi.userListJobsForProject(requestParameters)
jobs.value = jobsObject.value.content
} catch (err) {
error.value = 'ProjectAPI error fetching jobs for project.'
console.error('ProjectAPI error fetching jobs for project:', err)
} finally {
loading.value = false
}
}

function onPageChange (page) {
// the API page starts by 0 while vue pagination starts with 1
currentRequestParameters.page = page - 1
fetchProjectJobs(currentRequestParameters)
}

onMounted(async () => {
fetchProjectJobs(currentRequestParameters)
})

return {
projectId,
jobsObject,
jobs,
loading,
error,
currentRequestParameters,
fetchProjectJobs,
onPageChange,
}
},

methods: {
formatDate,
getTrafficLightClass,
},
}
</script>

<style scoped>
.background-color{
background-color: rgb(var(--v-theme-layer_01)) !important;
}
.traffic-light-none{
color: rgb(var(--v-theme-layer_01)) !important;
}
.traffic-light-red{
color: rgb(var(--v-theme-error)) !important;
}
.traffic-light-green{
color: rgb(var(--v-theme-success)) !important;
}
.traffic-light-yellow{
color: rgb(var(--v-theme-warning)) !important;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
class="ma-5"
rounded="lg"
:value="project"
@click="openProjectPage(project)"
>
<template #prepend>
<v-icon v-if="project.isOwned" :class="ownedClass" icon="mdi-cube" />
Expand All @@ -40,18 +41,24 @@

<script>
import defaultClient from '@/services/defaultClient'
import { useRouter } from 'vue-router'
import { ref } from 'vue'
import { useProjectStore } from '@/stores/projectStore'

export default {
name: 'ProjectsComponent',
name: 'ProjectListComponent',

setup () {
const projects = ref([])
const loading = ref(true)
const error = ref(null)
const router = useRouter()
const store = useProjectStore()

onMounted(async () => {
try {
projects.value = await defaultClient.withProjectApi.getAssignedProjectDataList()
store.storeProjects(projects)
} catch (err) {
error.value = 'ProjectAPI error fetching assigned projects.'
console.error('ProjectAPI error fetching assigned projects:', err)
Expand All @@ -60,13 +67,23 @@
}
})

const openProjectPage = project => {
router.push({
name: `/[id]`,
params: {
id: project.projectId,
},
})
}

return {
ownedClass: {
'project-owned': true,
},
projects,
loading,
error,
openProjectPage,
}
},
}
Expand Down
Loading

0 comments on commit cbe6c59

Please sign in to comment.