6
6
7
7
import React , { useCallback , useState } from "react" ;
8
8
import Alert from "../components/Alert" ;
9
+ import { Button } from "../components/Button" ;
9
10
import ConfirmationModal from "../components/ConfirmationModal" ;
11
+ import { TextInputField } from "../components/forms/TextInputField" ;
10
12
import { Heading2 , Subheading } from "../components/typography/headings" ;
11
13
import { useCurrentOrg , useOrganizationsInvalidator } from "../data/organizations/orgs-query" ;
14
+ import { useUpdateOrgMutation } from "../data/organizations/update-org-mutation" ;
15
+ import { useOnBlurError } from "../hooks/use-onblur-error" ;
12
16
import { teamsService } from "../service/public-api" ;
13
- import { getGitpodService , gitpodHostUrl } from "../service/service" ;
17
+ import { gitpodHostUrl } from "../service/service" ;
14
18
import { useCurrentUser } from "../user-context" ;
15
19
import { OrgSettingsPage } from "./OrgSettingsPage" ;
16
20
@@ -22,63 +26,44 @@ export default function TeamSettings() {
22
26
const [ teamNameToDelete , setTeamNameToDelete ] = useState ( "" ) ;
23
27
const [ teamName , setTeamName ] = useState ( org ?. name || "" ) ;
24
28
const [ slug , setSlug ] = useState ( org ?. slug || "" ) ;
25
- const [ errorMessage , setErrorMessage ] = useState < string | undefined > ( undefined ) ;
26
29
const [ updated , setUpdated ] = useState ( false ) ;
30
+ const updateOrg = useUpdateOrgMutation ( ) ;
27
31
28
32
const close = ( ) => setModal ( false ) ;
29
33
30
- const updateTeamInformation = useCallback ( async ( ) => {
31
- if ( ! org || errorMessage ) {
32
- return ;
33
- }
34
- try {
35
- await getGitpodService ( ) . server . updateTeam ( org . id , { name : teamName , slug } ) ;
36
- invalidateOrgs ( ) ;
37
- setUpdated ( true ) ;
38
- setTimeout ( ( ) => setUpdated ( false ) , 3000 ) ;
39
- } catch ( error ) {
40
- setErrorMessage ( `Failed to update organization information: ${ error . message } ` ) ;
41
- }
42
- } , [ org , errorMessage , slug , teamName , invalidateOrgs ] ) ;
34
+ const teamNameError = useOnBlurError (
35
+ teamName . length > 32
36
+ ? "Organization name must not be longer than 32 characters"
37
+ : "Organization name can not be blank" ,
38
+ ! ! teamName && teamName . length <= 32 ,
39
+ ) ;
43
40
44
- const onNameChange = useCallback (
45
- async ( event : React . ChangeEvent < HTMLInputElement > ) => {
46
- if ( ! org ) {
47
- return ;
48
- }
49
- const newName = event . target . value || "" ;
50
- setTeamName ( newName ) ;
51
- if ( newName . trim ( ) . length === 0 ) {
52
- setErrorMessage ( "Organization name can not be blank." ) ;
53
- return ;
54
- } else if ( newName . trim ( ) . length > 32 ) {
55
- setErrorMessage ( "Organization name must not be longer than 32 characters." ) ;
56
- return ;
57
- } else {
58
- setErrorMessage ( undefined ) ;
59
- }
60
- } ,
61
- [ org ] ,
41
+ const slugError = useOnBlurError (
42
+ slug . length > 100
43
+ ? "Organization slug must not be longer than 100 characters"
44
+ : "Organization slug can not be blank." ,
45
+ ! ! slug && slug . length <= 100 ,
62
46
) ;
63
47
64
- const onSlugChange = useCallback (
65
- async ( event : React . ChangeEvent < HTMLInputElement > ) => {
66
- if ( ! org ) {
48
+ const orgFormIsValid = teamNameError . isValid && slugError . isValid ;
49
+
50
+ const updateTeamInformation = useCallback (
51
+ async ( e : React . FormEvent ) => {
52
+ e . preventDefault ( ) ;
53
+
54
+ if ( ! orgFormIsValid ) {
67
55
return ;
68
56
}
69
- const newSlug = event . target . value || "" ;
70
- setSlug ( newSlug ) ;
71
- if ( newSlug . trim ( ) . length === 0 ) {
72
- setErrorMessage ( "Organization slug can not be blank." ) ;
73
- return ;
74
- } else if ( newSlug . trim ( ) . length > 100 ) {
75
- setErrorMessage ( "Organization slug must not be longer than 100 characters." ) ;
76
- return ;
77
- } else {
78
- setErrorMessage ( undefined ) ;
57
+
58
+ try {
59
+ await updateOrg . mutateAsync ( { name : teamName , slug } ) ;
60
+ setUpdated ( true ) ;
61
+ setTimeout ( ( ) => setUpdated ( false ) , 3000 ) ;
62
+ } catch ( error ) {
63
+ console . error ( error ) ;
79
64
}
80
65
} ,
81
- [ org ] ,
66
+ [ orgFormIsValid , updateOrg , teamName , slug ] ,
82
67
) ;
83
68
84
69
const deleteTeam = useCallback ( async ( ) => {
@@ -94,45 +79,47 @@ export default function TeamSettings() {
94
79
return (
95
80
< >
96
81
< OrgSettingsPage >
97
- < Heading2 > Organization Name</ Heading2 >
98
- < Subheading className = "max-w-2xl" >
99
- This is your organization's visible name within Gitpod. For example, the name of your company.
100
- </ Subheading >
101
- { errorMessage && (
82
+ < Heading2 > Organization Details</ Heading2 >
83
+ < Subheading className = "max-w-2xl" > Details of your organization within Gitpod.</ Subheading >
84
+
85
+ { updateOrg . isError && (
102
86
< Alert type = "error" closable = { true } className = "mb-2 max-w-xl rounded-md" >
103
- { errorMessage }
87
+ < span > Failed to update organization information: </ span >
88
+ < span > { updateOrg . error . message || "unknown error" } </ span >
104
89
</ Alert >
105
90
) }
106
91
{ updated && (
107
92
< Alert type = "message" closable = { true } className = "mb-2 max-w-xl rounded-md" >
108
93
Organization name has been updated.
109
94
</ Alert >
110
95
) }
111
- < div className = "flex flex-col lg:flex-row" >
112
- < div >
113
- < div className = "mt-4 mb-3" >
114
- < h4 > Name</ h4 >
115
- < input type = "text" value = { teamName } onChange = { onNameChange } />
116
- </ div >
117
- </ div >
118
- </ div >
119
- < div className = "flex flex-col lg:flex-row" >
120
- < div >
121
- < div className = "mt-4 mb-3" >
122
- < h4 > Slug</ h4 >
123
- < input type = "text" value = { slug } onChange = { onSlugChange } />
124
- </ div >
125
- </ div >
126
- </ div >
127
- < div className = "flex flex-row" >
128
- < button
129
- className = "primary"
130
- disabled = { ( org ?. name === teamName && org ?. slug === slug ) || ! ! errorMessage }
131
- onClick = { updateTeamInformation }
96
+ < form onSubmit = { updateTeamInformation } >
97
+ < TextInputField
98
+ label = "Name"
99
+ hint = "The name of your company or organization"
100
+ value = { teamName }
101
+ error = { teamNameError . message }
102
+ onChange = { setTeamName }
103
+ onBlur = { teamNameError . onBlur }
104
+ />
105
+
106
+ < TextInputField
107
+ label = "Slug"
108
+ hint = "The slug will be used for easier signin and discovery"
109
+ value = { slug }
110
+ error = { slugError . message }
111
+ onChange = { setSlug }
112
+ onBlur = { slugError . onBlur }
113
+ />
114
+
115
+ < Button
116
+ className = "mt-4"
117
+ htmlType = "submit"
118
+ disabled = { ( org ?. name === teamName && org ?. slug === slug ) || ! orgFormIsValid }
132
119
>
133
- Update Organization Name
134
- </ button >
135
- </ div >
120
+ Update Organization
121
+ </ Button >
122
+ </ form >
136
123
137
124
< Heading2 className = "pt-12" > Delete Organization</ Heading2 >
138
125
< Subheading className = "pb-4 max-w-2xl" >
0 commit comments