feat(pipelines):新增 添加 pipeline 功能。
This commit is contained in:
parent
846b58c49b
commit
282366cd72
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"css.validate": false,
|
"css.validate": false,
|
||||||
"scss.validate": false,
|
"scss.validate": false,
|
||||||
"editor.quickSuggestions": {
|
|
||||||
"strings": true,
|
|
||||||
"other": true,
|
|
||||||
},
|
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Formik"
|
"Formik"
|
||||||
]
|
]
|
||||||
|
@ -1045,7 +1045,7 @@
|
|||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "findPipelines",
|
"name": "listPipelines",
|
||||||
"description": null,
|
"description": null,
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
@ -1311,7 +1311,7 @@
|
|||||||
"description": null,
|
"description": null,
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "Pipeline",
|
"name": "pipeline",
|
||||||
"description": null,
|
"description": null,
|
||||||
"type": {
|
"type": {
|
||||||
"kind": "NON_NULL",
|
"kind": "NON_NULL",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { Fragment, FunctionalComponent, h } from 'preact';
|
import { Fragment, FunctionalComponent, h } from 'preact';
|
||||||
import { ProjectPanel } from './projects/project-panel';
|
import { ProjectPanel } from './projects/project-panel';
|
||||||
import styles from './app.scss';
|
import styles from './app.scss';
|
||||||
import { OverlayContainer } from './commons/overlay/overlay';
|
import { OverlayContainer } from './commons/overlay/overlay';
|
||||||
import { useObserver } from 'mobx-react';
|
import { useObserver } from 'mobx-react';
|
||||||
import { appStore } from '../app.store';
|
|
||||||
import Router, { Route } from 'preact-router';
|
import Router, { Route } from 'preact-router';
|
||||||
import { ProjectDetails } from '../routes/projects/project-details';
|
import { ProjectDetails } from '../routes/projects/project-details';
|
||||||
import { createApolloClient } from '../units/apollo-client';
|
import { createApolloClient } from '../units/apollo-client';
|
||||||
|
import { PipelineEditor } from '../routes/pipelines/pipeline-editor';
|
||||||
|
|
||||||
const client = createApolloClient();
|
const client = createApolloClient();
|
||||||
|
|
||||||
@ -32,6 +32,10 @@ const Board = () => {
|
|||||||
<main>
|
<main>
|
||||||
<Router>
|
<Router>
|
||||||
<Route path="/projects/:id" component={ProjectDetails} />
|
<Route path="/projects/:id" component={ProjectDetails} />
|
||||||
|
<Route
|
||||||
|
path="/dev"
|
||||||
|
component={() => <PipelineEditor projectId="test" />}
|
||||||
|
/>
|
||||||
</Router>
|
</Router>
|
||||||
</main>
|
</main>
|
||||||
<aside>
|
<aside>
|
||||||
|
10
src/components/pipelines/pipeline-list.constants.ts
Normal file
10
src/components/pipelines/pipeline-list.constants.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const LIST_PIPELINES = gql`
|
||||||
|
query ListPipelines($projectId: String) {
|
||||||
|
pipelines: listPipelines(projectId: $projectId) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
22
src/components/pipelines/pipeline-list.scss
Normal file
22
src/components/pipelines/pipeline-list.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.pipelineList {
|
||||||
|
@apply bg-red-200;
|
||||||
|
|
||||||
|
& > header {
|
||||||
|
@apply bg-white flex justify-between items-center;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
@apply bg-gray-50;
|
||||||
|
}
|
||||||
|
.addBtn {
|
||||||
|
@apply bg-red-400 text-white;
|
||||||
|
@apply py-1 px-2 m-2 rounded-lg;
|
||||||
|
@apply hover:bg-red-500;
|
||||||
|
}
|
||||||
|
.refetchBtn {
|
||||||
|
@apply text-red-400 flex items-center;
|
||||||
|
@apply w-4 h-4 m-2 rounded-full;
|
||||||
|
}
|
13
src/components/pipelines/pipeline-list.scss.d.ts
vendored
Normal file
13
src/components/pipelines/pipeline-list.scss.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
declare namespace PipelineListScssNamespace {
|
||||||
|
export interface IPipelineListScss {
|
||||||
|
addBtn: string;
|
||||||
|
item: string;
|
||||||
|
pipelineList: string;
|
||||||
|
refetchBtn: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const PipelineListScssModule: PipelineListScssNamespace.IPipelineListScss;
|
||||||
|
|
||||||
|
export = PipelineListScssModule;
|
87
src/components/pipelines/pipeline-list.tsx
Normal file
87
src/components/pipelines/pipeline-list.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { makeAutoObservable } from 'mobx';
|
||||||
|
import { useLocalObservable, useObserver } from 'mobx-react-lite';
|
||||||
|
import { 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 { createOverlay } from '../commons/overlay/overlay';
|
||||||
|
import { PipelineEditor } from '../../routes/pipelines/pipeline-editor';
|
||||||
|
import { LIST_PIPELINES } from './pipeline-list.constants';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
projectId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
pipelines?: Pipeline[];
|
||||||
|
currPipelineId?: string;
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
return this.pipelines?.map(pipeline => {
|
||||||
|
const isActive = this.currPipelineId === pipeline.id;
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={pipeline.id}
|
||||||
|
className={classNames(
|
||||||
|
{
|
||||||
|
isActive
|
||||||
|
},
|
||||||
|
styles.item
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<h3>{pipeline.name}</h3>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPipelines(pipelines: any[] | undefined) {
|
||||||
|
this.pipelines = pipelines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PipelineList = ({ projectId }: Props) => {
|
||||||
|
const { data, refetch } = useQuery<{ pipelines: Pipeline[] }>(
|
||||||
|
LIST_PIPELINES,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
projectId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const pipelines: Pipeline[] | undefined = data?.pipelines;
|
||||||
|
|
||||||
|
const store = useLocalObservable(() => new Store());
|
||||||
|
|
||||||
|
useMemo(() => store.setPipelines(pipelines), [store, pipelines]);
|
||||||
|
|
||||||
|
const items = useObserver(() => store.items);
|
||||||
|
|
||||||
|
const addPipeline = () => {
|
||||||
|
createOverlay({
|
||||||
|
content: <PipelineEditor projectId={projectId} />
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.pipelineList}>
|
||||||
|
<header>
|
||||||
|
<button className={styles.addBtn} onClick={addPipeline}>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
<button className={styles.refetchBtn} onClick={() => refetch()}>
|
||||||
|
<FontAwesomeIcon icon={faRedoAlt} />
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<ul>{items}</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
@ -1,34 +1 @@
|
|||||||
.editor {
|
@import '../../style//editor.scss';
|
||||||
@apply bg-white shadow-lg p-4 rounded-lg text-gray-800
|
|
||||||
top-1/4 left-1/2 absolute
|
|
||||||
transform -translate-x-1/2 -translate-y-1/2
|
|
||||||
w-5/6;
|
|
||||||
}
|
|
||||||
.form {
|
|
||||||
@apply bg-white;
|
|
||||||
& > * {
|
|
||||||
@apply block;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
@apply my-4 relative flex;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
@apply text-gray-700 w-20 inline-block text-right;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: ':';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.controller {
|
|
||||||
@apply border-b border-gray-300 flex-auto;
|
|
||||||
}
|
|
||||||
.submitBtn {
|
|
||||||
@apply px-2 py-1 rounded-full bg-red-400 text-white;
|
|
||||||
}
|
|
||||||
.cancelBtn {
|
|
||||||
@apply px-2 py-1 rounded-full bg-gray-100 text-gray-700 ml-2;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
@apply text-right;
|
|
||||||
}
|
|
||||||
}
|
|
@ -113,7 +113,7 @@ export type Query = {
|
|||||||
findProject: Project;
|
findProject: Project;
|
||||||
listLogs: LogList;
|
listLogs: LogList;
|
||||||
listBranches: BranchList;
|
listBranches: BranchList;
|
||||||
findPipelines: Array<Pipeline>;
|
listPipelines: Array<Pipeline>;
|
||||||
findPipeline: Pipeline;
|
findPipeline: Pipeline;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ export type QueryListBranchesArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QueryFindPipelinesArgs = {
|
export type QueryListPipelinesArgs = {
|
||||||
projectId?: Maybe<Scalars['String']>;
|
projectId?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ export type MutationDeleteProjectArgs = {
|
|||||||
|
|
||||||
|
|
||||||
export type MutationCreatePipelineArgs = {
|
export type MutationCreatePipelineArgs = {
|
||||||
Pipeline: CreatePipelineInput;
|
pipeline: CreatePipelineInput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
6
src/routes/pipelines/pipeline-editor.scss
Normal file
6
src/routes/pipelines/pipeline-editor.scss
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@import '../../style//editor.scss';
|
||||||
|
|
||||||
|
.workUnitMetadata {
|
||||||
|
min-height: 16rem;
|
||||||
|
@apply max-h-64;
|
||||||
|
}
|
17
src/routes/pipelines/pipeline-editor.scss.d.ts
vendored
Normal file
17
src/routes/pipelines/pipeline-editor.scss.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// This file is automatically generated from your CSS. Any edits will be overwritten.
|
||||||
|
declare namespace PipelineEditorScssNamespace {
|
||||||
|
export interface IPipelineEditorScss {
|
||||||
|
cancelBtn: string;
|
||||||
|
controller: string;
|
||||||
|
editor: string;
|
||||||
|
footer: string;
|
||||||
|
form: string;
|
||||||
|
label: string;
|
||||||
|
submitBtn: string;
|
||||||
|
workUnitMetadata: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const PipelineEditorScssModule: PipelineEditorScssNamespace.IPipelineEditorScss;
|
||||||
|
|
||||||
|
export = PipelineEditorScssModule;
|
213
src/routes/pipelines/pipeline-editor.tsx
Normal file
213
src/routes/pipelines/pipeline-editor.tsx
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import { Field, Form, Formik } 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 { Message } from '../../components/commons/message/index';
|
||||||
|
import { LIST_PIPELINES } from '../../components/pipelines/pipeline-list.constants';
|
||||||
|
|
||||||
|
const defaultWorkUnitMetadata: WorkUnitMetadata = {
|
||||||
|
version: 1,
|
||||||
|
units: [
|
||||||
|
{
|
||||||
|
type: PipelineUnits.Checkout,
|
||||||
|
scripts: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: PipelineUnits.InstallDependencies,
|
||||||
|
scripts: ['npm ci']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: PipelineUnits.Test,
|
||||||
|
scripts: ['npm run test']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: PipelineUnits.Deploy,
|
||||||
|
scripts: ['npm run build']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const FIND_PIPELINE = gql`
|
||||||
|
query FindPipeline($id: String!) {
|
||||||
|
pipeline: findPipeline(id: $id) {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
projectId
|
||||||
|
workUnitMetadata {
|
||||||
|
version
|
||||||
|
units {
|
||||||
|
type
|
||||||
|
scripts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CREATE_PIPELINE = gql`
|
||||||
|
mutation CreatePipeline($input: CreatePipelineInput!) {
|
||||||
|
pipeline: createPipeline(pipeline: $input) {
|
||||||
|
id
|
||||||
|
projectId
|
||||||
|
name
|
||||||
|
workUnitMetadata {
|
||||||
|
version
|
||||||
|
units {
|
||||||
|
type
|
||||||
|
scripts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const MODIFY_PIPELINE = gql`
|
||||||
|
mutation ModifyPipeline($id: String!, $input: UpdatePipelineInput!) {
|
||||||
|
pipeline: modifyPipeline(Pipeline: $input, id: $id) {
|
||||||
|
id
|
||||||
|
projectId
|
||||||
|
name
|
||||||
|
workUnitMetadata {
|
||||||
|
version
|
||||||
|
units {
|
||||||
|
type
|
||||||
|
scripts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props extends RoutableProps {
|
||||||
|
projectId: string;
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PipelineEditor = ({ projectId, id }: Props) => {
|
||||||
|
type FormValues = Partial<CreatePipelineInput | UpdatePipelineInput> & {
|
||||||
|
workUnitMetadata: string;
|
||||||
|
};
|
||||||
|
const isCreate = !id;
|
||||||
|
|
||||||
|
const [loadPipeline, { data: pipeline }] = useLazyQuery(FIND_PIPELINE);
|
||||||
|
useMemo(() => {
|
||||||
|
if (!isCreate) {
|
||||||
|
loadPipeline();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const formData: FormValues = pipeline ?? {
|
||||||
|
name: 'test',
|
||||||
|
projectId,
|
||||||
|
workUnitMetadata: JSON.stringify(defaultWorkUnitMetadata, null, 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
const [createPipeline] = useMutation<{ pipeline: Pipeline }>(
|
||||||
|
CREATE_PIPELINE,
|
||||||
|
{
|
||||||
|
update(cache, { data }) {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pipeline = data.pipeline;
|
||||||
|
cache.writeQuery({
|
||||||
|
query: LIST_PIPELINES,
|
||||||
|
variables: { projectId: pipeline.projectId },
|
||||||
|
data: {
|
||||||
|
pipelines: [
|
||||||
|
pipeline,
|
||||||
|
cache.readQuery({
|
||||||
|
query: LIST_PIPELINES,
|
||||||
|
variables: { projectId: pipeline.projectId }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const [modifyPipeline] = useMutation<Pipeline>(MODIFY_PIPELINE);
|
||||||
|
|
||||||
|
const { close } = useOverlay();
|
||||||
|
const cancel = (ev: MouseEvent) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitForm = async (values: FormValues) => {
|
||||||
|
let workUnitMetadata: WorkUnitMetadata;
|
||||||
|
try {
|
||||||
|
workUnitMetadata = JSON.parse(values.workUnitMetadata);
|
||||||
|
} catch (err) {
|
||||||
|
Message.error('流程描述无法解析为JSON!');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
values = Object.assign({}, values, { workUnitMetadata });
|
||||||
|
try {
|
||||||
|
if (isCreate) {
|
||||||
|
await createPipeline({
|
||||||
|
variables: { input: values }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await modifyPipeline({
|
||||||
|
variables: { input: values, id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
} finally {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.editor}>
|
||||||
|
<Formik initialValues={formData} onSubmit={submitForm}>
|
||||||
|
<Form className={styles.form}>
|
||||||
|
<label>
|
||||||
|
<span className={styles.label}>名称</span>
|
||||||
|
<Field
|
||||||
|
className={styles.controller}
|
||||||
|
name="name"
|
||||||
|
placeholder="流水线名称"
|
||||||
|
></Field>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span className={styles.label}>Branch</span>
|
||||||
|
<Field
|
||||||
|
className={styles.controller}
|
||||||
|
name="branch"
|
||||||
|
placeholder="Branch"
|
||||||
|
></Field>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span className={styles.label}>流程描述</span>
|
||||||
|
<Field
|
||||||
|
className={classNames(styles.controller, styles.workUnitMetadata)}
|
||||||
|
name="workUnitMetadata"
|
||||||
|
placeholder="流程描述(JSON)"
|
||||||
|
as="textarea"
|
||||||
|
></Field>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<button type="submit" className={styles.submitBtn}>
|
||||||
|
{isCreate ? '创建' : '修改'}
|
||||||
|
</button>
|
||||||
|
<button onClick={cancel} className={styles.cancelBtn}>
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
.projectDetails {
|
.projectDetails {
|
||||||
@apply h-full flex flex-col;
|
@apply h-full flex flex-col;
|
||||||
header {
|
& > header {
|
||||||
@apply bg-red-400 text-gray-50 p-2;
|
@apply bg-red-400 text-gray-50 p-2;
|
||||||
@apply grid grid-cols-2 grid-rows-2;
|
@apply grid grid-cols-2 grid-rows-2;
|
||||||
|
|
||||||
|
@ -6,12 +6,11 @@ import styles from './project-details.scss';
|
|||||||
import { createOverlay } from '../../components/commons/overlay/overlay';
|
import { createOverlay } from '../../components/commons/overlay/overlay';
|
||||||
import { ProjectEditor } from '../../components/projects/project-editor';
|
import { ProjectEditor } from '../../components/projects/project-editor';
|
||||||
import { CommitLogList } from '../../components/commit-logs/commit-log-list';
|
import { CommitLogList } from '../../components/commit-logs/commit-log-list';
|
||||||
import { BranchesList } from '../../components/branches-list/branches-list';
|
|
||||||
import { makeAutoObservable } from 'mobx';
|
import { makeAutoObservable } from 'mobx';
|
||||||
import { Observer, useLocalObservable } from 'mobx-react';
|
import { Observer, useLocalObservable } from 'mobx-react';
|
||||||
import { RoutableProps } from 'preact-router';
|
import { RoutableProps } from 'preact-router';
|
||||||
import { gql, useQuery } from '@apollo/client';
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import { useMemo } from 'preact/hooks';
|
import { PipelineList } from '../../components/pipelines/pipeline-list';
|
||||||
|
|
||||||
const FIND_PROJECT = gql`
|
const FIND_PROJECT = gql`
|
||||||
query FindProject($id: String!) {
|
query FindProject($id: String!) {
|
||||||
@ -53,10 +52,6 @@ export const ProjectDetails = ({ id, path }: Props) => {
|
|||||||
|
|
||||||
const project: Project | undefined = data?.project;
|
const project: Project | undefined = data?.project;
|
||||||
|
|
||||||
// useMemo(() => {
|
|
||||||
// store.project = data?.findProject;
|
|
||||||
// });
|
|
||||||
|
|
||||||
const store = useLocalObservable(() => new Store());
|
const store = useLocalObservable(() => new Store());
|
||||||
const editProject = () => {
|
const editProject = () => {
|
||||||
createOverlay({
|
createOverlay({
|
||||||
@ -88,7 +83,7 @@ export const ProjectDetails = ({ id, path }: Props) => {
|
|||||||
</header>
|
</header>
|
||||||
{project ? (
|
{project ? (
|
||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
<BranchesList project={project} onSelect={onSelectBranch} />
|
<PipelineList projectId={id} />
|
||||||
<Observer>
|
<Observer>
|
||||||
{(): any => (
|
{(): any => (
|
||||||
<CommitLogList project={project} branch={store.branch} />
|
<CommitLogList project={project} branch={store.branch} />
|
||||||
|
34
src/style/editor.scss
Normal file
34
src/style/editor.scss
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
.editor {
|
||||||
|
@apply bg-white shadow-lg p-4 rounded-lg text-gray-800
|
||||||
|
top-1/4 left-1/2 absolute
|
||||||
|
transform -translate-x-1/2 -translate-y-1/2
|
||||||
|
w-5/6;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
@apply bg-white;
|
||||||
|
& > * {
|
||||||
|
@apply block;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
@apply my-4 relative flex;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
@apply text-gray-700 w-20 inline-block text-right;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.controller {
|
||||||
|
@apply border-b border-gray-300 flex-auto;
|
||||||
|
}
|
||||||
|
.submitBtn {
|
||||||
|
@apply px-2 py-1 rounded-full bg-red-400 text-white;
|
||||||
|
}
|
||||||
|
.cancelBtn {
|
||||||
|
@apply px-2 py-1 rounded-full bg-gray-100 text-gray-700 ml-2;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
@apply text-right;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ export function createApolloClient() {
|
|||||||
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||||
if (graphQLErrors) {
|
if (graphQLErrors) {
|
||||||
console.error(graphQLErrors);
|
console.error(graphQLErrors);
|
||||||
|
Message.error(graphQLErrors.map(error => error.message).join());
|
||||||
graphQLErrors.forEach(({ message, locations, path }) => {
|
graphQLErrors.forEach(({ message, locations, path }) => {
|
||||||
console.log(
|
console.log(
|
||||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
|
Loading…
Reference in New Issue
Block a user