Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 669ffe38

Přidáno uživatelem Jaroslav Hrubý před asi 2 roky(ů)

Refactoring after code review

Zobrazit rozdíly:

webapp/api/api.ts
793 793
     * @memberof UserInfo
794 794
     */
795 795
    'assignedDocumentsCount'?: number;
796
    /**
797
     * 
798
     * @type {ERole}
799
     * @memberof UserInfo
800
     */
801
    'role'?: ERole;
796 802
}
797 803
/**
798 804
 * 
webapp/components/modals/AddUserModal.tsx
62 62
            visible={true}
63 63
            onCancel={handleCancel}
64 64
            footer={null}
65
            centered
65 66
        >
66 67
            <Form
67 68
                onFinish={onFinish}
webapp/components/modals/AssignDocumentModal.tsx
5 5
import { annotationController, userController } from '../../controllers';
6 6
import { useUnauthRedirect } from '../../hooks';
7 7
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
8
import { ShowToast } from '../../utils/alerts';
8 9

  
9 10
interface ModalProps {
10 11
    onCancel: () => void;
......
37 38
    const handleUpload = async () => {
38 39
        // console.log(documentsIds, selectedUsers);
39 40
        if (documentsIds && documentsIds.length !== 0) {
40
            await annotationController.annotationsPost({
41
                // @ts-ignore
42
                documentIdList: documentsIds,
43
                userIdList: selectedUsers,
44
            });
41
            await annotationController
42
                .annotationsPost({
43
                    // @ts-ignore
44
                    documentIdList: documentsIds,
45
                    userIdList: selectedUsers,
46
                })
47
                .then((r) => {
48
                    ShowToast(
49
                        'Přiřazení dokumentů proběhlo úspěšně',
50
                        'success',
51
                        3000,
52
                        'top-end'
53
                    );
54
                    onCancel();
55
                });
45 56
        }
46
        onCancel();
47 57
    };
48 58

  
49 59
    function onChange(event: any) {
webapp/components/modals/EditUserModal.tsx
38 38
            role: values.role,
39 39
        };
40 40
        if (userInfo.id) {
41
            await userController.userUserIdPut(userInfo.id, req);
41
            userController.userUserIdPut(userInfo.id, req).then((r) => {
42
                ShowToast(
43
                    'Úprava uživatele proběhla úspěšně',
44
                    'success',
45
                    3000,
46
                    'top-end'
47
                );
48
                onCancel();
49
            });
42 50
        }
43
        onCancel();
44 51
    };
45 52

  
46 53
    const onFinishFailed = (errorInfo: any) => {
......
54 61
            visible={true}
55 62
            onCancel={handleCancel}
56 63
            footer={null}
64
            centered
57 65
        >
58 66
            <Form
59 67
                onFinish={onFinish}
......
62 70
                labelCol={{ span: 4 }}
63 71
                layout="horizontal"
64 72
            >
65
                <Form.Item
66
                    label=""
67
                    name="role"
68
                    rules={[
69
                        {
70
                            required: true,
71
                            message: 'Prosím vyberte roli',
72
                        },
73
                    ]}
74
                >
75
                    <Radio.Group>
73
                <Form.Item label="" name="role">
74
                    <Radio.Group defaultValue={userInfo.role}>
76 75
                        <Radio.Button value={ERole.Annotator}>Anotátor</Radio.Button>
77 76
                        <Radio.Button value={ERole.Administrator}>Admin</Radio.Button>
78 77
                    </Radio.Group>
......
89 88
                >
90 89
                    <Input placeholder="Uživatelské jméno" />
91 90
                </Form.Item>
92
                <Form.Item
93
                    name="password"
94
                    rules={[{ required: true, message: 'Prosím zadejte heslo' }]}
95
                >
96
                    <Input.Password placeholder="Heslo" />
97
                </Form.Item>
98

  
99 91
                <Form.Item
100 92
                    name="name"
101 93
                    initialValue={userInfo.name}
webapp/components/modals/EditUserPasswordModal.tsx
1
import { Button, Form, Input, Modal, Radio } from 'antd';
2
import 'antd/dist/antd.css';
3
import React from 'react';
4
import { ChangeUserInfoRequest, ERole, UserInfo } from '../../api';
5
import { userController } from '../../controllers';
6
import { ShowToast } from '../../utils/alerts';
7

  
8
interface ModalProps {
9
    onCancel: () => void;
10
    userInfo: UserInfo;
11
}
12

  
13
/**
14
 * Creates a modal window that loads documents to the app.
15
 * @returns The modal window.
16
 */
