Projekt

Obecné

Profil

Stáhnout (16.3 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, Typography } from 'antd';
7
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
import { faTags } from '@fortawesome/free-solid-svg-icons';
9
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
10
import { MainLayout } from '../../layouts/MainLayout';
11
import { Container, Row, Stack } from 'react-bootstrap';
12
import { Table } from 'antd';
13
import { tagController } from '../../controllers';
14
import Search from 'antd/lib/input/Search';
15
import { DeleteOutlined, PlusOutlined, EditOutlined } from '@ant-design/icons';
16
import CategoryModal, {
17
    CategoryModalValues,
18
} from '../../components/modals/CategoryModal';
19
import TagModal, { TagModalValues } from '../../components/modals/TagModal';
20
import SubTagModal, { SubTagModalValues } from '../../components/modals/SubTagModal';
21

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

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

    
33
    /**
34
     * Data in a table.
35
     */
36
    const [shownData, setShownData] = useState<any[] | undefined>([]);
37

    
38
    // States that says if modal windows should be shown.
39
    const [showAddCategoryModal, setShowAddCategoryModal] = useState(false);
40
    const [showEditCategoryModal, setShowEditCategoryModal] = useState(false);
41
    const [showAddTagModal, setShowAddTagModal] = useState(false);
42
    const [showEditTagModal, setShowEditTagModal] = useState(false);
43
    const [showAddSubTagModal, setShowAddSubTagModal] = useState(false);
44
    const [showEditSubTagModal, setShowEditSubTagModal] = useState(false);
45

    
46
    /**
47
     * Currently selected record in a table.
48
     */
49
    const [selectedRecord, setSelectedRecord] = useState({
50
        key: '',
51
        name: '',
52
        description: '',
53
        color: '',
54
        disabledForAnnotators: false,
55
    });
56

    
57
    /**
58
     * Loads data on start.
59
     */
60
    useEffect(() => {
61
        if (!redirecting && role === 'ADMINISTRATOR') {
62
            loadData();
63
        }
64
    }, [logout, redirecting, role, router]);
65

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

    
129
    // ------------------------------------ ADD CATEGORY MODAL -------------------------------------
130

    
131
    const openAddCategoryModal = () => {
132
        setShowAddCategoryModal(true);
133
    };
134

    
135
    const submitAddCategoryModal = (val: CategoryModalValues) => {
136
        tagController
137
            .categoriesPost({
138
                name: val.name,
139
                description: val.description,
140
                color: val.color,
141
                disabledForAnnotators: val.disabledForAnnotators,
142
            })
143
            .then(
144
                () => loadData(),
145
                (reason) => console.log(reason)
146
            );
147

    
148
        setShowAddCategoryModal(false);
149
    };
150

    
151
    const cancelAddCategoryModal = () => {
152
        setShowAddCategoryModal(false);
153
    };
154

    
155
    // --------------------------------------------------------------------------------------------
156

    
157
    // ------------------------------------ EDIT CATEGORY MODAL -----------------------------------
158

    
159
    const openEditCategoryModal = () => {
160
        setShowEditCategoryModal(true);
161
    };
162

    
163
    const submitEditCategoryModel = (val: CategoryModalValues) => {
164
        tagController
165
            .categoryCategoryIdPut(selectedRecord.key, {
166
                name: val.name,
167
                description: val.description,
168
                color: val.color,
169
                disabledForAnnotators: val.disabledForAnnotators,
170
            })
171
            .then(() => loadData());
172

    
173
        setShowEditCategoryModal(false);
174
    };
175

    
176
    const cancelEditCategoryModal = () => {
177
        setShowEditCategoryModal(false);
178
    };
179

    
180
    // --------------------------------------------------------------------------------------------
181

    
182
    // ------------------------------------ ADD TAG MODAL -------------------------------------
183

    
184
    const openAddTagModal = () => {
185
        setShowAddTagModal(true);
186
    };
187

    
188
    const submitAddTagModal = (val: TagModalValues) => {
189
        tagController
190
            .tagsPost({
191
                categoryId: selectedRecord.key,
192
                name: val.name,
193
                description: val.description,
194
                color: val.color,
195
                sentimentEnabled: val.sentimentEnabled,
196
            })
197
            .then(() => loadData());
198

    
199
        setShowAddTagModal(false);
200
    };
201

    
202
    const cancelAddTagModal = () => {
203
        setShowAddTagModal(false);
204
    };
205

    
206
    // --------------------------------------------------------------------------------------------
