Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
521cebf5
authored
Sep 06, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(dashboard): simplify completed setup guide
parent
3f8a50cf
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
265 additions
and
23 deletions
+265
-23
web/src/features/dashboard/components/overview/__tests__/setup-guide.test.tsx
+241
-0
web/src/features/dashboard/components/overview/overview-dashboard.tsx
+0
-0
web/src/features/dashboard/index.tsx
+24
-23
No files found.
web/src/features/dashboard/components/overview/__tests__/setup-guide.test.tsx
0 → 100644
View file @
521cebf5
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import
{
QueryClient
,
QueryClientProvider
}
from
'@tanstack/react-query'
import
{
createMemoryHistory
,
createRootRoute
,
createRouter
,
RouterProvider
,
}
from
'@tanstack/react-router'
import
{
act
,
cleanup
,
render
,
screen
,
waitFor
}
from
'@testing-library/react'
import
userEvent
from
'@testing-library/user-event'
import
{
afterEach
,
beforeEach
,
describe
,
expect
,
it
,
vi
}
from
'vitest'
import
{
api
}
from
'@/lib/api'
import
{
useAuthStore
}
from
'@/stores/auth-store'
import
{
useSystemConfigStore
}
from
'@/stores/system-config-store'
import
{
OverviewDashboard
}
from
'../overview-dashboard'
const
storageKey
=
'dashboard_overview_setup_guide_expanded'
let
client
:
QueryClient
let
keyLookupError
:
Error
|
null
beforeEach
(()
=>
{
window
.
localStorage
.
clear
()
useSystemConfigStore
.
setState
(
useSystemConfigStore
.
getInitialState
(),
true
)
useAuthStore
.
getState
().
auth
.
setUser
({
id
:
1
,
username
:
'dashboard-user'
,
role
:
1
,
quota
:
1000000
,
used_quota
:
1000
,
request_count
:
1
,
})
client
=
new
QueryClient
({
defaultOptions
:
{
queries
:
{
retry
:
false
}
},
})
keyLookupError
=
null
vi
.
spyOn
(
api
,
'get'
).
mockImplementation
(
async
(
url
)
=>
{
switch
(
url
)
{
case
'/api/token/?p=1&size=10'
:
if
(
keyLookupError
)
throw
keyLookupError
return
{
data
:
{
success
:
true
,
data
:
{
items
:
[{
id
:
1
,
name
:
'App key'
,
key
:
'masked'
,
status
:
1
}],
},
},
}
case
'/api/status'
:
return
{
data
:
{
data
:
{
api_info_enabled
:
false
,
announcements_enabled
:
false
,
faq_enabled
:
false
,
uptime_kuma_enabled
:
false
,
},
},
}
case
'/api/user/models'
:
return
{
data
:
{
success
:
true
,
data
:
[
'gpt-4o-mini'
]
}
}
case
'/api/data/self'
:
return
{
data
:
{
success
:
true
,
data
:
[]
}
}
default
:
throw
new
Error
(
`Unexpected dashboard request:
${
url
}
`
)
}
})
})
afterEach
(()
=>
{
cleanup
()
client
.
clear
()
useAuthStore
.
setState
(
useAuthStore
.
getInitialState
(),
true
)
useSystemConfigStore
.
setState
(
useSystemConfigStore
.
getInitialState
(),
true
)
window
.
localStorage
.
clear
()
})
async
function
renderOverview
()
{
const
router
=
createRouter
({
routeTree
:
createRootRoute
({
component
:
OverviewDashboard
}),
history
:
createMemoryHistory
({
initialEntries
:
[
'/'
]
}),
})
await
router
.
load
()
return
render
(
<
QueryClientProvider
client=
{
client
}
>
<
RouterProvider
router=
{
router
}
/>
</
QueryClientProvider
>
)
}
describe
(
'overview setup guide'
,
()
=>
{
it
(
'shows usage first and only a header entry when setup is complete'
,
async
()
=>
{
await
renderOverview
()
const
toggle
=
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
})
expect
(
toggle
).
toHaveAttribute
(
'aria-expanded'
,
'false'
)
expect
(
screen
.
getAllByRole
(
'heading'
).
map
((
heading
)
=>
heading
.
textContent
)
).
toEqual
([
'Overview'
,
'Usage at a glance'
])
expect
(
screen
.
queryByText
(
'Setup guide complete'
)).
not
.
toBeInTheDocument
()
expect
(
screen
.
queryByText
(
'Setup progress: 3/3'
)).
not
.
toBeInTheDocument
()
for
(
const
name
of
[
'API Keys'
,
'Channels'
,
'Usage Logs'
,
'Pricing'
])
{
expect
(
screen
.
queryByRole
(
'button'
,
{
name
})).
not
.
toBeInTheDocument
()
}
const
panel
=
document
.
getElementById
(
toggle
.
getAttribute
(
'aria-controls'
)
??
''
)
expect
(
panel
).
toBeInTheDocument
()
expect
(
panel
).
not
.
toBeVisible
()
})
it
(
'toggles the completed guide with the keyboard and restores focus after hiding it'
,
async
()
=>
{
const
user
=
userEvent
.
setup
()
await
renderOverview
()
const
toggle
=
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
})
await
user
.
tab
()
expect
(
toggle
).
toHaveFocus
()
await
user
.
keyboard
(
'{Enter}'
)
expect
(
toggle
).
toHaveAttribute
(
'aria-expanded'
,
'true'
)
expect
(
document
.
getElementById
(
toggle
.
getAttribute
(
'aria-controls'
)
??
''
)
).
toBeVisible
()
expect
(
screen
.
getByRole
(
'heading'
,
{
name
:
'Build on your API gateway in minutes'
,
})
).
toBeVisible
()
await
waitFor
(()
=>
expect
(
screen
.
getByRole
(
'button'
,
{
name
:
/^API Keys/
})).
toBeVisible
()
)
await
user
.
keyboard
(
' '
)
expect
(
toggle
).
toHaveAttribute
(
'aria-expanded'
,
'false'
)
expect
(
toggle
).
toHaveFocus
()
await
user
.
click
(
toggle
)
await
user
.
click
(
screen
.
getByRole
(
'button'
,
{
name
:
'Hide setup guide'
}))
expect
(
toggle
).
toHaveAttribute
(
'aria-expanded'
,
'false'
)
expect
(
toggle
).
toHaveFocus
()
})
it
(
'restores the completed guide preference after remounting'
,
async
()
=>
{
const
user
=
userEvent
.
setup
()
const
first
=
await
renderOverview
()
await
user
.
click
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
}))
first
.
unmount
()
const
second
=
await
renderOverview
()
expect
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
})
).
toHaveAttribute
(
'aria-expanded'
,
'true'
)
await
user
.
click
(
screen
.
getByRole
(
'button'
,
{
name
:
'Hide setup guide'
}))
second
.
unmount
()
await
renderOverview
()
expect
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
})
).
toHaveAttribute
(
'aria-expanded'
,
'false'
)
expect
(
screen
.
queryByText
(
'Setup guide complete'
)).
not
.
toBeInTheDocument
()
})
it
(
'keeps the existing progress banner when an incomplete guide is manually collapsed'
,
async
()
=>
{
const
user
=
userEvent
.
setup
()
useAuthStore
.
getState
()
.
auth
.
setUser
({
id
:
1
,
username
:
'new-user'
,
role
:
1
})
await
renderOverview
()
await
user
.
click
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Hide setup guide'
})
)
expect
(
screen
.
getByText
(
'Setup progress: 1/3'
)).
toBeVisible
()
expect
(
screen
.
getByText
(
'Setup guide is collapsed. Expand it anytime.'
)
).
toBeVisible
()
expect
(
screen
.
getByRole
(
'button'
,
{
name
:
'API Keys'
})).
toBeVisible
()
expect
(
screen
.
queryByRole
(
'button'
,
{
name
:
'Setup guide'
})
).
not
.
toBeInTheDocument
()
await
user
.
click
(
screen
.
getByRole
(
'button'
,
{
name
:
'Show setup guide'
}))
expect
(
screen
.
getByRole
(
'button'
,
{
name
:
'Hide setup guide'
})
).
toBeVisible
()
})
it
(
'removes the collapsed progress banner when the remaining setup step completes'
,
async
()
=>
{
useAuthStore
.
getState
().
auth
.
setUser
({
id
:
1
,
username
:
'dashboard-user'
,
role
:
1
,
quota
:
1000000
,
})
window
.
localStorage
.
setItem
(
storageKey
,
'collapsed'
)
await
renderOverview
()
expect
(
await
screen
.
findByText
(
'Setup progress: 2/3'
)).
toBeVisible
()
act
(()
=>
{
useAuthStore
.
getState
().
auth
.
setUser
({
id
:
1
,
username
:
'dashboard-user'
,
role
:
1
,
quota
:
1000000
,
request_count
:
1
,
})
})
expect
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Setup guide'
})
).
toHaveAttribute
(
'aria-expanded'
,
'false'
)
expect
(
screen
.
queryByText
(
/Setup progress:/
)).
not
.
toBeInTheDocument
()
})
it
(
'does not show a completed setup entry when the key lookup fails'
,
async
()
=>
{
keyLookupError
=
new
Error
(
'Key lookup unavailable'
)
await
renderOverview
()
expect
(
await
screen
.
findByRole
(
'button'
,
{
name
:
'Hide setup guide'
})
).
toBeVisible
()
expect
(
screen
.
queryByRole
(
'button'
,
{
name
:
'Setup guide'
})
).
not
.
toBeInTheDocument
()
})
})
web/src/features/dashboard/components/overview/overview-dashboard.tsx
View file @
521cebf5
This diff is collapsed.
Click to expand it.
web/src/features/dashboard/index.tsx
View file @
521cebf5
...
@@ -317,34 +317,35 @@ export function Dashboard() {
...
@@ -317,34 +317,35 @@ export function Dashboard() {
)
:
null
)
:
null
const
sectionActions
=
modelActions
??
flowActions
const
sectionActions
=
modelActions
??
flowActions
if
(
activeSection
===
'overview'
)
{
return
<
OverviewDashboard
/>
}
return
(
return
(
<
SectionPageLayout
>
<
SectionPageLayout
>
<
SectionPageLayout
.
Title
>
{
t
(
meta
.
titleKey
)
}
</
SectionPageLayout
.
Title
>
<
SectionPageLayout
.
Title
>
{
t
(
meta
.
titleKey
)
}
</
SectionPageLayout
.
Title
>
<
SectionPageLayout
.
Content
>
<
SectionPageLayout
.
Content
>
<
div
className=
'space-y-3 sm:space-y-4'
>
<
div
className=
'space-y-3 sm:space-y-4'
>
{
activeSection
!==
'overview'
&&
(
<
div
className=
'flex flex-wrap items-center justify-between gap-1.5 sm:gap-2'
>
<
div
className=
'flex flex-wrap items-center justify-between gap-1.5 sm:gap-2'
>
{
showSectionTabs
?
(
{
showSectionTabs
?
(
<
Tabs
value=
{
activeSection
}
onValueChange=
{
handleSectionChange
}
>
<
Tabs
value=
{
activeSection
}
onValueChange=
{
handleSectionChange
}
>
<
TabsList
className=
'max-w-full flex-wrap justify-start group-data-horizontal/tabs:h-auto'
>
<
TabsList
className=
'max-w-full flex-wrap justify-start group-data-horizontal/tabs:h-auto'
>
{
visibleSections
.
map
((
section
)
=>
(
{
visibleSections
.
map
((
section
)
=>
(
<
TabsTrigger
key=
{
section
}
value=
{
section
}
>
<
TabsTrigger
key=
{
section
}
value=
{
section
}
>
{
t
(
SECTION_META
[
section
].
titleKey
)
}
{
t
(
SECTION_META
[
section
].
titleKey
)
}
</
TabsTrigger
>
</
TabsTrigger
>
))
}
))
}
</
TabsList
>
</
TabsList
>
</
Tabs
>
</
Tabs
>
)
:
(
)
:
(
<
div
/>
<
div
/>
)
}
)
}
{
sectionActions
!=
null
&&
(
{
sectionActions
!=
null
&&
(
<
div
className=
'flex shrink-0 flex-wrap items-center gap-1.5 sm:gap-2'
>
<
div
className=
'flex shrink-0 flex-wrap items-center gap-1.5 sm:gap-2'
>
{
sectionActions
}
{
sectionActions
}
</
div
>
</
div
>
)
}
)
}
</
div
>
</
div
>
)
}
{
activeSection
===
'overview'
&&
<
OverviewDashboard
/>
}
{
activeSection
===
'models'
&&
(
{
activeSection
===
'models'
&&
(
<>
<>
<
FadeIn
>
<
FadeIn
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment