From 611341a8ced9bb7f26540c0c26a04539c6c7585d Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sun, 7 Mar 2021 17:44:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(pipelines):=20=E7=BC=96=E8=BE=91=E5=92=8C?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pipelines/pipeline-list.constants.ts | 2 + src/components/pipelines/pipeline-list.scss | 39 +++- .../pipelines/pipeline-list.scss.d.ts | 5 +- src/components/pipelines/pipeline-list.tsx | 177 ++++++++++++++---- src/components/projects/project-panel.tsx | 1 - src/routes/pipelines/pipeline-editor.tsx | 42 +++-- src/routes/projects/project-details.tsx | 1 - 7 files changed, 203 insertions(+), 64 deletions(-) diff --git a/src/components/pipelines/pipeline-list.constants.ts b/src/components/pipelines/pipeline-list.constants.ts index 375b005..317ea3f 100644 --- a/src/components/pipelines/pipeline-list.constants.ts +++ b/src/components/pipelines/pipeline-list.constants.ts @@ -5,6 +5,8 @@ export const LIST_PIPELINES = gql` pipelines: listPipelines(projectId: $projectId) { id name + branch + projectId } } `; diff --git a/src/components/pipelines/pipeline-list.scss b/src/components/pipelines/pipeline-list.scss index 7374cbc..6a89403 100644 --- a/src/components/pipelines/pipeline-list.scss +++ b/src/components/pipelines/pipeline-list.scss @@ -1,22 +1,45 @@ .pipelineList { - @apply bg-red-200; + @apply bg-red-200 w-40; & > header { @apply bg-white flex justify-between items-center; - - } } +.list { + @apply bg-gray-100 overflow-y-auto max-h-full; +} .item { - @apply bg-gray-50; + @apply bg-white text-gray-700; + @apply border-l-4 border-white transition-colors; + @apply px-2 py-1 my-px; + @apply relative; + small { + @apply text-sm text-gray-500; + svg { + @apply mr-1 h-3 text-gray-300; + } + } +} +.activeItem { + @apply border-red-500; } .addBtn { @apply bg-red-400 text-white; - @apply py-1 px-2 m-2 rounded-lg; + @apply py-1 px-2 m-1 rounded-lg mr-auto; @apply hover:bg-red-500; } -.refetchBtn { +.actionBtn { @apply text-red-400 flex items-center; - @apply w-4 h-4 m-2 rounded-full; -} \ No newline at end of file + @apply w-4 h-4 flex items-center justify-center overflow-hidden text-center m-1; + svg { + } +} +.editPanel { + @apply absolute right-1 top-0 bottom-0; + @apply flex flex-col items-center justify-evenly; + @apply text-red-400 text-xs; + button { + @apply w-3; + } +} diff --git a/src/components/pipelines/pipeline-list.scss.d.ts b/src/components/pipelines/pipeline-list.scss.d.ts index b658987..1c7cba2 100644 --- a/src/components/pipelines/pipeline-list.scss.d.ts +++ b/src/components/pipelines/pipeline-list.scss.d.ts @@ -1,10 +1,13 @@ // This file is automatically generated from your CSS. Any edits will be overwritten. declare namespace PipelineListScssNamespace { export interface IPipelineListScss { + actionBtn: string; + activeItem: string; addBtn: string; + editPanel: string; item: string; + list: string; pipelineList: string; - refetchBtn: string; } } diff --git a/src/components/pipelines/pipeline-list.tsx b/src/components/pipelines/pipeline-list.tsx index f72703a..d1e9f51 100644 --- a/src/components/pipelines/pipeline-list.tsx +++ b/src/components/pipelines/pipeline-list.tsx @@ -1,16 +1,29 @@ -import { useQuery } from '@apollo/client'; +import { gql, useQuery, useMutation } from '@apollo/client'; import { makeAutoObservable } from 'mobx'; -import { useLocalObservable, useObserver } from 'mobx-react-lite'; -import { useMemo } from 'preact/hooks'; +import { useLocalObservable } from 'mobx-react-lite'; +import { useCallback, useMemo } from 'preact/hooks'; import { h } from 'preact'; import { Pipeline } from '../../generated/graphql'; import styles from './pipeline-list.scss'; import classNames from 'classnames'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faRedoAlt } from '@fortawesome/free-solid-svg-icons'; +import { + faCodeBranch, + faRedoAlt, + faEdit, + faEllipsisV, + faTrash +} from '@fortawesome/free-solid-svg-icons'; import { createOverlay } from '../commons/overlay/overlay'; import { PipelineEditor } from '../../routes/pipelines/pipeline-editor'; import { LIST_PIPELINES } from './pipeline-list.constants'; +import { Observer, observer } from 'mobx-react'; + +const DELETE_PIPELINE = gql` + mutation DeletePipeline($id: String!) { + deletePipeline(id: $id) + } +`; interface Props { projectId: string; @@ -19,36 +32,28 @@ interface Props { class Store { pipelines?: Pipeline[]; currPipelineId?: string; + editMode = false; constructor() { makeAutoObservable(this); } - - get items() { - return this.pipelines?.map(pipeline => { - const isActive = this.currPipelineId === pipeline.id; - return ( -
  • -

    {pipeline.name}

    -
  • - ); - }); - } + refetching = false; setPipelines(pipelines: any[] | undefined) { this.pipelines = pipelines; } + setRefetching(val: boolean) { + this.refetching = val; + } + setCurrPipelineId(id: string) { + this.currPipelineId = id; + } + toggleEditMode() { + this.editMode = !this.editMode; + } } export const PipelineList = ({ projectId }: Props) => { - const { data, refetch } = useQuery<{ pipelines: Pipeline[] }>( + const { data, refetch, loading } = useQuery<{ pipelines: Pipeline[] }>( LIST_PIPELINES, { variables: { @@ -63,25 +68,123 @@ export const PipelineList = ({ projectId }: Props) => { useMemo(() => store.setPipelines(pipelines), [store, pipelines]); - const items = useObserver(() => store.items); - - const addPipeline = () => { - createOverlay({ - content: - }); - }; - - return ( -
    + const Header = observer(() => { + const addPipeline = () => { + createOverlay({ + content: + }); + }; + const onRefetchBtnClick = () => { + store.setRefetching(true); + refetch().finally(() => store.setRefetching(false)); + }; + return (
    - +
    -
      {items}
    + ); + }); + const items = pipelines?.map(pipeline => ( + + {(): any => } + + )); + + return ( +
    +
    +
      {items}
    ); }; + +const Item = observer( + ({ pipeline, store }: { pipeline: Pipeline; store: Store }) => { + const [remove] = useMutation(DELETE_PIPELINE, { + variables: { id: pipeline.id }, + update(cache) { + const cacheData = cache.readQuery<{ pipelines: Pipeline[] }>({ + query: LIST_PIPELINES, + variables: { projectId: pipeline.projectId } + }); + const pipelines = cacheData?.pipelines?.slice(); + if (!pipelines) { + return; + } + const index = pipelines.findIndex( + (item: Pipeline) => item.id === pipeline.id + ); + if (index === -1) { + return; + } + pipelines.splice(index, 1); + cache.writeQuery({ + query: LIST_PIPELINES, + variables: { projectId: pipeline.projectId }, + data: { + pipelines + } + }); + } + }); + + const isActive = store.currPipelineId === pipeline.id; + const openModifyPanel = () => { + createOverlay({ + content: ( + + ) + }); + }; + const onRemoveBtnClick = () => { + remove(); + }; + const EditPanel = observer(() => { + return store.editMode ? ( +
    + + +
    + ) : null; + }); + return ( +
  • store.setCurrPipelineId(pipeline.id)} + > +

    {pipeline.name}

    + + + {pipeline.branch} + + {(): any => } +
  • + ); + } +); diff --git a/src/components/projects/project-panel.tsx b/src/components/projects/project-panel.tsx index bc48153..abba436 100644 --- a/src/components/projects/project-panel.tsx +++ b/src/components/projects/project-panel.tsx @@ -27,7 +27,6 @@ const FIND_PROJECTS = gql` sshUrl webUrl webHookSecret - deletedAt } } `; diff --git a/src/routes/pipelines/pipeline-editor.tsx b/src/routes/pipelines/pipeline-editor.tsx index d282103..95e19a5 100644 --- a/src/routes/pipelines/pipeline-editor.tsx +++ b/src/routes/pipelines/pipeline-editor.tsx @@ -1,17 +1,18 @@ -import { Field, Form, Formik } from 'formik'; +import { Field, Form, Formik, useFormik } from 'formik'; import { h } from 'preact'; import { RoutableProps } from 'preact-router'; import styles from './pipeline-editor.scss'; -import { - CreatePipelineInput, - Pipeline, - UpdatePipelineInput -} from '../../generated/graphql'; import { useOverlay } from '../../components/commons/overlay/overlay'; import { gql, useLazyQuery, useMutation } from '@apollo/client'; import { useMemo } from 'preact/hooks'; import classNames from 'classnames'; -import { WorkUnitMetadata, PipelineUnits } from '../../generated/graphql'; +import { + CreatePipelineInput, + UpdatePipelineInput, + WorkUnitMetadata, + PipelineUnits, + Pipeline +} from '../../generated/graphql'; import { Message } from '../../components/commons/message/index'; import { LIST_PIPELINES } from '../../components/pipelines/pipeline-list.constants'; @@ -41,7 +42,7 @@ const FIND_PIPELINE = gql` query FindPipeline($id: String!) { pipeline: findPipeline(id: $id) { name - id + branch projectId workUnitMetadata { version @@ -98,18 +99,28 @@ export const PipelineEditor = ({ projectId, id }: Props) => { }; const isCreate = !id; - const [loadPipeline, { data: pipeline }] = useLazyQuery(FIND_PIPELINE); + const [loadPipeline, { data }] = useLazyQuery(FIND_PIPELINE, { + variables: { id } + }); useMemo(() => { if (!isCreate) { loadPipeline(); } }, []); - const formData: FormValues = pipeline ?? { - name: 'test', - projectId, - workUnitMetadata: JSON.stringify(defaultWorkUnitMetadata, null, 2) - }; + const formData: FormValues = useMemo(() => { + if (data?.pipeline) { + const fd = JSON.parse(JSON.stringify(data.pipeline)); + delete fd?.workUnitMetadata.__typename; + fd.workUnitMetadata.units.forEach((item: any) => { + delete item.__typename; + }); + fd.workUnitMetadata = JSON.stringify(fd.workUnitMetadata, null, 2); + delete fd.__typename; + return fd; + } + return {}; + }, [data, data?.pipeline]); const [createPipeline] = useMutation<{ pipeline: Pipeline }>( CREATE_PIPELINE, @@ -167,10 +178,9 @@ export const PipelineEditor = ({ projectId, id }: Props) => { // } }; - return (
    - +