17
const EditUserModal: React.FC<ModalProps> = ({ onCancel, userInfo }: ModalProps) => {
18
    /**
19
     * Handles successful closing of the modal window.
20
     */
21
    const handleOk = () => {
22
        onCancel();
23
    };
24

  
25
    /**
26
     * Handles cancelling of the model window.
27
     */
28
    const handleCancel = () => {
29
        onCancel();
30
    };
31

  
32
    const onFinish = async (values: any) => {
33
        const req: ChangeUserInfoRequest = {
34
            password: values.password,
35
        };
36
        if (userInfo.id) {
37
            userController.userUserIdPut(userInfo.id, req).then((r) => {
38
                ShowToast('Heslo úspěšně změněno', 'success', 3000, 'top-end');
39
                onCancel();
40
            });
41
        }
42
    };
43

  
44
    const onFinishFailed = (errorInfo: any) => {
45
        ShowToast('Zadané údaje nejsou validní', 'error', 3000, 'top-end');
46
    };
47

  
48
    return (
49
        <Modal
50
            title={'Změnit heslo uživatele ' + userInfo.username}
51
            onOk={handleOk}
52
            visible={true}
53
            onCancel={handleCancel}
54
            footer={null}
55
            width={400}
56
            centered
57
        >
58
            <Form
59
                onFinish={onFinish}
60
                onFinishFailed={onFinishFailed}
61
                autoComplete="off"
62
                labelCol={{ span: 4 }}
63
                layout="horizontal"
64
            >
65
                <Form.Item
66
                    name="password"
67
                    rules={[{ required: true, message: 'Prosím zadejte heslo' }]}
68
                >
69
                    <Input.Password placeholder="Heslo" />
70
                </Form.Item>
71
                <Form.Item>
72
                    <Button type="primary" htmlType="submit" className="w-100">
73
                        Změnit
74
                    </Button>
75
                </Form.Item>
76
            </Form>
77
        </Modal>
78
    );
79
};
80

  
81
export default EditUserModal;
webapp/components/modals/UserDocumentsModal.tsx
1
import { List, Modal } from 'antd';
1
import { List, Modal, Skeleton, Tag } from 'antd';
2 2
import 'antd/dist/antd.css';
3 3
import React, { useContext, useEffect, useState } from 'react';
4
import { AnnotationListInfo } from '../../api';
4
import { AnnotationListInfo, EState } from '../../api';
5 5
import { userController } from '../../controllers';
6 6
import { useUnauthRedirect } from '../../hooks';
7 7
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
8
import {
9
    CheckCircleOutlined,
10
    ClockCircleOutlined,
11
    SyncOutlined,
12
} from '@ant-design/icons';
8 13

  
9 14
interface ModalProps {
10 15
    onCancel: () => void;
......
24 29
        onCancel();
25 30
    };
26 31

  
32
    const getStateTag = (state: EState) => {
33
        let color = 'green';
34
        let label = 'Hotovo';
35
        let icon = <CheckCircleOutlined />;
36
        if (state === EState.New) {
37
            color = 'volcano';
38
            label = 'Nový';
39
            icon = <ClockCircleOutlined />;
40
        }
41
        if (state === EState.InProgress) {
42
            color = 'orange';
43
            label = 'Rozpracováno';
44
            icon = <SyncOutlined spin />;
45
        }
46
        return (
47
            <Tag icon={icon} color={color} key={label}>
48
                {label.toUpperCase()}
49
            </Tag>
50
        );
51
    };
