Projekt

Obecné

Profil

Stáhnout (15.9 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 {
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
        { title: 'Popis', dataIndex: 'description', key: 'description' },
98
        {
99
            title: '',
100
            dataIndex: 'operations',
101
            key: 'operations',
102
            width: 200,
103
            render: (text: any, record: any, index: number) => (
104
                <Stack direction="horizontal">
105
                    {record.depth < 2 && (
106
                        <Button type="primary" onClick={addChild(record)}>
107
                            <FontAwesomeIcon icon={faPlus} />
108
                        </Button>
109
                    )}
110
                    <Button type="text" onClick={editRecord(record)}>
111
                        <FontAwesomeIcon icon={faPenToSquare} />
112
                    </Button>
113
                    <Popconfirm
114
                        title="Opravdu chcete smazat?"
115
                        onConfirm={deleteRecord(record)}
116
                    >
117
                        <Button type="primary" danger>
118
                            <FontAwesomeIcon icon={faTrashCan} />
119
                        </Button>
120
                    </Popconfirm>
121
                </Stack>
122
            ),
123
        },
124
    ];
125

    
126
    // ------------------------------------ ADD CATEGORY MODAL -------------------------------------
127

    
128
    const openAddCategoryModal = () => {
129
        setShowAddCategoryModal(true);
130
    };
131

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

    
145
        setShowAddCategoryModal(false);
146
    };
147

    
148
    const cancelAddCategoryModal = () => {
149
        setShowAddCategoryModal(false);
150
    };
151

    
152
    // --------------------------------------------------------------------------------------------
153

    
154
    // ------------------------------------ EDIT CATEGORY MODAL -----------------------------------
155

    
156
    const openEditCategoryModal = () => {
157
        setShowEditCategoryModal(true);
158
    };
159

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

    
170
        setShowEditCategoryModal(false);
171
    };
172

    
173
    const cancelEditCategoryModal = () => {
174
        setShowEditCategoryModal(false);
175
    };
176

    
177
    // --------------------------------------------------------------------------------------------
178

    
179
    // ------------------------------------ ADD TAG MODAL -------------------------------------
180

    
181
    const openAddTagModal = () => {
182
        setShowAddTagModal(true);
183
    };
184

    
185
    const submitAddTagModal = (val: TagModalValues) => {
186
        tagController
187
            .tagsPost({
188
                categoryId: selectedRecord.key,
189
                name: val.name,
190
                description: val.description,
191
                color: val.color,
192
            })
193
            .then(() => loadData());
194

    
195
        setShowAddTagModal(false);
196
    };
197

    
198
    const cancelAddTagModal = () => {
199
        setShowAddTagModal(false);
200
    };
201

    
202
    // --------------------------------------------------------------------------------------------
203

    
204
    // ------------------------------------ EDIT TAG MODAL -------------------------------------
205

    
206
    const openEditTagModal = () => {
207
        setShowEditTagModal(true);
208
    };
209

    
210
    const submitEditTagModal = (val: TagModalValues) => {
211
        tagController
212
            .tagTagIdPut(selectedRecord.key, {
213
                name: val.name,
214
                description: val.description,
215
                color: val.color,
216
            })
217
            .then(() => loadData());
218

    
219
        setShowEditTagModal(false);
220
    };
221

    
222
    const cancelEditTagModal = () => {
223
        setShowEditTagModal(false);
224
    };
225

    
226
    // --------------------------------------------------------------------------------------------
227

    
228
    // ------------------------------------ ADD SUB TAG MODAL -------------------------------------
229

    
230
    const openAddSubTagModal = () => {
231
        setShowAddSubTagModal(true);
232
    };
233

    
234
    const submitAddSubTagModal = (val: SubTagModalValues) => {
235
        tagController
236
            .subtagsPost({
237
                tagId: selectedRecord.key,
238
                name: val.name,
239
                description: val.description,
240
            })
241
            .then(() => loadData());
242

    
243
        setShowAddSubTagModal(false);
244
    };
245

    
246
    const cancelAddSubTagModal = () => {
247
        setShowAddSubTagModal(false);
248
    };
249

    
250
    // --------------------------------------------------------------------------------------------
251

    
252
    // ------------------------------------ EDIT SUB TAG MODAL -------------------------------------
253

    
254
    const openEditSubTagModal = () => {
255
        setShowEditSubTagModal(true);
256
    };
257

    
258
    const submitEditSubTagModal = (val: SubTagModalValues) => {
259
        tagController
260
            .subtagSubtagIdPut(selectedRecord.key, {
261
                name: val.name,
262
                description: val.description,
263
            })
264
            .then(() => loadData());
265

    
266
        setShowEditSubTagModal(false);
267
    };
