Skip to content

Commit

Permalink
feat: (WIP) flow for creating a renku-native project (#2870)(#2875)
Browse files Browse the repository at this point in the history
  • Loading branch information
ciyer committed Nov 14, 2023
1 parent c3d762c commit 962e779
Show file tree
Hide file tree
Showing 9 changed files with 480 additions and 0 deletions.
9 changes: 9 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { Cookie, Privacy } from "./privacy";
import { Project } from "./project";
import { ProjectList } from "./project/list";
import { NewProject } from "./project/new";
import ProjectV2New from "./features/projectsV2/new/ProjectV2New";
import { StyleGuide } from "./styleguide";
import AppContext from "./utils/context/appContext";
import { Url } from "./utils/helpers/url";
Expand Down Expand Up @@ -275,6 +276,14 @@ function CentralContentContainer(props) {
</ContainerWrap>
)}
/>
<Route
path={Url.get(Url.pages.projectV2.new)}
render={(p) => (
<ContainerWrap>
<ProjectV2New key="newProjectV2" {...p} />
</ContainerWrap>
)}
/>
<Route
path="/style-guide"
render={(p) => (
Expand Down
81 changes: 81 additions & 0 deletions client/src/features/projectsV2/new/ProjectV2New.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*!
* Copyright 2023 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed 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 FormSchema from "../../../components/formschema/FormSchema";
import type { NewProjectV2State } from "./projectV2New.slice";
import { useNewProjectV2Selector } from "./projectV2New.slice";
import ProjectV2NewForm from "./ProjectV2NewForm";

function ProjectV2NewAccessStepHeader() {
return (
<>
<b>Set up visibility and access</b>
<p>Decide who can see your project and who is allowed to work in it.</p>
</>
);
}

function ProjectV2NewHeader({
currentStep,
}: Pick<NewProjectV2State, "currentStep">) {
return (
<>
<p>
V2 Projects let you group together related resources and control who can
access them.
</p>
{currentStep === 0 && <ProjectV2NewMetadataStepHeader />}
{currentStep === 1 && <ProjectV2NewAccessStepHeader />}
{currentStep === 2 && <ProjectV2NewRepositoryStepHeader />}
</>
);
}

function ProjectV2NewMetadataStepHeader() {
return (
<>
<b>Describe your project</b>
<p>Provide some information to explain what your project is about.</p>
</>
);
}

function ProjectV2NewRepositoryStepHeader() {
return (
<>
<b>Associate some repositories (optional)</b>
<p>
You can associate one or more repositories with the project now if you
want. This can also be done later at any time.
</p>
</>
);
}

export default function ProjectV2New() {
const { currentStep } = useNewProjectV2Selector((state) => state);
return (
<FormSchema
showHeader={true}
title="New Project (V2)"
description={<ProjectV2NewHeader currentStep={currentStep} />}
>
<ProjectV2NewForm currentStep={currentStep} />
</FormSchema>
);
}
253 changes: 253 additions & 0 deletions client/src/features/projectsV2/new/ProjectV2NewForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*!
* Copyright 2023 - Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed 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 cx from "classnames";
import { useCallback } from "react";
import { Controller, useForm } from "react-hook-form";
import { useDispatch } from "react-redux";

import { Button, Form, FormText, Input, Label } from "reactstrap";

import styles from "./projectV2New.module.scss";
import type { NewProjectV2State } from "./projectV2New.slice";
import {
setAccess,
setCurrentStep,
setMetadata,
useNewProjectV2Selector,
} from "./projectV2New.slice";

function ProjectFormSubmitGroup({ currentStep }: ProjectV2NewFormProps) {
const dispatch = useDispatch();

const previousStep = useCallback(() => {
if (currentStep < 1) return;
const previousStep = (currentStep - 1) as typeof currentStep;
dispatch(setCurrentStep(previousStep));
}, [currentStep, dispatch]);

return (
<div className="d-flex justify-content-between">
<Button
className={cx(currentStep > 0 ? "visible" : "invisible")}
onClick={previousStep}
>
Back
</Button>
{currentStep < 2 && <Button type="submit">Next</Button>}
{currentStep === 2 && <Button type="submit">Create</Button>}
</div>
);
}

interface ProjectV2NewFormProps {
currentStep: NewProjectV2State["currentStep"];
}
export default function ProjectV2NewForm({
currentStep,
}: ProjectV2NewFormProps) {
return (
<div className={cx(styles.projectV2NewForm, "form-rk-green mb-4")}>
{currentStep === 0 && <h4>Describe the project</h4>}
{currentStep === 1 && <h4>Define access</h4>}
{currentStep === 2 && <h4>Add repositories</h4>}
{currentStep === 0 && (
<ProjectV2NewMetadataStepForm currentStep={currentStep} />
)}
{currentStep === 1 && (
<ProjectV2NewAccessStepForm currentStep={currentStep} />
)}
{currentStep === 2 && (
<ProjectFormSubmitGroup currentStep={currentStep} />
)}
</div>
);
}

function ProjectV2NewMetadataStepForm({ currentStep }: ProjectV2NewFormProps) {
const dispatch = useDispatch();
const { project } = useNewProjectV2Selector((state) => state);
const {
control,
formState: { errors },
handleSubmit,
} = useForm<NewProjectV2State["project"]["metadata"]>({
defaultValues: project.metadata,
});

const onSubmit = useCallback(
(data: NewProjectV2State["project"]["metadata"]) => {
dispatch(setMetadata(data));
if (currentStep > 1) return;
const nextStep = (currentStep + 1) as typeof currentStep;
dispatch(setCurrentStep(nextStep));
},
[currentStep, dispatch]
);
return (
<Form
className="form-rk-green"
noValidate
onSubmit={handleSubmit(onSubmit)}
>
<div className="mb-3">
<Label className="form-label" for="projectV2NewForm-name">
Name
</Label>
<Controller
control={control}
name="name"
render={({ field }) => (
<Input
className={cx("form-control", errors.name && "is-invalid")}
id="projectV2NewForm-name"
type="text"
{...field}
/>
)}
rules={{ required: true }}
/>
{errors.name ? null : (
<FormText className="input-hint">
The name you will use to refer to the project
</FormText>
)}
<div className="invalid-feedback">Please provide a name</div>
</div>

<div className="mb-3">
<Label className="form-label" for="projectV2NewForm-slug">
Slug
</Label>
<Controller
control={control}
name="slug"
render={({ field }) => (
<Input
className={cx("form-control", errors.slug && "is-invalid")}
id="projectV2NewForm-slug"
type="text"
{...field}
/>
)}
rules={{ required: true }}
/>
{errors.slug ? null : (
<FormText className="input-hint">
A short, machine-readable identifier for the project
</FormText>
)}
<div className="invalid-feedback">Please provide a slug</div>
</div>

<div className="mb-3">
<Label className="form-label" for="projectV2NewForm-slug">
Description
</Label>
<Controller
control={control}
name="description"
render={({ field }) => (
<Input
className={cx("form-control", errors.description && "is-invalid")}
id="projectV2NewForm-description"
type="textarea"
{...field}
/>
)}
rules={{ maxLength: 500, required: false }}
/>
{errors.description ? null : (
<FormText className="input-hint">
A brief (at most 500 character) description of the project.
</FormText>
)}
<div className="invalid-feedback">Please provide a description</div>
</div>
<ProjectFormSubmitGroup currentStep={currentStep} />
</Form>
);
}

function ProjectV2NewAccessStepForm({ currentStep }: ProjectV2NewFormProps) {
const dispatch = useDispatch();
const { project } = useNewProjectV2Selector((state) => state);
const {
control,
formState: { errors },
handleSubmit,
} = useForm<NewProjectV2State["project"]["access"]>({
defaultValues: project.access,
});

const onSubmit = useCallback(
(data: NewProjectV2State["project"]["access"]) => {
dispatch(setAccess(data));
if (currentStep > 1) return;
const nextStep = (currentStep + 1) as typeof currentStep;
dispatch(setCurrentStep(nextStep));
},
[currentStep, dispatch]
);
return (
<Form
className="form-rk-green"
noValidate
onSubmit={handleSubmit(onSubmit)}
>
<div className="mb-3">
<Label className="form-label" for="projectV2NewForm-name">
Visibility
</Label>
<Controller
control={control}
name="visibility"
render={({ field }) => (
<Input
className={cx("form-control", errors.visibility && "is-invalid")}
id="projectV2NewForm-visibility"
type="select"
{...field}
>
<option>Public</option>
<option>Private</option>
</Input>
)}
rules={{ required: true }}
/>
{errors.visibility ? null : (
<FormText className="input-hint">
Should the project be visible to everyone or only to members?
</FormText>
)}
<div className="invalid-feedback">Please select a visibility</div>
</div>
<div className="mb-3">
<Label className="form-label" for="projectV2NewForm-name">
Users
</Label>

<FormText className="input-hint">
Who has access to the project?
</FormText>
<div className="invalid-feedback">Please select a visibility</div>
</div>
<ProjectFormSubmitGroup currentStep={currentStep} />
</Form>
);
}
3 changes: 3 additions & 0 deletions client/src/features/projectsV2/new/projectV2New.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.projectV2NewForm {
width: 100%;
}
Loading

0 comments on commit 962e779

Please sign in to comment.