52

  
27 53
    useEffect(() => {
28 54
        if (!redirecting && role === 'ADMINISTRATOR' && userId) {
29 55
            userController
......
49 75
                style={{ maxHeight: 400, overflowY: 'auto', overflowX: 'hidden' }}
50 76
                renderItem={(item) => (
51 77
                    <List.Item key={item.annotationId}>
52
                        <List.Item.Meta title={item.documentName} />
78
                        <List.Item.Meta
79
                            title={item.documentName}
80
                            // @ts-ignore
81
                            avatar={getStateTag(item.state)}
82
                        />
53 83
                    </List.Item>
54 84
                )}
55 85
            />
webapp/pages/_app.tsx
1 1
// @ts-nocheck
2 2
import '../styles/globals.css';
3
import 'bootstrap/dist/css/bootstrap.css';
4 3
import '@fortawesome/fontawesome-svg-core/styles.css';
5 4

  
6 5
import type { AppProps } from 'next/app';
webapp/pages/documents/admin/index.tsx
3 3

  
4 4
import { useUnauthRedirect } from '../../../hooks';
5 5
import { useRouter } from 'next/router';
6
import { Button, Input, Space, Table, Typography } from 'antd';
6
import { Button, Space, Table, Typography } from 'antd';
7 7
import { faFileLines, faUser } from '@fortawesome/free-solid-svg-icons';
8 8
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9 9
import { LoggedUserContext } from '../../../contexts/LoggedUserContext';
......
12 12
import { DocumentListInfo, UserInfo } from '../../../api';
13 13
import { documentController, userController } from '../../../controllers';
14 14
import AssignDocumentModal from '../../../components/modals/AssignDocumentModal';
15
import { ShowToast } from '../../../utils/alerts';
15
import { ShowConfirmDelete, ShowToast } from '../../../utils/alerts';
16 16
import { TableDocInfo } from '../../../components/types/TableDocInfo';
17
import { SearchOutlined } from '@ant-design/icons';
18 17
import { UserFilter } from '../../../components/types/UserFilter';
18
import { getColumnSearchProps, getLocaleProps } from '../../../utils/tableUtils';
19
import { UserOutlined } from '@ant-design/icons';
20
import Swal from 'sweetalert2';
19 21

  
20 22
function AdminDocumentPage() {
21 23
    const redirecting = useUnauthRedirect('/login');
......
76 78
        setVisibleAssign(false);
77 79
    };
78 80

  
79
    const handleSearch = (
80
        selectedKeys: React.SetStateAction<string>[],
81
        confirm: () => void
82
    ) => {
83
        confirm();
84
    };
85

  
86
    const handleReset = (clearFilters: () => void) => {
87
        clearFilters();
81
    const removeUserDocument = (userId: string, documentId: string) => {
82
        Swal.fire({
83
            title: 'Opravdu si přejete uživateli odebrat dokument?',
84
            showCancelButton: true,
85
            confirmButtonText: 'Odebrat',
86
        }).then((result) => {
87
            // TODO call API
88
        });
88 89
    };
89 90

  
90
    const getColumnSearchProps = (dataIndex: string, searchLabel: string) => ({
91
        // @ts-ignore
92
        filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
93
            <div style={{ padding: 8 }}>
94
                <Input
95
                    placeholder={`Vyhledat ${searchLabel}`}
96
                    value={selectedKeys[0]}
97
                    onChange={(e) =>
98
                        setSelectedKeys(e.target.value ? [e.target.value] : [])
99
                    }
100
                    onPressEnter={() => handleSearch(selectedKeys, confirm)}
101
                    style={{ marginBottom: 8, display: 'block' }}
102
                />
103
                <Space>
104
                    <Button
105
                        type="primary"
106
                        onClick={() => handleSearch(selectedKeys, confirm)}
107
                        icon={<SearchOutlined />}
108
                        style={{ width: 90 }}
109
                    >
110
                        Hledat
111
                    </Button>
112
                    <Button
113
                        onClick={() => handleReset(clearFilters)}
114
                        style={{ width: 90 }}
115
                    >
116
                        Smazat
117
                    </Button>
118
                </Space>
119
            </div>
120
        ),
121
        filterIcon: (filtered: any) => (
122
            <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
123
        ),
124
        onFilter: (value: string, record: { [x: string]: { toString: () => string } }) =>
125
            record[dataIndex]
126
                ? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
127
                : '',
128
    });
129

  
130 91
    const columns = [
131 92
        {
132 93
            title: 'Název dokumentu',
133 94
            dataIndex: 'name',
134 95
            key: 'name',
135 96
            ...getColumnSearchProps('name', 'název'),
97
            sorter: {
98
                // @ts-ignore
99
                compare: (a, b) => a.name.localeCompare(b.name),
100
                multiple: 2,
101
            },
136 102
        },
137 103
        {
138 104
            title: 'Délka',
......
143 109
                compare: (a, b) => a.length - b.length,
144 110
                multiple: 1,
145 111
            },
146
            sortDirections: ['descend', 'ascend'],
147 112
        },
148 113
        {
149 114
            title: 'Anotátoři',
......
152 117
            render: (columnData: UserInfo[], record: DocumentListInfo, index: number) => {
153 118
                return (
154 119
                    <div>
155
                        {columnData.map((e) => (
156
                            <span
157
                                key={e.username + '.' + record.id}
158
                                title={e.username ?? ''}
159
                            >
160
                                <FontAwesomeIcon
161
                                    icon={faUser}
120
                        <Space>
121
                            {columnData.map((e) => (
122
                                <span
123
                                    key={e.username + '.' + record.id}
162 124
                                    title={e.username ?? ''}
163
                                    className={'me-2'}
164
                                />
165
                            </span>
166
                        ))}
125
                                >
126
                                    <FontAwesomeIcon
127
                                        icon={faUser}
128
                                        size={'2x'}
129
                                        onClick={() =>
130
                                            // @ts-ignore
131
                                            removeUserDocument(e.id, record.id)
132
                                        }
133
                                        title={e.username ?? ''}
134
                                        className={'me-2'}
135
                                    />
136
                                </span>
137
                            ))}
138
                        </Space>
167 139
                    </div>
168 140
                );
169 141
            },
......
176 148
            sorter: {
177 149
                // @ts-ignore
178 150
                compare: (a, b) => a.annotatingUsers.length - b.annotatingUsers.length,
179
                multiple: 1,
151
                multiple: 3,
180 152
            },
181
            sortDirections: ['descend', 'ascend'],
182 153
        },
183 154
    ];
184 155

  
......
204 175
            )}
