From 97345cbde25f03d751264ce6f3dba83e14a3052a Mon Sep 17 00:00:00 2001 From: Joel Perca Date: Tue, 29 Aug 2023 19:51:35 -0500 Subject: [PATCH] Refactoring JobsList, JobsTable components --- estela-web/src/components/JobsList/index.tsx | 242 ++++ estela-web/src/components/JobsTable/index.tsx | 163 +++ .../src/components/JobsTable/styles.scss | 9 + .../src/components/PaginationItem/index.tsx | 26 + .../ProjectCronJobListPage/index.tsx | 1181 ----------------- estela-web/src/components/index.ts | 2 + .../src/pages/CronJobDetailPage/index.tsx | 575 +------- .../pages/ProjectCronJobListPage/index.tsx | 251 ++-- .../src/pages/ProjectJobListPage/index.tsx | 686 ++-------- .../src/pages/SpiderDetailPage/index.tsx | 581 +------- 10 files changed, 749 insertions(+), 2967 deletions(-) create mode 100644 estela-web/src/components/JobsList/index.tsx create mode 100644 estela-web/src/components/JobsTable/index.tsx create mode 100644 estela-web/src/components/JobsTable/styles.scss create mode 100644 estela-web/src/components/PaginationItem/index.tsx delete mode 100644 estela-web/src/components/ProjectCronJobListPage/index.tsx diff --git a/estela-web/src/components/JobsList/index.tsx b/estela-web/src/components/JobsList/index.tsx new file mode 100644 index 00000000..985b3b21 --- /dev/null +++ b/estela-web/src/components/JobsList/index.tsx @@ -0,0 +1,242 @@ +import React, { Component } from "react"; +import { Checkbox, Col, Row, Space, Tag, Layout, Typography, Pagination } from "antd"; +import { JobsTable } from "../JobsTable"; +import { SpiderJobArg, SpiderJobTag } from "../../services"; + +import { PaginationItem } from "../PaginationItem"; + +const { Content } = Layout; +const { Text } = Typography; + +enum JobStatus { + Waiting, + Queued, + Running, + Completed, + Stopped, + WithError, +} + +interface SpiderData { + sid: number; + name: string; +} + +interface BaseInfo { + jid: number | undefined; + spider: SpiderData; + cid?: number | null | undefined; +} + +interface SpiderJobData { + info: BaseInfo; + key: number | undefined; + date: string; + tags: SpiderJobTag[] | undefined; + args: SpiderJobArg[] | undefined; + status: string | undefined; +} + +interface JobsListProps { + projectId: string; + tableStatus: boolean[]; + waitingJobs: SpiderJobData[]; + queueJobs: SpiderJobData[]; + runningJobs: SpiderJobData[]; + completedJobs: SpiderJobData[]; + stoppedJobs: SpiderJobData[]; + errorJobs: SpiderJobData[]; + count: number; + current: number; + pageSize: number; + onPageChange: (page: number) => void; + onChangeStatus: (index: number, count: number) => void; +} + +export class JobsList extends Component { + PAGE_SIZE = this.props.pageSize; + render(): JSX.Element { + const { + projectId, + tableStatus, + waitingJobs, + queueJobs, + runningJobs, + completedJobs, + stoppedJobs, + errorJobs, + count, + current, + onPageChange, + onChangeStatus, + } = this.props; + return ( + + + {tableStatus[JobStatus.Waiting] && ( + + {waitingJobs.length} + + } + projectId={projectId} + jobs={waitingJobs} + /> + )} + {tableStatus[JobStatus.Queued] && ( + + {queueJobs.length} + + } + projectId={projectId} + jobs={queueJobs} + /> + )} + {tableStatus[JobStatus.Running] && ( + + {runningJobs.length} + + } + projectId={projectId} + jobs={runningJobs} + /> + )} + {tableStatus[JobStatus.Completed] && ( + + {completedJobs.length} + + } + projectId={projectId} + jobs={completedJobs} + /> + )} + {tableStatus[JobStatus.Stopped] && ( + + {stoppedJobs.length} + + } + projectId={projectId} + jobs={stoppedJobs} + /> + )} + {tableStatus[JobStatus.WithError] && ( + + {errorJobs.length} + + } + projectId={projectId} + jobs={errorJobs} + /> + )} + + + + + + + STATUS + + onChangeStatus(JobStatus.Waiting, waitingJobs.length)} + > + + Waiting + + {waitingJobs.length} + + + +
+ onChangeStatus(JobStatus.Queued, queueJobs.length)} + > + + Queue + + {queueJobs.length} + + + +
+ onChangeStatus(JobStatus.Running, runningJobs.length)} + > + + Running + + {runningJobs.length} + + + +
+ onChangeStatus(JobStatus.Completed, completedJobs.length)} + > + + Completed + + {completedJobs.length} + + + +
+ onChangeStatus(JobStatus.Stopped, stoppedJobs.length)} + > + + Stopped + + {stoppedJobs.length} + + + +
+ onChangeStatus(JobStatus.WithError, errorJobs.length)} + > + + Error + + {errorJobs.length} + + + +
+
+ +
+ ); + } +} diff --git a/estela-web/src/components/JobsTable/index.tsx b/estela-web/src/components/JobsTable/index.tsx new file mode 100644 index 00000000..87afe109 --- /dev/null +++ b/estela-web/src/components/JobsTable/index.tsx @@ -0,0 +1,163 @@ +import React, { ReactElement, ReactNode } from "react"; +import { Link } from "react-router-dom"; +import { Typography, Table, Row, Space, Layout, Col, Tag, Button } from "antd"; +import "./styles.scss"; + +import Filter from "../../assets/icons/filter.svg"; +import Setting from "../../assets/icons/setting.svg"; +import { SpiderJobArg, SpiderJobTag } from "../../services"; + +const { Content } = Layout; +const { Text } = Typography; + +interface SpiderData { + sid: number; + name: string; +} + +interface BaseInfo { + jid: number | undefined; + spider: SpiderData; + cid?: number | null | undefined; +} + +interface SpiderJobData { + info: BaseInfo; + key: number | undefined; + date: string; + tags: SpiderJobTag[] | undefined; + args: SpiderJobArg[] | undefined; + status: string | undefined; +} + +interface JobsTableProps { + projectId: string; + titleLabel: string; + jobs: SpiderJobData[]; + tagElement: ReactNode; +} + +export const JobsTable: React.FC = ({ projectId, jobs, titleLabel, tagElement }) => { + const columns = [ + { + title: "JOB", + dataIndex: "info", + key: "info", + render: (info: BaseInfo): ReactElement => ( + + Job-{info.jid} + + ), + }, + { + title: "SPIDER", + dataIndex: "info", + key: "info", + render: (info: BaseInfo): ReactElement => ( + + {info.spider.name} + + ), + }, + { + title: "SCHEDULED JOB", + key: "info", + dataIndex: "info", + render: (info: BaseInfo): ReactElement => + info.cid ? ( + + Sche-Job-{info.cid} + + ) : ( + Not associated + ), + }, + { + title: "LAUNCH DATE", + dataIndex: "date", + key: "date", + }, + { + title: "ARGUMENTS", + dataIndex: "args", + key: "args", + render: (args: SpiderJobArg[]): ReactElement => ( + + {args.map((arg: SpiderJobArg, id: number) => ( + + {arg.name}: {arg.value} + + ))} + + ), + }, + { + title: "TAGS", + dataIndex: "tags", + key: "tags", + render: (tags: SpiderJobTag[]): ReactElement => ( + + {tags.map((tag: SpiderJobTag, id) => ( + + {tag.name} + + ))} + + ), + }, + ]; + + return ( + + + + {titleLabel} + {tagElement} + + + + + + + + + + + + + + + ); +}; diff --git a/estela-web/src/components/JobsTable/styles.scss b/estela-web/src/components/JobsTable/styles.scss new file mode 100644 index 00000000..71ca39b7 --- /dev/null +++ b/estela-web/src/components/JobsTable/styles.scss @@ -0,0 +1,9 @@ +$estela-background: #FBFCFD; + +.table-joblist .ant-table .ant-table-container .ant-table-content table thead.ant-table-thead { + border-radius: 8px; +} + +.table-joblist .ant-table .ant-table-container .ant-table-content table thead.ant-table-thead .ant-table-cell { + background-color: $estela-background; +} \ No newline at end of file diff --git a/estela-web/src/components/PaginationItem/index.tsx b/estela-web/src/components/PaginationItem/index.tsx new file mode 100644 index 00000000..1acd8ef7 --- /dev/null +++ b/estela-web/src/components/PaginationItem/index.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import type { PaginationProps } from "antd"; +import DoubleRight from "../../assets/icons/doubleRight.svg"; +import DoubleLeft from "../../assets/icons/doubleLeft.svg"; + +export const PaginationItem: PaginationProps["itemRender"] = (current, type) => { + if (type === "prev") { + return ( +
+ +