207

    
208
    // ------------------------------------ EDIT TAG MODAL -------------------------------------
209

    
210
    const openEditTagModal = () => {
211
        setShowEditTagModal(true);
212
    };
213

    
214
    const submitEditTagModal = (val: TagModalValues) => {
215
        tagController
216
            .tagTagIdPut(selectedRecord.key, {
217
                name: val.name,
218
                description: val.description,
219
                color: val.color,
220
                sentimentEnabled: val.sentimentEnabled,
221
            })
222
            .then(() => loadData());
223

    
224
        setShowEditTagModal(false);
225
    };
226

    
227
    const cancelEditTagModal = () => {
228
        setShowEditTagModal(false);
229
    };
230

    
231
    // --------------------------------------------------------------------------------------------
232

    
233
    // ------------------------------------ ADD SUB TAG MODAL -------------------------------------
234

    
235
    const openAddSubTagModal = () => {
236
        setShowAddSubTagModal(true);
237
    };
238

    
239
    const submitAddSubTagModal = (val: SubTagModalValues) => {
240
        tagController
241
            .subtagsPost({
242
                tagId: selectedRecord.key,
243
                name: val.name,
244
                description: val.description,
245
            })
246
            .then(() => loadData());
247

    
248
        setShowAddSubTagModal(false);
249
    };
250

    
251
    const cancelAddSubTagModal = () => {
252
        setShowAddSubTagModal(false);
253
    };
254

    
255
    // --------------------------------------------------------------------------------------------
256

    
257
    // ------------------------------------ EDIT SUB TAG MODAL -------------------------------------
258

    
259
    const openEditSubTagModal = () => {
260
        setShowEditSubTagModal(true);
261
    };
262

    
263
    const submitEditSubTagModal = (val: SubTagModalValues) => {
264
        tagController
265
            .subtagSubtagIdPut(selectedRecord.key, {
266
                name: val.name,
267
                description: val.description,
268
            })
269
            .then(() => loadData());
270

    
271
        setShowEditSubTagModal(false);
272
    };
273

    
274
    const cancelEditSubTagModal = () => {
275
        setShowEditSubTagModal(false);
276
    };
277

    
278
    // --------------------------------------------------------------------------------------------
279

    
280
    // ------------------------------------ RECORD OPERATIONS -------------------------------------
281

    
282
    const addChild = (record: any) => (e: any) => {
283
        setSelectedRecord(record);
284

    
285
        if (record.depth === 0) {
286
            openAddTagModal();
287
        } else if (record.depth === 1) {
288
            openAddSubTagModal();
289
        }
290
    };
291

    
292
    const editRecord = (record: any) => (e: any) => {
293
        setSelectedRecord(record);
294

    
295
        if (record.depth === 0) {
296
            openEditCategoryModal();
297
        } else if (record.depth === 1) {
298
            openEditTagModal();
299
        } else if (record.depth === 2) {
300
            openEditSubTagModal();
301
        }
302
    };
303

    
304
    const deleteRecord = (record: any) => (e: any) => {
305
        if (record.depth === 0) {
306
            tagController.categoryCategoryIdDelete(record.key).then(() => loadData());
307
        } else if (record.depth === 1) {
308
            tagController.tagTagIdDelete(record.key).then(() => loadData());
309
        } else if (record.depth === 2) {
310
            tagController.subtagSubtagIdDelete(record.key).then(() => loadData());
311
        }
312
    };
313

    
314
    // --------------------------------------------------------------------------------------------
315

    
316
    /**
317
     * Loads data from a server.
318
     */
319
    const loadData = () => {
320
        tagController.tagsGet().then((tagTree) => {
321
            if (typeof tagTree.data.tagCategories != 'undefined') {
322
                setTagData(tagTree.data.tagCategories);
323
                setShownData(mapData(tagTree.data.tagCategories));
324
            }
325
        });
326
    };
327

    
328
    const mapData = (data: any[] | null) => {
329
        return data?.map((catInfo) => {
330
            return {
331
                key: catInfo.id,
332
                name: catInfo.name,
333
                description: catInfo.description,
334
                color: catInfo.color,
335
                depth: 0,
336
                disabledForAnnotators: catInfo.disabledForAnnotators,
337
                ...(catInfo.tags?.length && {
338
                    children: catInfo.tags?.map((tagInfo: any) => {
339
                        return {
340
                            key: tagInfo.id,
341
                            name: tagInfo.name,
342
                            description: tagInfo.description,
343
                            color: tagInfo.color,
344
                            sentimentEnabled: tagInfo.sentimentEnabled,
345
                            depth: 1,
346
                            ...(tagInfo.subTags?.length && {
347
                                children: tagInfo.subTags?.map((subInfo: any) => {
348
                                    return {
349
                                        key: subInfo.id,
350
                                        name: subInfo.name,
351
                                        description: subInfo.description,
352
                                        depth: 2,
353
                                    };
354
                                }),
355
                            }),
356
                        };
357
                    }),
358
                }),
359
            };
360
        });
361
    };
