Skip to content

Commit ab5febc

Browse files
committed
feat: add support for extending user expiration date
1 parent d7aea1d commit ab5febc

File tree

12 files changed

+479
-182
lines changed

12 files changed

+479
-182
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@monaco-editor/react": "^4.7.0",
6060
"@noble/post-quantum": "^0.5.2",
6161
"@paralleldrive/cuid2": "2.2.2",
62-
"@remnawave/backend-contract": "2.3.27",
62+
"@remnawave/backend-contract": "2.3.30",
6363
"@simplewebauthn/browser": "^13.2.2",
6464
"@stablelib/base64": "^2.0.1",
6565
"@stablelib/x25519": "^2.0.1",

public/locales/en/remnawave.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,12 @@
756756
"change-active-internal-squads": "Change Active Internal Squads",
757757
"specify-internal-squads-that-will-be-assigned-to-the-user": "Specify internal squads that will be assigned to the user",
758758
"internal-squads": "Internal squads",
759-
"changes-the-active-internal-squads-for-all-selected-users": "Changes the active internal squads for all selected users"
759+
"changes-the-active-internal-squads-for-all-selected-users": "Changes the active internal squads for all selected users",
760+
"extend-expiration-date": "Extend Expiration Date",
761+
"enter-the-number-of-days-to-extend-the-expiration-date": "Enter the number of days to extend the expiration date",
762+
"extend-days": "Extend Days",
763+
"extend": "Extend",
764+
"extend-expiration-date-description": "Add days to the expiration date for selected users. Days will be added to the current expiration date of each user."
760765
}
761766
}
762767
},
@@ -1854,4 +1859,4 @@
18541859
"total-ram": "Total RAM",
18551860
"cpu-model": "CPU Model"
18561861
}
1857-
}
1862+
}

src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.actions.tab.feature.tsx

Lines changed: 126 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Button, Group, Paper, px, Stack, Text } from '@mantine/core'
2-
import { PiClockClockwise } from 'react-icons/pi'
1+
import { Button, Divider, Group, NumberInput, Paper, Stack, Text } from '@mantine/core'
32
import { useTranslation } from 'react-i18next'
3+
import { PiClockUser } from 'react-icons/pi'
44
import { modals } from '@mantine/modals'
55

6-
import { useBulkAllResetTrafficUsers } from '@shared/api/hooks'
6+
import { useBulkAllExtendUsersExpirationDate, useBulkAllResetTrafficUsers } from '@shared/api/hooks'
77

88
import { IProps } from './interfaces/props.interface'
99

@@ -20,6 +20,15 @@ export const BulkAllUserActionsActionsTabFeature = (props: IProps) => {
2020
}
2121
})
2222

23+
const { mutate: extendExpirationDate, isPending: isExtendExpirationDatePending } =
24+
useBulkAllExtendUsersExpirationDate({
25+
mutationFns: {
26+
onSuccess: () => {
27+
cleanUpDrawer()
28+
}
29+
}
30+
})
31+
2332
const handleResetTraffic = () => {
2433
modals.openConfirmModal({
2534
title: t('common.confirm-action'),
@@ -35,36 +44,129 @@ export const BulkAllUserActionsActionsTabFeature = (props: IProps) => {
3544
}
3645
})
3746
}
47+
48+
const handleExtendExpirationDate = () => {
49+
let userInput = 1
50+
51+
modals.open({
52+
title: t('bulk-user-actions.actions.tab.feature.extend-expiration-date'),
53+
centered: true,
54+
children: (
55+
<>
56+
<NumberInput
57+
allowDecimal={false}
58+
allowNegative={false}
59+
data-autofocus
60+
decimalScale={0}
61+
defaultValue={1}
62+
description={t(
63+
'bulk-user-actions.actions.tab.feature.enter-the-number-of-days-to-extend-the-expiration-date'
64+
)}
65+
label={t('bulk-user-actions.actions.tab.feature.extend-days')}
66+
max={9999}
67+
min={1}
68+
onChange={(value) => {
69+
userInput = Number(value)
70+
}}
71+
required
72+
step={1}
73+
/>
74+
<Button
75+
fullWidth
76+
mt="md"
77+
onClick={() => {
78+
modals.closeAll()
79+
extendExpirationDate({
80+
variables: {
81+
extendDays: userInput
82+
}
83+
})
84+
}}
85+
>
86+
{t('bulk-user-actions.actions.tab.feature.extend')}
87+
</Button>
88+
</>
89+
)
90+
})
91+
}
92+
3893
return (
3994
<Stack gap="md">
4095
<Text c="dimmed" size="sm">
4196
{t('bulk-all-user-actions-tabs.actions.tab.feature.perform-actions-on-all-users')}
4297
</Text>
4398

44-
<Paper p="md" withBorder>
45-
<Stack>
46-
<Group justify="apart">
47-
<Group>
48-
<PiClockClockwise color="cyan" size={px('1.2rem')} />
49-
<Text>
50-
{t('bulk-all-user-actions-tabs.actions.tab.feature.reset-traffic')}
99+
<Paper bg="dark.6" p="md" shadow="sm" withBorder>
100+
<Stack gap="md">
101+
<Group align="center" justify="space-between" wrap="nowrap">
102+
<Stack gap={4}>
103+
<Group gap="xs">
104+
<PiClockUser size={20} />
105+
<Text fw={600} size="md">
106+
{t(
107+
'bulk-user-actions.actions.tab.feature.extend-expiration-date'
108+
)}
109+
</Text>
110+
</Group>
111+
<Text c="dimmed" size="sm">
112+
{t(
113+
'bulk-user-actions.actions.tab.feature.extend-expiration-date-description'
114+
)}
51115
</Text>
52-
</Group>
53-
<Button
54-
color="cyan"
55-
loading={isResetTrafficPending}
56-
onClick={handleResetTraffic}
57-
variant="light"
58-
>
59-
{t('bulk-all-user-actions-tabs.actions.tab.feature.reset')}
60-
</Button>
116+
</Stack>
117+
</Group>
118+
</Stack>
119+
120+
<Divider mb="md" mt="xs" />
121+
122+
<Group justify="flex-end">
123+
<Button
124+
loading={isExtendExpirationDatePending}
125+
onClick={handleExtendExpirationDate}
126+
size="sm"
127+
style={{
128+
transition: 'all 0.2s ease'
129+
}}
130+
variant="light"
131+
>
132+
{t('bulk-user-actions.actions.tab.feature.extend')}
133+
</Button>
134+
</Group>
135+
</Paper>
136+
137+
<Paper bg="dark.6" p="md" shadow="sm" withBorder>
138+
<Stack gap="md">
139+
<Group align="center" justify="space-between" wrap="nowrap">
140+
<Stack gap={4}>
141+
<Group gap="xs">
142+
<PiClockUser size={20} />
143+
<Text fw={600} size="md">
144+
{t(
145+
'bulk-all-user-actions-tabs.actions.tab.feature.reset-traffic'
146+
)}
147+
</Text>
148+
</Group>
149+
<Text c="dimmed" size="sm">
150+
{t(
151+
'bulk-all-user-actions-tabs.actions.tab.feature.reset-traffic-description'
152+
)}
153+
</Text>
154+
</Stack>
61155
</Group>
62-
<Text c="dimmed" size="xs">
63-
{t(
64-
'bulk-all-user-actions-tabs.actions.tab.feature.reset-traffic-description'
65-
)}
66-
</Text>
67156
</Stack>
157+
158+
<Divider mb="md" mt="xs" />
159+
160+
<Group justify="flex-end">
161+
<Button
162+
color="cyan"
163+
loading={isResetTrafficPending}
164+
onClick={handleResetTraffic}
165+
variant="light"
166+
>
167+
{t('bulk-all-user-actions-tabs.actions.tab.feature.reset')}
168+
</Button>
169+
</Group>
68170
</Paper>
69171
</Stack>
70172
)

src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.danger.tab.feature.tsx

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Alert, Button, Group, Paper, px, Stack, Text } from '@mantine/core'
1+
import { Alert, Button, Divider, Group, Paper, Stack, Text } from '@mantine/core'
22
import { PiTrash, PiWarning } from 'react-icons/pi'
33
import { useTranslation } from 'react-i18next'
44
import { modals } from '@mantine/modals'
@@ -20,41 +20,46 @@ export const BulkAllUserActionsDangerTabFeature = (props: IProps) => {
2020
{t('bulk-all-user-actions-tabs.danger.tab.feature.danger-zone-description')}
2121
</Alert>
2222

23-
<Paper p="md" withBorder>
24-
<Stack>
25-
<Group justify="apart">
26-
<Group>
27-
<PiTrash color="var(--mantine-color-red-6)" size={px('1.2rem')} />
28-
<Text>
23+
<Paper bg="dark.6" p="md" shadow="sm" withBorder>
24+
<Stack gap="md">
25+
<Group align="center" justify="space-between" wrap="nowrap">
26+
<Stack gap={4}>
27+
<Group gap="xs">
28+
<PiTrash color="var(--mantine-color-red-6)" size={20} />
29+
<Text fw={600} size="md">
30+
{t(
31+
'bulk-all-user-actions-tabs.danger.tab.feature.delete-users-by-status'
32+
)}
33+
</Text>
34+
</Group>
35+
<Text c="dimmed" size="sm">
2936
{t(
30-
'bulk-all-user-actions-tabs.danger.tab.feature.delete-users-by-status'
37+
'bulk-all-user-actions-tabs.danger.tab.feature.delete-users-by-status-description'
3138
)}
3239
</Text>
33-
</Group>
34-
<Button
35-
color="red.6"
36-
onClick={() => {
37-
modals.open({
38-
title: t('common.confirm-action'),
39-
centered: true,
40-
children: (
41-
<DeleteAllUsersByStatusFeature
42-
cleanUpDrawer={cleanUpDrawer}
43-
/>
44-
)
45-
})
46-
}}
47-
variant="light"
48-
>
49-
{t('common.delete')}
50-
</Button>
40+
</Stack>
5141
</Group>
52-
<Text c="dimmed" size="xs">
53-
{t(
54-
'bulk-all-user-actions-tabs.danger.tab.feature.delete-users-by-status-description'
55-
)}
56-
</Text>
5742
</Stack>
43+
44+
<Divider mb="md" mt="xs" />
45+
46+
<Group justify="flex-end">
47+
<Button
48+
color="red.6"
49+
onClick={() => {
50+
modals.open({
51+
title: t('common.confirm-action'),
52+
centered: true,
53+
children: (
54+
<DeleteAllUsersByStatusFeature cleanUpDrawer={cleanUpDrawer} />
55+
)
56+
})
57+
}}
58+
variant="light"
59+
>
60+
{t('common.delete')}
61+
</Button>
62+
</Group>
5863
</Paper>
5964
</Stack>
6065
)

0 commit comments

Comments
 (0)