@ddj-v2/user-management 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +21 -0
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/asset/fig1.png +0 -0
- package/asset/fig2.png +0 -0
- package/index.ts +342 -0
- package/package.json +25 -0
- package/pages/user_manage_detail.page.js +268 -0
- package/pages/user_manage_main.page.js +200 -0
- package/templates/user_manage_detail.html +286 -0
- package/templates/user_manage_main.html +184 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
{% import "components/user.html" as user with context %}
|
|
2
|
+
{% import "components/form.html" as form with context %}
|
|
3
|
+
{% extends "manage_base.html" %}
|
|
4
|
+
|
|
5
|
+
{% block manage_content %}
|
|
6
|
+
<div class="section">
|
|
7
|
+
<div class="section__header">
|
|
8
|
+
<h1 class="section__title">{{ _('User Detail') }} - {{ udoc.uname }} (#{{ udoc._id }})</h1>
|
|
9
|
+
<div class="section__tools">
|
|
10
|
+
<a href="{{ url('user_manage_main') }}" class="rounded button">
|
|
11
|
+
<span class="icon icon-back"></span> {{ _('Back to List') }}
|
|
12
|
+
</a>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<!-- 用户基本信息 -->
|
|
17
|
+
<div class="section__body">
|
|
18
|
+
<div class="row">
|
|
19
|
+
<div class="medium-8 columns">
|
|
20
|
+
<h3>{{ _('Basic Information') }}</h3>
|
|
21
|
+
<form method="post">
|
|
22
|
+
<input type="hidden" name="operation" value="edit">
|
|
23
|
+
<div class="row">
|
|
24
|
+
<div class="medium-6 columns">
|
|
25
|
+
{{ form.form_text({
|
|
26
|
+
label: _('User ID'),
|
|
27
|
+
name: 'uid',
|
|
28
|
+
value: udoc._id,
|
|
29
|
+
readonly: true
|
|
30
|
+
}) }}
|
|
31
|
+
</div>
|
|
32
|
+
<div class="medium-6 columns">
|
|
33
|
+
{{ form.form_text({
|
|
34
|
+
label: _('Username'),
|
|
35
|
+
name: 'uname',
|
|
36
|
+
value: udoc.uname,
|
|
37
|
+
required: true
|
|
38
|
+
}) }}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="row">
|
|
42
|
+
<div class="medium-12 columns">
|
|
43
|
+
{{ form.form_text({
|
|
44
|
+
label: _('Email'),
|
|
45
|
+
name: 'mail',
|
|
46
|
+
value: udoc.mail,
|
|
47
|
+
type: 'email',
|
|
48
|
+
required: true
|
|
49
|
+
}) }}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="row">
|
|
53
|
+
<div class="medium-6 columns">
|
|
54
|
+
{{ form.form_text({
|
|
55
|
+
label: _('School'),
|
|
56
|
+
name: 'school',
|
|
57
|
+
value: udoc.school or ''
|
|
58
|
+
}) }}
|
|
59
|
+
</div>
|
|
60
|
+
<div class="medium-6 columns">
|
|
61
|
+
{{ form.form_text({
|
|
62
|
+
label: _('Registration Time'),
|
|
63
|
+
name: 'regat',
|
|
64
|
+
value: moment(udoc.regat).format('YYYY-MM-DD HH:mm:ss') if udoc.regat else '',
|
|
65
|
+
readonly: true
|
|
66
|
+
}) }}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="row">
|
|
70
|
+
<div class="medium-12 columns">
|
|
71
|
+
{{ form.form_textarea({
|
|
72
|
+
label: _('Bio'),
|
|
73
|
+
name: 'bio',
|
|
74
|
+
value: udoc.bio or '',
|
|
75
|
+
rows: 3
|
|
76
|
+
}) }}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="row">
|
|
80
|
+
<div class="medium-12 columns">
|
|
81
|
+
<button type="submit" class="rounded primary button">
|
|
82
|
+
<span class="icon icon-save"></span> {{ _('Save Changes') }}
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</form>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="medium-4 columns">
|
|
90
|
+
<h3>{{ _('User Statistics') }}</h3>
|
|
91
|
+
<dl class="large horizontal">
|
|
92
|
+
<dt>{{ _('User ID') }}</dt>
|
|
93
|
+
<dd>#{{ udoc._id }}</dd>
|
|
94
|
+
|
|
95
|
+
<dt>{{ _('Registration Time') }}</dt>
|
|
96
|
+
<dd>
|
|
97
|
+
{% if udoc.regat %}
|
|
98
|
+
{{ moment(udoc.regat).format('YYYY-MM-DD HH:mm') }}
|
|
99
|
+
{% else %}
|
|
100
|
+
-
|
|
101
|
+
{% endif %}
|
|
102
|
+
</dd>
|
|
103
|
+
|
|
104
|
+
<dt>{{ _('Last Login') }}</dt>
|
|
105
|
+
<dd>
|
|
106
|
+
{% if udoc.loginat %}
|
|
107
|
+
{{ moment(udoc.loginat).format('YYYY-MM-DD HH:mm') }}
|
|
108
|
+
{% else %}
|
|
109
|
+
-
|
|
110
|
+
{% endif %}
|
|
111
|
+
</dd>
|
|
112
|
+
|
|
113
|
+
<dt>{{ _('Current Privilege') }}</dt>
|
|
114
|
+
<dd>
|
|
115
|
+
{% if udoc.priv == -1 %}
|
|
116
|
+
<span class="badge badge--red">root</span>
|
|
117
|
+
{% elif udoc.priv == 0 %}
|
|
118
|
+
<span class="badge badge--gray">{{ _('Banned') }}</span>
|
|
119
|
+
{% elif udoc.priv == 8 %}
|
|
120
|
+
<span class="badge badge--yellow">Guest</span>
|
|
121
|
+
{% elif udoc.priv == 4 %}
|
|
122
|
+
<span class="badge badge--purple">System</span>
|
|
123
|
+
{% elif udoc.priv == 16842756 %}
|
|
124
|
+
<span class="badge badge--green">Default</span>
|
|
125
|
+
{% else %}
|
|
126
|
+
<span class="badge badge--blue">Other</span>
|
|
127
|
+
{% endif %}
|
|
128
|
+
<br><small>({{ udoc.priv }})</small>
|
|
129
|
+
</dd>
|
|
130
|
+
|
|
131
|
+
{% if dudoc %}
|
|
132
|
+
<dt>{{ _('Domain Info') }}</dt>
|
|
133
|
+
<dd>
|
|
134
|
+
{% if dudoc.displayName %}
|
|
135
|
+
{{ _('Display Name') }}: {{ dudoc.displayName }}<br>
|
|
136
|
+
{% endif %}
|
|
137
|
+
{% if dudoc.nSubmit %}
|
|
138
|
+
{{ _('Submissions') }}: {{ dudoc.nSubmit }}<br>
|
|
139
|
+
{% endif %}
|
|
140
|
+
{% if dudoc.nAccept %}
|
|
141
|
+
{{ _('Accepted') }}: {{ dudoc.nAccept }}<br>
|
|
142
|
+
{% endif %}
|
|
143
|
+
</dd>
|
|
144
|
+
{% endif %}
|
|
145
|
+
</dl>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<!-- 权限管理 -->
|
|
152
|
+
<div class="section">
|
|
153
|
+
<div class="section__header">
|
|
154
|
+
<h1 class="section__title">{{ _('Privilege Management') }}</h1>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="section__body">
|
|
157
|
+
<form method="post" class="row">
|
|
158
|
+
<input type="hidden" name="operation" value="setPriv">
|
|
159
|
+
<div class="medium-6 columns">
|
|
160
|
+
{{ form.form_text({
|
|
161
|
+
label: _('Privilege Value'),
|
|
162
|
+
name: 'priv',
|
|
163
|
+
value: udoc.priv,
|
|
164
|
+
type: 'number'
|
|
165
|
+
}) }}
|
|
166
|
+
<p class="help-text">
|
|
167
|
+
{{ _('Common values') }}:<br>
|
|
168
|
+
-1: root<br>
|
|
169
|
+
0: {{ _('Banned') }}<br>
|
|
170
|
+
4: System<br>
|
|
171
|
+
8: Guest<br>
|
|
172
|
+
16842756: Default<br>
|
|
173
|
+
{{ _('Other values will be marked as "Other"') }}
|
|
174
|
+
</p>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="medium-6 columns">
|
|
177
|
+
<label> </label>
|
|
178
|
+
<button type="submit" class="rounded button">
|
|
179
|
+
<span class="icon icon-key"></span> {{ _('Update Privilege') }}
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
182
|
+
</form>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<!-- 密码重置 -->
|
|
187
|
+
<div class="section">
|
|
188
|
+
<div class="section__header">
|
|
189
|
+
<h1 class="section__title">{{ _('Password Management') }}</h1>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="section__body">
|
|
192
|
+
<form method="post" class="row">
|
|
193
|
+
<input type="hidden" name="operation" value="resetPassword">
|
|
194
|
+
<div class="medium-6 columns">
|
|
195
|
+
{{ form.form_text({
|
|
196
|
+
label: _('New Password'),
|
|
197
|
+
name: 'password',
|
|
198
|
+
type: 'password',
|
|
199
|
+
required: true
|
|
200
|
+
}) }}
|
|
201
|
+
</div>
|
|
202
|
+
<div class="medium-6 columns">
|
|
203
|
+
<label> </label>
|
|
204
|
+
<button type="submit" class="rounded button warning" onclick="return confirm('{{ _('Are you sure to reset password?') }}')">
|
|
205
|
+
<span class="icon icon-refresh"></span> {{ _('Reset Password') }}
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
</form>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- 用户状态管理 -->
|
|
213
|
+
<div class="section">
|
|
214
|
+
<div class="section__header">
|
|
215
|
+
<h1 class="section__title">{{ _('User Status') }}</h1>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="section__body">
|
|
218
|
+
<div class="row">
|
|
219
|
+
{% if udoc.priv == 0 %}
|
|
220
|
+
<div class="medium-6 columns">
|
|
221
|
+
<form method="post" style="display: inline;">
|
|
222
|
+
<input type="hidden" name="operation" value="unban">
|
|
223
|
+
<button type="submit" class="rounded button success" onclick="return confirm('{{ _('Are you sure to unban this user?') }}')">
|
|
224
|
+
<span class="icon icon-check"></span> {{ _('Unban User') }}
|
|
225
|
+
</button>
|
|
226
|
+
</form>
|
|
227
|
+
</div>
|
|
228
|
+
{% else %}
|
|
229
|
+
{% if udoc.priv != -1 %}
|
|
230
|
+
<div class="medium-6 columns">
|
|
231
|
+
<form method="post" style="display: inline;">
|
|
232
|
+
<input type="hidden" name="operation" value="ban">
|
|
233
|
+
<button type="submit" class="rounded button alert" onclick="return confirm('{{ _('Are you sure to ban this user?') }}')">
|
|
234
|
+
<span class="icon icon-close"></span> {{ _('Ban User') }}
|
|
235
|
+
</button>
|
|
236
|
+
</form>
|
|
237
|
+
</div>
|
|
238
|
+
{% endif %}
|
|
239
|
+
{% endif %}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<style>
|
|
245
|
+
.badge {
|
|
246
|
+
display: inline-block;
|
|
247
|
+
padding: 2px 6px;
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
font-weight: bold;
|
|
250
|
+
line-height: 1;
|
|
251
|
+
color: #fff;
|
|
252
|
+
text-align: center;
|
|
253
|
+
white-space: nowrap;
|
|
254
|
+
vertical-align: baseline;
|
|
255
|
+
border-radius: 3px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.badge--red { background-color: #d9534f; }
|
|
259
|
+
.badge--blue { background-color: #5bc0de; }
|
|
260
|
+
.badge--green { background-color: #5cb85c; }
|
|
261
|
+
.badge--gray { background-color: #777; }
|
|
262
|
+
.badge--yellow { background-color: #f0ad4e; }
|
|
263
|
+
.badge--purple { background-color: #9b59b6; }
|
|
264
|
+
|
|
265
|
+
.help-text {
|
|
266
|
+
font-size: 12px;
|
|
267
|
+
color: #666;
|
|
268
|
+
margin-top: 5px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.warning {
|
|
272
|
+
background-color: #f0ad4e;
|
|
273
|
+
border-color: #eea236;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.success {
|
|
277
|
+
background-color: #5cb85c;
|
|
278
|
+
border-color: #4cae4c;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.alert {
|
|
282
|
+
background-color: #d9534f;
|
|
283
|
+
border-color: #d43f3a;
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
286
|
+
{% endblock %}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{% import "components/user.html" as user with context %}
|
|
2
|
+
{% import "components/sidemenu.html" as sidemenu with context %}
|
|
3
|
+
{% extends "manage_base.html" %}
|
|
4
|
+
|
|
5
|
+
{% block manage_content %}
|
|
6
|
+
<div class="section">
|
|
7
|
+
<div class="section__header">
|
|
8
|
+
<h1 class="section__title">{{ _('User Management') }}</h1>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<!-- 搜索和筛选 -->
|
|
12
|
+
<div class="section__body">
|
|
13
|
+
<form method="get" class="row">
|
|
14
|
+
<div class="medium-6 columns">
|
|
15
|
+
<label>{{ _('Search Users') }}</label>
|
|
16
|
+
<input type="text" name="search" value="{{ search }}" placeholder="{{ _('Username, Email or User ID') }}">
|
|
17
|
+
</div>
|
|
18
|
+
<div class="medium-4 columns">
|
|
19
|
+
<label>{{ _('Sort by') }}</label>
|
|
20
|
+
<select name="sort">
|
|
21
|
+
<option value="_id" {% if sort == '_id' %}selected{% endif %}>{{ _('User ID') }}</option>
|
|
22
|
+
<option value="uname" {% if sort == 'uname' %}selected{% endif %}>{{ _('Username') }}</option>
|
|
23
|
+
<option value="regat" {% if sort == 'regat' %}selected{% endif %}>{{ _('Registration Time') }}</option>
|
|
24
|
+
<option value="loginat" {% if sort == 'loginat' %}selected{% endif %}>{{ _('Last Login') }}</option>
|
|
25
|
+
<option value="priv" {% if sort == 'priv' %}selected{% endif %}>{{ _('Privilege') }}</option>
|
|
26
|
+
</select>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="medium-2 columns">
|
|
29
|
+
<label> </label>
|
|
30
|
+
<button type="submit" class="expanded rounded button">{{ _('Search') }}</button>
|
|
31
|
+
</div>
|
|
32
|
+
</form>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- 用户列表 -->
|
|
36
|
+
<div class="section__body no-padding">
|
|
37
|
+
<table class="data-table">
|
|
38
|
+
<colgroup>
|
|
39
|
+
<col class="col--uid">
|
|
40
|
+
<col class="col--user">
|
|
41
|
+
<col class="col--email">
|
|
42
|
+
<col class="col--regat">
|
|
43
|
+
<col class="col--loginat">
|
|
44
|
+
<col class="col--priv">
|
|
45
|
+
<col class="col--status">
|
|
46
|
+
<col class="col--actions">
|
|
47
|
+
</colgroup>
|
|
48
|
+
<thead>
|
|
49
|
+
<tr>
|
|
50
|
+
<th class="col--uid">{{ _('User ID') }}</th>
|
|
51
|
+
<th class="col--user">{{ _('Username') }}</th>
|
|
52
|
+
<th class="col--email">{{ _('Email') }}</th>
|
|
53
|
+
<th class="col--regat">{{ _('Registration Time') }}</th>
|
|
54
|
+
<th class="col--loginat">{{ _('Last Login') }}</th>
|
|
55
|
+
<th class="col--priv">{{ _('Privilege') }}</th>
|
|
56
|
+
<th class="col--status">{{ _('Status') }}</th>
|
|
57
|
+
<th class="col--actions">{{ _('Actions') }}</th>
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
{% for udoc in udocs %}
|
|
62
|
+
{% set dudoc = dudocMap[udoc._id] %}
|
|
63
|
+
<tr data-uid="{{ udoc._id }}">
|
|
64
|
+
<td class="col--uid">
|
|
65
|
+
{{ udoc._id }}
|
|
66
|
+
</td>
|
|
67
|
+
<td class="col--user">
|
|
68
|
+
{{ user.render_inline(udoc, badge=false) }}
|
|
69
|
+
{% if dudoc and dudoc.displayName %}
|
|
70
|
+
<br><small class="text-gray">{{ dudoc.displayName }}</small>
|
|
71
|
+
{% endif %}
|
|
72
|
+
</td>
|
|
73
|
+
<td class="col--email">
|
|
74
|
+
{{ udoc.mail }}
|
|
75
|
+
</td>
|
|
76
|
+
<td class="col--regat">
|
|
77
|
+
{% if udoc.regat %}
|
|
78
|
+
{{ moment(udoc.regat).format('YYYY-MM-DD HH:mm') }}
|
|
79
|
+
{% else %}
|
|
80
|
+
-
|
|
81
|
+
{% endif %}
|
|
82
|
+
</td>
|
|
83
|
+
<td class="col--loginat">
|
|
84
|
+
{% if udoc.loginat %}
|
|
85
|
+
{{ moment(udoc.loginat).format('YYYY-MM-DD HH:mm') }}
|
|
86
|
+
{% else %}
|
|
87
|
+
-
|
|
88
|
+
{% endif %}
|
|
89
|
+
</td>
|
|
90
|
+
<td class="col--priv">
|
|
91
|
+
{% if udoc.priv == -1 %}
|
|
92
|
+
<span class="badge badge--red">root</span>
|
|
93
|
+
{% elif udoc.priv == 0 %}
|
|
94
|
+
<span class="badge badge--gray">{{ _('Banned') }}</span>
|
|
95
|
+
{% elif udoc.priv == 8 %}
|
|
96
|
+
<span class="badge badge--yellow">Guest</span>
|
|
97
|
+
{% elif udoc.priv == 4 %}
|
|
98
|
+
<span class="badge badge--purple">System</span>
|
|
99
|
+
{% elif udoc.priv == 16842756 %}
|
|
100
|
+
<span class="badge badge--green">Default</span>
|
|
101
|
+
{% else %}
|
|
102
|
+
<span class="badge badge--blue">Other</span>
|
|
103
|
+
{% endif %}
|
|
104
|
+
<br><small class="text-gray">{{ udoc.priv }}</small>
|
|
105
|
+
</td>
|
|
106
|
+
<td class="col--status">
|
|
107
|
+
{% if udoc.priv == 0 %}
|
|
108
|
+
<span class="text-red">{{ _('Banned') }}</span>
|
|
109
|
+
{% else %}
|
|
110
|
+
<span class="text-green">{{ _('Active') }}</span>
|
|
111
|
+
{% endif %}
|
|
112
|
+
</td>
|
|
113
|
+
<td class="col--actions">
|
|
114
|
+
<a href="{{ url('user_manage_detail', uid=udoc._id) }}" class="button button--small">
|
|
115
|
+
{{ _('Edit') }}
|
|
116
|
+
</a>
|
|
117
|
+
</td>
|
|
118
|
+
</tr>
|
|
119
|
+
{% endfor %}
|
|
120
|
+
</tbody>
|
|
121
|
+
</table>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<!-- 分页 -->
|
|
125
|
+
{% if upcount > 50 %}
|
|
126
|
+
<div class="section__body">
|
|
127
|
+
<div class="row">
|
|
128
|
+
<div class="columns">
|
|
129
|
+
{% include "components/paginator.html" %}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
{% endif %}
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<style>
|
|
137
|
+
.col--uid { width: 80px; }
|
|
138
|
+
.col--user { width: 150px; }
|
|
139
|
+
.col--email { width: 200px; }
|
|
140
|
+
.col--regat { width: 120px; }
|
|
141
|
+
.col--loginat { width: 120px; }
|
|
142
|
+
.col--priv { width: 100px; }
|
|
143
|
+
.col--status { width: 80px; }
|
|
144
|
+
.col--actions { width: 100px; }
|
|
145
|
+
|
|
146
|
+
.badge {
|
|
147
|
+
display: inline-block;
|
|
148
|
+
padding: 2px 6px;
|
|
149
|
+
font-size: 11px;
|
|
150
|
+
font-weight: bold;
|
|
151
|
+
line-height: 1;
|
|
152
|
+
color: #fff;
|
|
153
|
+
text-align: center;
|
|
154
|
+
white-space: nowrap;
|
|
155
|
+
vertical-align: baseline;
|
|
156
|
+
border-radius: 3px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.badge--red { background-color: #d9534f; }
|
|
160
|
+
.badge--blue { background-color: #5bc0de; }
|
|
161
|
+
.badge--green { background-color: #5cb85c; }
|
|
162
|
+
.badge--gray { background-color: #777; }
|
|
163
|
+
.badge--yellow { background-color: #f0ad4e; }
|
|
164
|
+
.badge--purple { background-color: #9b59b6; }
|
|
165
|
+
|
|
166
|
+
.text-red { color: #d9534f; }
|
|
167
|
+
.text-green { color: #5cb85c; }
|
|
168
|
+
.text-gray { color: #777; }
|
|
169
|
+
|
|
170
|
+
.button--small {
|
|
171
|
+
padding: 4px 8px;
|
|
172
|
+
font-size: 12px;
|
|
173
|
+
}
|
|
174
|
+
</style>
|
|
175
|
+
|
|
176
|
+
<script>
|
|
177
|
+
$(document).ready(function() {
|
|
178
|
+
// 排序功能:当选择框改变时自动提交表单
|
|
179
|
+
$('select[name="sort"]').on('change', function() {
|
|
180
|
+
$(this).closest('form').submit();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
</script>
|
|
184
|
+
{% endblock %}
|