205 176

  
206 177
            <Table
207
                locale={{
208
                    filterSearchPlaceholder: 'Hledat uživatele',
209
                    triggerDesc: 'Seřadit sestupně',
210
                    triggerAsc: 'Seřadit vzestupně',
211
                    cancelSort: 'Vypnout řazení',
212
                }}
178
                locale={{ ...getLocaleProps() }}
213 179
                rowSelection={{
214 180
                    type: 'checkbox',
215 181
                    ...rowSelection,
webapp/pages/documents/annotator/index.tsx
13 13
import {
14 14
    CheckCircleOutlined,
15 15
    ClockCircleOutlined,
16
    SearchOutlined,
17 16
    SyncOutlined,
18 17
} from '@ant-design/icons';
18
import { getColumnSearchProps, getLocaleProps } from '../../../utils/tableUtils';
19 19

  
20 20
function UserDocumentPage() {
21 21
    const redirecting = useUnauthRedirect('/login');
......
39 39
        }
40 40
    }, [logout, redirecting, role, router]);
41 41

  
42
    const handleSearch = (
43
        selectedKeys: React.SetStateAction<string>[],
44
        confirm: () => void
45
    ) => {
46
        confirm();
47
    };
48

  
49
    const handleReset = (clearFilters: () => void) => {
50
        clearFilters();
51
    };
52

  
53
    const getColumnSearchProps = (dataIndex: string, searchLabel: string) => ({
54
        // @ts-ignore
55
        filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
56
            <div style={{ padding: 8 }}>
57
                <Input
58
                    placeholder={`Vyhledat ${searchLabel}`}
59
                    value={selectedKeys[0]}
60
                    onChange={(e) =>
61
                        setSelectedKeys(e.target.value ? [e.target.value] : [])
62
                    }
63
                    onPressEnter={() => handleSearch(selectedKeys, confirm)}
64
                    style={{ marginBottom: 8, display: 'block' }}
65
                />
66
                <Space>
67
                    <Button
68
                        type="primary"
69
                        onClick={() => handleSearch(selectedKeys, confirm)}
70
                        icon={<SearchOutlined />}
71
                        style={{ width: 90 }}
72
                    >
73
                        Hledat
74
                    </Button>
75
                    <Button
76
                        onClick={() => handleReset(clearFilters)}
77
                        style={{ width: 90 }}
78
                    >
79
                        Smazat
80
                    </Button>
81
                </Space>
82
            </div>
83
        ),
84
        filterIcon: (filtered: any) => (
85
            <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
86
        ),
87
        onFilter: (value: string, record: { [x: string]: { toString: () => string } }) =>
88
            record[dataIndex]
89
                ? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
90
                : '',
91
    });
