@@ -37,24 +37,11 @@ import { COMMANDS } from "@cocalc/frontend/course/commands";
37
37
import { ProjectTitle } from "@cocalc/frontend/projects/project-title" ;
38
38
import { plural } from "@cocalc/util/misc" ;
39
39
import { exec } from "@cocalc/frontend/frame-editors/generic/client" ;
40
-
41
- const COPY_OPTIONS = [
42
- "collaborator-policy" ,
43
- "email-invitation" ,
44
- "copy-limit" ,
45
- "restrict-student-projects" ,
46
- "nbgrader" ,
47
- "network-file-systems" ,
48
- "env-variables" ,
49
- "upgrades" ,
50
- "software-environment" ,
51
- ] as const ;
40
+ import { CONFIGURATION_GROUPS , ConfigurationGroup } from "./actions" ;
52
41
import { useFrameContext } from "@cocalc/frontend/app-framework" ;
53
42
54
- type CopyOptionKeys = ( typeof COPY_OPTIONS ) [ number ] ;
55
-
56
43
export type CopyConfigurationOptions = {
57
- [ K in CopyOptionKeys ] ?: boolean ;
44
+ [ K in ConfigurationGroup ] ?: boolean ;
58
45
} ;
59
46
60
47
export interface CopyConfigurationTargets {
@@ -74,13 +61,42 @@ export default function ConfigurationCopying({
74
61
actions,
75
62
close,
76
63
} : Props ) {
64
+ const [ error , setError ] = useState < string > ( "" ) ;
77
65
const { numTargets, numOptions } = useMemo ( ( ) => {
78
- const targets = ( settings . get ( "copy_config_targets" ) ?. toJS ( ) ??
79
- { } ) as CopyConfigurationTargets ;
80
- const options = ( settings . get ( "copy_config_options" ) ?. toJS ( ) ??
81
- { } ) as CopyConfigurationOptions ;
66
+ const targets = getTargets ( settings ) ;
67
+ const options = getOptions ( settings ) ;
82
68
return { numTargets : numTrue ( targets ) , numOptions : numTrue ( options ) } ;
83
69
} , [ settings ] ) ;
70
+ const [ copying , setCopying ] = useState < boolean > ( false ) ;
71
+
72
+ const copyConfiguration = async ( ) => {
73
+ try {
74
+ setCopying ( true ) ;
75
+ setError ( "" ) ;
76
+ const targets = getTargets ( settings ) ;
77
+ const options = getOptions ( settings ) ;
78
+ const t : { project_id : string ; path : string } [ ] = [ ] ;
79
+ for ( const key in targets ) {
80
+ if ( targets [ key ] === true ) {
81
+ t . push ( parseKey ( key ) ) ;
82
+ }
83
+ }
84
+ const g : ConfigurationGroup [ ] = [ ] ;
85
+ for ( const key in options ) {
86
+ if ( options [ key ] === true ) {
87
+ g . push ( key as ConfigurationGroup ) ;
88
+ }
89
+ }
90
+ await actions . configuration . copyConfiguration ( {
91
+ groups : g ,
92
+ targets : t ,
93
+ } ) ;
94
+ } catch ( err ) {
95
+ setError ( `${ err } ` ) ;
96
+ } finally {
97
+ setCopying ( false ) ;
98
+ }
99
+ } ;
84
100
85
101
return (
86
102
< Card
@@ -94,12 +110,18 @@ export default function ConfigurationCopying({
94
110
Copy configuration from this course to other courses.
95
111
</ div >
96
112
< div style = { { textAlign : "center" , margin : "15px 0" } } >
97
- < Button size = "large" disabled = { numTargets == 0 || numOptions == 0 } >
113
+ < Button
114
+ size = "large"
115
+ disabled = { numTargets == 0 || numOptions == 0 || copying }
116
+ onClick = { copyConfiguration }
117
+ >
98
118
< Icon name = "copy" />
99
- Copy { numOptions } { plural ( numOptions , "configuration item" ) } to{ " " }
100
- { numTargets } { plural ( numTargets , "target course" ) }
119
+ Copy{ copying ? "ing" : "" } { numOptions } { " " }
120
+ { plural ( numOptions , "configuration item" ) } to { numTargets } { " " }
121
+ { plural ( numTargets , "target course" ) } { copying && < Spin /> }
101
122
</ Button >
102
123
</ div >
124
+ < ShowError style = { { margin : "15px" } } error = { error } setError = { setError } />
103
125
< ConfigTargets
104
126
actions = { actions }
105
127
project_id = { project_id }
@@ -170,7 +192,8 @@ function ConfigTargets({
170
192
</ >
171
193
) : undefined }
172
194
</ Checkbox >
173
- < Tooltip mouseEnterDelay = { 1 }
195
+ < Tooltip
196
+ mouseEnterDelay = { 1 }
174
197
title = {
175
198
< > Open { path } in a new tab. (Use shift to open in background.)</ >
176
199
}
@@ -184,7 +207,9 @@ function ConfigTargets({
184
207
redux
185
208
. getProjectActions ( project_id )
186
209
. open_file ( { path, foreground } ) ;
187
- close ?.( ) ;
210
+ if ( foreground ) {
211
+ close ?.( ) ;
212
+ }
188
213
} }
189
214
>
190
215
< Icon name = "external-link" />
@@ -202,7 +227,10 @@ function ConfigTargets({
202
227
actions . set ( { copy_config_targets, table : "settings" } ) ;
203
228
} }
204
229
>
205
- < Tooltip mouseEnterDelay = { 1 } title = { < > Remove { path } from copy targets?</ > } >
230
+ < Tooltip
231
+ mouseEnterDelay = { 1 }
232
+ title = { < > Remove { path } from copy targets?</ > }
233
+ >
206
234
< Button size = "small" type = "link" >
207
235
< Icon name = "trash" />
208
236
</ Button >
@@ -232,7 +260,6 @@ function ConfigTargets({
232
260
. getProjectActions ( project_id )
233
261
. open_file ( { path, foreground : false } ) ;
234
262
}
235
- close ?.( ) ;
236
263
} ;
237
264
238
265
return (
@@ -241,7 +268,10 @@ function ConfigTargets({
241
268
< div style = { { flex : 1 } } >
242
269
< Divider >
243
270
Target Courses{ " " }
244
- < Tooltip mouseEnterDelay = { 1 } title = "Open all selected targets in background tabs." >
271
+ < Tooltip
272
+ mouseEnterDelay = { 1 }
273
+ title = "Open all selected targets in background tabs."
274
+ >
245
275
< a onClick = { openAll } > (open all)</ a >
246
276
</ Tooltip >
247
277
</ Divider >
@@ -258,7 +288,7 @@ function ConfigTargets({
258
288
actions . set ( { copy_config_targets, table : "settings" } ) ;
259
289
} }
260
290
>
261
- Clear
291
+ None
262
292
</ Button >
263
293
< Button
264
294
disabled = { numFalse ( targets ) == 0 }
@@ -288,7 +318,7 @@ function getOptions(settings) {
288
318
function ConfigOptions ( { settings, actions, numOptions } ) {
289
319
const options = getOptions ( settings ) ;
290
320
const v : JSX . Element [ ] = [ ] ;
291
- for ( const option of COPY_OPTIONS ) {
321
+ for ( const option of CONFIGURATION_GROUPS ) {
292
322
const { title, label, icon } = COMMANDS [ option ] ?? { } ;
293
323
v . push (
294
324
< Tooltip key = { option } title = { title } mouseEnterDelay = { 1 } >
@@ -319,20 +349,20 @@ function ConfigOptions({ settings, actions, numOptions }) {
319
349
size = "small"
320
350
onClick = { ( ) => {
321
351
const copy_config_options = { } as CopyConfigurationOptions ;
322
- for ( const option of COPY_OPTIONS ) {
352
+ for ( const option of CONFIGURATION_GROUPS ) {
323
353
copy_config_options [ option ] = false ;
324
354
}
325
355
actions . set ( { copy_config_options, table : "settings" } ) ;
326
356
} }
327
357
>
328
- Clear
358
+ None
329
359
</ Button >
330
360
< Button
331
- disabled = { numOptions == COPY_OPTIONS . length }
361
+ disabled = { numOptions == CONFIGURATION_GROUPS . length }
332
362
size = "small"
333
363
onClick = { ( ) => {
334
364
const copy_config_options = { } as CopyConfigurationOptions ;
335
- for ( const option of COPY_OPTIONS ) {
365
+ for ( const option of CONFIGURATION_GROUPS ) {
336
366
copy_config_options [ option ] = true ;
337
367
}
338
368
actions . set ( { copy_config_options, table : "settings" } ) ;
0 commit comments