project: Add last job status as for feedback feature.

This commit is contained in:
seiwailai 2021-04-16 23:28:09 +08:00
parent f5c176701b
commit 03d8987d93
3 changed files with 116 additions and 8 deletions

View File

@ -24,6 +24,7 @@ import ProjectDetail from './ProjectDetail';
import ProjectEdit from './ProjectEdit';
import ProjectJobTemplatesList from './ProjectJobTemplatesList';
import { OrganizationsAPI, ProjectsAPI } from '../../api';
import useWsProject from './useWsProject';
function Project({ i18n, setBreadcrumb }) {
const { me = {} } = useConfig();
@ -32,7 +33,7 @@ function Project({ i18n, setBreadcrumb }) {
const {
request: fetchProjectAndRoles,
result: { project, isNotifAdmin },
result: { projectDetail, isNotifAdmin },
isLoading: hasContentLoading,
error: contentError,
} = useRequest(
@ -58,12 +59,12 @@ function Project({ i18n, setBreadcrumb }) {
data.summary_fields.credentials = results;
}
return {
project: data,
projectDetail: data,
isNotifAdmin: notifAdminRes.data.results.length > 0,
};
}, [id]),
{
project: null,
projectDetail: null,
notifAdminRes: null,
}
);
@ -73,10 +74,12 @@ function Project({ i18n, setBreadcrumb }) {
}, [fetchProjectAndRoles, location.pathname]);
useEffect(() => {
if (project) {
setBreadcrumb(project);
if (projectDetail) {
setBreadcrumb(projectDetail);
}
}, [project, setBreadcrumb]);
}, [projectDetail, setBreadcrumb]);
const project = useWsProject(projectDetail);
const loadScheduleOptions = useCallback(() => {
return ProjectsAPI.readScheduleOptions(project.id);

View File

@ -1,8 +1,8 @@
import React, { useCallback } from 'react';
import React, { Fragment, useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button, List, ListItem } from '@patternfly/react-core';
import { Button, List, ListItem, Tooltip } from '@patternfly/react-core';
import { Project } from '../../../types';
import { Config } from '../../../contexts/Config';
@ -22,6 +22,8 @@ import { toTitleCase } from '../../../util/strings';
import useRequest, { useDismissableError } from '../../../util/useRequest';
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
import ProjectSyncButton from '../shared/ProjectSyncButton';
import StatusLabel from '../../../components/StatusLabel';
import { formatDateString } from '../../../util/dates';
function ProjectDetail({ project, i18n }) {
const {
@ -84,9 +86,44 @@ function ProjectDetail({ project, i18n }) {
);
}
const generateLastJobTooltip = job => {
return (
<Fragment>
<div>{i18n._(t`MOST RECENT SYNC`)}</div>
<div>
{i18n._(t`JOB ID:`)} {job.id}
</div>
<div>
{i18n._(t`STATUS:`)} {job.status.toUpperCase()}
</div>
{job.finished && (
<div>
{i18n._(t`FINISHED:`)} {formatDateString(job.finished)}
</div>
)}
</Fragment>
);
};
return (
<CardBody>
<DetailList gutter="sm">
<Detail
label={i18n._(t`Last Job Status`)}
value={
summary_fields.last_job && (
<Tooltip
position="top"
content={generateLastJobTooltip(summary_fields.last_job)}
key={summary_fields.last_job.id}
>
<Link to={`/jobs/project/${summary_fields.last_job.id}`}>
<StatusLabel status={summary_fields.last_job.status} />
</Link>
</Tooltip>
)
}
/>
<Detail
label={i18n._(t`Name`)}
value={name}

View File

@ -0,0 +1,68 @@
import { useState, useEffect } from 'react';
import useWebsocket from '../../util/useWebsocket';
export default function useWsProjects(initialProject) {
const [project, setProject] = useState(initialProject);
const lastMessage = useWebsocket({
jobs: ['status_changed'],
control: ['limit_reached_1'],
});
useEffect(() => {
setProject(initialProject);
}, [initialProject]);
useEffect(
() => {
if (
!project ||
!lastMessage?.unified_job_id ||
lastMessage.type !== 'project_update'
) {
return;
}
const last_status = project.summary_fields.last_job.status;
// In case if users spam the sync button, we will need to ensure
// the fluent UI on most recent sync tooltip and last job status.
// Thus, we will not update our last job status to `Pending` if
// there is current running job.
//
// For instance, we clicked sync for particular project for twice.
// For first sync, our last job status should immediately change
// to `Pending`, then `Waiting`, then `Running`, then result
// (which are `successful`, `failed`, `error`, `cancelled`.
// For second sync, if the status response is `pending` and we have
// running and waiting jobs, we should not update our UI to `Pending`,
// otherwise our most recent sync tooltip UI will lose our current running
// job and we cannot navigate to the job link through the link provided
// by most recent sync tooltip.
//
// More ideally, we should prevent any spamming on sync button using
// backend logic to reduce overload on server and we can have a
// less complex frontend implementation for fluent UI
if (
lastMessage.status === 'pending' &&
!['successful', 'failed', 'error', 'cancelled'].includes(last_status)
) {
return;
}
const updatedProject = {
...project,
summary_fields: {
...project.summary_fields,
last_job: {
id: lastMessage.unified_job_id,
status: lastMessage.status,
finished: lastMessage.finished,
},
},
};
setProject(updatedProject);
},
[lastMessage] // eslint-disable-line react-hooks/exhaustive-deps
);
return project;
}