Projekt

Obecné

Profil

Stáhnout (16.5 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 { DeleteOutlined, PlusOutlined, EditOutlined } from '@ant-design/icons';
21
import CategoryModal, {
22
    CategoryModalValues,
23
} from '../../components/modals/CategoryModal';
24
import TagModal, { TagModalValues } from '../../components/modals/TagModal';
25
import SubTagModal, { SubTagModalValues } from '../../components/modals/SubTagModal';
26

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

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

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

    
43
    // States that says if modal windows should be shown.
44
    const [showAddCategoryModal, setShowAddCategoryModal] = useState(false);
45
    const [showEditCategoryModal, setShowEditCategoryModal] = useState(false);
46
    const [showAddTagModal, setShowAddTagModal] = useState(false);
47
    const [showEditTagModal, setShowEditTagModal] = useState(false);
48
    const [showAddSubTagModal, setShowAddSubTagModal] = useState(false);
49
    const [showEditSubTagModal, setShowEditSubTagModal] = useState(false);
50

    
51
    /**
52
     * Currently selected record in a table.
53
     */
54
    const [selectedRecord, setSelectedRecord] = useState({
55
        key: '',
56
        name: '',
57
        description: '',
58
        color: '',
59
        disabledForAnnotators: false,
60
    });
61

    
62
    /**
63
     * Loads data on start.
64
     */
65
    useEffect(() => {
66
        if (!redirecting && role === 'ADMINISTRATOR') {
67
            loadData();
68
        }
69
    }, [logout, redirecting, role, router]);
70

    
71
    /**
72
     * Definition of columns of a table.
73
     */
74
    const columns = [
75
        { title: 'Název', dataIndex: 'name', key: 'name' },
76
        {
77
            title: 'Barva',
78
            dataIndex: 'color',
79
            key: 'color',
80
            width: 75,
81
            render: (
82
                columnData: string | null | undefined,
83
                record: any,
84
                index: number
85
            ) => {
86
                if (columnData) return <input type="color" value={columnData} disabled />;
87
            },
88
        },
89
        {
90
            title: 'Skrýt pro anotátory',
91
            dataIndex: 'disabledForAnnotators',
92
            key: 'disabledForAnnotators',
93
            width: 150,
94
            render: (columnData: boolean | undefined, record: any, index: number) =>
95
                columnData !== undefined && <Checkbox checked={columnData} disabled />,
96
        },
97
        {
98
            title: 'Povolit sentiment',
99
            dataIndex: 'sentimentEnabled',
100
            key: 'sentimentEnabled',
101
            width: 150,
102
            render: (columnData: boolean | undefined, record: any, index: number) =>
103
                columnData !== undefined && <Checkbox checked={columnData} disabled />,
104
        },
105
        { title: 'Popis', dataIndex: 'description', key: 'description' },
106
        {
107
            title: '',
108
            dataIndex: 'operations',
109
            key: 'operations',
110
            width: 200,
111
            render: (text: any, record: any, index: number) => (
112
                <Stack direction="horizontal">
113
                    {record.depth < 2 && (
114
                        <Tooltip title="Přidat potomka">
115
                            <Button type="primary" onClick={addChild(record)}>
116
                                <PlusOutlined />
117
                            </Button>
118
                        </Tooltip>
119
                    )}
120
                    <Tooltip title="Upravit">
121
                        <Button type="text" onClick={editRecord(record)}>
122
                            <EditOutlined />
123
                        </Button>
124
                    </Tooltip>
125
                    <Popconfirm
126
                        title="Opravdu chcete smazat?"
127
                        onConfirm={deleteRecord(record)}
128
                    >
129
                        <Tooltip title="Odstranit">
130
                            <Button type="primary" danger>
131
                                <DeleteOutlined />
132
                            </Button>
133
                        </Tooltip>
134
                    </Popconfirm>
135
                </Stack>
136
            ),
137
        },
138
    ];
139

    
140
    // ------------------------------------ ADD CATEGORY MODAL -------------------------------------
141

    
142
    const openAddCategoryModal = () => {
143
        setShowAddCategoryModal(true);
144
    };
145

    
146
    const submitAddCategoryModal = (val: CategoryModalValues) => {
147
        tagController
148
            .categoriesPost({
149
                name: val.name,
150
                description: val.description,
151
                color: val.color,
152
                disabledForAnnotators: val.disabledForAnnotators,
153
            })
154
            .then(
155
                () => loadData(),
156
                (reason) => console.log(reason)
157
            );
158

    
159
        setShowAddCategoryModal(false);
160
    };
161

    
162
    const cancelAddCategoryModal = () => {
163
        setShowAddCategoryModal(false);
164
    };
165

    
166
    // --------------------------------------------------------------------------------------------
167

    
168
    // ------------------------------------ EDIT CATEGORY MODAL -----------------------------------
169

    
170
    const openEditCategoryModal = () => {
171
        setShowEditCategoryModal(true);
172
    };
173

    
174
    const submitEditCategoryModel = (val: CategoryModalValues) => {
175
        tagController
176
            .categoryCategoryIdPut(selectedRecord.key, {
177
                name: val.name,
178
                description: val.description,
179
                color: val.color,
180
                disabledForAnnotators: val.disabledForAnnotators,
181
            })
182
            .then(() => loadData());
183

    
184
        setShowEditCategoryModal(false);
185
    };
186

    
187
    const cancelEditCategoryModal = () => {
188
        setShowEditCategoryModal(false);
189
    };
190

    
191
    // --------------------------------------------------------------------------------------------
192

    
193
    // ------------------------------------ ADD TAG MODAL -------------------------------------
194

    
195
    const openAddTagModal = () => {
196
        setShowAddTagModal(true);
197
    };
198

    
199
    const submitAddTagModal = (val: TagModalValues) => {
200
        tagController
201
            .tagsPost({
202
                categoryId: selectedRecord.key,
203
                name: val.name,
204
                description: val.description,
205
                color: val.color,
206
                sentimentEnabled: val.sentimentEnabled,
207
            })
208
            .then(() => loadData());
209

    
210
        setShowAddTagModal(false);
211
    };
212

    
213
    const cancelAddTagModal = () => {
214
        setShowAddTagModal(false);
215
    };
216

    
217
    // --------------------------------------------------------------------------------------------
218

    
219
    // ------------------------------------ EDIT TAG MODAL -------------------------------------
220

    
221
    const openEditTagModal = () => {
222
        setShowEditTagModal(true);
223
    };
224

    
225
    const submitEditTagModal = (val: TagModalValues) => {
226
        tagController
227
            .tagTagIdPut(selectedRecord.key, {
228
                name: val.name,
229
                description: val.description,
230
                color: val.color,
231
                sentimentEnabled: val.sentimentEnabled,
232
            })
233
            .then(() => loadData());
234

    
235
        setShowEditTagModal(false);
236
    };
237

    
238
    const cancelEditTagModal = () => {
239
        setShowEditTagModal(false);
240
    };
241

    
242
    // --------------------------------------------------------------------------------------------
243

    
244
    // ------------------------------------ ADD SUB TAG MODAL -------------------------------------
245

    
246
    const openAddSubTagModal = () => {
247
        setShowAddSubTagModal(true);
248
    };
249

    
250
    const submitAddSubTagModal = (val: SubTagModalValues) => {
251
        tagController
252
            .subtagsPost({
253
                tagId: selectedRecord.key,
254
                name: val.name,
255
                description: val.description,
256
            })
257
            .then(() => loadData());
258

    
259
        setShowAddSubTagModal(false);
260
    };
261

    
262
    const cancelAddSubTagModal = () => {
263
        setShowAddSubTagModal(false);
264
    };
265

    
266
    // --------------------------------------------------------------------------------------------
267

    
268
    // ------------------------------------ EDIT SUB TAG MODAL -------------------------------------
269

    
270
    const openEditSubTagModal = () => {
271
        setShowEditSubTagModal(true);
272
    };
273

    
274
    const submitEditSubTagModal = (val: SubTagModalValues) => {
275
        tagController
276
            .subtagSubtagIdPut(selectedRecord.key, {
277
                name: val.name,
278
                description: val.description,
279
            })
280
            .then(() => loadData());
281

    
282
        setShowEditSubTagModal(false);
283
    };
284

    
285
    const cancelEditSubTagModal = () => {
286
        setShowEditSubTagModal(false);
287
    };
288

    
289
    // --------------------------------------------------------------------------------------------
290

    
291
    // ------------------------------------ RECORD OPERATIONS -------------------------------------
292

    
293
    const addChild = (record: any) => (e: any) => {
294
        setSelectedRecord(record);
295

    
296
        if (record.depth === 0) {
297
            openAddTagModal();
298
        } else if (record.depth === 1) {
299
            openAddSubTagModal();
300
        }
301
    };
302

    
303
    const editRecord = (record: any) => (e: any) => {
304
        setSelectedRecord(record);
305

    
306
        if (record.depth === 0) {
307
            openEditCategoryModal();
308
        } else if (record.depth === 1) {
309
            openEditTagModal();
310
        } else if (record.depth === 2) {
311
            openEditSubTagModal();
312
        }
313
    };
314

    
315
    const deleteRecord = (record: any) => (e: any) => {
316
        if (record.depth === 0) {
317
            tagController.categoryCategoryIdDelete(record.key).then(() => loadData());
318
        } else if (record.depth === 1) {
319
            tagController.tagTagIdDelete(record.key).then(() => loadData());
320
        } else if (record.depth === 2) {
321
            tagController.subtagSubtagIdDelete(record.key).then(() => loadData());
322
        }
323
    };
324

    
325
    // --------------------------------------------------------------------------------------------
326

    
327
    /**
328
     * Loads data from a server.
329
     */
330
    const loadData = () => {
331
        tagController.tagsGet().then((tagTree) => {
332
            if (typeof tagTree.data.tagCategories != 'undefined') {
333
                setTagData(tagTree.data.tagCategories);
334
                setShownData(mapData(tagTree.data.tagCategories));
335
            }
336
        });
337
    };
338

    
339
    const mapData = (data: any[] | null) => {
340
        return data?.map((catInfo) => {
341
            return {
342
                key: catInfo.id,
343
                name: catInfo.name,
344
                description: catInfo.description,
345
                color: catInfo.color,
346
                depth: 0,
347
                disabledForAnnotators: catInfo.disabledForAnnotators,
348
                ...(catInfo.tags?.length && {
349
                    children: catInfo.tags?.map((tagInfo: any) => {
350
                        return {
351
                            key: tagInfo.id,
352
                            name: tagInfo.name,
353
                            description: tagInfo.description,
354
                            color: tagInfo.color,
355
                            sentimentEnabled: tagInfo.sentimentEnabled,
356
                            depth: 1,
357
                            ...(tagInfo.subTags?.length && {
358
                                children: tagInfo.subTags?.map((subInfo: any) => {
359
                                    return {
360
                                        key: subInfo.id,
361
                                        name: subInfo.name,
362
                                        description: subInfo.description,
363
                                        depth: 2,
364
                                    };
365
                                }),
366
                            }),
367
                        };
368
                    }),
369
                }),
370
            };
371
        });
372
    };
373

    
374
    /**
375
     * Searches given value in 'name' column in a table.
376
     * If the provided value is empty the method loads all data from a server.
377
     * @param value Value that is searched.
378
     */
379
    const searchTag = (value: string) => {
380
        let data = mapData(tagData);
381

    
382
        if (value) {
383
            data = data?.filter((category) => {
384
                category.children = category.children?.filter((tag: any) => {
385
                    tag.children = tag.children?.filter((subTag: any) =>
386
                        subTag.name.toLowerCase().includes(value.toLowerCase())
387
                    );
388

    
389
                    return (
390
                        tag.children?.length > 0 ||
391
                        tag.name.toLowerCase().includes(value.toLowerCase())
392
                    );
393
                });
394

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

    
402
        setShownData(data);
403
    };
404

    
405
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
406
        <MainLayout>
407
            {showAddCategoryModal && (
408
                <CategoryModal
409
                    title="Vytvořit kategorii"
410
                    submitText="Přidat"
411
                    onCancel={cancelAddCategoryModal}
412
                    onSubmit={submitAddCategoryModal}
413
                />
414
            )}
415

    
416
            {showEditCategoryModal && (
417
                <CategoryModal
418
                    title="Editovat kategorii"
419
                    submitText="Změnit"
420
                    onCancel={cancelEditCategoryModal}
421
                    onSubmit={submitEditCategoryModel}
422
                    defaultValues={selectedRecord}
423
                />
424
            )}
425

    
426
            {showAddTagModal && (
427
                <TagModal
428
                    title="Vytvořit tag"
429
                    submitText="Přidat"
430
                    onCancel={cancelAddTagModal}
431
                    onSubmit={submitAddTagModal}
432
                    defaultValues={{ color: selectedRecord.color }}
433
                />
434
            )}
435

    
436
            {showEditTagModal && (
437
                <TagModal
438
                    title="Editovat tag"
439
                    submitText="Změnit"
440
                    onCancel={cancelEditTagModal}
441
                    onSubmit={submitEditTagModal}
442
                    defaultValues={selectedRecord}
443
                />
444
            )}
445

    
446
            {showAddSubTagModal && (
447
                <SubTagModal
448
                    title="Vytvořit sub tag"
449
                    submitText="Přidat"
450
                    onCancel={cancelAddSubTagModal}
451
                    onSubmit={submitAddSubTagModal}
452
                />
453
            )}
454

    
455
            {showEditSubTagModal && (
456
                <SubTagModal
457
                    title="Editovat sub tag"
458
                    submitText="Změnit"
459
                    onCancel={cancelEditSubTagModal}
460
                    onSubmit={submitEditSubTagModal}
461
                    defaultValues={selectedRecord}
462
                />
463
            )}
464

    
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
        </MainLayout>
493
    );
494
}
495

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