This is an automated email from the ASF dual-hosted git repository. mintsweet pushed a commit to branch feat-3673 in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit 3ad660599f04260c04cb58a436f0a36d7224a39e Author: mintsweet <[email protected]> AuthorDate: Mon Sep 4 14:20:10 2023 +1200 refactor(config-ui): the content about pipeline --- .../src/pages/blueprint/detail/status-panel.tsx | 68 +++++-- config-ui/src/pages/index.ts | 1 - .../src/pages/pipeline/components/cancel/index.tsx | 54 ------ .../src/pages/pipeline/components/info/styled.ts | 57 ------ .../src/pages/pipeline/components/rerun/index.tsx | 58 ------ .../src/pages/pipeline/components/status/index.tsx | 75 -------- .../src/pages/pipeline/components/status/styled.ts | 39 ---- .../src/pages/pipeline/components/task/styled.ts | 80 -------- .../src/pages/pipeline/components/tasks/styled.ts | 87 --------- config-ui/src/pages/project/home/api.ts | 4 +- config-ui/src/pages/project/home/index.tsx | 2 +- config-ui/src/{pages => routes}/pipeline/api.ts | 16 +- .../pipeline/components/duration.tsx} | 14 +- .../{pages => routes}/pipeline/components/index.ts | 5 +- .../pipeline/components/info.tsx} | 74 +++++--- .../src/routes/pipeline/components/status.tsx | 51 ++++++ .../pipeline/components/table.tsx} | 85 ++++----- .../pipeline/components/task.tsx} | 62 ++++--- .../pipeline/components/tasks.tsx} | 47 ++--- config-ui/src/routes/pipeline/constant.ts | 43 +++++ config-ui/src/{pages => routes}/pipeline/index.ts | 3 +- .../index.tsx => routes/pipeline/pipeline.tsx} | 40 ++-- config-ui/src/routes/pipeline/pipelines.tsx | 56 ++++++ config-ui/src/routes/pipeline/styled.ts | 202 +++++++++++++++++++++ config-ui/src/{pages => routes}/pipeline/types.ts | 10 +- 25 files changed, 608 insertions(+), 625 deletions(-) diff --git a/config-ui/src/pages/blueprint/detail/status-panel.tsx b/config-ui/src/pages/blueprint/detail/status-panel.tsx index 506380545..11320384f 100644 --- a/config-ui/src/pages/blueprint/detail/status-panel.tsx +++ b/config-ui/src/pages/blueprint/detail/status-panel.tsx @@ -23,7 +23,9 @@ import { Tooltip2 } from '@blueprintjs/popover2'; import { Card, IconButton, Dialog } from '@/components'; import { getCron } from '@/config'; -import { PipelineContextProvider, PipelineInfo, PipelineTasks, PipelineHistorical } from '@/pages'; +import { useAutoRefresh } from '@/hooks'; +import * as PipelineT from '@/routes/pipeline/types'; +import { PipelineInfo, PipelineTasks, PipelineTable } from '@/routes/pipeline'; import { formatTime, operator } from '@/utils'; import { BlueprintType, FromEnum } from '../types'; @@ -46,6 +48,28 @@ export const StatusPanel = ({ from, blueprint, pipelineId, onRefresh }: Props) = const cron = useMemo(() => getCron(blueprint.isManual, blueprint.cronConfig), [blueprint]); + const { loading, data } = useAutoRefresh<PipelineT.Pipeline[]>( + async () => { + const res = await API.getBlueprintPipelines(blueprint.id); + return res.pipelines; + }, + [], + { + cancel: (data) => + !!( + data && + data.every((it) => + [ + PipelineT.PipelineStatus.COMPLETED, + PipelineT.PipelineStatus.PARTIAL, + PipelineT.PipelineStatus.CANCELLED, + PipelineT.PipelineStatus.FAILED, + ].includes(it.status), + ) + ), + }, + ); + const handleShowDeleteDialog = () => { setIsOpen(true); }; @@ -143,25 +167,31 @@ export const StatusPanel = ({ from, blueprint, pipelineId, onRefresh }: Props) = </S.BlueprintAction> )} - <PipelineContextProvider> - <div className="block"> - <h3>Current Pipeline</h3> - {!pipelineId ? ( - <Card>There is no current run for this blueprint.</Card> - ) : ( - <> + {/* <PipelineContextProvider> */} + <div className="block"> + <h3>Current Pipeline</h3> + {!pipelineId ? ( + <Card>There is no current run for this blueprint.</Card> + ) : ( + <> + <Card> <PipelineInfo id={pipelineId} /> - <Card style={{ marginTop: 16 }}> - <PipelineTasks id={pipelineId} /> - </Card> - </> - )} - </div> - <div className="block"> - <h3>Historical Pipelines</h3> - <PipelineHistorical blueprintId={blueprint.id} /> - </div> - </PipelineContextProvider> + </Card> + <Card> + <PipelineTasks id={pipelineId} /> + </Card> + </> + )} + </div> + <div className="block"> + <h3>Historical Pipelines</h3> + {!data?.length ? ( + <Card>There are no historical runs associated with this blueprint.</Card> + ) : ( + <PipelineTable loading={loading} dataSource={data} /> + )} + </div> + {/* </PipelineContextProvider> */} <Dialog isOpen={isOpen} diff --git a/config-ui/src/pages/index.ts b/config-ui/src/pages/index.ts index 3554c4b95..555e0869f 100644 --- a/config-ui/src/pages/index.ts +++ b/config-ui/src/pages/index.ts @@ -18,5 +18,4 @@ export * from './blueprint'; export * from './connection'; -export * from './pipeline'; export * from './project'; diff --git a/config-ui/src/pages/pipeline/components/cancel/index.tsx b/config-ui/src/pages/pipeline/components/cancel/index.tsx deleted file mode 100644 index 3dc22308a..000000000 --- a/config-ui/src/pages/pipeline/components/cancel/index.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { useState } from 'react'; - -import { IconButton } from '@/components'; -import { operator } from '@/utils'; - -import { StatusEnum } from '../../types'; -import * as API from '../../api'; - -import { usePipeline } from '../context'; - -interface Props { - id: ID; - status: StatusEnum; -} - -export const PipelineCancel = ({ id, status }: Props) => { - const [canceling, setCanceling] = useState(false); - - const { setVersion } = usePipeline(); - - const handleSubmit = async () => { - const [success] = await operator(() => API.deletePipeline(id), { - setOperating: setCanceling, - }); - - if (success) { - setVersion((v) => v + 1); - } - }; - - if (![StatusEnum.ACTIVE, StatusEnum.RUNNING, StatusEnum.RERUN].includes(status)) { - return null; - } - - return <IconButton loading={canceling} icon="disable" tooltip="Cancel" onClick={handleSubmit} />; -}; diff --git a/config-ui/src/pages/pipeline/components/info/styled.ts b/config-ui/src/pages/pipeline/components/info/styled.ts deleted file mode 100644 index de9a3e7f3..000000000 --- a/config-ui/src/pages/pipeline/components/info/styled.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Colors } from '@blueprintjs/core'; -import styled from 'styled-components'; - -import { Card } from '@/components'; - -export const Wrapper = styled(Card)` - ul { - display: flex; - align-items: center; - } - - li { - flex: 5; - display: flex; - flex-direction: column; - - &:last-child { - flex: 1; - } - - & > span { - font-size: 12px; - color: #94959f; - text-align: center; - } - - & > strong { - display: flex; - align-items: center; - justify-content: center; - margin-top: 8px; - } - } - - p.message { - margin: 8px 0 0; - color: ${Colors.RED3}; - } -`; diff --git a/config-ui/src/pages/pipeline/components/rerun/index.tsx b/config-ui/src/pages/pipeline/components/rerun/index.tsx deleted file mode 100644 index 7f1eb1445..000000000 --- a/config-ui/src/pages/pipeline/components/rerun/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -import { useState } from 'react'; - -import { IconButton } from '@/components'; -import { operator } from '@/utils'; - -import { StatusEnum } from '../../types'; -import * as API from '../../api'; - -import { usePipeline } from '../context'; - -interface Props { - type: 'pipeline' | 'task'; - id: ID; - status: StatusEnum; -} - -export const PipelineRerun = ({ type, id, status }: Props) => { - const [reruning, setReruning] = useState(false); - - const { setVersion } = usePipeline(); - - const handleSubmit = async () => { - const [success] = await operator(() => (type === 'task' ? API.taskRerun(id) : API.pipelineRerun(id)), { - setOperating: setReruning, - }); - - if (success) { - setVersion((v) => v + 1); - } - }; - - if (![StatusEnum.COMPLETED, StatusEnum.PARTIAL, StatusEnum.FAILED, StatusEnum.CANCELLED].includes(status)) { - return null; - } - - if (type === 'task') { - return <IconButton loading={reruning} icon="repeat" tooltip="Rerun task" onClick={handleSubmit} />; - } - - return <IconButton loading={reruning} icon="repeat" tooltip="Rerun failed tasks" onClick={handleSubmit} />; -}; diff --git a/config-ui/src/pages/pipeline/components/status/index.tsx b/config-ui/src/pages/pipeline/components/status/index.tsx deleted file mode 100644 index 72ced128b..000000000 --- a/config-ui/src/pages/pipeline/components/status/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Icon, IconName } from '@blueprintjs/core'; -import classNames from 'classnames'; - -import { Loading } from '@/components'; - -import { StatusEnum } from '../../types'; - -import * as S from './styled'; - -export const STATUS_ICON = { - [StatusEnum.CREATED]: 'stopwatch', - [StatusEnum.PENDING]: 'stopwatch', - [StatusEnum.ACTIVE]: 'loading', - [StatusEnum.RUNNING]: 'loading', - [StatusEnum.RERUN]: 'loading', - [StatusEnum.COMPLETED]: 'tick-circle', - [StatusEnum.PARTIAL]: 'tick-circle', - [StatusEnum.FAILED]: 'delete', - [StatusEnum.CANCELLED]: 'undo', -}; - -export const STATUS_LABEL = { - [StatusEnum.CREATED]: 'Created (Pending)', - [StatusEnum.PENDING]: 'Created (Pending)', - [StatusEnum.ACTIVE]: 'In Progress', - [StatusEnum.RUNNING]: 'In Progress', - [StatusEnum.RERUN]: 'In Progress', - [StatusEnum.COMPLETED]: 'Succeeded', - [StatusEnum.PARTIAL]: 'Partial Success', - [StatusEnum.FAILED]: 'Failed', - [StatusEnum.CANCELLED]: 'Cancelled', -}; - -interface Props { - status: StatusEnum; -} - -export const PipelineStatus = ({ status }: Props) => { - const statusCls = classNames({ - ready: [StatusEnum.CREATED, StatusEnum.PENDING].includes(status), - loading: [StatusEnum.ACTIVE, StatusEnum.RUNNING, StatusEnum.RERUN].includes(status), - success: [StatusEnum.COMPLETED, StatusEnum.PARTIAL].includes(status), - error: status === StatusEnum.FAILED, - cancel: status === StatusEnum.CANCELLED, - }); - - return ( - <S.Wrapper className={statusCls}> - {STATUS_ICON[status] === 'loading' ? ( - <Loading style={{ marginRight: 4 }} size={14} /> - ) : ( - <Icon style={{ marginRight: 4 }} icon={STATUS_ICON[status] as IconName} /> - )} - <span>{STATUS_LABEL[status]}</span> - </S.Wrapper> - ); -}; diff --git a/config-ui/src/pages/pipeline/components/status/styled.ts b/config-ui/src/pages/pipeline/components/status/styled.ts deleted file mode 100644 index 87ed2db8c..000000000 --- a/config-ui/src/pages/pipeline/components/status/styled.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Colors } from '@blueprintjs/core'; -import styled from 'styled-components'; - -export const Wrapper = styled.div` - &.ready, - &.cancel { - color: #94959f; - } - - &.loading { - color: #7497f7; - } - - &.success { - color: ${Colors.GREEN3}; - } - - &.error { - color: ${Colors.RED3}; - } -`; diff --git a/config-ui/src/pages/pipeline/components/task/styled.ts b/config-ui/src/pages/pipeline/components/task/styled.ts deleted file mode 100644 index 35b153954..000000000 --- a/config-ui/src/pages/pipeline/components/task/styled.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { Colors } from '@blueprintjs/core'; -import styled from 'styled-components'; - -export const Wrapper = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 0; - height: 80px; - border-bottom: 1px solid #dbe4fd; - box-sizing: border-box; -`; - -export const Info = styled.div` - flex: auto; - overflow: hidden; - - .title { - display: flex; - align-items: center; - margin-bottom: 8px; - - & > img { - width: 20px; - } - - & > strong { - margin: 0 4px; - } - - & > span { - flex: auto; - overflow: hidden; - } - } - - p { - padding-left: 26px; - margin: 0; - font-size: 12px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - - &.error { - color: ${Colors.RED3}; - } - } -`; - -export const Duration = styled.div` - display: flex; - flex-direction: column; - align-items: center; - flex: 0 0 80px; - text-align: right; - - .bp4-icon { - margin-top: 4px; - cursor: pointer; - } -`; diff --git a/config-ui/src/pages/pipeline/components/tasks/styled.ts b/config-ui/src/pages/pipeline/components/tasks/styled.ts deleted file mode 100644 index d6def19bb..000000000 --- a/config-ui/src/pages/pipeline/components/tasks/styled.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import styled from 'styled-components'; - -export const Wrapper = styled.div` - position: relative; - padding-right: 36px; - - .collapse-control { - position: absolute; - right: 0; - top: 0; - } -`; - -export const Inner = styled.div` - overflow: auto; -`; - -export const Header = styled.ul` - display: flex; - align-items: center; - - li { - display: flex; - justify-content: space-between; - align-items: center; - flex: 0 0 30%; - padding: 8px 12px; - - &.ready, - &.cancel { - color: #94959f; - background-color: #f9f9fa; - } - - &.loading { - color: #7497f7; - background-color: #e9efff; - } - - &.success { - color: #4db764; - background-color: #edfbf0; - } - - &.error { - color: #e34040; - background-color: #feefef; - } - } - - li + li { - margin-left: 16px; - } -`; - -export const Tasks = styled.ul` - display: flex; - align-items: flex-start; - - li { - flex: 0 0 30%; - padding-bottom: 8px; - overflow: hidden; - } - - li + li { - margin-left: 16px; - } -`; diff --git a/config-ui/src/pages/project/home/api.ts b/config-ui/src/pages/project/home/api.ts index 539e91b38..9a9a7d09b 100644 --- a/config-ui/src/pages/project/home/api.ts +++ b/config-ui/src/pages/project/home/api.ts @@ -19,7 +19,7 @@ import { request } from '@/utils'; import type { BlueprintType } from '@/pages/blueprint'; -import type { PipelineType } from '@/pages/pipeline'; +import * as T from '@/routes/pipeline/types'; type GetProjectsParams = { page: number; @@ -31,7 +31,7 @@ type GetProjectsResponse = { name: string; createdAt: string; blueprint: BlueprintType; - lastPipeline: PipelineType; + lastPipeline: T.Pipeline; }>; count: number; }; diff --git a/config-ui/src/pages/project/home/index.tsx b/config-ui/src/pages/project/home/index.tsx index c9dd39005..79dca269c 100644 --- a/config-ui/src/pages/project/home/index.tsx +++ b/config-ui/src/pages/project/home/index.tsx @@ -26,7 +26,7 @@ import { getCron, cronPresets } from '@/config'; import { useConnections, useRefreshData } from '@/hooks'; import { DOC_URL } from '@/release'; import { formatTime, operator } from '@/utils'; -import { PipelineStatus } from '@/pages/pipeline'; +import { PipelineStatus } from '@/routes/pipeline'; import { validName, encodeName } from '../utils'; import { BlueprintType, ModeEnum } from '../../blueprint'; diff --git a/config-ui/src/pages/pipeline/api.ts b/config-ui/src/routes/pipeline/api.ts similarity index 88% rename from config-ui/src/pages/pipeline/api.ts rename to config-ui/src/routes/pipeline/api.ts index f00fbf623..261e04a99 100644 --- a/config-ui/src/pages/pipeline/api.ts +++ b/config-ui/src/routes/pipeline/api.ts @@ -18,25 +18,27 @@ import { request } from '@/utils'; -export const getPipeline = (id: ID) => request(`/pipelines/${id}`); +import * as T from './types'; -export const getPipelineTasks = (id: ID) => request(`/pipelines/${id}/tasks`); +export const getPipelines = (): Promise<{ count: number; pipelines: T.Pipeline[] }> => request('/pipelines'); + +export const getPipeline = (id: ID) => request(`/pipelines/${id}`); export const deletePipeline = (id: ID) => request(`/pipelines/${id}`, { method: 'delete', }); -export const getPipelineHistorical = (id: ID) => request(`/blueprints/${id}/pipelines`); - -export const pipelineRerun = (id: ID) => +export const rerunPipeline = (id: ID) => request(`/pipelines/${id}/rerun`, { method: 'post', }); +export const getPipelineLog = (id: ID) => request(`/pipelines/${id}/logging.tar.gz`); + +export const getPipelineTasks = (id: ID) => request(`/pipelines/${id}/tasks`); + export const taskRerun = (id: ID) => request(`/tasks/${id}/rerun`, { method: 'post', }); - -export const getPipelineLog = (id: ID) => request(`/pipelines/${id}/logging.tar.gz`); diff --git a/config-ui/src/pages/pipeline/components/duration/index.tsx b/config-ui/src/routes/pipeline/components/duration.tsx similarity index 83% rename from config-ui/src/pages/pipeline/components/duration/index.tsx rename to config-ui/src/routes/pipeline/components/duration.tsx index f154e5a19..829cb548c 100644 --- a/config-ui/src/pages/pipeline/components/duration/index.tsx +++ b/config-ui/src/routes/pipeline/components/duration.tsx @@ -16,13 +16,12 @@ * */ -import React from 'react'; import dayjs from 'dayjs'; -import { StatusEnum } from '../../types'; +import * as T from '../types'; interface Props { - status: StatusEnum; + status: T.PipelineStatus; beganAt: string | null; finishedAt: string | null; } @@ -32,7 +31,14 @@ export const PipelineDuration = ({ status, beganAt, finishedAt }: Props) => { return <span>-</span>; } - if (![StatusEnum.CANCELLED, StatusEnum.COMPLETED, StatusEnum.PARTIAL, StatusEnum.FAILED].includes(status)) { + if ( + ![ + T.PipelineStatus.CANCELLED, + T.PipelineStatus.COMPLETED, + T.PipelineStatus.PARTIAL, + T.PipelineStatus.FAILED, + ].includes(status) + ) { return <span>{dayjs(beganAt).toNow(true)}</span>; } diff --git a/config-ui/src/pages/pipeline/components/index.ts b/config-ui/src/routes/pipeline/components/index.ts similarity index 93% rename from config-ui/src/pages/pipeline/components/index.ts rename to config-ui/src/routes/pipeline/components/index.ts index 8d5dc2b64..fca3387b9 100644 --- a/config-ui/src/pages/pipeline/components/index.ts +++ b/config-ui/src/routes/pipeline/components/index.ts @@ -16,8 +16,7 @@ * */ -export * from './context'; -export * from './historical'; -export * from './info'; export * from './status'; +export * from './table'; +export * from './info'; export * from './tasks'; diff --git a/config-ui/src/pages/pipeline/components/info/index.tsx b/config-ui/src/routes/pipeline/components/info.tsx similarity index 51% rename from config-ui/src/pages/pipeline/components/info/index.tsx rename to config-ui/src/routes/pipeline/components/info.tsx index 0ee64a87d..7399b98ad 100644 --- a/config-ui/src/pages/pipeline/components/info/index.tsx +++ b/config-ui/src/routes/pipeline/components/info.tsx @@ -16,41 +16,60 @@ * */ -import React from 'react'; +import { useState } from 'react'; -import { Loading } from '@/components'; +import { Loading, IconButton } from '@/components'; import { useAutoRefresh } from '@/hooks'; -import { formatTime } from '@/utils'; +import { formatTime, operator } from '@/utils'; -import type { PipelineType } from '../../types'; -import { StatusEnum } from '../../types'; -import * as API from '../../api'; +import * as T from '../types'; +import * as S from '../styled'; +import * as API from '../api'; -import { usePipeline } from '../context'; -import { PipelineStatus } from '../status'; -import { PipelineDuration } from '../duration'; -import { PipelineCancel } from '../cancel'; -import { PipelineRerun } from '../rerun'; - -import * as S from './styled'; +import { PipelineStatus } from './status'; +import { PipelineDuration } from './duration'; interface Props { id: ID; - style?: React.CSSProperties; } -export const PipelineInfo = ({ id, style }: Props) => { - const { version } = usePipeline(); +export const PipelineInfo = ({ id }: Props) => { + const [operating, setOperating] = useState(false); - const { data } = useAutoRefresh<PipelineType>(() => API.getPipeline(id), [version], { + const { data } = useAutoRefresh<T.Pipeline>(() => API.getPipeline(id), [], { cancel: (data) => { return !!( data && - [StatusEnum.COMPLETED, StatusEnum.PARTIAL, StatusEnum.FAILED, StatusEnum.CANCELLED].includes(data.status) + [ + T.PipelineStatus.COMPLETED, + T.PipelineStatus.PARTIAL, + T.PipelineStatus.FAILED, + T.PipelineStatus.CANCELLED, + ].includes(data.status) ); }, }); + const handleCancel = async () => { + const [success] = await operator(() => API.deletePipeline(id), { + setOperating, + }); + + // if (success) { + // setVersion((v) => v + 1); + // } + }; + + const handleRerun = async () => { + const [success] = await operator(() => API.rerunPipeline(id), { + setOperating, + }); + + // if (success) { + // setVersion((v) => v + 1); + // } + }; + if (!data) { return <Loading />; } @@ -58,7 +77,7 @@ export const PipelineInfo = ({ id, style }: Props) => { const { status, beganAt, finishedAt, stage, finishedTasks, totalTasks, message } = data; return ( - <S.Wrapper style={style}> + <S.Info> <ul> <li> <span>Status</span> @@ -87,11 +106,20 @@ export const PipelineInfo = ({ id, style }: Props) => { </strong> </li> <li> - <PipelineCancel id={id} status={status} /> - <PipelineRerun type="pipeline" id={id} status={status} /> + {[T.PipelineStatus.ACTIVE, T.PipelineStatus.RUNNING, T.PipelineStatus.RERUN].includes(status) && ( + <IconButton loading={operating} icon="disable" tooltip="Cancel" onClick={handleCancel} /> + )} + {[ + T.PipelineStatus.COMPLETED, + T.PipelineStatus.PARTIAL, + T.PipelineStatus.FAILED, + T.PipelineStatus.CANCELLED, + ].includes(status) && ( + <IconButton loading={operating} icon="repeat" tooltip="Rerun failed tasks" onClick={handleRerun} /> + )} </li> </ul> - {StatusEnum.FAILED === status && <p className="'message'">{message}</p>} - </S.Wrapper> + {T.PipelineStatus.FAILED === status && <p className="'message'">{message}</p>} + </S.Info> ); }; diff --git a/config-ui/src/routes/pipeline/components/status.tsx b/config-ui/src/routes/pipeline/components/status.tsx new file mode 100644 index 000000000..b07226672 --- /dev/null +++ b/config-ui/src/routes/pipeline/components/status.tsx @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Icon, IconName } from '@blueprintjs/core'; +import classNames from 'classnames'; + +import { Loading } from '@/components'; + +import * as T from '../types'; +import * as S from '../styled'; +import * as C from '../constant'; + +interface Props { + status: T.PipelineStatus; +} + +export const PipelineStatus = ({ status }: Props) => { + const cls = classNames({ + ready: [T.PipelineStatus.CREATED, T.PipelineStatus.PENDING].includes(status), + loading: [T.PipelineStatus.ACTIVE, T.PipelineStatus.RUNNING, T.PipelineStatus.RERUN].includes(status), + success: [T.PipelineStatus.COMPLETED, T.PipelineStatus.PARTIAL].includes(status), + error: status === T.PipelineStatus.FAILED, + cancel: status === T.PipelineStatus.CANCELLED, + }); + + return ( + <S.StatusWrapper className={cls}> + {C.PipeLineStatusIcon[status] === 'loading' ? ( + <Loading style={{ marginRight: 4 }} size={14} /> + ) : ( + <Icon style={{ marginRight: 4 }} icon={C.PipeLineStatusIcon[status] as IconName} /> + )} + <span>{C.PipeLineStatusLabel[status]}</span> + </S.StatusWrapper> + ); +}; diff --git a/config-ui/src/pages/pipeline/components/historical/index.tsx b/config-ui/src/routes/pipeline/components/table.tsx similarity index 65% rename from config-ui/src/pages/pipeline/components/historical/index.tsx rename to config-ui/src/routes/pipeline/components/table.tsx index 570d80665..c40f19627 100644 --- a/config-ui/src/pages/pipeline/components/historical/index.tsx +++ b/config-ui/src/routes/pipeline/components/table.tsx @@ -22,48 +22,37 @@ import { pick } from 'lodash'; import { saveAs } from 'file-saver'; import { DEVLAKE_ENDPOINT } from '@/config'; -import type { ColumnType } from '@/components'; -import { Card, Table, Inspector, Dialog, IconButton } from '@/components'; -import { useAutoRefresh } from '@/hooks'; +import { Table, ColumnType, IconButton, Inspector, Dialog } from '@/components'; import { formatTime } from '@/utils'; -import type { PipelineType } from '../../types'; -import { StatusEnum } from '../../types'; -import * as API from '../../api'; +import * as T from '../types'; +import * as API from '../api'; -import { usePipeline } from '../context'; -import { PipelineStatus } from '../status'; -import { PipelineDuration } from '../duration'; -import { PipelineTasks } from '../tasks'; +import { PipelineStatus } from './status'; +import { PipelineDuration } from './duration'; +import { PipelineTasks } from './tasks'; interface Props { - blueprintId: ID; + loading: boolean; + dataSource: T.Pipeline[]; + pagination?: { + total: number; + page: number; + pageSize: number; + onChange: (page: number) => void; + }; + noData?: { + text?: React.ReactNode; + btnText?: string; + onCreate?: () => void; + }; } -export const PipelineHistorical = ({ blueprintId }: Props) => { +export const PipelineTable = ({ dataSource, pagination, noData }: Props) => { const [JSON, setJSON] = useState<any>(null); - const [ID, setID] = useState<ID | null>(null); - - const { version } = usePipeline(); - - const { data } = useAutoRefresh<PipelineType[]>( - async () => { - const res = await API.getPipelineHistorical(blueprintId); - return res.pipelines; - }, - [version], - { - cancel: (data) => - !!( - data && - data.every((it) => - [StatusEnum.COMPLETED, StatusEnum.PARTIAL, StatusEnum.CANCELLED, StatusEnum.FAILED].includes(it.status), - ) - ), - }, - ); + const [id, setId] = useState<ID | null>(null); - const handleShowJSON = (row: PipelineType) => { + const handleShowJSON = (row: T.Pipeline) => { setJSON(pick(row, ['id', 'name', 'plan', 'skipOnFail'])); }; @@ -75,12 +64,17 @@ export const PipelineHistorical = ({ blueprintId }: Props) => { }; const handleShowDetails = (id: ID) => { - setID(id); + setId(id); }; const columns = useMemo( () => [ + { + title: 'ID', + dataIndex: 'id', + key: 'id', + }, { title: 'Status', dataIndex: 'status', @@ -92,20 +86,19 @@ export const PipelineHistorical = ({ blueprintId }: Props) => { dataIndex: 'beganAt', key: 'beganAt', align: 'center', - render: (val: string | null) => (val ? formatTime(val) : '-'), + render: (val) => formatTime(val), }, { title: 'Completed at', dataIndex: 'finishedAt', key: 'finishedAt', align: 'center', - render: (val: string | null) => (val ? formatTime(val) : '-'), + render: (val) => formatTime(val), }, { title: 'Duration', dataIndex: ['status', 'beganAt', 'finishedAt'], key: 'duration', - align: 'center', render: ({ status, beganAt, finishedAt }) => ( <PipelineDuration status={status} beganAt={beganAt} finishedAt={finishedAt} /> ), @@ -123,23 +116,19 @@ export const PipelineHistorical = ({ blueprintId }: Props) => { </ButtonGroup> ), }, - ] as ColumnType<PipelineType>, + ] as ColumnType<T.Pipeline>, [], ); - if (!data) { - return <Card>There are no historical runs associated with this blueprint.</Card>; - } - return ( - <div> - <Table columns={columns} dataSource={data} /> + <> + <Table columns={columns} dataSource={dataSource} pagination={pagination} noData={noData} /> {JSON && <Inspector isOpen title={`Pipeline ${JSON?.id}`} data={JSON} onClose={() => setJSON(null)} />} - {ID && ( - <Dialog style={{ width: 820 }} isOpen title={`Pipeline ${ID}`} footer={null} onCancel={() => setID(null)}> - <PipelineTasks id={ID} /> + {id && ( + <Dialog style={{ width: 820 }} isOpen title={`Pipeline ${id}`} footer={null} onCancel={() => setId(null)}> + <PipelineTasks id={id} /> </Dialog> )} - </div> + </> ); }; diff --git a/config-ui/src/pages/pipeline/components/task/index.tsx b/config-ui/src/routes/pipeline/components/task.tsx similarity index 70% rename from config-ui/src/pages/pipeline/components/task/index.tsx rename to config-ui/src/routes/pipeline/components/task.tsx index 297a6a57b..6ecaf61af 100644 --- a/config-ui/src/pages/pipeline/components/task/index.tsx +++ b/config-ui/src/routes/pipeline/components/task.tsx @@ -16,25 +16,26 @@ * */ -import { useMemo } from 'react'; +import { useState, useMemo } from 'react'; import { Intent } from '@blueprintjs/core'; -import { TextTooltip } from '@/components'; +import { TextTooltip, IconButton } from '@/components'; import { getPluginConfig } from '@/plugins'; +import { operator } from '@/utils'; -import type { TaskType } from '@/pages'; -import { StatusEnum } from '@/pages'; +import * as T from '../types'; +import * as S from '../styled'; +import * as API from '../api'; -import { PipelineDuration } from '../duration'; -import { PipelineRerun } from '../rerun'; - -import * as S from './styled'; +import { PipelineDuration } from './duration'; interface Props { - task: TaskType; + task: T.PipelineTask; } export const PipelineTask = ({ task }: Props) => { + const [operating, setOperating] = useState(false); + const { id, beganAt, finishedAt, status, message, progressDetail } = task; const [icon, name] = useMemo(() => { @@ -86,19 +87,29 @@ export const PipelineTask = ({ task }: Props) => { return [config.icon, name]; }, [task]); + const handleRerun = async () => { + const [success] = await operator(() => API.taskRerun(id), { + setOperating, + }); + + // if (success) { + // setVersion((v) => v + 1); + // } + }; + return ( - <S.Wrapper> - <S.Info> + <S.Task> + <div className="info"> <div className="title"> <img src={icon} alt="" /> - <strong>Task{task.id}</strong> + <strong>Task{id}</strong> <span> <TextTooltip content={name}>{name}</TextTooltip> </span> </div> - {[status === StatusEnum.CREATED, StatusEnum.PENDING].includes(status) && <p>Subtasks pending</p>} + {[status === T.PipelineStatus.CREATED, T.PipelineStatus.PENDING].includes(status) && <p>Subtasks pending</p>} - {[StatusEnum.ACTIVE, StatusEnum.RUNNING].includes(status) && ( + {[T.PipelineStatus.ACTIVE, T.PipelineStatus.RUNNING].includes(status) && ( <p> Subtasks running <strong style={{ marginLeft: 8 }}> @@ -107,20 +118,27 @@ export const PipelineTask = ({ task }: Props) => { </p> )} - {status === StatusEnum.COMPLETED && <p>All Subtasks completed</p>} + {status === T.PipelineStatus.COMPLETED && <p>All Subtasks completed</p>} - {status === StatusEnum.FAILED && ( + {status === T.PipelineStatus.FAILED && ( <TextTooltip intent={Intent.DANGER} content={message}> <p className="error">Task failed: hover to view the reason</p> </TextTooltip> )} - {status === StatusEnum.CANCELLED && <p>Subtasks canceled</p>} - </S.Info> - <S.Duration> + {status === T.PipelineStatus.CANCELLED && <p>Subtasks canceled</p>} + </div> + <div className="duration"> <PipelineDuration status={status} beganAt={beganAt} finishedAt={finishedAt} /> - <PipelineRerun type="task" id={id} status={status} /> - </S.Duration> - </S.Wrapper> + {[ + T.PipelineStatus.COMPLETED, + T.PipelineStatus.PARTIAL, + T.PipelineStatus.FAILED, + T.PipelineStatus.CANCELLED, + ].includes(status) && ( + <IconButton loading={operating} icon="repeat" tooltip="Rerun task" onClick={handleRerun} /> + )} + </div> + </S.Task> ); }; diff --git a/config-ui/src/pages/pipeline/components/tasks/index.tsx b/config-ui/src/routes/pipeline/components/tasks.tsx similarity index 77% rename from config-ui/src/pages/pipeline/components/tasks/index.tsx rename to config-ui/src/routes/pipeline/components/tasks.tsx index 3380aa455..a4b0cdcec 100644 --- a/config-ui/src/pages/pipeline/components/tasks/index.tsx +++ b/config-ui/src/routes/pipeline/components/tasks.tsx @@ -23,14 +23,11 @@ import { groupBy, sortBy } from 'lodash'; import { Loading } from '@/components'; import { useAutoRefresh } from '@/hooks'; -import type { TaskType } from '../../types'; -import { StatusEnum } from '../../types'; -import * as API from '../../api'; +import * as T from '../types'; +import * as S from '../styled'; +import * as API from '../api'; -import { usePipeline } from '../context'; -import { PipelineTask } from '../task'; - -import * as S from './styled'; +import { PipelineTask } from './task'; interface Props { id: ID; @@ -40,19 +37,21 @@ interface Props { export const PipelineTasks = ({ id, style }: Props) => { const [isOpen, setIsOpen] = useState(true); - const { version } = usePipeline(); + // const { version } = usePipeline(); - const { data } = useAutoRefresh<TaskType[]>( + const { data } = useAutoRefresh<T.PipelineTask[]>( async () => { const taskRes = await API.getPipelineTasks(id); return taskRes.tasks; }, - [version], + [], { cancel: (data) => { return !!( data && - data.every((task) => [StatusEnum.COMPLETED, StatusEnum.FAILED, StatusEnum.CANCELLED].includes(task.status)) + data.every((task) => + [T.PipelineStatus.COMPLETED, T.PipelineStatus.FAILED, T.PipelineStatus.CANCELLED].includes(task.status), + ) ); }, }, @@ -63,23 +62,25 @@ export const PipelineTasks = ({ id, style }: Props) => { const handleToggleOpen = () => setIsOpen(!isOpen); return ( - <S.Wrapper style={style}> - <S.Inner> - <S.Header> + <S.Tasks> + <div className="inner"> + <S.TasksHeader> {Object.keys(stages).map((key) => { let status; switch (true) { - case !!stages[key].find((task) => [StatusEnum.ACTIVE, StatusEnum.RUNNING].includes(task.status)): + case !!stages[key].find((task) => + [T.PipelineStatus.ACTIVE, T.PipelineStatus.RUNNING].includes(task.status), + ): status = 'loading'; break; - case stages[key].every((task) => task.status === StatusEnum.COMPLETED): + case stages[key].every((task) => task.status === T.PipelineStatus.COMPLETED): status = 'success'; break; - case !!stages[key].find((task) => task.status === StatusEnum.FAILED): + case !!stages[key].find((task) => task.status === T.PipelineStatus.FAILED): status = 'error'; break; - case !!stages[key].find((task) => task.status === StatusEnum.CANCELLED): + case !!stages[key].find((task) => task.status === T.PipelineStatus.CANCELLED): status = 'cancel'; break; default: @@ -97,9 +98,9 @@ export const PipelineTasks = ({ id, style }: Props) => { </li> ); })} - </S.Header> + </S.TasksHeader> <Collapse isOpen={isOpen}> - <S.Tasks> + <S.TasksList> {Object.keys(stages).map((key) => ( <li key={key}> {stages[key].map((task) => ( @@ -107,15 +108,15 @@ export const PipelineTasks = ({ id, style }: Props) => { ))} </li> ))} - </S.Tasks> + </S.TasksList> </Collapse> - </S.Inner> + </div> <Button className="collapse-control" minimal icon={isOpen ? 'chevron-down' : 'chevron-up'} onClick={handleToggleOpen} /> - </S.Wrapper> + </S.Tasks> ); }; diff --git a/config-ui/src/routes/pipeline/constant.ts b/config-ui/src/routes/pipeline/constant.ts new file mode 100644 index 000000000..79782789b --- /dev/null +++ b/config-ui/src/routes/pipeline/constant.ts @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import * as T from './types'; + +export const PipeLineStatusIcon = { + [T.PipelineStatus.CREATED]: 'stopwatch', + [T.PipelineStatus.PENDING]: 'stopwatch', + [T.PipelineStatus.ACTIVE]: 'loading', + [T.PipelineStatus.RUNNING]: 'loading', + [T.PipelineStatus.RERUN]: 'loading', + [T.PipelineStatus.COMPLETED]: 'tick-circle', + [T.PipelineStatus.PARTIAL]: 'tick-circle', + [T.PipelineStatus.FAILED]: 'delete', + [T.PipelineStatus.CANCELLED]: 'undo', +}; + +export const PipeLineStatusLabel = { + [T.PipelineStatus.CREATED]: 'Created (Pending)', + [T.PipelineStatus.PENDING]: 'Created (Pending)', + [T.PipelineStatus.ACTIVE]: 'In Progress', + [T.PipelineStatus.RUNNING]: 'In Progress', + [T.PipelineStatus.RERUN]: 'In Progress', + [T.PipelineStatus.COMPLETED]: 'Succeeded', + [T.PipelineStatus.PARTIAL]: 'Partial Success', + [T.PipelineStatus.FAILED]: 'Failed', + [T.PipelineStatus.CANCELLED]: 'Cancelled', +}; diff --git a/config-ui/src/pages/pipeline/index.ts b/config-ui/src/routes/pipeline/index.ts similarity index 93% rename from config-ui/src/pages/pipeline/index.ts rename to config-ui/src/routes/pipeline/index.ts index eecb8b531..2e0c4e264 100644 --- a/config-ui/src/pages/pipeline/index.ts +++ b/config-ui/src/routes/pipeline/index.ts @@ -16,5 +16,6 @@ * */ -export * from './types'; export * from './components'; +export * from './pipelines'; +export * from './pipeline'; diff --git a/config-ui/src/pages/pipeline/components/context/index.tsx b/config-ui/src/routes/pipeline/pipeline.tsx similarity index 56% rename from config-ui/src/pages/pipeline/components/context/index.tsx rename to config-ui/src/routes/pipeline/pipeline.tsx index 77b883559..1cf17f9b8 100644 --- a/config-ui/src/pages/pipeline/components/context/index.tsx +++ b/config-ui/src/routes/pipeline/pipeline.tsx @@ -15,24 +15,32 @@ * limitations under the License. * */ +import { useParams } from 'react-router-dom'; -import React, { useContext, useState } from 'react'; +import { PageHeader, Card } from '@/components'; -const PipelineContext = React.createContext<{ - version: number; - setVersion: React.Dispatch<React.SetStateAction<number>>; -}>({ - version: 0, - setVersion: () => {}, -}); +import { PipelineInfo, PipelineTasks } from './components'; -interface Props { - children: React.ReactNode; -} +export const Pipeline = () => { + const { id } = useParams(); -export const PipelineContextProvider = ({ children }: Props) => { - const [version, setVersion] = useState(0); - return <PipelineContext.Provider value={{ version, setVersion }}>{children}</PipelineContext.Provider>; + return ( + <PageHeader + breadcrumbs={[ + { name: 'Advanced', path: '/blueprints' }, + { name: 'Pipelines', path: '/pipelines' }, + { + name: id as string, + path: `/pipelines/${id}`, + }, + ]} + > + <Card> + <PipelineInfo id={id as string} /> + </Card> + <Card> + <PipelineTasks id={id as string} /> + </Card> + </PageHeader> + ); }; - -export const usePipeline = () => useContext(PipelineContext); diff --git a/config-ui/src/routes/pipeline/pipelines.tsx b/config-ui/src/routes/pipeline/pipelines.tsx new file mode 100644 index 000000000..b990054b8 --- /dev/null +++ b/config-ui/src/routes/pipeline/pipelines.tsx @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { useState, useMemo } from 'react'; + +import { PageHeader } from '@/components'; +import { useRefreshData } from '@/hooks'; + +import { PipelineTable } from './components'; +import * as API from './api'; + +export const Pipelines = () => { + const [page, setPage] = useState(1); + const [pageSize] = useState(20); + + const { ready, data } = useRefreshData(() => API.getPipelines()); + + const [dataSource, total] = useMemo(() => [(data?.pipelines ?? []).map((it) => it), data?.count ?? 0], [data]); + + return ( + <PageHeader + breadcrumbs={[ + { name: 'Advanced', path: '/blueprints' }, + { name: 'Pipelines', path: '/pipelines' }, + ]} + > + <PipelineTable + loading={!ready} + dataSource={dataSource} + pagination={{ + total, + page, + pageSize, + onChange: setPage, + }} + noData={{ + text: 'Add new projects to see engineering metrics based on projects.', + }} + /> + </PageHeader> + ); +}; diff --git a/config-ui/src/routes/pipeline/styled.ts b/config-ui/src/routes/pipeline/styled.ts new file mode 100644 index 000000000..bcda461dd --- /dev/null +++ b/config-ui/src/routes/pipeline/styled.ts @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Colors } from '@blueprintjs/core'; +import styled from 'styled-components'; + +export const StatusWrapper = styled.div` + &.ready, + &.cancel { + color: #94959f; + } + + &.loading { + color: #7497f7; + } + + &.success { + color: ${Colors.GREEN3}; + } + + &.error { + color: ${Colors.RED3}; + } +`; + +export const Info = styled.div` + ul { + display: flex; + align-items: center; + } + + li { + flex: 5; + display: flex; + flex-direction: column; + + &:last-child { + flex: 1; + } + + & > span { + font-size: 12px; + color: #94959f; + text-align: center; + } + + & > strong { + display: flex; + align-items: center; + justify-content: center; + margin-top: 8px; + } + } + + p.message { + margin: 8px 0 0; + color: ${Colors.RED3}; + } +`; + +export const Tasks = styled.div` + position: relative; + padding-right: 36px; + + .inner { + overflow: auto; + } + + .collapse-control { + position: absolute; + right: 0; + top: 0; + } +`; + +export const TasksHeader = styled.ul` + display: flex; + align-items: center; + + li { + display: flex; + justify-content: space-between; + align-items: center; + flex: 0 0 30%; + padding: 8px 12px; + + &.ready, + &.cancel { + color: #94959f; + background-color: #f9f9fa; + } + + &.loading { + color: #7497f7; + background-color: #e9efff; + } + + &.success { + color: #4db764; + background-color: #edfbf0; + } + + &.error { + color: #e34040; + background-color: #feefef; + } + } + + li + li { + margin-left: 16px; + } +`; + +export const TasksList = styled.ul` + display: flex; + align-items: flex-start; + + li { + flex: 0 0 30%; + padding-bottom: 8px; + overflow: hidden; + } + + li + li { + margin-left: 16px; + } +`; + +export const Task = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 0; + height: 80px; + border-bottom: 1px solid #dbe4fd; + box-sizing: border-box; + + .info { + flex: auto; + overflow: hidden; + + .title { + display: flex; + align-items: center; + margin-bottom: 8px; + + & > img { + width: 20px; + } + + & > strong { + margin: 0 4px; + } + + & > span { + flex: auto; + overflow: hidden; + } + } + + p { + padding-left: 26px; + margin: 0; + font-size: 12px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + &.error { + color: ${Colors.RED3}; + } + } + } + + .duration { + display: flex; + flex-direction: column; + align-items: center; + flex: 0 0 80px; + text-align: right; + + .bp4-icon { + margin-top: 4px; + cursor: pointer; + } + } +`; diff --git a/config-ui/src/pages/pipeline/types.ts b/config-ui/src/routes/pipeline/types.ts similarity index 91% rename from config-ui/src/pages/pipeline/types.ts rename to config-ui/src/routes/pipeline/types.ts index 275a230eb..cd4fecbd4 100644 --- a/config-ui/src/pages/pipeline/types.ts +++ b/config-ui/src/routes/pipeline/types.ts @@ -16,7 +16,7 @@ * */ -export enum StatusEnum { +export enum PipelineStatus { CREATED = 'TASK_CREATED', PENDING = 'TASK_PENDING', ACTIVE = 'TASK_ACTIVE', @@ -28,9 +28,9 @@ export enum StatusEnum { CANCELLED = 'TASK_CANCELLED', } -export type PipelineType = { +export type Pipeline = { id: ID; - status: StatusEnum; + status: PipelineStatus; beganAt: string | null; finishedAt: string | null; stage: number; @@ -39,10 +39,10 @@ export type PipelineType = { message: string; }; -export type TaskType = { +export type PipelineTask = { id: ID; plugin: string; - status: StatusEnum; + status: PipelineStatus; pipelineRow: number; pipelineCol: number; beganAt: string | null;
