File size: 9,793 Bytes
d646dd7
 
 
 
 
 
 
bb59211
 
d646dd7
 
 
bb59211
d646dd7
 
 
 
 
 
 
bb59211
d646dd7
 
 
bb59211
d646dd7
 
 
bb59211
d646dd7
 
 
 
 
 
 
 
 
bb59211
 
 
 
d646dd7
 
bb59211
 
 
d646dd7
 
 
 
 
 
 
bb59211
 
 
 
 
 
d646dd7
 
 
bb59211
d646dd7
 
 
 
 
 
bb59211
 
d646dd7
 
 
bb59211
d646dd7
bb59211
 
 
d646dd7
 
 
 
 
 
 
bb59211
d646dd7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb59211
d646dd7
 
 
 
 
 
 
 
 
bb59211
d646dd7
 
 
 
bb59211
 
d646dd7
 
 
 
 
849e0ae
d646dd7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
{% extends "base.html" %}
{% block title %}Users — EgoData{% endblock %}

{% block content %}
<div class="flex-1 overflow-y-auto hide-scrollbar p-6">
    <div class="flex items-center justify-between mb-6">
        <div>
            <h2 class="text-lg font-semibold text-gray-200"><span data-i18n="users_title">{{ t('users_title') }}</span></h2>
            <p class="text-xs text-gray-500 mt-1"><span data-i18n="users_subtitle">{{ t('users_subtitle') }}</span></p>
        </div>
        <button onclick="openUserModal()" class="bg-blue-600 hover:bg-blue-500 text-white text-xs px-3 py-1.5 rounded font-medium flex items-center gap-1.5">
            <iconify-icon icon="ant-design:plus-outlined" class="icon-sm"></iconify-icon>
            <span data-i18n="users_new">{{ t('users_new') }}</span>
        </button>
    </div>

    <div id="user-alert" class="hidden mb-4 rounded-lg border px-4 py-3 text-sm"></div>

    <div class="grid grid-cols-3 gap-4 mb-6">
        <div class="bg-gray-900 rounded-lg p-4 border border-gray-800">
            <div class="text-xs text-gray-500"><span data-i18n="users_total">{{ t('users_total') }}</span></div>
            <div id="count-total" class="text-2xl font-bold text-white mt-1">—</div>
        </div>
        <div class="bg-gray-900 rounded-lg p-4 border border-gray-800">
            <div class="text-xs text-gray-500"><span data-i18n="users_active">{{ t('users_active') }}</span></div>
            <div id="count-active" class="text-2xl font-bold text-green-400 mt-1">—</div>
        </div>
        <div class="bg-gray-900 rounded-lg p-4 border border-gray-800">
            <div class="text-xs text-gray-500"><span data-i18n="users_disabled_expired">{{ t('users_disabled_expired') }}</span></div>
            <div id="count-inactive" class="text-2xl font-bold text-yellow-400 mt-1">—</div>
        </div>
    </div>

    <div class="bg-gray-900 rounded-lg border border-gray-800 overflow-hidden">
        <div class="flex items-center gap-3 p-4 border-b border-gray-800">
            <input id="user-search" oninput="renderUsers()" placeholder="Search username or email..."
                   class="bg-gray-800 text-sm rounded px-3 py-1.5 border border-gray-700 w-64 focus:border-blue-500 focus:outline-none">
            <select id="user-role-filter" onchange="loadUsers()" class="bg-gray-800 text-sm rounded px-3 py-1.5 border border-gray-700 focus:border-blue-500 focus:outline-none">
                <option value="" data-i18n="users_all_roles">{{ t('users_all_roles') }}</option>
                <option value="admin" data-i18n="role_admin">{{ t('role_admin') }}</option>
                <option value="engineer" data-i18n="role_engineer">{{ t('role_engineer') }}</option>
                <option value="reviewer" data-i18n="role_reviewer">{{ t('role_reviewer') }}</option>
            </select>
            <select id="user-status-filter" onchange="loadUsers()" class="bg-gray-800 text-sm rounded px-3 py-1.5 border border-gray-700 focus:border-blue-500 focus:outline-none">
                <option value="" data-i18n="users_all_status">{{ t('users_all_status') }}</option>
                <option value="active" data-i18n="status_active">{{ t('status_active') }}</option>
                <option value="disabled" data-i18n="status_disabled">{{ t('status_disabled') }}</option>
            </select>
            <span id="user-list-count" class="text-xs text-gray-600 ml-auto"></span>
        </div>
        <div class="overflow-x-auto">
            <table class="w-full text-sm" style="min-width: 900px">
                <thead class="text-xs text-gray-500 bg-gray-950/40">
                    <tr>
                        <th class="text-left font-medium px-4 py-3"><span data-i18n="col_user">{{ t('col_user') }}</span></th>
                        <th class="text-left font-medium px-4 py-3"><span data-i18n="col_role">{{ t('col_role') }}</span></th>
                        <th class="text-left font-medium px-4 py-3"><span data-i18n="col_status">{{ t('col_status') }}</span></th>
                        <th class="text-left font-medium px-4 py-3"><span data-i18n="col_last_login">{{ t('col_last_login') }}</span></th>
                        <th class="text-left font-medium px-4 py-3"><span data-i18n="col_expires">{{ t('col_expires') }}</span></th>
                        <th class="text-right font-medium px-4 py-3"><span data-i18n="col_actions">{{ t('col_actions') }}</span></th>
                    </tr>
                </thead>
                <tbody id="users-body">
                    <tr><td colspan="6" class="px-4 py-10 text-center text-gray-600" data-i18n="loading">{{ t('loading') }}</td></tr>
                </tbody>
            </table>
        </div>
    </div>

    <div class="mt-3 text-xs text-gray-600">
        <span class="text-yellow-500" data-i18n="users_disable">{{ t('users_disable') }}</span> <span data-i18n="users_disable_warning">{{ t('users_disable_warning') }}</span>
        <span class="ml-3 text-red-400" data-i18n="users_delete">{{ t('users_delete') }}</span> <span data-i18n="users_delete_warning">{{ t('users_delete_warning') }}</span>
    </div>

    <div class="mt-6 bg-gray-900 rounded-lg border border-gray-800 p-4">
        <h3 class="text-sm font-semibold text-gray-300 mb-3"><span data-i18n="users_role_perms">{{ t('users_role_perms') }}</span></h3>
        <div class="grid grid-cols-3 gap-4 text-xs">
            <div><div class="text-blue-400 font-medium mb-1">Admin</div><div class="text-gray-500"><span data-i18n="users_perm_admin">{{ t('users_perm_admin') }}</span></div></div>
            <div><div class="text-purple-400 font-medium mb-1">Engineer</div><div class="text-gray-500"><span data-i18n="users_perm_engineer">{{ t('users_perm_engineer') }}</span></div></div>
            <div><div class="text-green-400 font-medium mb-1">Reviewer</div><div class="text-gray-500"><span data-i18n="users_perm_reviewer">{{ t('users_perm_reviewer') }}</span></div></div>
        </div>
    </div>
</div>

<div id="user-modal" class="hidden fixed inset-0 z-50 bg-black/60 flex items-center justify-center p-4">
    <div class="w-full max-w-lg bg-gray-900 border border-gray-700 rounded-lg shadow-2xl">
        <div class="flex items-center justify-between px-5 py-4 border-b border-gray-800">
            <h3 id="user-modal-title" class="text-sm font-semibold text-gray-200"><span data-i18n="users_new">{{ t('users_new') }}</span></h3>
            <button onclick="closeUserModal()" class="text-gray-500 hover:text-gray-200"><iconify-icon icon="ant-design:close-outlined"></iconify-icon></button>
        </div>
        <form id="user-form" onsubmit="saveUser(event)" class="p-5 space-y-4 text-xs">
            <input type="hidden" id="user-id">
            <div class="grid grid-cols-2 gap-3">
                <label class="block text-gray-500">Username
                    <input id="user-username" required maxlength="64" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
                </label>
                <label class="block text-gray-500">Role
                    <select id="user-role" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
                        <option value="engineer">Engineer</option>
                        <option value="reviewer">Reviewer</option>
                        <option value="admin">Admin</option>
                    </select>
                </label>
            </div>
            <label class="block text-gray-500">Email
                <input id="user-email" type="email" maxlength="128" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
            </label>
            <label class="block text-gray-500"><span data-i18n="field_password">{{ t('field_password') }}</span> <span id="password-hint" class="text-gray-600">(at least 6 characters)</span>
                <input id="user-password" type="password" minlength="6" maxlength="128" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
            </label>
            <div class="grid grid-cols-2 gap-3">
                <label id="status-field" class="block text-gray-500">Status
                    <select id="user-status" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
                        <option value="active">Active</option>
                        <option value="disabled">Disabled</option>
                    </select>
                </label>
                <label class="block text-gray-500"><span data-i18n="field_expires_at">{{ t('field_expires_at') }}</span> <span class="text-gray-600" data-i18n="users_optional">{{ t('users_optional') }}</span>
                    <input id="user-expires" type="datetime-local" class="mt-1 w-full bg-gray-800 text-sm rounded px-3 py-2 border border-gray-700 focus:border-blue-500 focus:outline-none">
                </label>
            </div>
            <div class="flex justify-end gap-2 pt-2">
                <button type="button" onclick="closeUserModal()" class="bg-gray-800 hover:bg-gray-700 text-gray-300 text-xs px-4 py-2 rounded"><span data-i18n="cancel">{{ t('cancel') }}</span></button>
                <button id="user-save-btn" type="submit" class="bg-blue-600 hover:bg-blue-500 text-white text-xs px-4 py-2 rounded font-medium"><span data-i18n="users_create">{{ t('users_create') }}</span></button>
            </div>
        </form>
    </div>
</div>

<script src="/static/js/users.js?v={{ asset_version }}"></script>
{% endblock %}