-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
9 changed files
with
480 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
253
client/src/features/projectsV2/new/ProjectV2NewForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.projectV2NewForm { | ||
width: 100%; | ||
} |
Oops, something went wrong.