92

  
93 42
    const columns = [
94 43
        {
95 44
            title: 'Název dokumentu',
96 45
            dataIndex: 'documentName',
97 46
            key: 'documentName',
98 47
            ...getColumnSearchProps('documentName', 'název'),
48
            // @ts-ignore
49
            sorter: (a, b) => a.documentName.localeCompare(b.documentName),
99 50
        },
100 51
        {
101 52
            title: 'Stav anotace',
......
163 114
                <FontAwesomeIcon icon={faFileLines} /> Dokumenty
164 115
            </Typography.Title>
165 116
            <Table
166
                locale={{
167
                    triggerDesc: 'Seřadit sestupně',
168
                    triggerAsc: 'Seřadit vzestupně',
169
                    cancelSort: 'Vypnout řazení',
170
                }}
117
                locale={{ ...getLocaleProps() }}
171 118
                // @ts-ignore
172 119
                columns={columns}
173 120
                // @ts-ignore
webapp/pages/users/index.tsx
3 3

  
4 4
import { useUnauthRedirect } from '../../hooks';
5 5
import { useRouter } from 'next/router';
6
import { Badge, Button, Input, Space, Table, Typography } from 'antd';
6
import { Badge, Button, Dropdown, Menu, Space, Table, Typography } from 'antd';
7 7
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8 8
import { faUsers } from '@fortawesome/free-solid-svg-icons';
9 9
import { LoggedUserContext } from '../../contexts/LoggedUserContext';
......
12 12
import { userController } from '../../controllers';
13 13
import AddUserModal from '../../components/modals/AddUserModal';
14 14
import UserDocumentsModal from '../../components/modals/UserDocumentsModal';
15
import { ShowConfirmDelete } from '../../utils/alerts';
15
import { ShowConfirmDelete, ShowToast } from '../../utils/alerts';
16 16
import EditUserModal from '../../components/modals/EditUserModal';
17
import { SearchOutlined } from '@ant-design/icons';
17
import { DeleteOutlined, EditOutlined, LockOutlined } from '@ant-design/icons';
18
import { getColumnSearchProps, getLocaleProps } from '../../utils/tableUtils';
19
import EditUserPasswordModal from '../../components/modals/EditUserPasswordModal';
18 20

  
19 21
function UsersPage() {
20 22
    const redirecting = useUnauthRedirect('/login');
......
23 25

  
24 26
    const [addVisible, setAddVisible] = React.useState(false);
25 27
    const [editVisible, setEditVisible] = React.useState(false);
28
    const [passwordEditVisible, setPasswordEditVisible] = React.useState(false);
26 29
    const [docsVisible, setDocsVisible] = React.useState(false);
27 30
    const [users, setUsers] = useState<UserInfo[]>([]);
28 31

  
......
52 55

  
53 56
    const deleteUser = (userId: string) => {
54 57
        ShowConfirmDelete(() => {
55
            userController.userUserIdDelete(userId).then(() => fetchData());
58
            userController.userUserIdDelete(userId).then(() => {
59
                ShowToast('Uživatel úspěšně odstraněn', 'success', 3000, 'top-end');
60
                fetchData();
61
            });
56 62
        }, 'uživatele');
57 63
    };
58 64

  
......
63 69
        setUserInfo(userId);
64 70
        setEditVisible(true);
65 71
    };
72
    const showEditPasswordModal = (userId: string) => {
73
        setUserInfo(userId);
74
        setPasswordEditVisible(true);
75
    };
66 76
    const showDocsModal = (userId: string) => {
67 77
        setUserInfo(userId);
68 78
        setDocsVisible(true);
......
73 83
        setAddVisible(false);
74 84
        setDocsVisible(false);
75 85
        setEditVisible(false);
86
        setPasswordEditVisible(false);
76 87
    };
77 88

  
78
    const handleSearch = (
79
        selectedKeys: React.SetStateAction<string>[],
80
        confirm: () => void
81
    ) => {
82
        confirm();
83
    };
84

  
85
    const handleReset = (clearFilters: () => void) => {
86
        clearFilters();
87
    };
88

  
89
    const getColumnSearchProps = (dataIndex: string, searchLabel: string) => ({
90
        // @ts-ignore
91
        filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
92
            <div style={{ padding: 8 }}>
93
                <Input
94
                    placeholder={`Vyhledat ${searchLabel}`}
95
                    value={selectedKeys[0]}
96
                    onChange={(e) =>
97
                        setSelectedKeys(e.target.value ? [e.target.value] : [])
98
                    }
99
                    onPressEnter={() => handleSearch(selectedKeys, confirm)}
100
                    style={{ marginBottom: 8, display: 'block' }}
101
                />
102
                <Space>
103
                    <Button
104
                        type="primary"
105
                        onClick={() => handleSearch(selectedKeys, confirm)}
106
                        icon={<SearchOutlined />}
107
                        style={{ width: 90 }}
108
                    >
109
                        Hledat
110
                    </Button>
111
                    <Button
112
                        onClick={() => handleReset(clearFilters)}
113
                        style={{ width: 90 }}
114
                    >
115
                        Smazat
116
                    </Button>
117
                </Space>
118
            </div>
119
        ),
120
        filterIcon: (filtered: any) => (
121
            <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
122
        ),
123
        onFilter: (value: string, record: { [x: string]: { toString: () => string } }) =>
124
            record[dataIndex]
125
                ? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
126
                : '',
127
    });
89
    const menu = (id: string) => (
90
        <Menu>
91
            <Menu.Item
92
                icon={<EditOutlined />}
93
                key={'edit'}
94
                onClick={() => showEditModal(id)}
95
            >
96
                Upravit
97
            </Menu.Item>
98
            <Menu.Item icon={<LockOutlined />} onClick={() => showEditPasswordModal(id)}>
99
                Změnit Heslo
100
            </Menu.Item>
101
            <Menu.Item icon={<DeleteOutlined />} onClick={() => deleteUser(id)}>
102
                Smazat
103
            </Menu.Item>
104
        </Menu>
105
    );
128 106

  
129 107
    const columns = [
130 108
        {
......
132 110
            dataIndex: 'name',
133 111
            key: 'name',
134 112
            ...getColumnSearchProps('name', 'jméno'),
113
            // @ts-ignore
114
            sorter: (a, b) => a.name.localeCompare(b.name),
135 115
        },
136 116
        {
137 117
            title: 'Příjmení',
138 118
            dataIndex: 'surname',
139 119
            key: 'surname',
140 120
            ...getColumnSearchProps('surname', 'příjmení'),
121
            // @ts-ignore
122
            sorter: (a, b) => a.surname.localeCompare(b.surname),
141 123
        },
142 124
        {
143 125
            title: 'Uživatelské jméno',
144 126
            dataIndex: 'username',
145 127
            key: 'username',
146 128
            ...getColumnSearchProps('username', 'uživatelské jméno'),
129
            // @ts-ignore
130
            sorter: (a, b) => a.username.localeCompare(b.username),
131
        },
132
        {
133
            title: 'Role',
134
            dataIndex: 'role',
135
            key: 'role',
136
            filters: [
137
                {
138
                    text: 'Administrátor',
139
                    value: 'ADMINISTRATOR',
140
                },
141
                {
142
                    text: 'Anotátor',
143
                    value: 'ANNOTATOR',
144
                },
145
            ],
146
            // @ts-ignore
147
            onFilter: (value, record) => record.role.indexOf(value) === 0,
148
            // @ts-ignore
149
            sorter: (a, b) => a.username.localeCompare(b.username),
150
            render: (role: string) => {
151
                let label = '';
152
                if (role === 'ANNOTATOR') {
153
                    label = 'Anotátor';
154
                }
155
                if (role === 'ADMINISTRATOR') {
156
                    label = 'Administrátor';
157
                }
158
                return label;
159
            },
147 160
        },
148 161
        {
149 162
            title: 'Přiřazeno dokumentů',
......
169 182
            title: '',
170 183
            key: 'operations',
171 184
            dataIndex: 'id',
172
            align: 'center' as 'center',
185
            align: 'right' as 'right',
186
            width: '150px',
173 187
            // @ts-ignore
174 188
            render: (id) => (
175 189
                <div>
176 190
                    <Space size={'middle'}>
177
                        <Button onClick={() => showEditModal(id)}>Upravit</Button>
178
                        <Button onClick={() => deleteUser(id)}>Smazat</Button>
191
                        {/*{menu(id)}*/}
192
                        {<Dropdown.Button overlay={menu(id)} placement="bottom" />}
179 193
                    </Space>
180 194
                </div>
181 195
            ),
......
193 207
            </Button>
194 208
            {addVisible && <AddUserModal onCancel={hideModals} />}
195 209
            {editVisible && <EditUserModal userInfo={user} onCancel={hideModals} />}
210
            {passwordEditVisible && (
211
                <EditUserPasswordModal userInfo={user} onCancel={hideModals} />
212
            )}
196 213
            {docsVisible && <UserDocumentsModal userId={user.id} onCancel={hideModals} />}
197 214

  
198 215
            <Table
199
                locale={{
200
                    triggerDesc: 'Seřadit sestupně',
201
                    triggerAsc: 'Seřadit vzestupně',
202
                    cancelSort: 'Vypnout řazení',
203
                }}
216
                locale={{ ...getLocaleProps() }}
204 217
                // @ts-ignore
205 218
                columns={columns}
206 219
                dataSource={users}
207
                scroll={{ y: 500 }}
220
                scroll={{ y: 800 }}
208 221
            />
209 222
        </MainLayout>
210 223
    );
webapp/utils/tableUtils.tsx
1
import { Button, Input, Space } from 'antd';
2
import { SearchOutlined } from '@ant-design/icons';
3
import React from 'react';
4
import { style } from 'dom-helpers';
5

  
6
export const getLocaleProps = () => ({
7
    filterSearchPlaceholder: 'Hledat uživatele',
8
    triggerDesc: 'Seřadit sestupně',
9
    triggerAsc: 'Seřadit vzestupně',
10
    cancelSort: 'Vypnout řazení',
11
});
12

  
13
const handleSearch = (
14
    selectedKeys: React.SetStateAction<string>[],
15
    confirm: () => void
16
) => {
17
    confirm();
18
};
19

  
20
const handleReset = (clearFilters: () => void) => {
21
    clearFilters();
22
};
23

  
24
export const getColumnSearchProps = (dataIndex: string, searchLabel: string) => ({
25
    // @ts-ignore
26
    filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
27
        <div style={{ padding: 8 }}>
28
            <Input
29
                placeholder={`Vyhledat ${searchLabel}`}
30
                value={selectedKeys[0]}
31
                onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
32
                onPressEnter={() => handleSearch(selectedKeys, confirm)}
33
                style={{ marginBottom: 8, display: 'block' }}
34
            />
35
            <Space>
36
                <Button
37
                    type="primary"
38
                    onClick={() => handleSearch(selectedKeys, confirm)}
39
                    icon={<SearchOutlined />}
40
                    style={{ width: 90 }}
41
                >
42
                    Hledat
43
                </Button>
44
                <Button onClick={() => handleReset(clearFilters)} style={{ width: 90 }}>
45
                    Smazat
46
                </Button>
47
            </Space>
48
        </div>
49
    ),
50
    filterIcon: (filtered: any) => (
51
        <SearchOutlined
52
            style={{ color: filtered ? '#1890ff' : '#757575', fontSize: '140%' }}
53
        />
54
    ),
55
    onFilter: (value: string, record: { [x: string]: { toString: () => string } }) =>
56
        record[dataIndex]
57
            ? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
58
            : '',
59
});

Také k dispozici: Unified diff