Skip to content

Commit 2033b99

Browse files
x0sinaImMohammad20000
authored andcommitted
fix: resolve loading bar conflict with route guard in settings pages
1 parent 48515fc commit 2033b99

File tree

5 files changed

+38
-7
lines changed

5 files changed

+38
-7
lines changed

dashboard/public/statics/locales/ru.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@
822822
"userDialog.onHoldExpireDuration": "Продолжительность срока действия",
823823
"userDialog.onHoldExpireDurationPlaceholder": "например, 7",
824824
"userDialog.optional": "необязательно",
825-
"userDialog.periodicUsageReset": "Период сброса трафика",
825+
"userDialog.periodicUsageReset": "Периодич. сброс",
826826
"userDialog.protocols": "Протоколы",
827827
"userDialog.relative": "Относительно",
828828
"userDialog.resetStrategyAnnually": "Ежегодно",

dashboard/src/components/common/date-picker.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,9 @@ export function DatePicker({
366366
<div className="hidden lg:flex items-center gap-1.5 flex-wrap border-t p-3">
367367
{[
368368
{ label: '7d', days: 7 },
369-
{ label: '30d', days: 30 },
370-
{ label: '60d', days: 60 },
371-
{ label: '90d', days: 90 },
369+
{ label: '1m', days: 30 },
370+
{ label: '2m', days: 60 },
371+
{ label: '3m', days: 90 },
372372
{ label: '1y', days: 365 },
373373
].map(({ label, days }) => {
374374
const handleShortcut = () => {

dashboard/src/components/dialogs/user-modal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ const ExpiryDateField = ({
150150
{ label: '1m', days: 30 },
151151
{ label: '2m', days: 60 },
152152
{ label: '3m', days: 90 },
153+
{ label: '6m', days: 180 },
153154
{ label: '1y', days: 365 },
154155
]
155156

dashboard/src/components/layout/route-guard.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { useAdmin } from '@/hooks/use-admin'
2-
import { useEffect } from 'react'
2+
import { useEffect, useRef } from 'react'
33
import { useLocation, useNavigate } from 'react-router'
44

55
export default function RouteGuard({ children }: { children: React.ReactNode }) {
66
const { admin } = useAdmin()
77
const location = useLocation()
88
const navigate = useNavigate()
99
const is_sudo = admin?.is_sudo || false
10+
const hasNavigatedRef = useRef(false)
1011

1112
useEffect(() => {
12-
if (!admin) return // Wait for admin data to load
13+
if (!admin) {
14+
hasNavigatedRef.current = false
15+
return // Wait for admin data to load
16+
}
1317

1418
if (!is_sudo) {
1519
const currentPath = location.pathname
@@ -20,6 +24,12 @@ export default function RouteGuard({ children }: { children: React.ReactNode })
2024

2125
// If current route is allowed, don't redirect
2226
if (isAllowedRoute) {
27+
hasNavigatedRef.current = false
28+
return
29+
}
30+
31+
// Prevent multiple navigations for the same route change
32+
if (hasNavigatedRef.current) {
2333
return
2434
}
2535

@@ -28,12 +38,14 @@ export default function RouteGuard({ children }: { children: React.ReactNode })
2838
const isRestrictedRoute = restrictedRoutes.some(route => currentPath.startsWith(route))
2939

3040
if (isRestrictedRoute) {
41+
hasNavigatedRef.current = true
3142
navigate('/users', { replace: true })
3243
return
3344
}
3445

3546
// Handle settings routes
3647
if (currentPath === '/settings') {
48+
hasNavigatedRef.current = true
3749
navigate('/settings/theme', { replace: true })
3850
return
3951
}
@@ -43,11 +55,19 @@ export default function RouteGuard({ children }: { children: React.ReactNode })
4355

4456
if (restrictedSettingsRoutes.includes(currentPath)) {
4557
// Redirecting non-sudo admin from restricted settings
58+
hasNavigatedRef.current = true
4659
navigate('/settings/theme', { replace: true })
4760
return
4861
}
62+
} else {
63+
hasNavigatedRef.current = false
4964
}
5065
}, [admin, is_sudo, location.pathname, navigate])
5166

67+
// Reset navigation flag when pathname changes (after navigation completes)
68+
useEffect(() => {
69+
hasNavigatedRef.current = false
70+
}, [location.pathname])
71+
5272
return <>{children}</>
5373
}

dashboard/src/components/layout/top-loading-bar.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,17 @@ function TopLoadingBar({ height = 3, color, shadow = true, className = '' }: Top
133133

134134
lastLocation = currentPath
135135

136-
if (!shouldIgnoreRoute(pathname) && ref.current) {
136+
if (shouldIgnoreRoute(pathname)) {
137+
// For ignored routes, ensure any existing loading bar is completed immediately
138+
if (ref.current) {
139+
ref.current.complete()
140+
}
141+
// Clear any pending timeout
142+
if (maxTimeoutRef.current) {
143+
clearTimeout(maxTimeoutRef.current)
144+
maxTimeoutRef.current = undefined
145+
}
146+
} else if (ref.current) {
137147
// Start loading bar on route change with continuous animation
138148
ref.current.continuousStart()
139149

0 commit comments

Comments
 (0)