362

    
363
    /**
364
     * Searches given value in 'name' column in a table.
365
     * If the provided value is empty the method loads all data from a server.
366
     * @param value Value that is searched.
367
     */
368
    const searchTag = (value: string) => {
369
        let data = mapData(tagData);
370

    
371
        if (value) {
372
            data = data?.filter((category) => {
373
                category.children = category.children?.filter((tag: any) => {
374
                    tag.children = tag.children?.filter((subTag: any) =>
375
                        subTag.name.toLowerCase().includes(value.toLowerCase())
376
                    );
377

    
378
                    return (
379
                        tag.children?.length > 0 ||
380
                        tag.name.toLowerCase().includes(value.toLowerCase())
381
                    );
382
                });
383

    
384
                return (
385
                    category.children?.length > 0 ||
386
                    category.name.toLowerCase().includes(value.toLowerCase())
387
                );
388
            });
389
        }
390

    
391
        setShownData(data);
392
    };
393

    
394
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
395
        <MainLayout>
396
            {showAddCategoryModal && (
397
                <CategoryModal
398
                    title="Vytvořit kategorii"
399
                    submitText="Přidat"
400
                    onCancel={cancelAddCategoryModal}
401
                    onSubmit={submitAddCategoryModal}
402
                />
403
            )}
404

    
405
            {showEditCategoryModal && (
406
                <CategoryModal
407
                    title="Editovat kategorii"
408
                    submitText="Změnit"
409
                    onCancel={cancelEditCategoryModal}
410
                    onSubmit={submitEditCategoryModel}
411
                    defaultValues={selectedRecord}
412
                />
413
            )}
414

    
415
            {showAddTagModal && (
416
                <TagModal
417
                    title="Vytvořit tag"
418
                    submitText="Přidat"
419
                    onCancel={cancelAddTagModal}
420
                    onSubmit={submitAddTagModal}
421
                    defaultValues={{ color: selectedRecord.color }}
422
                />
423
            )}
424

    
425
            {showEditTagModal && (
426
                <TagModal
427
                    title="Editovat tag"
428
                    submitText="Změnit"
429
                    onCancel={cancelEditTagModal}
430
                    onSubmit={submitEditTagModal}
431
                    defaultValues={selectedRecord}
432
                />
433
            )}
434

    
435
            {showAddSubTagModal && (
436
                <SubTagModal
437
                    title="Vytvořit sub tag"
438
                    submitText="Přidat"
439
                    onCancel={cancelAddSubTagModal}
440
                    onSubmit={submitAddSubTagModal}
441
                />
442
            )}
443

    
444
            {showEditSubTagModal && (
445
                <SubTagModal
446
                    title="Editovat sub tag"
447
                    submitText="Změnit"
448
                    onCancel={cancelEditSubTagModal}
449
                    onSubmit={submitEditSubTagModal}
450
                    defaultValues={selectedRecord}
451
                />
452
            )}
453

    
454
            <Container>
455
                <Row>
456
                    <Typography.Title level={2}>
457
                        <FontAwesomeIcon icon={faTags} /> Značky
458
                    </Typography.Title>
459
                </Row>
460
                <Row>
461
                    <Table
462
                        columns={columns}
463
                        dataSource={shownData}
464
                        scroll={{ y: 'calc(100vh - 300px)' }}
465
                        size="small"
466
                        title={() => (
467
                            <Stack direction="horizontal" gap={3}>
468
                                <Button type="primary" onClick={openAddCategoryModal}>
469
                                    Přidat kategorii
470
                                </Button>
471
                                <Search
472
                                    placeholder="Vyhledání tagu"
473
                                    onSearch={searchTag}
474
                                    style={{ width: 200 }}
475
                                    className="ms-auto"
476
                                />
477
                            </Stack>
478
                        )}
479
                    />
480
                </Row>
481
            </Container>
482
        </MainLayout>
483
    );
484
}
485

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