Projekt

Obecné

Profil

Stáhnout (16.6 KB) Statistiky
| Větev: | Tag: | Revize:
1 8c45ccb0 hrubyjar
import 'antd/dist/antd.css';
2 cd2f7b19 Dominik Poch
import React, { useContext, useEffect, useState } from 'react';
3 8c45ccb0 hrubyjar
4
import { useUnauthRedirect } from '../../hooks';
5
import { useRouter } from 'next/router';
6 d0ed1dbf Dominik Poch
import { Button, Checkbox, Popconfirm, Tooltip, Typography } from 'antd';
7 8c45ccb0 hrubyjar
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8 cdd4201c Dominik Poch
import {
9
    faTags,
10
    faPlus,
11
    faPenToSquare,
12
    faTrashCan,
13
} from '@fortawesome/free-solid-svg-icons';
14 8c45ccb0 hrubyjar
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
15 51f70e00 Lukáš Vlček
import { MainLayout } from '../../layouts/MainLayout';
16 fe33e057 Dominik Poch
import { Container, Row, Stack } from 'react-bootstrap';
17 cd2f7b19 Dominik Poch
import { Table } from 'antd';
18
import { tagController } from '../../controllers';
19 fe33e057 Dominik Poch
import Search from 'antd/lib/input/Search';
20
import CategoryModal, {
21
    CategoryModalValues,
22
} from '../../components/modals/CategoryModal';
23
import TagModal, { TagModalValues } from '../../components/modals/TagModal';
24
import SubTagModal, { SubTagModalValues } from '../../components/modals/SubTagModal';
25 8c45ccb0 hrubyjar
26 fe33e057 Dominik Poch
/**
27
 * Creates a tag management page.
28
 * @returns The tag management page.
29
 */
