Skip to content

Commit 17e1b30

Browse files
author
shuai
committed
feat: Blocked users can choose the duration of the block #1361
1 parent 19eca49 commit 17e1b30

7 files changed

Lines changed: 150 additions & 24 deletions

File tree

i18n/en_US.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,9 @@ ui:
10701070
day: day
10711071
hours: hours
10721072
days: days
1073+
month: month
1074+
months: months
1075+
year: year
10731076
reaction:
10741077
heart: heart
10751078
smile: smile
@@ -1930,6 +1933,7 @@ ui:
19301933
created_at: Created time
19311934
delete_at: Deleted time
19321935
suspend_at: Suspended time
1936+
suspend_until: Suspend until
19331937
status: Status
19341938
role: Role
19351939
action: Action
@@ -1964,6 +1968,8 @@ ui:
19641968
suspend_user:
19651969
title: Suspend this user
19661970
content: A suspended user can't log in.
1971+
label: How long will the user be suspended for?
1972+
forever: Forever
19671973
questions:
19681974
page_title: Questions
19691975
unlisted: Unlisted
@@ -2024,6 +2030,12 @@ ui:
20242030
label: Timezone
20252031
msg: Timezone cannot be empty.
20262032
text: Choose a city in the same timezone as you.
2033+
avatar:
2034+
label: Default avatar
2035+
text: For users without a custom avatar of their own.
2036+
gravatar_base_url:
2037+
label: Gravatar base URL
2038+
text: URL of the Gravatar provider's API base. Ignored when empty.
20272039
smtp:
20282040
page_title: SMTP
20292041
from_email:

i18n/zh_CN.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,9 @@ ui:
10561056
day:
10571057
hours: 小时
10581058
days:
1059+
month:
1060+
months:
1061+
year:
10591062
reaction:
10601063
heart: 爱心
10611064
smile: 微笑
@@ -1890,6 +1893,7 @@ ui:
18901893
created_at: 创建时间
18911894
delete_at: 删除时间
18921895
suspend_at: 封禁时间
1896+
suspend_until: 封禁到期
18931897
status: 状态
18941898
role: 角色
18951899
action: 操作
@@ -1924,6 +1928,8 @@ ui:
19241928
suspend_user:
19251929
title: 挂起此用户
19261930
content: 被封禁的用户将无法登录。
1931+
label: 用户将被封禁多长时间?
1932+
forever: 永久
19271933
questions:
19281934
page_title: 问题
19291935
unlisted: 已隐藏
@@ -1984,6 +1990,12 @@ ui:
19841990
label: 时区
19851991
msg: 时区不能为空。
19861992
text: 选择一个与您相同时区的城市。
1993+
avatar:
1994+
label: 默认头像
1995+
text: 没有自定义头像的用户。
1996+
gravatar_base_url:
1997+
label: Gravatar 根路径 URL
1998+
text: Gravatar 提供商的 API 基础的 URL。当为空时忽略。
19871999
smtp:
19882000
page_title: SMTP
19892001
from_email:

ui/src/common/constants.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -663,42 +663,52 @@ export const DEFAULT_THEME_COLOR = '#0033ff';
663663
export const SUSPENSE_USER_TIME = [
664664
{
665665
label: 'hours',
666-
value: '24',
666+
time: '24',
667+
value: '24h',
667668
},
668669
{
669670
label: 'hours',
670-
value: '48',
671+
time: '48',
672+
value: '48h',
671673
},
672674
{
673675
label: 'hours',
674-
value: '72',
676+
time: '72',
677+
value: '72h',
675678
},
676679
{
677680
label: 'days',
678-
value: '7',
681+
time: '7',
682+
value: '7d',
679683
},
680684
{
681685
label: 'days',
682-
value: '14',
686+
time: '14',
687+
value: '14d',
683688
},
684689
{
685690
label: 'months',
686-
value: '1',
691+
time: '1',
692+
value: '1m',
687693
},
688694
{
689695
label: 'months',
690-
value: '2',
696+
time: '2',
697+
value: '2m',
691698
},
692699
{
693700
label: 'months',
694-
value: '3',
701+
time: '3',
702+
value: '3m',
695703
},
696704
{
697705
label: 'months',
698-
value: '6',
706+
time: '6',
707+
value: '6m',
699708
},
700709
{
701710
label: 'year',
702-
value: '1',
711+
time: '1',
712+
value: '1y',
703713
},
704714
];

