Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7cf686c

Browse files
authored
feat: Add Create Workspace Form (#73)
Fixes#38 This adds a create-workspace form (with only 1 field, probably the simplest form ever 😄 )![image](https://user-images.githubusercontent.com/88213859/151108220-8a540c75-e55b-49af-8199-c69394508700.png)It currently redirects to a path `/workspaces/<unique id>`, but that isn't implemented yet - but you can see the workspace show up on the projects page.
1 parentb586a35 commit7cf686c

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

‎site/api.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ export namespace Project {
6767
}
6868
}
6969

70+
exportinterfaceCreateWorkspaceRequest{
71+
name:string
72+
project_id:string
73+
}
74+
7075
// Must be kept in sync with backend Workspace struct
7176
exportinterfaceWorkspace{
7277
id:string
@@ -77,6 +82,31 @@ export interface Workspace {
7782
name:string
7883
}
7984

85+
exportnamespaceWorkspace{
86+
exportconstcreate=async(request:CreateWorkspaceRequest):Promise<Workspace>=>{
87+
constresponse=awaitfetch(`/api/v2/workspaces/me`,{
88+
method:"POST",
89+
headers:{
90+
"Content-Type":"application/json",
91+
},
92+
body:JSON.stringify(request),
93+
})
94+
95+
constbody=awaitresponse.json()
96+
if(!response.ok){
97+
thrownewError(body.message)
98+
}
99+
100+
// Let SWR know that both the /api/v2/workspaces/* and /api/v2/projects/*
101+
// endpoints will need to fetch new data.
102+
constmutateWorkspacesPromise=mutate("/api/v2/workspaces")
103+
constmutateProjectsPromise=mutate("/api/v2/projects")
104+
awaitPromise.all([mutateWorkspacesPromise,mutateProjectsPromise])
105+
106+
returnbody
107+
}
108+
}
109+
80110
exportconstlogin=async(email:string,password:string):Promise<LoginResponse>=>{
81111
constresponse=awaitfetch("/api/v2/login",{
82112
method:"POST",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import{render,screen}from"@testing-library/react"
2+
importReactfrom"react"
3+
import{CreateWorkspaceForm}from"./CreateWorkspaceForm"
4+
import{MockProject,MockWorkspace}from"./../test_helpers"
5+
6+
describe("CreateWorkspaceForm",()=>{
7+
it("renders",async()=>{
8+
// Given
9+
constonSubmit=()=>Promise.resolve(MockWorkspace)
10+
constonCancel=()=>Promise.resolve()
11+
12+
// When
13+
render(<CreateWorkspaceFormproject={MockProject}onSubmit={onSubmit}onCancel={onCancel}/>)
14+
15+
// Then
16+
// Simple smoke test to verify form renders
17+
constelement=awaitscreen.findByText("Create Workspace")
18+
expect(element).toBeDefined()
19+
})
20+
})

‎site/forms/CreateWorkspaceForm.tsx‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
importButtonfrom"@material-ui/core/Button"
2+
import{makeStyles}from"@material-ui/core/styles"
3+
import{FormikContextType,useFormik}from"formik"
4+
importReactfrom"react"
5+
import*asYupfrom"yup"
6+
7+
import{FormTextField,FormTitle,FormSection}from"../components/Form"
8+
import{LoadingButton}from"../components/Button"
9+
import{Project,Workspace,CreateWorkspaceRequest}from"../api"
10+
11+
exportinterfaceCreateWorkspaceForm{
12+
project:Project
13+
onSubmit:(request:CreateWorkspaceRequest)=>Promise<Workspace>
14+
onCancel:()=>void
15+
}
16+
17+
constvalidationSchema=Yup.object({
18+
name:Yup.string().required("Name is required"),
19+
})
20+
21+
exportconstCreateWorkspaceForm:React.FC<CreateWorkspaceForm>=({ project, onSubmit, onCancel})=>{
22+
conststyles=useStyles()
23+
24+
constform:FormikContextType<{name:string}>=useFormik<{name:string}>({
25+
initialValues:{
26+
name:"",
27+
},
28+
enableReinitialize:true,
29+
validationSchema:validationSchema,
30+
onSubmit:({ name})=>{
31+
returnonSubmit({
32+
project_id:project.id,
33+
name:name,
34+
})
35+
},
36+
})
37+
38+
return(
39+
<divclassName={styles.root}>
40+
<FormTitle
41+
title="Create Workspace"
42+
detail={
43+
<span>
44+
for project<strong>{project.name}</strong>
45+
</span>
46+
}
47+
/>
48+
<FormSectiontitle="Name">
49+
<FormTextField
50+
form={form}
51+
formFieldName="name"
52+
fullWidth
53+
helperText="A unique name describing your workspace."
54+
label="Workspace Name"
55+
placeholder="my-workspace"
56+
required
57+
/>
58+
</FormSection>
59+
60+
<divclassName={styles.footer}>
61+
<ButtonclassName={styles.button}onClick={onCancel}variant="outlined">
62+
Cancel
63+
</Button>
64+
<LoadingButton
65+
loading={form.isSubmitting}
66+
className={styles.button}
67+
onClick={form.submitForm}
68+
variant="contained"
69+
color="primary"
70+
type="submit"
71+
>
72+
Submit
73+
</LoadingButton>
74+
</div>
75+
</div>
76+
)
77+
}
78+
79+
constuseStyles=makeStyles(()=>({
80+
root:{
81+
maxWidth:"1380px",
82+
width:"100%",
83+
display:"flex",
84+
flexDirection:"column",
85+
alignItems:"center",
86+
},
87+
footer:{
88+
display:"flex",
89+
flex:"0",
90+
flexDirection:"row",
91+
justifyContent:"center",
92+
alignItems:"center",
93+
},
94+
button:{
95+
margin:"1em",
96+
},
97+
}))
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
importReactfrom"react"
2+
import{makeStyles}from"@material-ui/core/styles"
3+
import{useRouter}from"next/router"
4+
importuseSWRfrom"swr"
5+
6+
import*asAPIfrom"../../../../api"
7+
import{useUser}from"../../../../contexts/UserContext"
8+
import{ErrorSummary}from"../../../../components/ErrorSummary"
9+
import{FullScreenLoader}from"../../../../components/Loader/FullScreenLoader"
10+
import{CreateWorkspaceForm}from"../../../../forms/CreateWorkspaceForm"
11+
12+
constCreateWorkspacePage:React.FC=()=>{
13+
constrouter=useRouter()
14+
conststyles=useStyles()
15+
const{ me}=useUser(/* redirectOnError */true)
16+
const{ organization,project:projectName}=router.query
17+
const{data:project,error:projectError}=useSWR<API.Project,Error>(
18+
`/api/v2/projects/${organization}/${projectName}`,
19+
)
20+
21+
if(projectError){
22+
return<ErrorSummaryerror={projectError}/>
23+
}
24+
25+
if(!me||!project){
26+
return<FullScreenLoader/>
27+
}
28+
29+
constonCancel=async()=>{
30+
awaitrouter.push(`/projects/${organization}/${project}`)
31+
}
32+
33+
constonSubmit=async(req:API.CreateWorkspaceRequest)=>{
34+
constworkspace=awaitAPI.Workspace.create(req)
35+
awaitrouter.push(`/workspaces/${workspace.id}`)
36+
returnworkspace
37+
}
38+
39+
return(
40+
<divclassName={styles.root}>
41+
<CreateWorkspaceFormonCancel={onCancel}onSubmit={onSubmit}project={project}/>
42+
</div>
43+
)
44+
}
45+
46+
constuseStyles=makeStyles((theme)=>({
47+
root:{
48+
display:"flex",
49+
flexDirection:"column",
50+
alignItems:"center",
51+
height:"100vh",
52+
backgroundColor:theme.palette.background.paper,
53+
},
54+
}))
55+
56+
exportdefaultCreateWorkspacePage

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp