Projekt

Obecné

Profil

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