ui/src/components/Header/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ const Header: FC = () => {
8787
if (theme_config?.[theme]?.navbar_style) {
8888
// const color = theme_config[theme].navbar_style.startsWith('#')
8989
themeMode = isLight(theme_config[theme].navbar_style) ? 'light' : 'dark';
90-
console.log('isLightTheme', themeMode);
9190
navbarStyle = `theme-${themeMode}`;
9291
}
9392

ui/src/pages/Admin/Users/components/Action/index.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ interface Props {
4242
currentUser;
4343
refreshUsers: () => void;
4444
showDeleteModal: (val) => void;
45+
showSuspenseModal: (val) => void;
4546
userData;
4647
}
4748

@@ -53,6 +54,7 @@ const UserOperation = ({
5354
refreshUsers,
5455
showDeleteModal,
5556
userData,
57+
showSuspenseModal,
5658
}: Props) => {
5759
const { t } = useTranslation('translation', { keyPrefix: 'admin.users' });
5860
const Toast = useToast();
@@ -170,17 +172,9 @@ const UserOperation = ({
170172

171173
if (type === 'suspend') {
172174
// cons
173-
Modal.confirm({
174-
title: t('suspend_user.title'),
175-
content: t('suspend_user.content'),
176-
cancelBtnVariant: 'link',
177-
cancelText: t('cancel', { keyPrefix: 'btns' }),
178-
confirmBtnVariant: 'danger',
179-
confirmText: t('suspend', { keyPrefix: 'btns' }),
180-
onConfirm: () => {
181-
// active -> suspended
182-
postUserStatus('suspended');
183-
},
175+
showSuspenseModal({
176+
show: true,
177+
userId: userData.user_id,
184178
});
185179
}
186180

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { useState } from 'react';
21+
import { Modal, Button, Form } from 'react-bootstrap';
22+
import { useTranslation } from 'react-i18next';
23+
24+
import { changeUserStatus } from '@/services';
25+
import { SUSPENSE_USER_TIME } from '@/common/constants';
26+
import { toastStore } from '@/stores';
27+
28+
const SuspenseUserModal = ({ show, userId, onClose, refreshUsers }) => {
29+
const { t } = useTranslation('translation', { keyPrefix: 'admin.users' });
30+
const [checkVal, setCheckVal] = useState('forever');
31+
32+
const handleClose = () => {
33+
onClose();
34+
setCheckVal('forever');
35+
};
36+
37+
const handleSubmit = (e) => {
38+
e.preventDefault();
39+
changeUserStatus({
40+
user_id: userId,
41+
status: 'suspended',
42+
suspend_duration: checkVal,
43+
}).then(() => {
44+
toastStore.getState().show({
45+
msg: t('user_suspended', { keyPrefix: 'messages' }),
46+
variant: 'success',
47+
});
48+
refreshUsers?.();
49+
handleClose();
50+
});
51+
};
52+
53+
return (
54+
<Modal show={show} onHide={handleClose}>
55+
<Modal.Header closeButton>
56+
<Modal.Title>{t('suspend_user.title')}</Modal.Title>
57+
</Modal.Header>
58+
<Modal.Body>
59+
<p>{t('suspend_user.content')}</p>
60+
<Form>
61+
<Form.Group controlId="delete_user" className="mb-3">
62+
<Form.Label>{t('suspend_user.label')}</Form.Label>
63+
<Form.Select
64+
value={checkVal}
65+
onChange={(e) => setCheckVal(e.target.value)}>
66+
<option value="forever">{t('suspend_user.forever')}</option>
67+
{SUSPENSE_USER_TIME.map((item) => {
68+
return (
69+
<option key={item.value} value={item.value}>
70+
{item.time} {t(item.label, { keyPrefix: 'dates' })}
71+
</option>
72+
);
73+
})}
74+
</Form.Select>
75+
</Form.Group>
76+
</Form>
77+
</Modal.Body>
78+
<Modal.Footer>
79+
<Button variant="link" onClick={handleClose}>
80+
{t('cancel', { keyPrefix: 'btns' })}
81+
</Button>
82+
<Button variant="danger" onClick={handleSubmit}>
83+
{t('suspend', { keyPrefix: 'btns' })}
84+
</Button>
85+
</Modal.Footer>
86+
</Modal>
87+
);
88+
};
89+
90+
export default SuspenseUserModal;

ui/src/pages/Admin/Users/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { useSearchParams } from 'react-router-dom';
2323
import { useTranslation } from 'react-i18next';
2424

2525
import classNames from 'classnames';
26+
import dayjs from 'dayjs';
2627

2728
import {
2829
Pagination,
@@ -297,7 +298,14 @@ const Users: FC = () => {
297298
<FormatTime time={user.suspended_at} />
298299
</td>
299300
<td className="text-nowrap">
300-
<FormatTime time={user.suspended_at} />
301+
{user.suspended_until <= 0 ||
302+
Number(
303+
dayjs(user.suspended_until * 1000).format('YYYY'),
304+
) > 2099
305+
? t('suspend_user.forever')
306+
: dayjs(user.suspended_until * 1000).format(
307+
t('long_date_with_time', { keyPrefix: 'dates' }),
308+
)}
301309
</td>
302310
</>
303311
)}
@@ -357,13 +365,14 @@ const Users: FC = () => {
357365
/>
358366
<SuspenseUserModal
359367
show={suspenseUserModalState.show}
368+
userId={suspenseUserModalState.userId}
360369
onClose={() => {
361370
handleSuspenseUserModalState({
362371
show: false,
363372
userId: '',
364373
});
365374
}}
366-
onDelete={(val) => handleDelete(val)}
375+
refreshUsers={refreshUsers}
367376
/>
368377
</>
369378
);

0 commit comments

Comments
 (0)