7
7
Add collaborators to a project
8
8
*/
9
9
10
- import { Alert , Input , Select } from "antd" ;
10
+ import { Alert , Button , Card , Input , Select } from "antd" ;
11
11
import {
12
12
React ,
13
13
redux ,
@@ -18,7 +18,7 @@ import {
18
18
useTypedRedux ,
19
19
useState ,
20
20
} from "../app-framework" ;
21
- import { Button , ButtonToolbar , Well } from "../antd-bootstrap" ;
21
+ import { Well } from "../antd-bootstrap" ;
22
22
import { A , Icon , Loading , ErrorDisplay , Gap } from "../components" ;
23
23
import { webapp_client } from "../webapp-client" ;
24
24
import { SITE_NAME } from "@cocalc/util/theme" ;
@@ -39,6 +39,9 @@ import { alert_message } from "../alerts";
39
39
import { useStudentProjectFunctionality } from "@cocalc/frontend/course" ;
40
40
import Sandbox from "./sandbox" ;
41
41
import track from "@cocalc/frontend/user-tracking" ;
42
+ import { SiteLicenseInput } from "@cocalc/frontend/site-licenses/input" ;
43
+ import { applyLicense } from "@cocalc/frontend/project/settings/site-license" ;
44
+ import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project" ;
42
45
43
46
interface RegisteredUser {
44
47
sort ?: string ;
@@ -85,6 +88,10 @@ export const AddCollaborators: React.FC<Props> = ({
85
88
where,
86
89
mode = "project" ,
87
90
} ) => {
91
+ const unlicensedLimit = useTypedRedux (
92
+ "customize" ,
93
+ "unlicensed_project_collaborator_limit" ,
94
+ ) ;
88
95
const isFlyout = mode === "flyout" ;
89
96
const student = useStudentProjectFunctionality ( project_id ) ;
90
97
const user_map = useTypedRedux ( "users" , "user_map" ) ;
@@ -119,6 +126,13 @@ export const AddCollaborators: React.FC<Props> = ({
119
126
const [ email_body_editing , set_email_body_editing ] = useState < boolean > ( false ) ;
120
127
const [ invite_result , set_invite_result ] = useState < string > ( "" ) ;
121
128
129
+ const hasLicense = ( project ?. get ( "site_license" ) ?. size ?? 0 ) > 0 ;
130
+ const limitExceeded =
131
+ ! ! unlicensedLimit &&
132
+ ! hasLicense &&
133
+ ( project ?. get ( "users" ) . size ?? 1 ) + selected_entries . length >
134
+ unlicensedLimit ;
135
+
122
136
const isMountedRef = useIsMountedRef ( ) ;
123
137
124
138
const project_actions = useActions ( "projects" ) ;
@@ -454,24 +468,15 @@ export const AddCollaborators: React.FC<Props> = ({
454
468
/>
455
469
< div
456
470
style = { {
457
- border : "1px solid lightgrey" ,
458
- padding : "10px" ,
459
- borderRadius : "5px" ,
471
+ padding : "20px 0" ,
460
472
backgroundColor : "white" ,
461
473
marginBottom : "15px" ,
462
474
} }
463
475
>
464
476
{ render_email_body_error ( ) }
465
477
{ render_email_textarea ( ) }
466
478
</ div >
467
- < ButtonToolbar >
468
- < Button
469
- bsStyle = "primary"
470
- onClick = { send_email_invite }
471
- disabled = { ! ! email_body_editing }
472
- >
473
- Send Invitation
474
- </ Button >
479
+ < div style = { { display : "flex" } } >
475
480
< Button
476
481
onClick = { ( ) => {
477
482
set_email_to ( "" ) ;
@@ -481,7 +486,15 @@ export const AddCollaborators: React.FC<Props> = ({
481
486
>
482
487
Cancel
483
488
</ Button >
484
- </ ButtonToolbar >
489
+ < Gap />
490
+ < Button
491
+ type = "primary"
492
+ onClick = { send_email_invite }
493
+ disabled = { ! ! email_body_editing }
494
+ >
495
+ Send Invitation
496
+ </ Button >
497
+ </ div >
485
498
</ Well >
486
499
</ div >
487
500
) ;
@@ -640,14 +653,14 @@ export const AddCollaborators: React.FC<Props> = ({
640
653
disabled = false ;
641
654
}
642
655
}
643
- if ( email_body_error ) {
656
+ if ( email_body_error || limitExceeded ) {
644
657
disabled = true ;
645
658
}
646
659
return (
647
- < div >
660
+ < div style = { { display : "flex" } } >
648
661
< Button onClick = { reset } > Cancel</ Button >
649
662
< Gap />
650
- < Button disabled = { disabled } onClick = { add_selected } bsStyle = "primary" >
663
+ < Button disabled = { disabled } onClick = { add_selected } type = "primary" >
651
664
< Icon name = "user-plus" /> { label }
652
665
</ Button >
653
666
</ div >
@@ -678,6 +691,30 @@ export const AddCollaborators: React.FC<Props> = ({
678
691
< div
679
692
style = { isFlyout ? { paddingLeft : "5px" , paddingRight : "5px" } : undefined }
680
693
>
694
+ { limitExceeded && (
695
+ < Card
696
+ size = "small"
697
+ title = {
698
+ < h4 >
699
+ < div style = { { float : "right" } } >
700
+ < BuyLicenseForProject project_id = { project_id } />
701
+ </ div >
702
+ < Icon name = "key" /> Select License
703
+ </ h4 >
704
+ }
705
+ style = { { margin : "10px 0" } }
706
+ >
707
+ < SiteLicenseInput
708
+ requireValid
709
+ confirmLabel = { "Add this license" }
710
+ onChange = { ( license_id ) => {
711
+ applyLicense ( { project_id, license_id } ) ;
712
+ } }
713
+ requireLicense
714
+ requireMessage = { `A license is required to have more than ${ unlicensedLimit } collaborators on this project.` }
715
+ />
716
+ </ Card >
717
+ ) }
681
718
{ err && < ErrorDisplay error = { err } onClose = { ( ) => set_err ( "" ) } /> }
682
719
{ state == "searching" && < Loading /> }
683
720
{ render_search ( ) }
0 commit comments