Projekt

Obecné

Profil

Stáhnout (16.6 KB) Statistiky
| Větev: | Tag: | Revize:
1
import 'antd/dist/antd.css';
2
import React, { useContext, useEffect, useState } from 'react';
3

    
4
import { useUnauthRedirect } from '../../hooks';
5
import { useRouter } from 'next/router';
6
import { Button, Checkbox, Popconfirm, Tooltip, Typography } from 'antd';
7
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
import {
9
    faTags,
10
    faPlus,
11
    faPenToSquare,
12
    faTrashCan,
13
} from '@fortawesome/free-solid-svg-icons';
14
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
15
import { MainLayout } from '../../layouts/MainLayout';
16
import { Container, Row, Stack } from 'react-bootstrap';
17
import { Table } from 'antd';
18
import { tagController } from '../../controllers';
19
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

    
26
/**
27
 * Creates a tag management page.
28
 * @returns The tag management page.
29
 */
30
function TagsPage() {
31
    const redirecting = useUnauthRedirect('/login');
32
    const { logout, role } = useContext(LoggedUserContext);
33
    const router = useRouter();
34

    
35
    const [tagData, setTagData] = useState<any[] | null>([]);
36

    
37
    /**
38
     * Data in a table.
39
     */
40
    const [shownData, setShownData] = useState<any[] | undefined>([]);
41

    
42
    // 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
    useEffect(() => {
65
        if (!redirecting && role === 'ADMINISTRATOR') {
66
            loadData();
67
        }
68
    }, [logout, redirecting, role, router]);
69

    
70
    /**
71
     * Definition of columns of a table.
72
     */
73
    const columns = [
74
        { title: 'Název', dataIndex: 'name', key: 'name' },
75
        {
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
        {
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
        { title: 'Popis', dataIndex: 'description', key: 'description' },
105
        {
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
                        <Tooltip title="Přidat potomka">
114
                            <Button type="primary" onClick={addChild(record)}>
115
                                <FontAwesomeIcon icon={faPlus} />
116
                            </Button>
117
                        </Tooltip>
118
                    )}
119
                    <Tooltip title="Upravit">
120
                        <Button type="text" onClick={editRecord(record)}>
121
                            <FontAwesomeIcon icon={faPenToSquare} />
122
                        </Button>
123
                    </Tooltip>
124
                    <Popconfirm
125
                        title="Opravdu chcete smazat?"
126
                        onConfirm={deleteRecord(record)}
127
                    >
128
                        <Tooltip title="Odstranit">
129
                            <Button type="primary" danger>
130
                                <FontAwesomeIcon icon={faTrashCan} />
131
                            </Button>
132
                        </Tooltip>
133
                    </Popconfirm>
134
                </Stack>
135
            ),
136
        },
137
    ];
138

    
139
    // ------------------------------------ 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
                sentimentEnabled: val.sentimentEnabled,
206
            })
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
                sentimentEnabled: val.sentimentEnabled,
231
            })
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
                setTagData(tagTree.data.tagCategories);
333
                setShownData(mapData(tagTree.data.tagCategories));
334
            }
335
        });
336
    };
337

    
338
    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
                            sentimentEnabled: tagInfo.sentimentEnabled,
355
                            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
    /**
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
        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

    
394
                return (
395
                    category.children?.length > 0 ||
396
                    category.name.toLowerCase().includes(value.toLowerCase())
397
                );
398
            });
399
        }
400

    
401
        setShownData(data);
402
    };
403

    
404
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
405
        <MainLayout>
406
            {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
                    defaultValues={{ color: selectedRecord.color }}
432
                />
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
            <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
                        dataSource={shownData}
474
                        scroll={{ y: 'calc(100vh - 300px)' }}
475
                        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
                                    allowClear
487
                                />
488
                            </Stack>
489
                        )}
490
                    />
491
                </Row>
492
            </Container>
493
        </MainLayout>
494
    );
495
}
496

    
497
export default TagsPage;
    (1-1/1)