From 93482bec3fd90d714b0599a721e1ee963f941618 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Wed, 5 May 2021 11:21:48 +0800 Subject: [PATCH] feat(projects, pipeline): project detail page. --- graphql.schema.json | 38 ++++----- src/generated/graphql.tsx | 14 ++-- src/layouts/default.tsx | 132 ++++++++++++++++++------------- src/layouts/header-container.tsx | 7 ++ src/layouts/index.tsx | 3 +- src/pipelines/pipeline-list.tsx | 53 +++++++++++++ src/projects/index.ts | 4 + src/projects/project-detail.tsx | 59 ++++++++++++++ src/projects/project-editor.tsx | 4 +- src/projects/project-panel.tsx | 9 +-- src/routes.tsx | 29 ++++--- 11 files changed, 248 insertions(+), 104 deletions(-) create mode 100644 src/layouts/header-container.tsx create mode 100644 src/pipelines/pipeline-list.tsx create mode 100644 src/projects/index.ts create mode 100644 src/projects/project-detail.tsx diff --git a/graphql.schema.json b/graphql.schema.json index 456d1f0..7c76869 100644 --- a/graphql.schema.json +++ b/graphql.schema.json @@ -644,7 +644,7 @@ "deprecationReason": null }, { - "name": "modifyPipeline", + "name": "updatePipeline", "description": null, "args": [ { @@ -662,22 +662,6 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "id", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null } ], "type": { @@ -1427,7 +1411,7 @@ "deprecationReason": null }, { - "name": "listPipelines", + "name": "pipelines", "description": null, "args": [ { @@ -1464,7 +1448,7 @@ "deprecationReason": null }, { - "name": "findPipeline", + "name": "pipeline", "description": null, "args": [ { @@ -1790,6 +1774,22 @@ "defaultValue": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null } ], "interfaces": null, diff --git a/src/generated/graphql.tsx b/src/generated/graphql.tsx index a582234..4e14971 100644 --- a/src/generated/graphql.tsx +++ b/src/generated/graphql.tsx @@ -66,7 +66,7 @@ export type Mutation = { updateProject: Project; removeProject: Scalars['Float']; createPipeline: Pipeline; - modifyPipeline: Pipeline; + updatePipeline: Pipeline; deletePipeline: Scalars['Float']; createPipelineTask: PipelineTask; }; @@ -92,9 +92,8 @@ export type MutationCreatePipelineArgs = { }; -export type MutationModifyPipelineArgs = { +export type MutationUpdatePipelineArgs = { Pipeline: UpdatePipelineInput; - id: Scalars['String']; }; @@ -171,8 +170,8 @@ export type Query = { hello: Hello; projects: Array; project: Project; - listPipelines: Array; - findPipeline: Pipeline; + pipelines: Array; + pipeline: Pipeline; listPipelineTaskByPipelineId: Array; findPipelineTask: PipelineTask; }; @@ -183,12 +182,12 @@ export type QueryProjectArgs = { }; -export type QueryListPipelinesArgs = { +export type QueryPipelinesArgs = { projectId?: Maybe; }; -export type QueryFindPipelineArgs = { +export type QueryPipelineArgs = { id: Scalars['String']; }; @@ -237,6 +236,7 @@ export type UpdatePipelineInput = { branch: Scalars['String']; name: Scalars['String']; workUnitMetadata: WorkUnitMetadataInput; + id: Scalars['String']; }; export type UpdateProjectInput = { diff --git a/src/layouts/default.tsx b/src/layouts/default.tsx index bddb4c6..8be51b6 100644 --- a/src/layouts/default.tsx +++ b/src/layouts/default.tsx @@ -1,4 +1,4 @@ -import React, { FC } from "react"; +import React, { FC, useCallback, useRef, useState } from "react"; import clsx from "clsx"; import { createStyles, @@ -16,13 +16,15 @@ import IconButton from "@material-ui/core/IconButton"; import MenuIcon from "@material-ui/icons/Menu"; import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; import ChevronRightIcon from "@material-ui/icons/ChevronRight"; -import { ProjectPanel } from '../projects/project-panel'; +import { ProjectPanel } from "../projects/project-panel"; +import { HeaderContainerProvider } from "./header-container"; const drawerWidth = 240; const useStyles = makeStyles((theme: Theme) => createStyles({ root: { display: "flex", + overflow: "hidden", }, appBar: { zIndex: theme.zIndex.drawer + 1, @@ -70,17 +72,24 @@ const useStyles = makeStyles((theme: Theme) => alignItems: "center", justifyContent: "flex-end", padding: theme.spacing(0, 1), + flex: "none", // necessary for content to be below app bar ...theme.mixins.toolbar, }, + headerContaner: { + flex: "auto", + }, content: { flexGrow: 1, - padding: theme.spacing(3), + height: "100vh", + display: "flex", + flexFlow: "column", + padding: theme.spacing(0), }, }) ); -export const DefaultLayout: FC = ({children}) => { +export const DefaultLayout: FC = ({ children }) => { const classes = useStyles(); const theme = useTheme(); const [open, setOpen] = React.useState(true); @@ -93,62 +102,71 @@ export const DefaultLayout: FC = ({children}) => { setOpen(false); }; + const [headerContainer, setHeaderContainer] = useState(undefined); + const onRefChange = useCallback( + (node) => { + setHeaderContainer(node); + }, + [setHeaderContainer] + ); + return (
- - - - - - - - Mini variant drawer - - - - + + + + + +
+
+
+ -
- - {theme.direction === "rtl" ? ( - - ) : ( - - )} - -
- - - -
-
-
- { children } -
+ }), + }} + > +
+ + {theme.direction === "rtl" ? ( + + ) : ( + + )} + +
+ + + +
+
+
+ + + {children} + +
); -} +}; diff --git a/src/layouts/header-container.tsx b/src/layouts/header-container.tsx new file mode 100644 index 0000000..60e52ae --- /dev/null +++ b/src/layouts/header-container.tsx @@ -0,0 +1,7 @@ +import { createContext, useContext } from 'react'; + +const Context = createContext(undefined); + +export const HeaderContainerProvider = Context.Provider; + +export const useHeaderContainer = () => useContext(Context); \ No newline at end of file diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index e06f8e4..b0777c5 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1 +1,2 @@ -export * from './default'; \ No newline at end of file +export * from './default'; +export * from './header-container'; \ No newline at end of file diff --git a/src/pipelines/pipeline-list.tsx b/src/pipelines/pipeline-list.tsx new file mode 100644 index 0000000..4aa55bc --- /dev/null +++ b/src/pipelines/pipeline-list.tsx @@ -0,0 +1,53 @@ +import { gql, useQuery } from '@apollo/client'; +import { Link } from '@curi/react-dom'; +import { List, ListItem, Typography, ListItemText } from '@material-ui/core'; +import { FC } from 'react'; +import { Pipeline } from '../generated/graphql'; +import { Fragment } from 'react'; +import { CallMerge } from '@material-ui/icons'; + +interface Props { + projectId: string; +} + +const PIPELINES = gql` + query Pipelines($projectId: String!) { + pipelines(projectId: $projectId) { + id + name + branch + } + } +` + +export const PipelineList: FC = ({ projectId }) => { + const {data, loading} = useQuery<{pipelines: Pipeline[]}, {projectId: string}>(PIPELINES, { + variables: {projectId} + }) + return ( + + {data?.pipelines.map((pipeline) => ( + + + + ))} + + ); +} + +const Item = ({pipeline}: {pipeline: Pipeline}) => { + + return ( + + + + {pipeline.branch} + + } + /> + + ); +} \ No newline at end of file diff --git a/src/projects/index.ts b/src/projects/index.ts new file mode 100644 index 0000000..9f54a31 --- /dev/null +++ b/src/projects/index.ts @@ -0,0 +1,4 @@ +export * from './project-detail'; +export * from './project-panel'; +export * from './project-editor'; +export * from './queries'; \ No newline at end of file diff --git a/src/projects/project-detail.tsx b/src/projects/project-detail.tsx new file mode 100644 index 0000000..d086fea --- /dev/null +++ b/src/projects/project-detail.tsx @@ -0,0 +1,59 @@ +import { Project } from "../generated/graphql"; +import React, { FC, Fragment } from "react"; +import { Grid, makeStyles, Paper, Portal, Typography } from "@material-ui/core"; +import { useHeaderContainer } from "../layouts"; +import { PipelineList } from "../pipelines/pipeline-list"; + +interface Props { + project: Project; +} + +const useStyles = makeStyles(() => ({ + root: { + height: "100%", + flex: "auto", + overflow: "hidden" + }, + pipelineListContainer: { + height: "100%", + width: "100%", + overflow: "auto" + }, +})); + +export const ProjectDetail: FC = ({ project }) => { + const headerContainer = useHeaderContainer(); + + const classes = useStyles(); + + return ( + + + + + + {project.name} + + + {project.comment} + + + Options + + + + + + + + + + + ); +}; diff --git a/src/projects/project-editor.tsx b/src/projects/project-editor.tsx index 9373d85..199519f 100644 --- a/src/projects/project-editor.tsx +++ b/src/projects/project-editor.tsx @@ -3,10 +3,8 @@ import { Button, LinearProgress, makeStyles, Paper } from "@material-ui/core"; import { Form, Formik, Field, FormikHelpers } from "formik"; import { TextField } from "formik-material-ui"; import { not } from "ramda"; -import React, { FC } from "react"; +import { FC } from "react"; import { - CreateProjectInput, - UpdateProjectInput, Project, } from "../generated/graphql"; import * as Yup from "yup"; diff --git a/src/projects/project-panel.tsx b/src/projects/project-panel.tsx index e1b232f..077544d 100644 --- a/src/projects/project-panel.tsx +++ b/src/projects/project-panel.tsx @@ -1,7 +1,6 @@ import { gql, useQuery } from '@apollo/client'; import { Link, useRouter } from '@curi/react-dom'; import { Box, List, ListItem } from '@material-ui/core'; -import { makeStyles, Theme, createStyles } from '@material-ui/core'; import { find, propEq } from 'ramda'; import React, { useState, FC, useEffect } from 'react'; import { Project } from '../generated/graphql'; @@ -22,12 +21,6 @@ const PROJECTS = gql` } `; -const useStyles = makeStyles((theme: Theme) => - createStyles({ - - }) -); - export function ProjectPanel() { return (
@@ -68,7 +61,7 @@ const ProjectList: FC<{}> = () => { const items = projects?.map((item) => ( setCurrentProject(item)} diff --git a/src/routes.tsx b/src/routes.tsx index 683e215..7d34b84 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,9 +1,9 @@ import { ApolloClient, InMemoryCache } from "@apollo/client"; import { prepareRoutes } from "@curi/router"; import { omit } from 'ramda'; +import React from 'react'; import { CreateProjectInput, Project } from './generated/graphql'; -import { ProjectEditor } from './projects/project-editor'; -import { PROJECT } from './projects/queries'; +import { ProjectDetail, ProjectEditor, PROJECT } from "./projects"; export default prepareRoutes([ { @@ -30,7 +30,10 @@ export default prepareRoutes([ { name: "edit-project", path: "projects/:projectId/edit", - async resolve(matched, { client }: { client: ApolloClient }) { + async resolve( + matched, + { client }: { client: ApolloClient } + ) { const { data } = await client.query<{ project: Project }>({ query: PROJECT, variables: { id: matched?.params.projectId }, @@ -48,14 +51,22 @@ export default prepareRoutes([ { name: "project-detail", path: "projects/:projectId", - resolve() { - const body = import( - /* webpackChunkName: "article-editor" */ "./projects/project-panel" - ).then((m) => m.ProjectPanel); - return body; + async resolve( + matched, + { client }: { client: ApolloClient } + ) { + const { data } = await client.query<{ project: Project }>({ + query: PROJECT, + variables: { id: matched?.params.projectId }, + }); + return { + body: () => ( + + ), + }; }, respond({ resolved }) { - return { body: resolved }; + return resolved; }, }, ]);