Projekt

Obecné

Profil

Stáhnout (18.7 KB) Statistiky
| Větev: | Tag: | Revize:
1 8c45ccb0 hrubyjar
import 'antd/dist/antd.css';
2 3396af93 Dominik Poch
import React, { FocusEvent, useContext, useEffect, useState } from 'react';
3 8c45ccb0 hrubyjar
4
import { useUnauthRedirect } from '../../../hooks';
5
import { useRouter } from 'next/router';
6 af9efa4f Vojtěch Bartička
import { Button, Row, Space, Table, Tag, Typography, Input } from 'antd';
7 c6109e2d Lukáš Vlček
import { faFileLines, faUser } from '@fortawesome/free-solid-svg-icons';
8 8c45ccb0 hrubyjar
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9
import { LoggedUserContext } from '../../../contexts/LoggedUserContext';
10 bae9fbba Jaroslav Hrubý
import { MainLayout } from '../../../layouts/MainLayout';
11 4a7bae81 Jaroslav Hrubý
import AddDocumentModal from '../../../components/modals/AddDocumentModal';
12 ff61085f Lukáš Vlček
import {
13
    DocumentListInfo,
14 87a3d8d1 Lukáš Vlček
    DocumentListResponse,
15 ff61085f Lukáš Vlček
    DocumentUserInfo,
16
    EState,
17
} from '../../../api';
18 c6109e2d Lukáš Vlček
import { documentController, userController } from '../../../controllers';
19 9bfa1e39 Jaroslav Hrubý
import AssignDocumentModal from '../../../components/modals/AssignDocumentModal';
20 c4f198c3 Jaroslav Hrubý
import { ShowConfirm, ShowToast } from '../../../utils/alerts';
21 43d49a98 Jaroslav Hrubý
import { TableDocInfo } from '../../../components/types/TableDocInfo';
22 ff61085f Lukáš Vlček
import {
23
    getAnnotationStateColor,
24 c4f198c3 Jaroslav Hrubý
    getAnnotationStateString,
25 ff61085f Lukáš Vlček
    getNameTruncated,
26
    getUserInfoAlt,
27
} from '../../../utils/strings';
28 87a3d8d1 Lukáš Vlček
import { ABadge, BadgeStyle } from '../../../components/common/ABadge';
29 e7eca311 Lukáš Vlček
import SetRequiredAnnotationsCountModal from '../../../components/modals/SetRequiredAnnotationsCountModal';
30 d7167305 Jaroslav Hrubý
import DocPreviewModal from '../../../components/modals/DocPreviewModal';
31 0db53e25 Jaroslav Hrubý
import { UserFilter } from '../../../components/types/UserFilter';
32 669ffe38 Jaroslav Hrubý
import { getColumnSearchProps, getLocaleProps } from '../../../utils/tableUtils';
33 c4f198c3 Jaroslav Hrubý
import {
34
    CheckCircleOutlined,
35
    ClockCircleOutlined,
36
    SyncOutlined,
37 af9efa4f Vojtěch Bartička
    UserOutlined,
38 c4f198c3 Jaroslav Hrubý
} from '@ant-design/icons';
39
import { SweetAlertIcon } from 'sweetalert2';
40 3396af93 Dominik Poch
import { Stack } from 'react-bootstrap';
41 8c45ccb0 hrubyjar
42
function AdminDocumentPage() {
43
    const redirecting = useUnauthRedirect('/login');
44
    const { logout, role } = useContext(LoggedUserContext);
45 c4f198c3 Jaroslav Hrubý
    const [finalizing, setFinalizing] = React.useState(false);
46 9bfa1e39 Jaroslav Hrubý
    const [visibleAdd, setVisibleAdd] = React.useState(false);
47
    const [visibleAssign, setVisibleAssign] = React.useState(false);
48 d7167305 Jaroslav Hrubý
    const [visiblePreview, setVisiblePreview] = React.useState(false);
49 e7eca311 Lukáš Vlček
    const [visibleSetCount, setVisibleSetCount] = React.useState(false);
50
51 8c45ccb0 hrubyjar
    const router = useRouter();
52
53 43d49a98 Jaroslav Hrubý
    const [documents, setDocuments] = useState<TableDocInfo[]>([]);
54 0db53e25 Jaroslav Hrubý
    const [userFilters, setUserFilters] = useState<UserFilter[]>([]);
55 cca0bfa0 Lukáš Vlček
    const [selectedDocs, setSelectedDocs] = useState<string[]>([]);
56 d7167305 Jaroslav Hrubý
    const [previewDocContent, setPreviewDocContent] = useState<string>();
57
    const [previewDocName, setPreviewDocName] = useState<string>();
58 3396af93 Dominik Poch
    const [annotationCount, setAnnotationCount] = useState<number>();
59 c6109e2d Lukáš Vlček
60 43d49a98 Jaroslav Hrubý
    async function fetchData() {
61
        const docs = (await documentController.documentsGet(0, 1000)).data.documents;
62
        // @ts-ignore
63
        const tableDocs: TableDocInfo[] = docs?.map((doc, index) => {
64
            return { key: index, ...doc };
65
        });
66 9bfa1e39 Jaroslav Hrubý
67 0db53e25 Jaroslav Hrubý
        const users = (await userController.usersGet()).data.users;
68
        // @ts-ignore
69
        const filters: UserFilter[] = users?.map((user) => {
70
            return {
71
                text: user.name + ' ' + user.surname,
72
                value: user.username,
73
            };
74
        });
75
        setUserFilters(filters);
76
77 3396af93 Dominik Poch
        let annotationCountRes =
78
            await documentController.documentsRequiredAnnotationsGlobalGet();
79
        setAnnotationCount(annotationCountRes.data.requiredAnnotationsGlobal);
80
81 43d49a98 Jaroslav Hrubý
        if (!docs) {
82
            setDocuments([]);
83
        } else {
84
            setDocuments(tableDocs);
85 c6109e2d Lukáš Vlček
        }
86 43d49a98 Jaroslav Hrubý
    }
87 c6109e2d Lukáš Vlček
88 43d49a98 Jaroslav Hrubý
    useEffect(() => {
89 06d1aa21 Jaroslav Hrubý
        if (!redirecting && role === 'ADMINISTRATOR') {
90 c6109e2d Lukáš Vlček
            fetchData();
91 8c45ccb0 hrubyjar
        }
92
    }, [logout, redirecting, role, router]);
93
94 c4f198c3 Jaroslav Hrubý
    const finalizeDocumentConfirm = async (
95
        document: DocumentListInfo,
96
        recreate: boolean
97
    ) => {
98
        let desc = recreate ? 'Dosavadní změny finální verze budou smazány' : '';
99
        let icon: SweetAlertIcon = recreate ? 'warning' : 'question';
100
101
        const doneAnnotations = document.annotatingUsers?.filter(
102
            (usr) => usr.state === EState.Done
103
        ).length;
104
105
        if (
106
            doneAnnotations !== undefined &&
107
            document.requiredAnnotations !== undefined &&
108
            doneAnnotations < document.requiredAnnotations
109
        ) {
110
            icon = 'warning';
111
            desc =
112
                'Není dokončen požadovaný počet anotací <br /> (dokončeno ' +
113
                doneAnnotations +
114
                ' z ' +
115
                document.requiredAnnotations +
116
                ')';
117
        }
118
        recreate
119
            ? ShowConfirm(
120
                  () => finalizeDocument(document.id),
121
                  'vytvořit novou finální verzi dokumentu',
122
                  desc,
123
                  icon
124
              )
125
            : ShowConfirm(
126
                  () => finalizeDocument(document.id),
127
                  'vytvořit finální verzi dokumentu',
128
                  desc,
129
                  icon
130
              );
131
    };
132
133
    const finalizeDocument = async (documentId: string | undefined) => {
134
        setFinalizing(true);
135
        const finalAnnotationId = (
136
            await documentController.documentDocumentIdFinalPost(
137
                documentId ? documentId : ''
138
            )
139
        ).data.finalAnnotationId;
140
        if (!finalAnnotationId) {
141
            ShowToast('Finální verzi se nepovedlo vytvořit', 'error');
142
        } else {
143
            router.push({
144
                pathname: '/annotation/[annotationId]',
145
                query: { annotationId: finalAnnotationId, final: true },
146
            });
147
        }
148
        setFinalizing(false);
149
    };
150
151
    const editFinalizedDocument = async (finalAnnotationId: string) => {
152
        setFinalizing(true);
153
        if (!finalAnnotationId) {
154
            ShowToast('Finální verze dosud neexistuje', 'warning');
155
        } else {
156
            router.push({
157
                pathname: '/annotation/[annotationId]',
158
                query: { annotationId: finalAnnotationId, final: true },
159
            });
160
        }
161
        setFinalizing(false);
162
    };
163
164
    const getFinalizationStateIcon = (state: EState) => {
165
        const color = getAnnotationStateColor(state);
166
        const label = getAnnotationStateString(state);
167
        let icon = <CheckCircleOutlined />;
168
        if (state === 'NEW') {
169
            icon = <ClockCircleOutlined />;
170
        }
171
        if (state === 'IN_PROGRESS') {
172
            icon = <SyncOutlined />;
173
        }
174
175
        return (
176
            <Tag icon={icon} color={color} key={label}>
177
                {label.toUpperCase()}
178
            </Tag>
179
        );
180
    };
181
182 9bfa1e39 Jaroslav Hrubý
    const showAssignModal = () => {
183
        if (selectedDocs.length == 0) {
184
            ShowToast('Vyberte dokument pro přiřazení', 'warning', 3000, 'top-end');
185
        } else {
186
            setVisibleAssign(true);
187
        }
188
    };
189 e7eca311 Lukáš Vlček
    const showRequiredAnnotationsCountModal = () => {
190
        if (selectedDocs.length == 0) {
191
            ShowToast(
192
                'Vyberte dokument, pro které chcete nastavit požadovaný počet anotací',
193
                'warning',
194
                3000,
195
                'top-end'
196
            );
197
        } else {
198
            setVisibleSetCount(true);
199
        }
200
    };
201 9bfa1e39 Jaroslav Hrubý
    const showAddModal = () => {
202
        setVisibleAdd(true);
203 4a7bae81 Jaroslav Hrubý
    };
204
205 d7167305 Jaroslav Hrubý
    const showPreviewModal = async (id: string, name: string) => {
206
        const documentContent = (await documentController.documentDocumentIdGet(id)).data
207
            .content;
208 e97b42bc Jaroslav Hrubý
        if (documentContent) {
209
            setPreviewDocName(name);
210
            setPreviewDocContent(documentContent);
211
            setVisiblePreview(true);
212
        }
213 d7167305 Jaroslav Hrubý
    };
214
215 3396af93 Dominik Poch
    const changeDefaultAnotationCount = (e: FocusEvent<HTMLInputElement>) => {
216
        documentController.documentsRequiredAnnotationsGlobalPost({
217
            requiredAnnotations: parseInt(e.currentTarget.value),
218
        });
219
    };
220
221 4a7bae81 Jaroslav Hrubý
    const hideModal = () => {
222 43d49a98 Jaroslav Hrubý
        fetchData();
223 9bfa1e39 Jaroslav Hrubý
        setVisibleAdd(false);
224
        setVisibleAssign(false);
225 e7eca311 Lukáš Vlček
        setVisibleSetCount(false);
226 d7167305 Jaroslav Hrubý
        setVisiblePreview(false);
227 8c45ccb0 hrubyjar
    };
228
229 c6109e2d Lukáš Vlček
    const columns = [
230
        {
231
            title: 'Název dokumentu',
232
            dataIndex: 'name',
233
            key: 'name',
234 eba090c1 Lukáš Vlček
            width: '30%',
235 0db53e25 Jaroslav Hrubý
            ...getColumnSearchProps('name', 'název'),
236 669ffe38 Jaroslav Hrubý
            sorter: {
237
                // @ts-ignore
238
                compare: (a, b) => a.name.localeCompare(b.name),
239
                multiple: 2,
240
            },
241 c6109e2d Lukáš Vlček
        },
242
        {
243
            title: 'Délka',
244
            dataIndex: 'length',
245
            key: 'length',
246 eba090c1 Lukáš Vlček
            width: '5%',
247 c4f198c3 Jaroslav Hrubý
            align: 'center' as 'center',
248 0db53e25 Jaroslav Hrubý
            sorter: {
249
                // @ts-ignore
250
                compare: (a, b) => a.length - b.length,
251
                multiple: 1,
252
            },
253 c6109e2d Lukáš Vlček
        },
254 87a3d8d1 Lukáš Vlček
        {
255
            title: 'Dokončeno | přiřazeno | vyžadováno',
256
            key: 'annotationCounts',
257 c4f198c3 Jaroslav Hrubý
            width: '15%',
258
            align: 'center' as 'center',
259 87a3d8d1 Lukáš Vlček
            render: (
260
                columnData: DocumentListResponse,
261
                record: DocumentListInfo,
262
                index: number
263
            ) => {
264
                const finished =
265
                    record.annotatingUsers?.filter((d) => d.state === EState.Done)
266
                        .length ?? 0;
267
268
                return (
269
                    <div>
270
                        <ABadge
271
                            style={
272
                                finished === record.annotatingUsers?.length
273
                                    ? BadgeStyle.SUCCESS
274
                                    : BadgeStyle.WARNING
275
                            }
276
                        >
277
                            {finished}
278
                        </ABadge>
279
                        {' | '}
280
                        <ABadge
281
                            style={
282 ef2143b8 Lukáš Vlček
                                (record.annotatingUsers?.length ?? 0) >=
283
                                (record.requiredAnnotations ?? 0)
284 87a3d8d1 Lukáš Vlček
                                    ? BadgeStyle.SUCCESS
285
                                    : BadgeStyle.WARNING
286
                            }
287
                        >
288
                            {record.annotatingUsers?.length}
289
                        </ABadge>
290
                        {' | '}
291
                        <ABadge style={BadgeStyle.GENERAL}>
292
                            {record.requiredAnnotations}
293
                        </ABadge>
294
                    </div>
295
                );
296
            },
297
        },
298 c6109e2d Lukáš Vlček
        {
299
            title: 'Anotátoři',
300
            dataIndex: 'annotatingUsers',
301
            key: 'annotatingUsers',
302 eba090c1 Lukáš Vlček
            width: '30%',
303 ff61085f Lukáš Vlček
            render: (
304
                columnData: DocumentUserInfo[],
305
                record: DocumentListInfo,
306
                index: number
307
            ) => {
308 c6109e2d Lukáš Vlček
                return (
309
                    <div>
310
                        {columnData.map((e) => (
311
                            <span
312
                                key={e.username + '.' + record.id}
313 ff61085f Lukáš Vlček
                                title={getUserInfoAlt(e) + '\nStav: ' + e.state}
314
                                style={{
315
                                    color: getAnnotationStateColor(e.state),
316
                                    padding: 3,
317
                                }}
318
                                className={'me-3'}
319 c6109e2d Lukáš Vlček
                            >
320
                                <FontAwesomeIcon
321
                                    icon={faUser}
322 ff61085f Lukáš Vlček
                                    title={getUserInfoAlt(e)}
323 c6109e2d Lukáš Vlček
                                    className={'me-2'}
324
                                />
325 c4f198c3 Jaroslav Hrubý
                                {record.finalAnnotations?.some(
326
                                    (annot) => annot.userId === e.id
327
                                ) ? (
328
                                    <u>{getNameTruncated(e)}</u>
329
                                ) : (
330
                                    getNameTruncated(e)
331
                                )}
332 c6109e2d Lukáš Vlček
                            </span>
333
                        ))}
334
                    </div>
335
                );
336
            },
337 0db53e25 Jaroslav Hrubý
            filters: userFilters,
338
            filterSearch: true,
339
            // @ts-ignore
340
            onFilter: (value, record) =>
341
                // @ts-ignore
342
                record.annotatingUsers.find((user) => user['username'] === value),
343
            sorter: {
344
                // @ts-ignore
345
                compare: (a, b) => a.annotatingUsers.length - b.annotatingUsers.length,
346 669ffe38 Jaroslav Hrubý
                multiple: 3,
347 0db53e25 Jaroslav Hrubý
            },
348 c6109e2d Lukáš Vlček
        },
349 d7167305 Jaroslav Hrubý
        {
350
            title: '',
351
            key: 'action',
352
            dataIndex: ['id', 'name'],
353 c4f198c3 Jaroslav Hrubý
            align: 'center' as 'center',
354 eba090c1 Lukáš Vlček
            width: '5%',
355 d7167305 Jaroslav Hrubý
            // @ts-ignore
356
            render: (text, row) => (
357 eba090c1 Lukáš Vlček
                <Button
358
                    style={{ width: '80px' }}
359
                    key={row.id}
360
                    onClick={() => showPreviewModal(row.id, row.name)}
361
                >
362 d7167305 Jaroslav Hrubý
                    Náhled
363
                </Button>
364
            ),
365
        },
366 c4f198c3 Jaroslav Hrubý
        {
367
            title: 'Finální verze dokumentu',
368
            key: 'final',
369
            dataIndex: ['id', 'finalizedExists'],
370 eba090c1 Lukáš Vlček
            width: '15%',
371 c4f198c3 Jaroslav Hrubý
            // @ts-ignore
372
            render: (text, row) =>
373
                row.finalizedExists ? (
374 eba090c1 Lukáš Vlček
                    <>
375
                        <Row>
376
                            <Space>
377
                                <Button
378
                                    disabled={finalizing}
379
                                    onClick={() => finalizeDocumentConfirm(row.id, true)}
380
                                >
381
                                    Znovu vytvořit
382
                                </Button>
383
                            </Space>
384
                        </Row>
385
                        <Row style={{ marginTop: '5px' }}>
386
                            <Space>
387
                                <Button
388
                                    disabled={finalizing}
389
                                    onClick={() =>
390
                                        editFinalizedDocument(row.finalizedAnnotationId)
391
                                    }
392
                                >
393
                                    Upravit
394
                                </Button>
395
                                {getFinalizationStateIcon(row.finalizedState)}
396
                            </Space>
397
                        </Row>
398
                    </>
399 c4f198c3 Jaroslav Hrubý
                ) : (
400
                    <Button
401
                        disabled={finalizing}
402
                        onClick={() => finalizeDocumentConfirm(row, false)}
403
                    >
404
                        Finalizovat
405
                    </Button>
406
                ),
407
            filters: [
408
                {
409
                    text: 'Nefinalizováno',
410
                    value: null,
411
                },
412
                {
413
                    text: 'Nový',
414
                    value: 'NEW',
415
                },
416
                {
417
                    text: 'Rozpracováno',
418
                    value: 'IN_PROGRESS',
419
                },
420
                {
421
                    text: 'Hotovo',
422
                    value: 'DONE',
423
                },
424
            ],
425
            // @ts-ignore
426
            onFilter: (value, record) => record.finalizedState === value,
427
        },
428 c6109e2d Lukáš Vlček
    ];
429
430 9bfa1e39 Jaroslav Hrubý
    const rowSelection = {
431
        onChange: (selectedRowKeys: React.Key[], selectedRows: DocumentListInfo[]) => {
432 43d49a98 Jaroslav Hrubý
            // @ts-ignore
433 9bfa1e39 Jaroslav Hrubý
            setSelectedDocs(selectedRows.map((row) => row.id));
434
        },
435
    };
436
437 8c45ccb0 hrubyjar
    return redirecting || role !== 'ADMINISTRATOR' ? null : (
438
        <MainLayout>
439 c3b99cdb Lukáš Vlček
            <div
440
                style={{
441
                    display: 'flex',
442
                    flexDirection: 'row',
443
                    justifyContent: 'space-between',
444
                    flexWrap: 'wrap',
445
                }}
446
            >
447
                <Typography.Title level={2}>
448
                    <FontAwesomeIcon icon={faFileLines} /> Dokumenty
449
                </Typography.Title>
450
451
                <Stack
452
                    style={{
453
                        width: '400px',
454
                        border: '1px solid lightgray',
455
                        borderRadius: 3,
456
                        padding: 10,
457
                        marginBottom: 30,
458
                        display: 'flex',
459
                        flexDirection: 'row',
460
                        justifyContent: 'space-between',
461
                    }}
462
                    direction="horizontal"
463
                    key={annotationCount}
464
                >
465
                    <span>Výchozí požadovaný počet anotací:</span>
466
                    <Input
467
                        style={{ width: '100px' }}
468
                        defaultValue={annotationCount}
469
                        onBlur={changeDefaultAnotationCount}
470
                    />
471
                </Stack>
472
            </div>
473
474
            <div
475
                style={{
476
                    padding: '10px',
477
                    display: 'flex',
478
                    flexDirection: 'row',
479
                    justifyContent: 'flex-start',
480
                    gap: '20px',
481
                }}
482
            >
483
                <Button type={'primary'} onClick={showAddModal}>
484
                    Nahrát dokument
485
                </Button>
486
487
                <div>
488
                    <Button
489
                        onClick={showAssignModal}
490
                        disabled={!(selectedDocs?.length > 0)}
491
                    >
492
                        Přiřadit vybrané dokumenty uživatelům
493
                    </Button>
494
                    <Button
495
                        onClick={showRequiredAnnotationsCountModal}
496
                        disabled={!(selectedDocs?.length > 0)}
497
                    >
498
                        Nastavit požadovaný počet anotací vybraným dokumentům
499
                    </Button>
500
                </div>
501
            </div>
502 3396af93 Dominik Poch
503 9bfa1e39 Jaroslav Hrubý
            {visibleAdd && <AddDocumentModal onCancel={hideModal} />}
504
            {visibleAssign && (
505
                <AssignDocumentModal documentsIds={selectedDocs} onCancel={hideModal} />
506
            )}
507 d7167305 Jaroslav Hrubý
            {visiblePreview && (
508
                <DocPreviewModal
509
                    onCancel={hideModal}
510 329a602d Jaroslav Hrubý
                    documentName={previewDocName ?? ''}
511
                    content={
512
                        previewDocContent ?? 'Nastala chyba při načítání obsahu dokumentu'
513 cca0bfa0 Lukáš Vlček
                    }
514
                />
515 d8873409 Vojtěch Bartička
            )}
516 e7eca311 Lukáš Vlček
            {visibleSetCount && (
517
                <SetRequiredAnnotationsCountModal
518
                    documentsIds={selectedDocs}
519
                    onCancel={hideModal}
520 d7167305 Jaroslav Hrubý
                />
521
            )}
522 c6109e2d Lukáš Vlček
523
            <Table
524 669ffe38 Jaroslav Hrubý
                locale={{ ...getLocaleProps() }}
525 9bfa1e39 Jaroslav Hrubý
                rowSelection={{
526
                    type: 'checkbox',
527
                    ...rowSelection,
528
                }}
529 0db53e25 Jaroslav Hrubý
                // @ts-ignore
530 c6109e2d Lukáš Vlček
                columns={columns}
531
                dataSource={documents}
532 9bfa1e39 Jaroslav Hrubý
                size="middle"
533
                scroll={{ y: 600 }}
534 c6109e2d Lukáš Vlček
            />
535 8c45ccb0 hrubyjar
        </MainLayout>
536
    );
537
}
538
539
export default AdminDocumentPage;