Prev

+
+ ); + } + if (type === "next") { + return ( +
+

Next

+ +
+ ); + } + if (type === "page") { + return
{current}
; + } +}; diff --git a/estela-web/src/components/ProjectCronJobListPage/index.tsx b/estela-web/src/components/ProjectCronJobListPage/index.tsx deleted file mode 100644 index 0a7fe388..00000000 --- a/estela-web/src/components/ProjectCronJobListPage/index.tsx +++ /dev/null @@ -1,1181 +0,0 @@ -import React, { Component, ReactElement } from "react"; -import moment from "moment"; -import { - Layout, - Pagination, - Row, - Space, - Table, - Modal, - Col, - Button, - Input, - Form, - Switch, - Select, - Tag, - Checkbox, - Radio, - message, - InputNumber, - TimePicker, - DatePicker, - ConfigProvider, -} from "antd"; -import { DatePickerProps, RadioChangeEvent, notification, Typography } from "antd"; -import { Link, RouteComponentProps } from "react-router-dom"; -import "./styles.scss"; -import history from "../../history"; -import { ApiService } from "../../services"; -import Add from "../../assets/icons/add.svg"; -import { - ApiProjectsSpidersCronjobsCreateRequest, - ApiProjectsSpidersCronjobsUpdateRequest, - ApiProjectsSpidersCronjobsDeleteRequest, - ApiProjectsSpidersCronjobsRunOnceRequest, - SpiderCronJobUpdateStatusEnum, - ApiProjectsSpidersListRequest, - ApiProjectsCronjobsRequest, - SpiderCronJobStatusEnum, - SpiderDataStatusEnum, - ApiProjectsReadRequest, - SpiderCronJobCreate, - ProjectCronJob, - SpiderCronJob, - Project, - Spider, -} from "../../services/api"; -import { - resourceNotAllowedNotification, - invalidDataNotification, - incorrectDataNotification, - Spin, - PaginationItem, -} from "../../shared"; -import { convertDateToString } from "../../utils"; -import { checkExternalError } from "ExternalComponents/CardNotification"; - -const { Option } = Select; -const { Content } = Layout; -const { Text } = Typography; - -interface Ids { - sid: number; - cid: number | undefined; -} - -interface Tags { - name: string; - key: number; -} - -interface TagsData { - name: string; -} - -interface OptionDataPersistance { - label: string; - key: number; - value: number; -} - -interface OptionDataRepeat { - label: string; - key: number; - value: string; -} - -interface Args { - name: string; - value: string; -} - -interface SpiderCronJobData { - id: Ids; - key: number | undefined; - date: string; - status: string | undefined; - schedule: string | undefined; - dataExpiryDays: number | undefined | null; - dataStatus: string | undefined; - tags: TagsData[] | undefined; - args: Args[] | undefined; -} - -interface ArgsData { - name: string; - value: string; - key: number; -} - -interface EnvVarsData { - name: string; - value: string; - key: number; -} - -interface ProjectCronJobListPageState { - name: string; - spiders: Spider[]; - cronjobs: SpiderCronJobData[]; - selectedRows: SpiderCronJobData[]; - expression: string; - repeat: string; - currentDay: number; - spiderId: string; - dataExpiryDays: number | undefined; - dataStatus: SpiderDataStatusEnum | undefined; - uniqueCollection: boolean; - date: moment.Moment; - recurrence: string; - recurrenceNum: number; - schedulesFlag: boolean[]; - weekDays: boolean[]; - loadedCronjobs: boolean; - loadedSpiders: boolean; - modal: boolean; - count: number; - current: number; - args: ArgsData[]; - envVars: EnvVarsData[]; - tags: TagsData[]; - newTags: Tags[]; - newArgName: string; - newArgValue: string; - newEnvVarName: string; - newEnvVarValue: string; - newTagName: string; - externalComponent: JSX.Element; - loading: boolean; - expressionError: boolean; -} - -interface RouteParams { - projectId: string; -} - -export class ProjectCronJobListPage extends Component, ProjectCronJobListPageState> { - PAGE_SIZE = 10; - state: ProjectCronJobListPageState = { - name: "", - spiders: [], - expression: "", - repeat: "hourly", - currentDay: 1, - dataExpiryDays: 1, - dataStatus: undefined, - uniqueCollection: false, - spiderId: "", - date: moment(), - recurrence: "weeks", - recurrenceNum: 1, - schedulesFlag: [true, false], - weekDays: new Array(7).fill(false), - args: [], - envVars: [], - tags: [], - newTags: [], - newArgName: "", - newArgValue: "", - newEnvVarName: "", - newEnvVarValue: "", - newTagName: "", - cronjobs: [], - selectedRows: [], - loadedCronjobs: false, - loadedSpiders: false, - modal: false, - count: 0, - current: 0, - externalComponent: <>, - loading: false, - expressionError: false, - }; - - apiService = ApiService(); - projectId: string = this.props.match.params.projectId; - countKey = 0; - hourFormat = "HH:mm"; - dateFormat = "MMM D, YYYY"; - - dataPersistenceOptions = [ - { label: "1 day", key: 1, value: 1 }, - { label: "1 week", key: 2, value: 7 }, - { label: "1 month", key: 3, value: 30 }, - { label: "3 months", key: 4, value: 90 }, - { label: "6 months", key: 5, value: 180 }, - { label: "1 year", key: 6, value: 365 }, - { label: "Forever", key: 7, value: 720 }, - ]; - - repeatOptions = [ - { label: "Hourly", key: 1, value: "hourly" }, - { label: "Daily", key: 2, value: "daily" }, - { label: "Weekly", key: 3, value: "weekly" }, - { label: "Monthly", key: 4, value: "monthly" }, - { label: "Yearly", key: 5, value: "yearly" }, - { label: "Custom ...", key: 6, value: "custom" }, - ]; - - recurrenceOptions = [ - { label: "Days", key: 1, value: "days" }, - { label: "Weeks", key: 2, value: "weeks" }, - { label: "Months", key: 3, value: "months" }, - { label: "Years", key: 4, value: "years" }, - ]; - - weekOptions = [ - { label: "S", key: 0, value: 0 }, - { label: "M", key: 1, value: 1 }, - { label: "T", key: 2, value: 2 }, - { label: "W", key: 3, value: 3 }, - { label: "T", key: 4, value: 4 }, - { label: "F", key: 5, value: 5 }, - { label: "S", key: 6, value: 6 }, - ]; - - columns = [ - { - title: "ENABLED", - key: "status", - dataIndex: "status", - render: (status: string, cronjob: SpiderCronJobData) => { - return ( - this.updateStatus(cronjob)} - /> - ); - }, - }, - { - title: "SCHEDULED JOB", - dataIndex: "id", - key: "id", - render: (id: Ids): ReactElement => ( - {id.cid} - ), - }, - { - title: "SPIDER", - dataIndex: "id", - key: "id", - render: (id: Ids): ReactElement => ( - {id.sid} - ), - }, - { - title: "ARGUMENTS", - dataIndex: "args", - key: "args", - render: (args: Args[]): ReactElement => ( - - {args.map((arg: Args, id: number) => ( - - {arg.name}: {arg.value} - - ))} - - ), - }, - { - title: "EXPRESSION", - key: "schedule", - dataIndex: "schedule", - }, - { - title: "TAGS", - key: "tags", - dataIndex: "tags", - render: (tags: TagsData[]): ReactElement => ( - - {tags.map((tag: TagsData, id) => ( - - {tag.name} - - ))} - - ), - }, - { - title: "PERSISTENCE", - key: "dataExpiryDays", - dataIndex: "dataExpiryDays", - render: (dataExpiryDays: number, cronjob: SpiderCronJobData): ReactElement => ( -

{cronjob.dataStatus == "PENDING" ? `${dataExpiryDays} days` : "Forever"}

- ), - }, - ]; - - async componentDidMount(): Promise { - const requestParams: ApiProjectsReadRequest = { pid: this.projectId }; - this.apiService.apiProjectsRead(requestParams).then( - (response: Project) => { - this.setState({ name: response.name }); - }, - (error: unknown) => { - error; - resourceNotAllowedNotification(); - }, - ); - this.getCronJobs(1); - this.getProjectSpiders(1); - const weekDays = this.state.weekDays; - weekDays[moment().day() % 7] = true; - this.setState({ currentDay: moment().day(), weekDays: weekDays }); - } - - getProjectSpiders = async (page: number): Promise => { - const requestParams: ApiProjectsSpidersListRequest = { pid: this.projectId, page, pageSize: this.PAGE_SIZE }; - this.apiService.apiProjectsSpidersList(requestParams).then( - (results) => { - if (results.results.length == 0 || results.results == undefined) { - this.setState({ spiders: [], loadedSpiders: true }); - } else { - this.setState({ - spiders: [...results.results], - dataStatus: results.results[0].dataStatus, - dataExpiryDays: results.results[0].dataExpiryDays, - spiderId: String(results.results[0].sid), - loadedSpiders: true, - }); - } - }, - (error: unknown) => { - error; - resourceNotAllowedNotification(); - }, - ); - }; - - getCronJobs = async (page: number): Promise => { - const requestParams: ApiProjectsCronjobsRequest = { - pid: this.projectId, - page, - pageSize: this.PAGE_SIZE, - }; - - await this.apiService.apiProjectsCronjobs(requestParams).then((response: ProjectCronJob) => { - const data = response.results.map((cronjob: SpiderCronJob, iterator: number) => ({ - key: iterator, - id: { sid: cronjob.spider, cid: cronjob.cjid }, - date: convertDateToString(cronjob.created), - status: cronjob.status, - schedule: cronjob.schedule, - dataExpiryDays: cronjob.dataExpiryDays, - dataStatus: cronjob.dataStatus, - tags: cronjob.ctags, - args: cronjob.cargs, - })); - const cronjobs: SpiderCronJobData[] = data; - this.setState({ cronjobs: [...cronjobs], loadedCronjobs: true, count: response.count, current: page }); - }); - }; - - getCustomExpression = (): string => { - const { date, recurrence, recurrenceNum, weekDays } = this.state; - let days = ""; - weekDays.map((day, index) => { - if (day) { - days += index + ","; - } - }); - - switch (recurrence) { - case "days": - return `${date.minutes()} ${date.hours()} */${recurrenceNum} * *`; - case "weeks": - return `${date.minutes()} ${date.hours()} * * ${days.slice(0, -1)}`; - case "months": - return `${date.minutes()} ${date.hours()} ${date.date()} */${recurrenceNum} *`; - case "years": - return `${date.minutes()} ${date.hours()} ${date.date()} ${date.month() + 1}/${recurrenceNum * 12} *`; - default: - return `${date.minutes()} ${date.hours()} * ${recurrenceNum * 7} * ${days.slice(0, -1)}`; - } - }; - - getExpression = (): string => { - const { repeat, date } = this.state; - switch (repeat) { - case "hourly": - return `${date.minutes()} * * * *`; - case "daily": - return `${date.minutes()} ${date.hours()} * * *`; - case "weekly": - return `${date.minutes()} ${date.hours()} * * ${date.day()}`; - case "monthly": - return `${date.minutes()} ${date.hours()} ${date.date()} * *`; - case "yearly": - return `${date.minutes()} ${date.hours()} ${date.date()} ${date.month() + 1} *`; - default: - return this.getCustomExpression(); - } - }; - - handleSubmit = (): void => { - this.setState({ loading: true }); - let expression = ""; - if (this.state.schedulesFlag[0]) { - if (this.state.expression == "") { - this.setState({ loading: false, expressionError: true }); - notification.error({ - message: "Invalid Cron Expression", - description: "Please enter a valid expression", - }); - return; - } - expression = this.state.expression; - } else { - expression = this.getExpression(); - } - - const requestData = { - cargs: [...this.state.args], - cenvVars: [...this.state.envVars], - ctags: [...this.state.tags], - schedule: expression, - uniqueCollection: this.state.uniqueCollection, - dataStatus: String(this.state.dataStatus), - dataExpiryDays: `0/${this.state.dataExpiryDays}`, - }; - const request: ApiProjectsSpidersCronjobsCreateRequest = { - data: requestData, - pid: this.projectId, - sid: this.state.spiderId, - }; - this.apiService.apiProjectsSpidersCronjobsCreate(request).then( - (response: SpiderCronJobCreate) => { - this.setState({ loading: false }); - history.push(`/projects/${this.projectId}/spiders/${this.state.spiderId}/cronjobs/${response.cjid}`); - }, - async (error) => { - this.setState({ loading: false }); - const data = await error.json(); - const [errorComponent, err] = checkExternalError(data); - if (err) { - invalidDataNotification(data.detail); - this.setState({ externalComponent: <> }); - this.setState({ externalComponent: errorComponent }); - } else { - incorrectDataNotification(); - } - this.setState({ modal: false }); - }, - ); - }; - - updateStatus = (cronjob: SpiderCronJobData): void => { - const _status = - cronjob.status == SpiderCronJobUpdateStatusEnum.Disabled - ? SpiderCronJobUpdateStatusEnum.Active - : SpiderCronJobUpdateStatusEnum.Disabled; - const request: ApiProjectsSpidersCronjobsUpdateRequest = { - cjid: Number(cronjob.id.cid), - sid: String(cronjob.id.sid), - pid: this.projectId, - data: { - status: _status, - }, - }; - this.apiService.apiProjectsSpidersCronjobsUpdate(request).then( - (response) => { - message.success(`ScheduledJob ${response.cjid} ${response.status}`); - this.getCronJobs(1); - }, - (error: unknown) => { - error; - incorrectDataNotification(); - }, - ); - }; - - handleInputChange = (event: React.ChangeEvent): void => { - const { - target: { value, name }, - } = event; - if (name === "newArgName") { - this.setState({ newArgName: value }); - } else if (name === "newArgValue") { - this.setState({ newArgValue: value }); - } else if (name === "newEnvVarName") { - this.setState({ newEnvVarName: value }); - } else if (name === "newEnvVarValue") { - this.setState({ newEnvVarValue: value }); - } else if (name === "newTagName") { - this.setState({ newTagName: value }); - } - }; - - addArgument = (): void => { - const args = [...this.state.args]; - const newArgName = this.state.newArgName.trim(); - const newArgValue = this.state.newArgValue.trim(); - if (newArgName && newArgValue && newArgName.indexOf(" ") == -1) { - args.push({ name: newArgName, value: newArgValue, key: this.countKey++ }); - this.setState({ args: [...args], newArgName: "", newArgValue: "" }); - } else { - invalidDataNotification("Invalid argument name/value pair."); - } - }; - - onChangeSchedule = (id: number): void => { - const checked = [false, false]; - checked[id] = true; - this.setState({ schedulesFlag: checked, repeat: "hourly" }); - if (id == 1) { - this.setState({ date: moment() }); - } - }; - - onChangeUniqueCollection = (e: RadioChangeEvent): void => { - this.setState({ uniqueCollection: e.target.value }); - }; - - addEnvVar = (): void => { - const envVars = [...this.state.envVars]; - const newEnvVarName = this.state.newEnvVarName.trim(); - const newEnvVarValue = this.state.newEnvVarValue.trim(); - if (newEnvVarName && newEnvVarValue && newEnvVarName.indexOf(" ") == -1) { - envVars.push({ name: newEnvVarName, value: newEnvVarValue, key: this.countKey++ }); - this.setState({ envVars: [...envVars], newEnvVarName: "", newEnvVarValue: "" }); - } else { - invalidDataNotification("Invalid environment variable name/value pair."); - } - }; - - addTag = (): void => { - const tags = [...this.state.tags]; - const newTags = [...this.state.newTags]; - const newTagName = this.state.newTagName.trim(); - if (newTagName && newTagName.indexOf(" ") == -1) { - newTags.push({ name: newTagName, key: this.countKey++ }); - tags.push({ name: newTagName }); - this.setState({ tags: [...tags], newTags: [...newTags], newTagName: "" }); - } else { - invalidDataNotification("Invalid tag name."); - } - }; - - handleRemoveEnvVar = (id: number): void => { - const envVars = [...this.state.envVars]; - envVars.splice(id, 1); - this.setState({ envVars: [...envVars] }); - }; - - handleRemoveTag = (id: number): void => { - const tags = [...this.state.tags]; - tags.splice(id, 1); - this.setState({ tags: [...tags] }); - }; - - handleRemoveArg = (id: number): void => { - const args = [...this.state.args]; - args.splice(id, 1); - this.setState({ args: [...args] }); - }; - - handleRepeatChange = (value: string): void => { - this.setState({ repeat: value }); - }; - - handleRecurrenceChange = (value: string): void => { - this.setState({ recurrence: value }); - }; - - handlePersistenceChange = (value: number): void => { - if (value == 720) { - this.setState({ dataStatus: SpiderDataStatusEnum.Persistent }); - } else { - this.setState({ dataExpiryDays: value }); - } - }; - - handleSpiderChange = (value: string): void => { - const spiderId = this.state.spiders.find((spider) => { - return spider.name === value; - }); - this.setState({ spiderId: String(spiderId?.sid) }); - }; - - handleWeekChange = (value: number): void => { - if (value % 7 != this.state.currentDay) { - const weekDays = [...this.state.weekDays]; - weekDays[value] = !weekDays[value]; - this.setState({ weekDays: weekDays }); - } - }; - - onPageChange = async (page: number): Promise => { - await this.getCronJobs(page); - }; - - onChangeRecurrence = (value: number | null) => { - this.setState({ recurrenceNum: Number(value) }); - }; - - onChangeExpression = (e: React.ChangeEvent) => { - this.setState({ expression: e.target.value, expressionError: false }); - }; - - onChangeDate: DatePickerProps["onChange"] = (date) => { - this.setState({ date: moment(date) }); - }; - - runOnceRows = (): void => { - this.state.selectedRows.map((row) => { - this.runOnce(row); - }); - }; - - deleteRows = (): void => { - this.state.selectedRows.map((row) => { - this.deleteCronjob(row); - }); - this.setState({ selectedRows: [] }); - }; - - goCronjobDetail = (): void => { - this.editCronjob(this.state.selectedRows[0]); - }; - - editCronjob = (cronjob: SpiderCronJobData): void => { - history.push(`/projects/${this.projectId}/spiders/${cronjob.id.sid}/cronjobs/${cronjob.id.cid}`); - }; - - runOnce = (cronjob: SpiderCronJobData): void => { - const requestParams: ApiProjectsSpidersCronjobsRunOnceRequest = { - pid: this.projectId, - sid: String(cronjob.id.sid), - cjid: Number(cronjob.id.cid), - }; - this.apiService.apiProjectsSpidersCronjobsRunOnce(requestParams).then( - async (response: SpiderCronJob) => { - response; - }, - (error: unknown) => { - error; - }, - ); - }; - - deleteCronjob = (cronjob: SpiderCronJobData): void => { - const requestParams: ApiProjectsSpidersCronjobsDeleteRequest = { - pid: this.projectId, - sid: String(cronjob.id.sid), - cjid: Number(cronjob.id.cid), - }; - this.apiService.apiProjectsSpidersCronjobsDelete(requestParams).then( - () => { - this.getCronJobs(1); - }, - (error: unknown) => { - message.error(`Failed action: ${error}`); - }, - ); - }; - - rowSelection = { - onChange: (selectedRowKeys: React.Key[], selectedRows: SpiderCronJobData[]) => { - this.setState({ selectedRows: selectedRows }); - }, - }; - - render(): JSX.Element { - const { - loadedCronjobs, - loadedSpiders, - cronjobs, - repeat, - date, - uniqueCollection, - selectedRows, - recurrence, - recurrenceNum, - schedulesFlag, - weekDays, - spiders, - modal, - externalComponent, - count, - current, - args, - envVars, - tags, - newArgName, - newArgValue, - newEnvVarName, - newEnvVarValue, - newTagName, - expressionError, - dataStatus, - dataExpiryDays, - loading, - } = this.state; - return ( - - {loadedCronjobs && loadedSpiders ? ( - - -
- -
-

SCHEDULED JOBS

- - - - {externalComponent} - - NEW SCHEDULED JOB -

- } - onCancel={() => this.setState({ modal: false })} - footer={null} - > - -
- -

Spider

- -
- -

Data persistence

- -
- - -

Unique Collection

- - Yes - No - -
-
- -

Arguments

- - - {args.map((arg: ArgsData, id) => ( - this.handleRemoveArg(id)} - > - {arg.name}: {arg.value} - - ))} - - - - - - - -
- -

Environment Variables

- - - {envVars.map((envVar: EnvVarsData, id: number) => ( - this.handleRemoveEnvVar(id)} - > - {envVar.name}: {envVar.value} - - ))} - - - - - - - -
- -

Tags

- - {tags.map((tag: TagsData, id) => ( - this.handleRemoveTag(id)} - > - {tag.name} - - ))} - - - - - -
- - -

