1
1
import { useState } from "react" ;
2
- import { Alert , Button , Input , Modal , Progress , Space , Spin } from "antd" ;
2
+ import {
3
+ Alert ,
4
+ Button ,
5
+ Input ,
6
+ Modal ,
7
+ Progress ,
8
+ Space ,
9
+ Spin ,
10
+ Switch ,
11
+ } from "antd" ;
3
12
import { Icon } from "@cocalc/frontend/components/icon" ;
4
13
import { webapp_client } from "@cocalc/frontend/webapp-client" ;
5
14
import type { ColumnsType } from "../../fields" ;
6
- import { Set } from "immutable" ;
15
+ import { Set as iSet } from "immutable" ;
7
16
import { plural } from "@cocalc/util/misc" ;
8
17
import ShowError from "@cocalc/frontend/components/error" ;
9
18
import { map as awaitMap } from "awaiting" ;
@@ -12,7 +21,7 @@ const MAX_PARALLEL_TASKS = 15;
12
21
interface Props {
13
22
title : string ;
14
23
onClose : ( ) => void ;
15
- selected : Set < any > | undefined ;
24
+ selected : iSet < string > | undefined ;
16
25
data : { account_id : string ; tags ?: string [ ] } [ ] ;
17
26
primaryKey : string ;
18
27
columns : ColumnsType [ ] ;
@@ -33,17 +42,18 @@ export default function TagAccounts({
33
42
const [ loading , setLoading ] = useState < boolean > ( false ) ;
34
43
const [ progress , setProgress ] = useState < number > ( 0 ) ;
35
44
const [ error , setError ] = useState < string > ( "" ) ;
45
+ const [ add , setAdd ] = useState < boolean > ( true ) ;
36
46
37
47
if ( selected == null ) {
38
48
return null ;
39
49
}
40
50
41
51
const save = async ( ) => {
42
- const tags = value
52
+ const tags0 = value
43
53
. split ( "," )
44
54
. map ( ( x ) => x . trim ( ) )
45
55
. filter ( ( x ) => ! ! x ) ;
46
- if ( tags . length == 0 || selected == null || selected . size == 0 ) {
56
+ if ( tags0 . length == 0 || selected == null || selected . size == 0 ) {
47
57
setValue ( "" ) ;
48
58
return ;
49
59
}
@@ -57,21 +67,42 @@ export default function TagAccounts({
57
67
setLoading ( true ) ;
58
68
let done = 0 ;
59
69
let goal = selected . size ;
70
+ const check = ( ) => {
71
+ done += 1 ;
72
+ setProgress ( Math . round ( ( done * 100 ) / goal ) ) ;
73
+ } ;
74
+
60
75
const task = async ( account_id ) => {
76
+ let tags ;
77
+ if ( add ) {
78
+ tags = Array . from (
79
+ new Set ( tagsByAccount [ account_id ] . concat ( tags0 ) ) ,
80
+ ) . sort ( ) ;
81
+ } else {
82
+ const x = new Set ( tagsByAccount [ account_id ] ) ;
83
+ const n = x . size ;
84
+ for ( const tag of tags0 ) {
85
+ x . delete ( tag ) ;
86
+ }
87
+ if ( x . size == n ) {
88
+ check ( ) ;
89
+ return ;
90
+ }
91
+ tags = Array . from ( x ) . sort ( ) ;
92
+ }
61
93
try {
62
94
await webapp_client . async_query ( {
63
95
query : {
64
96
crm_accounts : {
65
97
account_id,
66
- tags : tagsByAccount [ account_id ] . concat ( tags ) ,
98
+ tags,
67
99
} ,
68
100
} ,
69
101
} ) ;
70
102
} catch ( err ) {
71
103
errors . push ( `${ err } ` ) ;
72
104
}
73
- done += 1 ;
74
- setProgress ( Math . round ( ( done * 100 ) / goal ) ) ;
105
+ check ( ) ;
75
106
} ;
76
107
await awaitMap ( Array . from ( selected ) , MAX_PARALLEL_TASKS , task ) ;
77
108
setValue ( "" ) ;
@@ -94,8 +125,15 @@ export default function TagAccounts({
94
125
footer = { null }
95
126
title = {
96
127
< div style = { { margin : "0 15px" } } >
97
- < Icon name = "tags-outlined" /> Tag { selected . size } Selected{ " " }
98
- { plural ( selected . size , "Account" ) }
128
+ < Icon name = "tags-outlined" /> { add ? "Tag" : "Untag" } { selected . size } { " " }
129
+ Selected { plural ( selected . size , "Account" ) }
130
+ < Switch
131
+ style = { { float : "right" , marginRight : "30px" } }
132
+ checkedChildren = "Add"
133
+ unCheckedChildren = "Remove"
134
+ defaultChecked
135
+ onChange = { setAdd }
136
+ />
99
137
</ div >
100
138
}
101
139
onCancel = { onClose }
@@ -107,24 +145,26 @@ export default function TagAccounts({
107
145
value = { value }
108
146
onChange = { ( e ) => setValue ( e . target . value ) }
109
147
placeholder = "Tag or tag1,tag2,..."
110
- size = "large"
111
148
/>
112
149
< Button
113
- size = "large"
114
- style = { { height : "41px" /* hack around antd bug?*/ } }
150
+ style = { { height : "37px" /* hack around antd bug?*/ } }
115
151
disabled = { ! value . trim ( ) || loading }
116
152
onClick = { ( ) => {
117
153
save ( ) ;
118
154
} }
119
155
>
120
- Tag { plural ( selected . size , "Account" ) } { loading && < Spin /> }
156
+ { ! add ? "Untag" : "Tag" } { plural ( selected . size , "Account" ) } { " " }
157
+ { loading && < Spin /> }
121
158
</ Button >
122
159
</ Space . Compact >
123
160
< Alert
124
161
style = { { margin : "15px 0" } }
125
162
type = "info"
126
163
message = {
127
- < > The above tags will be applied to each selected account.</ >
164
+ < >
165
+ The above tags will be { add ? "added to" : "removed from" } each
166
+ selected account.
167
+ </ >
128
168
}
129
169
/>
130
170
{ loading && (
0 commit comments