268

    
269
    const cancelEditSubTagModal = () => {
270
        setShowEditSubTagModal(false);
271
    };
272

    
273
    // --------------------------------------------------------------------------------------------
274

    
275
    // ------------------------------------ RECORD OPERATIONS -------------------------------------
276

    
277
    const addChild = (record: any) => (e: any) => {
278
        setSelectedRecord(record);
279

    
280
        if (record.depth === 0) {
281
            openAddTagModal();
282
        } else if (record.depth === 1) {
283
            openAddSubTagModal();
284
        }
285
    };
286

    
287
    const editRecord = (record: any) => (e: any) => {
288
        setSelectedRecord(record);
289

    
290
        if (record.depth === 0) {
291
            openEditCategoryModal();
292
        } else if (record.depth === 1) {
293
            openEditTagModal();
294
        } else if (record.depth === 2) {
295
            openEditSubTagModal();
296
        }
297
    };
298

    
299
    const deleteRecord = (record: any) => (e: any) => {
300
        if (record.depth === 0) {
301
            tagController.categoryCategoryIdDelete(record.key).then(() => loadData());
302
        } else if (record.depth === 1) {
303
            tagController.tagTagIdDelete(record.key).then(() => loadData());
304
        } else if (record.depth === 2) {
305
            tagController.subtagSubtagIdDelete(record.key).then(() => loadData());
306
        }
307
    };
308

    
309
    // --------------------------------------------------------------------------------------------
310

    
311
    /**
312
     * Loads data from a server.
313
     */
314
    const loadData = () => {
315
        tagController.tagsGet().then((tagTree) => {
316
            if (typeof tagTree.data.tagCategories != 'undefined') {
317
                setTagData(tagTree.data.tagCategories);
318
                setShownData(mapData(tagTree.data.tagCategories));
319
            }
320
        });
321
    };
322

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

    
357
    /**
358
     * Searches given value in 'name' column in a table.
359
     * If the provided value is empty the method loads all data from a server.
360
     * @param value Value that is searched.
361
     */
362
    const searchTag = (value: string) => {
363
        let data = mapData(tagData);
364

    
365
        if (value) {
366
            data = data?.filter((category) => {
367
                category.children = category.children?.filter((tag: any) => {
368
                    tag.children = tag.children?.filter((subTag: any) =>
369
                        subTag.name.toLowerCase().includes(value.toLowerCase())
370
                    );
371

    
372
                    return (
373
                        tag.children?.length > 0 ||
374
                        tag.name.toLowerCase().includes(value.toLowerCase())
375
                    );
376
                });
377

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

    
385
        setShownData(data);
386
    };
387

    
388
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
389
        <MainLayout>
390
            {showAddCategoryModal && (
391
                <CategoryModal
392
                    title="Vytvořit kategorii"
393
                    submitText="Přidat"
394
                    onCancel={cancelAddCategoryModal}
395
                    onSubmit={submitAddCategoryModal}
396
                />
397
            )}
398

    
399
            {showEditCategoryModal && (
400
                <CategoryModal
401
                    title="Editovat kategorii"
402
                    submitText="Změnit"
403
                    onCancel={cancelEditCategoryModal}
404
                    onSubmit={submitEditCategoryModel}
405
                    defaultValues={selectedRecord}
406
                />
407
            )}
408

    
409
            {showAddTagModal && (
410
                <TagModal
411
                    title="Vytvořit tag"
412
                    submitText="Přidat"
413
                    onCancel={cancelAddTagModal}
414
                    onSubmit={submitAddTagModal}
415
                    defaultValues={{ color: selectedRecord.color }}
416
                />
417
            )}
418

    
419
            {showEditTagModal && (
420
                <TagModal
421
                    title="Editovat tag"
422
                    submitText="Změnit"
423
                    onCancel={cancelEditTagModal}
424
                    onSubmit={submitEditTagModal}
425
                    defaultValues={selectedRecord}
426
                />
427
            )}
428

    
429
            {showAddSubTagModal && (
430
                <SubTagModal
431
                    title="Vytvořit sub tag"
432
                    submitText="Přidat"
433
                    onCancel={cancelAddSubTagModal}
434
                    onSubmit={submitAddSubTagModal}
435
                />
436
            )}
437

    
438
            {showEditSubTagModal && (
439
                <SubTagModal
440
                    title="Editovat sub tag"
441
                    submitText="Změnit"
442
                    onCancel={cancelEditSubTagModal}
443
                    onSubmit={submitEditSubTagModal}
444
                    defaultValues={selectedRecord}
445
                />
446
            )}
447

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

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