@@ -11,25 +11,86 @@ import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
11
11
import RefreshIcon from "@material-ui/icons/Refresh" ;
12
12
import { useCallback , useEffect , useState } from "react" ;
13
13
import DataTable from "react-data-table-component" ;
14
+ import { DndProvider , useDrag , useDrop } from "react-dnd" ;
15
+ import { HTML5Backend } from "react-dnd-html5-backend" ;
14
16
import { useParams } from "react-router-dom" ;
15
17
import API from "utils/API" ;
16
18
import { parseValue , replaceValue , setValue } from "utils/ChangeHelper" ;
17
19
import { formatDistance } from "date-fns" ;
18
20
import AddMember from "./components/AddMember" ;
21
+ import AddGroup from "./components/AddGroup" ;
19
22
import DeleteMember from "./components/DeleteMember" ;
20
23
import ManagedIP from "./components/ManagedIP" ;
21
24
import MemberName from "./components/MemberName" ;
22
25
import MemberSettings from "./components/MemberSettings" ;
23
26
24
27
import { useTranslation } from "react-i18next" ;
25
28
29
+ const MemberItemType = "MEMBER_ITEM" ;
30
+
31
+ const DraggableRow = ( { zoneId, row, content, ...other } ) => {
32
+ const [ , drag ] = useDrag ( {
33
+ type : MemberItemType ,
34
+ item : { zoneId, row } ,
35
+ } ) ;
36
+
37
+ return (
38
+ < div style = { { userSelect : "text" } } ref = { ( node ) => drag ( node ) } { ...other } >
39
+ { content }
40
+ </ div >
41
+ ) ;
42
+ } ;
43
+
44
+ const DropZone = ( { zoneId, moveRow, children } ) => {
45
+ const [ , drop ] = useDrop ( {
46
+ accept : MemberItemType ,
47
+ canDrop : ( item , monitor ) => {
48
+ return item . zoneId !== zoneId ;
49
+ } ,
50
+ drop : ( item , monitor ) => {
51
+ if ( monitor . canDrop ( ) ) {
52
+ moveRow ( item . zoneId , item . row , zoneId ) ;
53
+ }
54
+ } ,
55
+ collect : ( monitor ) => ( {
56
+ isOver : monitor . isOver ( ) ,
57
+ canDrop : monitor . canDrop ( ) ,
58
+ } ) ,
59
+ } ) ;
60
+
61
+ return (
62
+ < div
63
+ style = { { all : "unset" , display : "contents" } }
64
+ ref = { ( node ) => drop ( node ) }
65
+ >
66
+ { children }
67
+ </ div >
68
+ ) ;
69
+ } ;
70
+
26
71
function NetworkMembers ( { network } ) {
27
72
const { nwid } = useParams ( ) ;
28
73
const [ members , setMembers ] = useState ( [ ] ) ;
74
+ const [ groups , setGroups ] = useState ( [ ] ) ;
75
+ const [ extraGroups , setExtraGroups ] = useState ( [ ] ) ;
76
+
77
+ const addGroup = useCallback (
78
+ async ( name ) => {
79
+ if ( ! groups . includes ( name ) && ! groups . includes ( name ) ) {
80
+ let mutableExtraGroups = [ ...extraGroups ] ;
81
+ mutableExtraGroups . push ( name ) ;
82
+ setExtraGroups ( mutableExtraGroups ) ;
83
+ }
84
+ } ,
85
+ [ groups , extraGroups ]
86
+ ) ;
29
87
30
88
const fetchData = useCallback ( async ( ) => {
31
89
try {
32
90
const members = await API . get ( "network/" + nwid + "/member" ) ;
91
+ let groupSet = new Set ( ) ;
92
+ members . data . forEach ( ( x ) => groupSet . add ( x . group ) ) ;
93
+ setGroups ( [ ...groupSet ] ) ;
33
94
setMembers ( members . data ) ;
34
95
console . log ( "Members:" , members . data ) ;
35
96
} catch ( err ) {
@@ -62,7 +123,13 @@ function NetworkMembers({ network }) {
62
123
} ) ;
63
124
let mutableMembers = [ ...members ] ;
64
125
mutableMembers [ index ] = updatedMember ;
126
+ const groups = new Set ( ) ;
127
+ mutableMembers . forEach ( ( x ) => groups . add ( x . group ) ) ;
128
+ let mutableExtraGroups = extraGroups . filter ( ( x ) => ! groups . has ( x ) ) ;
129
+
65
130
setMembers ( mutableMembers ) ;
131
+ setGroups ( [ ...groups ] ) ;
132
+ setExtraGroups ( mutableExtraGroups ) ;
66
133
67
134
const data = setValue ( { } , key1 , key2 , value ) ;
68
135
sendReq ( member [ "config" ] [ "id" ] , data ) ;
@@ -163,44 +230,95 @@ function NetworkMembers({ network }) {
163
230
} ,
164
231
] ;
165
232
233
+ const changeMemberGroup = ( oldGroup , member , newGroup ) => {
234
+ member . group = newGroup ;
235
+
236
+ let mutableMembers = [ ...members ] ;
237
+ const groups = new Set ( ) ;
238
+ mutableMembers . forEach ( ( x ) => groups . add ( x . group ) ) ;
239
+
240
+ // Remove extra group
241
+ let mutableExtraGroups = extraGroups . filter ( ( x ) => ! groups . has ( x ) ) ;
242
+
243
+ setGroups ( [ ...groups ] ) ;
244
+ setExtraGroups ( mutableExtraGroups ) ;
245
+ setMembers ( mutableMembers ) ;
246
+
247
+ const data = setValue ( { } , "group" , null , newGroup ) ;
248
+ sendReq ( member [ "config" ] [ "id" ] , data ) ;
249
+ } ;
250
+
166
251
return (
167
252
< Accordion defaultExpanded = { true } >
168
253
< AccordionSummary expandIcon = { < ExpandMoreIcon /> } >
169
254
< Typography > { t ( "member" , { count : members . length } ) } </ Typography >
170
255
</ AccordionSummary >
171
256
< AccordionDetails >
172
- < Grid container direction = "column" spacing = { 3 } >
173
- < IconButton color = "primary" onClick = { fetchData } >
174
- < RefreshIcon />
175
- </ IconButton >
176
- < Grid container >
177
- { members . length ? (
178
- < DataTable
179
- noHeader = { true }
180
- columns = { columns }
181
- data = { [ ...members ] }
182
- />
183
- ) : (
184
- < Grid
185
- container
186
- spacing = { 0 }
187
- direction = "column"
188
- alignItems = "center"
189
- justifyContent = "center"
190
- style = { {
191
- minHeight : "50vh" ,
192
- } }
193
- >
194
- < Typography variant = "h6" style = { { padding : "10%" } } >
195
- { t ( "noDevices" ) } < b > { nwid } </ b > .
196
- </ Typography >
197
- </ Grid >
198
- ) }
199
- </ Grid >
200
- < Grid item >
201
- < AddMember nwid = { nwid } callback = { fetchData } />
257
+ < DndProvider backend = { HTML5Backend } >
258
+ < Grid container direction = "column" spacing = { 3 } >
259
+ { groups
260
+ . concat ( extraGroups )
261
+ . sort ( )
262
+ . map ( ( group ) => (
263
+ < Accordion defaultExpanded = { group == "" } key = { group } >
264
+ < AccordionSummary expandIcon = { < ExpandMoreIcon /> } >
265
+ < Typography > { group || "Ungrouped" } </ Typography >
266
+ </ AccordionSummary >
267
+ < AccordionDetails >
268
+ < Grid container direction = "column" spacing = { 3 } >
269
+ < IconButton color = "primary" onClick = { fetchData } >
270
+ < RefreshIcon />
271
+ </ IconButton >
272
+ < Grid container >
273
+ < DropZone zoneId = { group } moveRow = { changeMemberGroup } >
274
+ { members . length ? (
275
+ < DataTable
276
+ noHeader = { true }
277
+ columns = { columns }
278
+ data = { [
279
+ ...members . filter ( ( x ) => x . group == group ) ,
280
+ ] }
281
+ renderRow = { ( row , content ) => (
282
+ < DraggableRow
283
+ zoneId = { group }
284
+ row = { row }
285
+ content = { content }
286
+ key = { row . config . address }
287
+ />
288
+ ) }
289
+ />
290
+ ) : (
291
+ < Grid
292
+ container
293
+ spacing = { 0 }
294
+ direction = "column"
295
+ alignItems = "center"
296
+ justifyContent = "center"
297
+ style = { {
298
+ minHeight : "50vh" ,
299
+ } }
300
+ >
301
+ < Typography
302
+ variant = "h6"
303
+ style = { { padding : "10%" } }
304
+ >
305
+ { t ( "noDevices" ) } < b > { nwid } </ b > .
306
+ </ Typography >
307
+ </ Grid >
308
+ ) }
309
+ </ DropZone >
310
+ </ Grid >
311
+ < Grid item > </ Grid >
312
+ </ Grid >
313
+ </ AccordionDetails >
314
+ </ Accordion >
315
+ ) ) }
316
+ < Grid item >
317
+ < AddMember nwid = { nwid } callback = { fetchData } />
318
+ < AddGroup callback = { addGroup } />
319
+ </ Grid >
202
320
</ Grid >
203
- </ Grid >
321
+ </ DndProvider >
204
322
</ AccordionDetails >
205
323
</ Accordion >
206
324
) ;
0 commit comments