@@ -9,7 +9,7 @@ Create a new project
9
9
10
10
import { Button , Card , Col , Form , Input , Row } from "antd" ;
11
11
import { delay } from "awaiting" ;
12
-
12
+ import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project" ;
13
13
import { Alert , Well } from "@cocalc/frontend/antd-bootstrap" ;
14
14
import {
15
15
CSS ,
@@ -48,9 +48,10 @@ interface Props {
48
48
49
49
type EditState = "edit" | "view" | "saving" ;
50
50
51
- export const NewProjectCreator : React . FC < Props > = ( props : Props ) => {
52
- const { start_in_edit_mode, default_value } = props ;
53
-
51
+ export const NewProjectCreator : React . FC < Props > = ( {
52
+ start_in_edit_mode,
53
+ default_value,
54
+ } : Props ) => {
54
55
const managed_licenses = useTypedRedux ( "billing" , "managed_licenses" ) ;
55
56
56
57
// view --> edit --> saving --> view
@@ -60,7 +61,6 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
60
61
const [ title_text , set_title_text ] = useState < string > ( default_value ?? "" ) ;
61
62
const [ error , set_error ] = useState < string > ( "" ) ;
62
63
const [ show_advanced , set_show_advanced ] = useState < boolean > ( false ) ;
63
- const [ show_add_license , set_show_add_license ] = useState < boolean > ( false ) ;
64
64
const [ title_prefill , set_title_prefill ] = useState < boolean > ( false ) ;
65
65
const [ license_id , set_license_id ] = useState < string > ( "" ) ;
66
66
const [ warnBoost , setWarnBoost ] = useState < boolean > ( false ) ;
@@ -72,6 +72,12 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
72
72
73
73
const is_anonymous = useTypedRedux ( "account" , "is_anonymous" ) ;
74
74
const customize_kucalc = useTypedRedux ( "customize" , "kucalc" ) ;
75
+ const requireLicense = ! ! useTypedRedux (
76
+ "customize" ,
77
+ "require_license_to_create_project" ,
78
+ ) ;
79
+ const [ show_add_license , set_show_add_license ] =
80
+ useState < boolean > ( requireLicense ) ;
75
81
76
82
// onprem and cocalc.com use licenses to adjust quota configs – but only cocalc.com has custom software images
77
83
const show = useMemo (
@@ -110,7 +116,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
110
116
set_error ( "" ) ;
111
117
set_custom_software ( { } ) ;
112
118
set_show_advanced ( false ) ;
113
- set_show_add_license ( false ) ;
119
+ set_show_add_license ( requireLicense ) ;
114
120
set_title_prefill ( true ) ;
115
121
set_license_id ( "" ) ;
116
122
}
@@ -131,6 +137,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
131
137
title : title_text ,
132
138
image : await derive_project_img_name ( custom_software ) ,
133
139
start : true , // used to not start, due to apply_default_upgrades, but upgrades are deprecated
140
+ license : license_id ,
134
141
} ;
135
142
try {
136
143
project_id = await actions . create_project ( opts ) ;
@@ -140,9 +147,6 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
140
147
set_error ( `Error creating project -- ${ err } ` ) ;
141
148
return ;
142
149
}
143
- if ( isValidUUID ( license_id ) ) {
144
- await actions . add_site_license_to_project ( project_id , license_id ) ;
145
- }
146
150
track ( "create-project" , {
147
151
how : "projects-page" ,
148
152
project_id,
@@ -224,10 +228,13 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
224
228
) ;
225
229
}
226
230
227
- function create_disabled ( ) {
231
+ function isDisabled ( ) {
232
+ if ( requireLicense && ! license_id ) {
233
+ return true ;
234
+ }
228
235
return (
229
236
// no name of new project
230
- title_text === "" ||
237
+ ! title_text ?. trim ( ) ||
231
238
// currently saving (?)
232
239
state === "saving" ||
233
240
// user wants a non-default image, but hasn't selected one yet
@@ -283,8 +290,20 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
283
290
function render_add_license ( ) {
284
291
if ( ! show_add_license ) return ;
285
292
return (
286
- < Card size = "small" title = "Select license" style = { CARD_STYLE } >
293
+ < Card
294
+ size = "small"
295
+ title = {
296
+ < >
297
+ < div style = { { float : "right" } } >
298
+ < BuyLicenseForProject size = "small" />
299
+ </ div >
300
+ < Icon name = "key" /> Select License
301
+ </ >
302
+ }
303
+ style = { CARD_STYLE }
304
+ >
287
305
< SiteLicenseInput
306
+ requireValid
288
307
confirmLabel = { "Add this license" }
289
308
onChange = { addSiteLicense }
290
309
/>
@@ -322,6 +341,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
322
341
return (
323
342
< div style = { TOGGLE_STYLE } >
324
343
< Button
344
+ disabled = { requireLicense }
325
345
onClick = { ( ) => set_show_add_license ( true ) }
326
346
type = "link"
327
347
style = { TOGGLE_BUTTON_STYLE }
@@ -345,7 +365,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
345
365
>
346
366
add/remove licenses
347
367
</ A > { " " }
348
- in the project settings later on .
368
+ in project settings later.
349
369
</ div >
350
370
) ;
351
371
}
@@ -381,36 +401,48 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
381
401
/>
382
402
</ Form . Item >
383
403
</ Form >
404
+ < div style = { { color : COLORS . GRAY , float : "right" } } >
405
+ You can change the title at any time.
406
+ </ div >
384
407
</ Col >
385
408
< Col sm = { 12 } >
386
409
< div style = { { color : COLORS . GRAY , marginLeft : "30px" } } >
387
- A < A href = "https://doc.cocalc.com/project.html" > project</ A > is an
388
- isolated private computational workspace that you can share with
389
- others. You can easily change the project's title at any time in
390
- project settings.
410
+ A < A href = "https://doc.cocalc.com/project.html" > project</ A > is a
411
+ private computational workspace that you can use with
412
+ collaborators that you explicitly invite. You can attach powerful{ " " }
413
+ < A href = "https://doc.cocalc.com/compute_server.html" >
414
+ GPUs, CPUs
415
+ </ A > { " " }
416
+ and{ " " }
417
+ < A href = "https://doc.cocalc.com/cloud_file_system.html" >
418
+ storage
419
+ </ A > { " " }
420
+ to a project.
391
421
</ div >
392
422
</ Col >
393
423
</ Row >
394
- { render_advanced_toggle ( ) }
395
- { render_advanced ( ) }
396
424
{ render_add_license_toggle ( ) }
397
425
{ render_add_license ( ) }
398
426
{ render_license ( ) }
427
+ { render_advanced_toggle ( ) }
428
+ { render_advanced ( ) }
399
429
< Row >
400
430
< Col sm = { 24 } style = { { marginTop : "10px" } } >
401
- < Button . Group >
402
- < Button disabled = { state === "saving" } onClick = { cancel_editing } >
403
- Cancel
404
- </ Button >
405
- < Button
406
- disabled = { create_disabled ( ) }
407
- onClick = { ( ) => create_project ( ) }
408
- type = "primary"
409
- >
410
- Create Project
411
- { create_disabled ( ) ? " (enter a title above!)" : "" }
412
- </ Button >
413
- </ Button . Group >
431
+ < Button
432
+ disabled = { state === "saving" }
433
+ onClick = { cancel_editing }
434
+ style = { { marginRight : "8px" } }
435
+ >
436
+ Cancel
437
+ </ Button >
438
+ < Button
439
+ disabled = { isDisabled ( ) }
440
+ onClick = { ( ) => create_project ( ) }
441
+ type = "primary"
442
+ >
443
+ Create Project
444
+ { requireLicense && ! license_id && < > (select license above)</ > }
445
+ </ Button >
414
446
</ Col >
415
447
</ Row >
416
448
< Row >
0 commit comments