Select a period

- - - this.onChangeSchedule(0)} - /> -

 By cron schedule expression

-
- {schedulesFlag[0] && ( - -

Expression

- - {expressionError && ( - - Enter a valid expression - - )} -

- More information about cron schedule - expressions  - - here - -

-
- )} -
- - - this.onChangeSchedule(1)} - /> -

 By planning

-
- {schedulesFlag[1] && ( - - - - -

Date

- -
- -

Hour

- -
-
-
- -

Repeat

- -
- {repeat === "custom" && ( - -

- Custom recurrence -

- -

Every

- - -
- {recurrence === "weeks" && ( - -

- Repeat on -

- - {this.weekOptions.map( - ( - option: OptionDataPersistance, - ) => ( - { - this.handleWeekChange( - option.value, - ); - }} - checked={ - weekDays[option.key] - } - > - {option.label} - - ), - )} - -
- )} -
- )} -
- )} -
- - - - - - - - - - -

No scheduled jobs yet.

}> -
- - - - - - - - - - - - - - ) : ( - - )} - - ); - } -} diff --git a/estela-web/src/components/index.ts b/estela-web/src/components/index.ts index fea844e3..0371082d 100644 --- a/estela-web/src/components/index.ts +++ b/estela-web/src/components/index.ts @@ -4,3 +4,5 @@ export { HeaderSection } from "./Stats/HeaderSection"; export { EstelaBanner } from "./EstelaBanner"; export { ProjectHealth } from "./Stats/ProjectHealth"; export { RightSidedModal } from "./UI/RightSidedModal"; +export { JobsList } from "./JobsList"; +export { PaginationItem } from "./PaginationItem"; diff --git a/estela-web/src/pages/CronJobDetailPage/index.tsx b/estela-web/src/pages/CronJobDetailPage/index.tsx index 7eb111bd..37b29fc7 100644 --- a/estela-web/src/pages/CronJobDetailPage/index.tsx +++ b/estela-web/src/pages/CronJobDetailPage/index.tsx @@ -1,4 +1,4 @@ -import React, { Component, ReactElement } from "react"; +import React, { Component } from "react"; import moment from "moment"; import { Layout, @@ -10,13 +10,11 @@ import { Tabs, Tag, Modal, - Table, Switch, Button, InputNumber, DatePicker, TimePicker, - Pagination, Select, Radio, Checkbox, @@ -33,8 +31,6 @@ import { ApiService } from "../../services"; import Copy from "../../assets/icons/copy.svg"; import Run from "../../assets/icons/play.svg"; import Edit from "../../assets/icons/edit.svg"; -import Filter from "../../assets/icons/filter.svg"; -import Setting from "../../assets/icons/setting.svg"; import Pause from "../../assets/icons/pause.svg"; import { ApiProjectsSpidersCronjobsUpdateRequest, @@ -48,21 +44,18 @@ import { SpiderCronJobDataStatusEnum, SpiderCronJobUpdate, SpiderCronJobUpdateDataStatusEnum, + SpiderJobTag, + SpiderJobArg, } from "../../services/api"; -import { resourceNotAllowedNotification, incorrectDataNotification, Spin, PaginationItem } from "../../shared"; +import { resourceNotAllowedNotification, incorrectDataNotification, Spin } from "../../shared"; import { convertDateToString } from "../../utils"; +import { JobsList } from "../../components/JobsList"; + const { Option } = Select; const { Content } = Layout; const { Text } = Typography; -const waiting = 0; -const queued = 1; -const running = 2; -const completed = 3; -const stopped = 4; -const withError = 5; - interface ArgsData { name: string; value: string; @@ -77,15 +70,19 @@ interface SpiderData { name: string; } -interface SpiderJobData { - id: number | undefined; +interface BaseInfo { + jid: number | undefined; spider: SpiderData; + cid?: number | null | undefined; +} + +interface SpiderJobData { + info: BaseInfo; key: number | undefined; date: string; status: string | undefined; - cronjob: number | null | undefined; - tags: TagsData[] | undefined; - args: ArgsData[] | undefined; + tags: SpiderJobTag[] | undefined; + args: SpiderJobArg[] | undefined; } interface OptionDataRepeat { @@ -101,13 +98,13 @@ interface OptionDataPersistance { } interface CronJobDetailPageState { + spiderName: string; loaded: boolean; name: string | undefined; args: ArgsData[]; envVars: SpiderJobEnvVar[]; tags: TagsData[]; status: string | undefined; - jobs: SpiderJobData[]; created: string; currentDay: number; modalVisible: boolean; @@ -143,15 +140,15 @@ interface RouteParams { } export class CronJobDetailPage extends Component, CronJobDetailPageState> { - PAGE_SIZE = 10; + PAGE_SIZE = 2; initial_schedule = ""; state = { + spiderName: "", loaded: false, name: "", args: [], envVars: [], tags: [], - jobs: [], currentDay: 1, date: moment(), expression: "", @@ -223,65 +220,6 @@ export class CronJobDetailPage extends Component ( - - {spider.name} - - ), - }, - { - title: "JOB", - dataIndex: "id", - key: "id", - render: (jobID: number): ReactElement => ( - - Job-{jobID} - - ), - }, - { - title: "LAUNCH DATE", - dataIndex: "date", - key: "date", - }, - { - title: "ARGUMENTS", - dataIndex: "args", - key: "args", - render: (args: ArgsData[]): ReactElement => ( - - {args.map((arg: ArgsData, id: number) => ( - - {arg.name}: {arg.value} - - ))} - - ), - }, - { - title: "TAGS", - dataIndex: "tags", - key: "tags", - render: (tags: TagsData[]): ReactElement => ( - - {tags.map((tag: TagsData, id) => ( - - {tag.name} - - ))} - - ), - }, - ]; - async componentDidMount(): Promise { const requestParams: ApiProjectsSpidersCronjobsReadRequest = { pid: this.projectId, @@ -296,29 +234,14 @@ export class CronJobDetailPage extends Component job.status === "WAITING"); - const queueJobs = data.data.filter((job: SpiderJobData) => job.status === "IN_QUEUE"); - const runningJobs = data.data.filter((job: SpiderJobData) => job.status === "RUNNING"); - const completedJobs = data.data.filter((job: SpiderJobData) => job.status === "COMPLETED"); - const stoppedJobs = data.data.filter((job: SpiderJobData) => job.status === "STOPPED"); - const errorJobs = data.data.filter((job: SpiderJobData) => job.status === "ERROR"); - - const tableStatus = [ - waitingJobs.length === 0 ? false : true, - queueJobs.length === 0 ? false : true, - runningJobs.length === 0 ? false : true, - completedJobs.length === 0 ? false : true, - stoppedJobs.length === 0 ? false : true, - errorJobs.length === 0 ? false : true, - ]; + this.updateJobs(data, 1); const weekDays = this.state.weekDays; weekDays[moment().day() % 7] = true; - const jobs: SpiderJobData[] = data.data; this.setState({ name: response.name, - spiderName: response.spider.name, + spiderName: (response.spider as unknown as SpiderData).name, args: [...args], envVars: [...envVars], tags: [...tags], @@ -326,19 +249,11 @@ export class CronJobDetailPage extends Component ({ key: iterator, - id: job.jid, - spider: job.spider, + info: { + jid: job.jid, + spider: job.spider as unknown as SpiderData, + cid: job.cronjob, + }, args: job.args, date: convertDateToString(job.created), status: job.jobStatus, tags: job.tags, - cronjob: job.cronjob, })); return { data: data, count: response.count, current: page }; }; - onPageChange = async (page: number): Promise => { - const data = await this.getJobs(page); + updateJobs = (data: { data: SpiderJobData[]; count: number; current: number }, page: number) => { const jobs: SpiderJobData[] = data.data; - const waitingJobs = jobs.filter((job: SpiderJobData) => job.status === "WAITING"); const queueJobs = jobs.filter((job: SpiderJobData) => job.status === "IN_QUEUE"); const runningJobs = jobs.filter((job: SpiderJobData) => job.status === "RUNNING"); @@ -447,19 +362,23 @@ export class CronJobDetailPage extends Component => { + const data = await this.getJobs(page); + this.updateJobs(data, page); + }; + onChangeDataPersistence = ({ target: { value } }: RadioChangeEvent): void => { if (value === 720) { this.setState({ dataStatus: SpiderCronJobUpdateDataStatusEnum.Persistent, modified: true }); @@ -490,8 +409,7 @@ export class CronJobDetailPage extends Component { response; const data = await this.getJobs(1); - const jobs: SpiderJobData[] = data.data; - this.setState({ jobs: [...jobs] }); + this.updateJobs(data, 1); }, (error: unknown) => { error; @@ -975,416 +893,21 @@ export class CronJobDetailPage extends Component - - - {tableStatus[waiting] && ( - - - - - Waiting - - - {waitingJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[queued] && ( - - - - - In queue - - - {queueJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[running] && ( - - - - - Running - - - {runningJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[completed] && ( - - - - - Completed - - - {completedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[stopped] && ( - - - - - Stopped - - - {stoppedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[withError] && ( - - - - - Error - - - {errorJobs.length} - - - - - - - - -
- - - - - - - )} - - - - - - - STATUS - - this.onChangeStatus(waiting, waitingJobs.length)} - > - - - Waiting - - - {waitingJobs.length} - - - -
- this.onChangeStatus(queued, queueJobs.length)} - > - - Queue - - {queueJobs.length} - - - -
- this.onChangeStatus(running, runningJobs.length)} - > - - - Running - - - {runningJobs.length} - - - -
- this.onChangeStatus(completed, completedJobs.length)} - > - - - Completed - - - {completedJobs.length} - - - -
- this.onChangeStatus(stopped, stoppedJobs.length)} - > - - - Stopped - - - {stoppedJobs.length} - - - -
- this.onChangeStatus(withError, errorJobs.length)} - > - - Error - - {errorJobs.length} - - - -
-
- - + ); diff --git a/estela-web/src/pages/ProjectCronJobListPage/index.tsx b/estela-web/src/pages/ProjectCronJobListPage/index.tsx index 76f5395c..f7f81354 100644 --- a/estela-web/src/pages/ProjectCronJobListPage/index.tsx +++ b/estela-web/src/pages/ProjectCronJobListPage/index.tsx @@ -1,6 +1,6 @@ import React, { Component, ReactElement } from "react"; -import { Layout, Pagination, Row, Space, Table, Col, Button, Switch, Tag, message, ConfigProvider } from "antd"; -import { Link, RouteComponentProps } from "react-router-dom"; +import { Layout, Row, Col, message, Switch, ConfigProvider, Table, Space, Button, Pagination, Tag } from "antd"; +import { RouteComponentProps, Link } from "react-router-dom"; import "./styles.scss"; import history from "../../history"; import { ApiService } from "../../services"; @@ -10,21 +10,18 @@ import { ApiProjectsSpidersCronjobsRunOnceRequest, SpiderCronJobUpdateStatusEnum, ApiProjectsCronjobsRequest, - SpiderCronJobStatusEnum, ApiProjectsReadRequest, ProjectCronJob, SpiderCronJob, + SpiderJobArg, + SpiderJobTag, Project, + SpiderCronJobStatusEnum, } from "../../services/api"; -import { - resourceNotAllowedNotification, - incorrectDataNotification, - Spin, - PaginationItem, - RouteParams, -} from "../../shared"; +import { resourceNotAllowedNotification, incorrectDataNotification, Spin, RouteParams } from "../../shared"; import CronjobCreateModal from "../CronjobCreateModal"; import { convertDateToString } from "../../utils"; +import { PaginationItem } from "../../components"; const { Content } = Layout; @@ -33,30 +30,17 @@ interface SpiderData { name: string; } -interface BaseInfo { - spider: SpiderData; - cid: number | undefined; -} - -interface TagsData { - name: string; -} - -interface Args { - name: string; - value: string; -} - interface SpiderCronJobData { - id: BaseInfo; + cjid: number | undefined; + spider: SpiderData; key: number | undefined; date: string; status: string | undefined; schedule: string | undefined; dataExpiryDays: number | undefined | null; dataStatus: string | undefined; - tags: TagsData[] | undefined; - args: Args[] | undefined; + tags: SpiderJobTag[] | undefined; + args: SpiderJobArg[] | undefined; } interface ProjectCronJobListPageState { @@ -125,88 +109,6 @@ export class ProjectCronJobListPage extends Component { - return ( - this.updateStatus(cronjob)} - /> - ); - }, - }, - { - title: "SCHEDULED JOB", - dataIndex: "info", - key: "info", - render: (info: BaseInfo): ReactElement => ( - - Sche-Job-{info.cid} - - ), - }, - { - title: "SPIDER", - dataIndex: "info", - key: "info", - render: (info: BaseInfo): ReactElement => ( - - {info.spider.name} - - ), - }, - { - title: "ARGUMENTS", - dataIndex: "args", - key: "args", - render: (args: Args[]): ReactElement => ( - - {args.map((arg: Args, id: number) => ( - - {arg.name}: {arg.value} - - ))} - - ), - }, - { - title: "EXPRESSION", - key: "schedule", - dataIndex: "schedule", - }, - { - title: "TAGS", - key: "tags", - dataIndex: "tags", - render: (tags: TagsData[]): ReactElement => ( - - {tags.map((tag: TagsData, id) => ( - - {tag.name} - - ))} - - ), - }, - { - title: "PERSISTENCE", - key: "dataExpiryDays", - dataIndex: "dataExpiryDays", - render: (dataExpiryDays: number, cronjob: SpiderCronJobData): ReactElement => ( -

{cronjob.dataStatus == "PENDING" ? `${dataExpiryDays} days` : "Forever"}

- ), - }, - ]; - async componentDidMount(): Promise { const requestParams: ApiProjectsReadRequest = { pid: this.projectId }; this.apiService.apiProjectsRead(requestParams).then( @@ -230,8 +132,9 @@ export class ProjectCronJobListPage extends Component { const data = response.results.map((cronjob: SpiderCronJob, iterator: number) => ({ + cjid: cronjob.cjid, key: iterator, - info: { spider: cronjob.spider, cid: cronjob.cjid }, + spider: cronjob.spider as unknown as SpiderData, date: convertDateToString(cronjob.created), status: cronjob.status, schedule: cronjob.schedule, @@ -251,8 +154,8 @@ export class ProjectCronJobListPage extends Component { - history.push(`/projects/${this.projectId}/spiders/${cronjob.info.spider.sid}/cronjobs/${cronjob.info.cid}`); + history.push(`/projects/${this.projectId}/spiders/${cronjob.spider.sid}/cronjobs/${cronjob.cjid}`); }; runOnce = (cronjob: SpiderCronJobData): void => { const requestParams: ApiProjectsSpidersCronjobsRunOnceRequest = { pid: this.projectId, - sid: String(cronjob.info.spider.sid), - cjid: Number(cronjob.info.cid), + sid: String(cronjob.spider.sid), + cjid: Number(cronjob.cjid), }; this.apiService.apiProjectsSpidersCronjobsRunOnce(requestParams).then( async (response: SpiderCronJob) => { @@ -315,8 +218,8 @@ export class ProjectCronJobListPage extends Component { const requestParams: ApiProjectsSpidersCronjobsDeleteRequest = { pid: this.projectId, - sid: String(cronjob.info.spider.sid), - cjid: Number(cronjob.info.cid), + sid: String(cronjob.spider.sid), + cjid: Number(cronjob.cjid), }; this.apiService.apiProjectsSpidersCronjobsDelete(requestParams).then( () => { @@ -334,6 +237,88 @@ export class ProjectCronJobListPage extends Component { + return ( + this.updateStatus(cronjob)} + /> + ); + }, + }, + { + title: "SCHEDULED JOB", + dataIndex: "cjid", + key: "cjid", + render: (cjid: number, record: SpiderCronJobData): ReactElement => ( + + Sche-Job-{cjid} + + ), + }, + { + title: "SPIDER", + dataIndex: "spider", + key: "spider", + render: (spider: SpiderData): ReactElement => ( + + {spider.name} + + ), + }, + { + title: "ARGUMENTS", + dataIndex: "args", + key: "args", + render: (args: SpiderJobArg[]): ReactElement => ( + + {args.map((arg: SpiderJobArg, id: number) => ( + + {arg.name}: {arg.value} + + ))} + + ), + }, + { + title: "EXPRESSION", + key: "schedule", + dataIndex: "schedule", + }, + { + title: "TAGS", + key: "tags", + dataIndex: "tags", + render: (tags: SpiderJobTag[]): ReactElement => ( + + {tags.map((tag: SpiderJobTag, id) => ( + + {tag.name} + + ))} + + ), + }, + { + title: "PERSISTENCE", + key: "dataExpiryDays", + dataIndex: "dataExpiryDays", + render: (dataExpiryDays: number, cronjob: SpiderCronJobData): ReactElement => ( +

{cronjob.dataStatus == "PENDING" ? `${dataExpiryDays} days` : "Forever"}

+ ), + }, + ]; + render(): JSX.Element { const { loadedCronjobs, cronjobs, selectedRows, count, current } = this.state; return ( @@ -354,22 +339,24 @@ export class ProjectCronJobListPage extends Component - +

No scheduled jobs yet.

}> -
+ +
+ - - - + + - {tableStatus[waiting] && ( - - - - - Waiting - - - {waitingJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[queued] && ( - - - - - In queue - - - {queueJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[running] && ( - - - - - Running - - - {runningJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[completed] && ( - - - - - Finished - - - {completedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[stopped] && ( - - - - - Stopped - - - {stoppedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[withError] && ( - - - - - Error - - - {errorJobs.length} - - - - - - - - -
- - - - - - - )} - - - - - - - STATUS - - this.onChangeStatus(waiting, waitingJobs.length)} - > - - - Waiting - - - {waitingJobs.length} - - - -
- this.onChangeStatus(queued, queueJobs.length)} - > - - - Queue - - - {queueJobs.length} - - - -
- this.onChangeStatus(running, runningJobs.length)} - > - - - Running - - - {runningJobs.length} - - - -
- this.onChangeStatus(completed, completedJobs.length)} - > - - - Completed - - - {completedJobs.length} - - - -
- this.onChangeStatus(stopped, stoppedJobs.length)} - > - - - Stopped - - - {stoppedJobs.length} - - - -
- this.onChangeStatus(withError, errorJobs.length)} - > - - - Error - - - {errorJobs.length} - - - -
-
- - + ) : ( diff --git a/estela-web/src/pages/SpiderDetailPage/index.tsx b/estela-web/src/pages/SpiderDetailPage/index.tsx index fcc8694c..d32380b9 100644 --- a/estela-web/src/pages/SpiderDetailPage/index.tsx +++ b/estela-web/src/pages/SpiderDetailPage/index.tsx @@ -1,19 +1,5 @@ -import React, { Component, ReactElement } from "react"; -import { - Button, - Layout, - Pagination, - Typography, - message, - Row, - Table, - Col, - Tabs, - Radio, - Checkbox, - Space, - Tag, -} from "antd"; +import React, { Component } from "react"; +import { Button, Layout, Typography, message, Row, Col, Tabs, Radio } from "antd"; import type { RadioChangeEvent } from "antd"; import { RouteComponentProps, Link } from "react-router-dom"; import { EnvVarsSetting } from "../../components/EnvVarsSettingsPage"; @@ -22,8 +8,6 @@ import "./styles.scss"; import CronjobCreateModal from "../CronjobCreateModal"; import JobCreateModal from "../JobCreateModal"; import { ApiService } from "../../services"; -import Setting from "../../assets/icons/setting.svg"; -import Filter from "../../assets/icons/filter.svg"; import { ApiProjectsSpidersReadRequest, @@ -40,25 +24,29 @@ import { SpiderJobTag, SpiderJobEnvVar, } from "../../services/api"; -import { resourceNotAllowedNotification, Spin, PaginationItem } from "../../shared"; +import { resourceNotAllowedNotification, Spin } from "../../shared"; import { convertDateToString, handleInvalidDataError } from "../../utils"; +import { JobsList } from "../../components"; const { Content } = Layout; const { Text } = Typography; -const waiting = 0; -const queued = 1; -const running = 2; -const completed = 3; -const stopped = 4; -const withError = 5; +interface SpiderData { + sid: number; + name: string; +} + +interface BaseInfo { + jid: number | undefined; + spider: SpiderData; + cid?: number | null | undefined; +} interface SpiderJobData { - id: number | null | undefined; - key: number | null | undefined; - spider: string | undefined; - jobStatus: string | null | undefined; - cronjob: number | null | undefined; + info: BaseInfo; + key: number | undefined; + date: string; + status: string | undefined; args: SpiderJobArg[] | undefined; tags: SpiderJobTag[] | undefined; } @@ -134,91 +122,21 @@ export class SpiderDetailPage extends Component projectId: string = this.props.match.params.projectId; spiderId: string = this.props.match.params.spiderId; - columns = [ - { - title: "JOB", - dataIndex: "id", - key: "id", - render: (jobID: number): ReactElement => ( - - Job-{jobID} - - ), - }, - { - title: "SCHEDULED JOB", - dataIndex: "cronjob", - key: "cronjob", - render: (cronjob: number): ReactElement => - cronjob ? ( - - Sche-Job-{cronjob} - - ) : ( - Not associated - ), - }, - { - title: "ARGUMENTS", - dataIndex: "args", - key: "args", - render: (args: SpiderJobArg[]): ReactElement => - args.length !== 0 ? ( - <> - {args.map((arg, index) => { - return ( - - {arg.name}={arg.value} - - ); - })} - - ) : ( - <> - ), - }, - { - title: "TAGS", - dataIndex: "tags", - key: "tags", - render: (tags: SpiderJobTag[]): ReactElement => - tags.length !== 0 ? ( - <> - {tags.map((tag, index) => { - return ( - - {tag.name} - - ); - })} - - ) : ( - <> - ), - }, - ]; - async componentDidMount(): Promise { const requestParams: ApiProjectsSpidersReadRequest = { pid: this.projectId, sid: parseInt(this.spiderId) }; this.apiService.apiProjectsSpidersRead(requestParams).then( async (response: Spider) => { const data = await this.getSpiderJobs(1); - const waitingJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "WAITING"); - const queueJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "IN_QUEUE"); - const runningJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "RUNNING"); - const completedJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "COMPLETED"); - const stoppedJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "STOPPED"); - const errorJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "ERROR"); + const waitingJobs = data.data.filter((job: SpiderJobData) => job.status === "WAITING"); + const queueJobs = data.data.filter((job: SpiderJobData) => job.status === "IN_QUEUE"); + const runningJobs = data.data.filter((job: SpiderJobData) => job.status === "RUNNING"); + const completedJobs = data.data.filter((job: SpiderJobData) => job.status === "COMPLETED"); + const stoppedJobs = data.data.filter((job: SpiderJobData) => job.status === "STOPPED"); + const errorJobs = data.data.filter((job: SpiderJobData) => job.status === "ERROR"); const scheduledJobsCount = data.data - .filter((job: SpiderJobData) => job.cronjob !== null && job.cronjob !== undefined) - .map((job: SpiderJobData) => job.cronjob) + .filter((job: SpiderJobData) => job.info.cid !== null && job.info.cid !== undefined) + .map((job: SpiderJobData) => job.info.cid) .filter((cronjob, index, self) => { return self.indexOf(cronjob) === index; }).length; @@ -298,14 +216,17 @@ export class SpiderDetailPage extends Component pageSize: this.PAGE_SIZE, }; const response = await this.apiService.apiProjectsSpidersJobsList(requestParams); - const data = response.results.map((job: SpiderJob, iterator: number) => ({ + const data: SpiderJobData[] = response.results.map((job: SpiderJob, iterator: number) => ({ key: iterator, - id: job.jid, - spider: job.spider, + info: { + jid: job.jid, + spider: job.spider as unknown as SpiderData, + cid: job.cronjob, + }, args: job.args, + date: convertDateToString(job.created), tags: job.tags, - jobStatus: job.jobStatus, - cronjob: job.cronjob, + status: job.jobStatus, })); return { data, count: response.count, current: page }; }; @@ -345,12 +266,12 @@ export class SpiderDetailPage extends Component this.setState({ loaded: false }); const data = await this.getSpiderJobs(page); - const waitingJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "WAITING"); - const queueJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "IN_QUEUE"); - const runningJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "RUNNING"); - const completedJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "COMPLETED"); - const stoppedJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "STOPPED"); - const errorJobs = data.data.filter((job: SpiderJobData) => job.jobStatus === "ERROR"); + const waitingJobs = data.data.filter((job: SpiderJobData) => job.status === "WAITING"); + const queueJobs = data.data.filter((job: SpiderJobData) => job.status === "IN_QUEUE"); + const runningJobs = data.data.filter((job: SpiderJobData) => job.status === "RUNNING"); + const completedJobs = data.data.filter((job: SpiderJobData) => job.status === "COMPLETED"); + const stoppedJobs = data.data.filter((job: SpiderJobData) => job.status === "STOPPED"); + const errorJobs = data.data.filter((job: SpiderJobData) => job.status === "ERROR"); const tableStatus = [ !(waitingJobs.length === 0), @@ -459,413 +380,21 @@ export class SpiderDetailPage extends Component - - - {tableStatus[waiting] && ( - - - - - Waiting - - - {waitingJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[queued] && ( - - - - - In queue - - - {queueJobs.length} - - - - - - - - -
- - - - - - - - )} - {tableStatus[running] && ( - - - - - Running - - - {runningJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[completed] && ( - - - - - Completed - - - {completedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[stopped] && ( - - - - - Stopped - - - {stoppedJobs.length} - - - - - - - - -
- - - - - - - )} - {tableStatus[withError] && ( - - - - Error - - {errorJobs.length} - - - - - - - - -
- - - - - - - )} - - - - - - - STATUS - - this.onChangeStatus(waiting, waitingJobs.length)} - > - - Waiting - - {waitingJobs.length} - - - -
- this.onChangeStatus(queued, queueJobs.length)} - > - - In queue - - {queueJobs.length} - - - -
- this.onChangeStatus(running, runningJobs.length)} - > - - Running - - {runningJobs.length} - - - -
- this.onChangeStatus(completed, completedJobs.length)} - > - - Completed - - {completedJobs.length} - - - -
- this.onChangeStatus(stopped, stoppedJobs.length)} - > - - Stopped - - {stoppedJobs.length} - - - -
- this.onChangeStatus(withError, errorJobs.length)} - > - - Error - - {errorJobs.length} - - - -
-
- - + ); };