30 8c45ccb0 hrubyjar
function TagsPage() {
31
    const redirecting = useUnauthRedirect('/login');
32
    const { logout, role } = useContext(LoggedUserContext);
33
    const router = useRouter();
34
35 41e65564 Dominik Poch
    const [tagData, setTagData] = useState<any[] | null>([]);
36
37 fe33e057 Dominik Poch
    /**
38
     * Data in a table.
39
     */
40 41e65564 Dominik Poch
    const [shownData, setShownData] = useState<any[] | undefined>([]);
41 cd2f7b19 Dominik Poch
42 fe33e057 Dominik Poch
    // States that says if modal windows should be shown.
43
    const [showAddCategoryModal, setShowAddCategoryModal] = useState(false);
44
    const [showEditCategoryModal, setShowEditCategoryModal] = useState(false);
45
    const [showAddTagModal, setShowAddTagModal] = useState(false);
46
    const [showEditTagModal, setShowEditTagModal] = useState(false);
47
    const [showAddSubTagModal, setShowAddSubTagModal] = useState(false);
48
    const [showEditSubTagModal, setShowEditSubTagModal] = useState(false);
49
50
    /**
51
     * Currently selected record in a table.
52
     */
53
    const [selectedRecord, setSelectedRecord] = useState({
54
        key: '',
55
        name: '',
56
        description: '',
57
        color: '',
58
        disabledForAnnotators: false,
59
    });
60
61
    /**
62
     * Loads data on start.
63
     */
64 8c45ccb0 hrubyjar
    useEffect(() => {
65 06d1aa21 Jaroslav Hrubý
        if (!redirecting && role === 'ADMINISTRATOR') {
66 fe33e057 Dominik Poch
            loadData();
67 8c45ccb0 hrubyjar
        }
68
    }, [logout, redirecting, role, router]);
69
70 fe33e057 Dominik Poch
    /**
71
     * Definition of columns of a table.
72
     */
73 cd2f7b19 Dominik Poch
    const columns = [
74
        { title: 'Název', dataIndex: 'name', key: 'name' },
75 fe33e057 Dominik Poch
        {
76
            title: 'Barva',
77
            dataIndex: 'color',
78
            key: 'color',
79
            width: 75,
80
            render: (
81
                columnData: string | null | undefined,
82
                record: any,
83
                index: number
84
            ) => {
85
                if (columnData) return <input type="color" value={columnData} disabled />;
86
            },
87
        },
88
        {
89
            title: 'Skrýt pro anotátory',
90
            dataIndex: 'disabledForAnnotators',
91
            key: 'disabledForAnnotators',
92
            width: 150,
93
            render: (columnData: boolean | undefined, record: any, index: number) =>
94
                columnData !== undefined && <Checkbox checked={columnData} disabled />,
95
        },
96 0e4d7bd0 Dominik Poch
        {
97
            title: 'Povolit sentiment',
98
            dataIndex: 'sentimentEnabled',
99
            key: 'sentimentEnabled',
100
            width: 150,
101
            render: (columnData: boolean | undefined, record: any, index: number) =>
102
                columnData !== undefined && <Checkbox checked={columnData} disabled />,
103
        },
104 cd2f7b19 Dominik Poch
        { title: 'Popis', dataIndex: 'description', key: 'description' },
105 fe33e057 Dominik Poch
        {
106
            title: '',
107
            dataIndex: 'operations',
108
            key: 'operations',
109
            width: 200,
110
            render: (text: any, record: any, index: number) => (
111
                <Stack direction="horizontal">
112
                    {record.depth < 2 && (
113 d0ed1dbf Dominik Poch
                        <Tooltip title="Přidat potomka">
114
                            <Button type="primary" onClick={addChild(record)}>
115 fa93df26 Dominik Poch
                                <FontAwesomeIcon icon={faPlus} />
116 d0ed1dbf Dominik Poch
                            </Button>
117
                        </Tooltip>
118 fe33e057 Dominik Poch
                    )}
119 d0ed1dbf Dominik Poch
                    <Tooltip title="Upravit">
120
                        <Button type="text" onClick={editRecord(record)}>
121 fa93df26 Dominik Poch
                            <FontAwesomeIcon icon={faPenToSquare} />
122 d0ed1dbf Dominik Poch
                        </Button>
123
                    </Tooltip>
124 fe33e057 Dominik Poch
                    <Popconfirm
125
                        title="Opravdu chcete smazat?"
126
                        onConfirm={deleteRecord(record)}
127
                    >
128 d0ed1dbf Dominik Poch
                        <Tooltip title="Odstranit">
129
                            <Button type="primary" danger>
130 fa93df26 Dominik Poch
                                <FontAwesomeIcon icon={faTrashCan} />
131 d0ed1dbf Dominik Poch
                            </Button>
132
                        </Tooltip>
133 fe33e057 Dominik Poch
                    </Popconfirm>
134
                </Stack>
135
            ),
136
        },
137 cd2f7b19 Dominik Poch
    ];
138
139 fe33e057 Dominik Poch
    // ------------------------------------ ADD CATEGORY MODAL -------------------------------------
140
141
    const openAddCategoryModal = () => {
142
        setShowAddCategoryModal(true);
143
    };
144
145
    const submitAddCategoryModal = (val: CategoryModalValues) => {
146
        tagController
147
            .categoriesPost({
148
                name: val.name,
149
                description: val.description,
150
                color: val.color,
151
                disabledForAnnotators: val.disabledForAnnotators,
152
            })
153
            .then(
154
                () => loadData(),
155
                (reason) => console.log(reason)
156
            );
157
158
        setShowAddCategoryModal(false);
159
    };
160
161
    const cancelAddCategoryModal = () => {
162
        setShowAddCategoryModal(false);
163
    };
164
165
    // --------------------------------------------------------------------------------------------
166
167
    // ------------------------------------ EDIT CATEGORY MODAL -----------------------------------
168
169
    const openEditCategoryModal = () => {
170
        setShowEditCategoryModal(true);
171
    };
172
173
    const submitEditCategoryModel = (val: CategoryModalValues) => {
174
        tagController
175
            .categoryCategoryIdPut(selectedRecord.key, {
176
                name: val.name,
177
                description: val.description,
178
                color: val.color,
179
                disabledForAnnotators: val.disabledForAnnotators,
180
            })
181
            .then(() => loadData());
182
183
        setShowEditCategoryModal(false);
184
    };
185
186
    const cancelEditCategoryModal = () => {
187
        setShowEditCategoryModal(false);
188
    };
189
190
    // --------------------------------------------------------------------------------------------
191
192
    // ------------------------------------ ADD TAG MODAL -------------------------------------
193
194
    const openAddTagModal = () => {
195
        setShowAddTagModal(true);
196
    };
197
198
    const submitAddTagModal = (val: TagModalValues) => {
199
        tagController
200
            .tagsPost({
201
                categoryId: selectedRecord.key,
202
                name: val.name,
203
                description: val.description,
204
                color: val.color,
205 0e4d7bd0 Dominik Poch
                sentimentEnabled: val.sentimentEnabled,
206 fe33e057 Dominik Poch
            })
207
            .then(() => loadData());
208
209
        setShowAddTagModal(false);
210
    };
211
212
    const cancelAddTagModal = () => {
213
        setShowAddTagModal(false);
214
    };
215
216
    // --------------------------------------------------------------------------------------------
217
218
    // ------------------------------------ EDIT TAG MODAL -------------------------------------
219
220
    const openEditTagModal = () => {
221
        setShowEditTagModal(true);
222
    };
223
224
    const submitEditTagModal = (val: TagModalValues) => {
225
        tagController
226
            .tagTagIdPut(selectedRecord.key, {
227
                name: val.name,
228
                description: val.description,
229
                color: val.color,
230 0e4d7bd0 Dominik Poch
                sentimentEnabled: val.sentimentEnabled,
231 fe33e057 Dominik Poch
            })
232
            .then(() => loadData());
233
234
        setShowEditTagModal(false);
235
    };
236
237
    const cancelEditTagModal = () => {
238
        setShowEditTagModal(false);
239
    };
240
241
    // --------------------------------------------------------------------------------------------
242
243
    // ------------------------------------ ADD SUB TAG MODAL -------------------------------------
244
245
    const openAddSubTagModal = () => {
246
        setShowAddSubTagModal(true);
247
    };
248
249
    const submitAddSubTagModal = (val: SubTagModalValues) => {
250
        tagController
251
            .subtagsPost({
252
                tagId: selectedRecord.key,
253
                name: val.name,
254
                description: val.description,
255
            })
256
            .then(() => loadData());
257
258
        setShowAddSubTagModal(false);
259
    };
260
261
    const cancelAddSubTagModal = () => {
262
        setShowAddSubTagModal(false);
263
    };
264
265
    // --------------------------------------------------------------------------------------------
266
267
    // ------------------------------------ EDIT SUB TAG MODAL -------------------------------------
268
269
    const openEditSubTagModal = () => {
270
        setShowEditSubTagModal(true);
271
    };
272
273
    const submitEditSubTagModal = (val: SubTagModalValues) => {
274
        tagController
275
            .subtagSubtagIdPut(selectedRecord.key, {
276
                name: val.name,
277
                description: val.description,
278
            })
279
            .then(() => loadData());
280
281
        setShowEditSubTagModal(false);
282
    };
283
284
    const cancelEditSubTagModal = () => {
285
        setShowEditSubTagModal(false);
286
    };
287
288
    // --------------------------------------------------------------------------------------------
289
290
    // ------------------------------------ RECORD OPERATIONS -------------------------------------
291
292
    const addChild = (record: any) => (e: any) => {
293
        setSelectedRecord(record);
294
295
        if (record.depth === 0) {
296
            openAddTagModal();
297
        } else if (record.depth === 1) {
298
            openAddSubTagModal();
299
        }
300
    };
301
302
    const editRecord = (record: any) => (e: any) => {
303
        setSelectedRecord(record);
304
305
        if (record.depth === 0) {
306
            openEditCategoryModal();
307
        } else if (record.depth === 1) {
308
            openEditTagModal();
309
        } else if (record.depth === 2) {
310
            openEditSubTagModal();
311
        }
312
    };
313
314
    const deleteRecord = (record: any) => (e: any) => {
315
        if (record.depth === 0) {
316
            tagController.categoryCategoryIdDelete(record.key).then(() => loadData());
317
        } else if (record.depth === 1) {
318
            tagController.tagTagIdDelete(record.key).then(() => loadData());
319
        } else if (record.depth === 2) {
320
            tagController.subtagSubtagIdDelete(record.key).then(() => loadData());
321
        }
322
    };
323
324
    // --------------------------------------------------------------------------------------------
325
326
    /**
327
     * Loads data from a server.
328
     */
329
    const loadData = () => {
330
        tagController.tagsGet().then((tagTree) => {
331
            if (typeof tagTree.data.tagCategories != 'undefined') {
332 41e65564 Dominik Poch
                setTagData(tagTree.data.tagCategories);
333
                setShownData(mapData(tagTree.data.tagCategories));
334 fe33e057 Dominik Poch
            }
335
        });
336
    };
337
338 41e65564 Dominik Poch
    const mapData = (data: any[] | null) => {
339
        return data?.map((catInfo) => {
340
            return {
341
                key: catInfo.id,
342
                name: catInfo.name,
343
                description: catInfo.description,
344
                color: catInfo.color,
345
                depth: 0,
346
                disabledForAnnotators: catInfo.disabledForAnnotators,
347
                ...(catInfo.tags?.length && {
348
                    children: catInfo.tags?.map((tagInfo: any) => {
349
                        return {
350
                            key: tagInfo.id,
351
                            name: tagInfo.name,
352
                            description: tagInfo.description,
353
                            color: tagInfo.color,
354 0e4d7bd0 Dominik Poch
                            sentimentEnabled: tagInfo.sentimentEnabled,
355 41e65564 Dominik Poch
                            depth: 1,
356
                            ...(tagInfo.subTags?.length && {
357
                                children: tagInfo.subTags?.map((subInfo: any) => {
358
                                    return {
359
                                        key: subInfo.id,
360
                                        name: subInfo.name,
361
                                        description: subInfo.description,
362
                                        depth: 2,
363
                                    };
364
                                }),
365
                            }),
366
                        };
367
                    }),
368
                }),
369
            };
370
        });
371
    };
372
373 fe33e057 Dominik Poch
    /**
374
     * Searches given value in 'name' column in a table.
375
     * If the provided value is empty the method loads all data from a server.
376
     * @param value Value that is searched.
377
     */
378
    const searchTag = (value: string) => {
379 41e65564 Dominik Poch
        let data = mapData(tagData);
380
381
        if (value) {
382
            data = data?.filter((category) => {
383
                category.children = category.children?.filter((tag: any) => {
384
                    tag.children = tag.children?.filter((subTag: any) =>
385
                        subTag.name.toLowerCase().includes(value.toLowerCase())
386
                    );
387
388
                    return (
389
                        tag.children?.length > 0 ||
390
                        tag.name.toLowerCase().includes(value.toLowerCase())
391
                    );
392
                });
393 fe33e057 Dominik Poch
394 41e65564 Dominik Poch
                return (
395
                    category.children?.length > 0 ||
396
                    category.name.toLowerCase().includes(value.toLowerCase())
397 fe33e057 Dominik Poch
                );
398
            });
399 41e65564 Dominik Poch
        }
400 fe33e057 Dominik Poch
401 41e65564 Dominik Poch
        setShownData(data);
402 fe33e057 Dominik Poch
    };
403
404 8c45ccb0 hrubyjar
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
405
        <MainLayout>
406 fe33e057 Dominik Poch
            {showAddCategoryModal && (
407
                <CategoryModal
408
                    title="Vytvořit kategorii"
409
                    submitText="Přidat"
410
                    onCancel={cancelAddCategoryModal}
411
                    onSubmit={submitAddCategoryModal}
412
                />
413
            )}
414
415
            {showEditCategoryModal && (
416
                <CategoryModal
417
                    title="Editovat kategorii"
418
                    submitText="Změnit"
419
                    onCancel={cancelEditCategoryModal}
420
                    onSubmit={submitEditCategoryModel}
421
                    defaultValues={selectedRecord}
422
                />
423
            )}
424
425
            {showAddTagModal && (
426
                <TagModal
427
                    title="Vytvořit tag"
428
                    submitText="Přidat"
429
                    onCancel={cancelAddTagModal}
430
                    onSubmit={submitAddTagModal}
431 67683ef3 Dominik Poch
                    defaultValues={{ color: selectedRecord.color }}
432 fe33e057 Dominik Poch
                />
433
            )}
434
435
            {showEditTagModal && (
436
                <TagModal
437
                    title="Editovat tag"
438
                    submitText="Změnit"
439
                    onCancel={cancelEditTagModal}
440
                    onSubmit={submitEditTagModal}
441
                    defaultValues={selectedRecord}
442
                />
443
            )}
444
445
            {showAddSubTagModal && (
446
                <SubTagModal
447
                    title="Vytvořit sub tag"
448
                    submitText="Přidat"
449
                    onCancel={cancelAddSubTagModal}
450
                    onSubmit={submitAddSubTagModal}
451
                />
452
            )}
453
454
            {showEditSubTagModal && (
455
                <SubTagModal
456
                    title="Editovat sub tag"
457
                    submitText="Změnit"
458
                    onCancel={cancelEditSubTagModal}
459
                    onSubmit={submitEditSubTagModal}
460
                    defaultValues={selectedRecord}
461
                />
462
            )}
463
464 cd2f7b19 Dominik Poch
            <Container>
465
                <Row>
466
                    <Typography.Title level={2}>
467
                        <FontAwesomeIcon icon={faTags} /> Značky
468
                    </Typography.Title>
469
                </Row>
470
                <Row>
471
                    <Table
472
                        columns={columns}
473 41e65564 Dominik Poch
                        dataSource={shownData}
474 cd2f7b19 Dominik Poch
                        scroll={{ y: 'calc(100vh - 300px)' }}
475 fe33e057 Dominik Poch
                        size="small"
476
                        title={() => (
477
                            <Stack direction="horizontal" gap={3}>
478
                                <Button type="primary" onClick={openAddCategoryModal}>
479
                                    Přidat kategorii
480
                                </Button>
481
                                <Search
482
                                    placeholder="Vyhledání tagu"
483
                                    onSearch={searchTag}
484
                                    style={{ width: 200 }}
485
                                    className="ms-auto"
486 8755ddb8 Dominik Poch
                                    allowClear
487 fe33e057 Dominik Poch
                                />
488
                            </Stack>
489
                        )}
490 cd2f7b19 Dominik Poch
                    />
491
                </Row>
492
            </Container>
493 8c45ccb0 hrubyjar
        </MainLayout>
494
    );
495
}
496
497
export default TagsPage;