11'use client' ;
22
33import * as React from 'react' ;
4- import { Folder , Home , Package , SettingsIcon , Container , Puzzle } from 'lucide-react' ;
4+ import { AlertCircle , HelpCircle , Heart , LogOut } from 'lucide-react' ;
55import { NavMain } from '@/components/layout/nav-main' ;
6- import { NavUser } from '@/components/layout/nav-user' ;
76import { TeamSwitcher } from '@/components/ui/team-switcher' ;
87import {
98 Sidebar ,
109 SidebarContent ,
1110 SidebarFooter ,
1211 SidebarHeader ,
13- SidebarRail
12+ SidebarRail ,
13+ SidebarMenu ,
14+ SidebarMenuButton ,
15+ SidebarMenuItem
1416} from '@/components/ui/sidebar' ;
15- import { useAppSelector , useAppDispatch } from '@/redux/hooks' ;
16- import { useGetUserOrganizationsQuery } from '@/redux/services/users/userApi' ;
17- import { useNavigationState } from '@/hooks/use_navigation_state' ;
18- import { setActiveOrganization } from '@/redux/features/users/userSlice' ;
19- import { useTranslation } from '@/hooks/use-translation' ;
20- import { useRBAC } from '@/lib/rbac' ;
21-
22- const data = {
23- navMain : [
24- {
25- title : 'navigation.dashboard' ,
26- url : '/dashboard' ,
27- icon : Home ,
28- resource : 'dashboard'
29- } ,
30- {
31- title : 'navigation.extensions' ,
32- url : '/extensions' ,
33- icon : Puzzle ,
34- resource : 'extensions'
35- } ,
36- {
37- title : 'navigation.selfHost' ,
38- url : '/self-host' ,
39- icon : Package ,
40- resource : 'deploy'
41- } ,
42- {
43- title : 'navigation.containers' ,
44- url : '/containers' ,
45- icon : Container ,
46- resource : 'container'
47- } ,
48- {
49- title : 'navigation.fileManager' ,
50- url : '/file-manager' ,
51- icon : Folder ,
52- resource : 'file-manager'
53- } ,
54- {
55- title : 'navigation.settings' ,
56- url : '/settings/general' ,
57- icon : SettingsIcon ,
58- resource : 'settings' ,
59- items : [
60- {
61- title : 'navigation.general' ,
62- url : '/settings/general' ,
63- resource : 'settings'
64- } ,
65- {
66- title : 'navigation.notifications' ,
67- url : '/settings/notifications' ,
68- resource : 'notification'
69- } ,
70- {
71- title : 'navigation.team' ,
72- url : '/settings/teams' ,
73- resource : 'organization'
74- } ,
75- {
76- title : 'navigation.domains' ,
77- url : '/settings/domains' ,
78- resource : 'domain'
79- }
80- ]
81- }
82- ]
83- } ;
17+ import { useAppSidebar } from '@/hooks/use-app-sidebar' ;
18+ import { LogoutDialog } from '@/components/ui/logout-dialog' ;
8419
8520export function AppSidebar ( {
8621 toggleAddTeamModal,
@@ -90,71 +25,23 @@ export function AppSidebar({
9025 toggleAddTeamModal ?: ( ) => void ;
9126 addTeamModalOpen ?: boolean ;
9227} ) {
93- const { t } = useTranslation ( ) ;
94- const user = useAppSelector ( ( state ) => state . auth . user ) ;
95- const { isLoading, refetch } = useGetUserOrganizationsQuery ( ) ;
96- const organizations = useAppSelector ( ( state ) => state . user . organizations ) ;
97- const { activeNav, setActiveNav } = useNavigationState ( ) ;
98- const activeOrg = useAppSelector ( ( state ) => state . user . activeOrganization ) ;
99- const dispatch = useAppDispatch ( ) ;
100- const { canAccessResource } = useRBAC ( ) ;
101-
102- const hasAnyPermission = React . useMemo ( ( ) => {
103- const allowedResources = [ 'dashboard' , 'settings' , 'extensions' ] ;
104-
105- return ( resource : string ) => {
106- if ( ! user || ! activeOrg ) return false ;
107-
108- if ( allowedResources . includes ( resource ) ) {
109- return true ;
110- }
111-
112- return (
113- canAccessResource ( resource as any , 'read' ) ||
114- canAccessResource ( resource as any , 'create' ) ||
115- canAccessResource ( resource as any , 'update' ) ||
116- canAccessResource ( resource as any , 'delete' )
117- ) ;
118- } ;
119- } , [ user , activeOrg , canAccessResource ] ) ;
120-
121- const filteredNavItems = React . useMemo (
122- ( ) =>
123- data . navMain
124- . filter ( ( item ) => {
125- if ( ! item . resource ) return false ;
126-
127- if ( item . items ) {
128- const filteredSubItems = item . items . filter (
129- ( subItem ) => subItem . resource && hasAnyPermission ( subItem . resource )
130- ) ;
131- return filteredSubItems . length > 0 ;
132- }
133-
134- return hasAnyPermission ( item . resource ) ;
135- } )
136- . map ( ( item ) => ( {
137- ...item ,
138- title : t ( item . title as any ) ,
139- items : item . items ?. map ( ( subItem ) => ( {
140- ...subItem ,
141- title : t ( subItem . title as any )
142- } ) )
143- } ) ) ,
144- [ data . navMain , hasAnyPermission , t ]
145- ) ;
146-
147- React . useEffect ( ( ) => {
148- if ( organizations && organizations . length > 0 && ! activeOrg ) {
149- dispatch ( setActiveOrganization ( organizations [ 0 ] . organization ) ) ;
150- }
151- } , [ organizations , activeOrg , dispatch ] ) ;
152-
153- React . useEffect ( ( ) => {
154- if ( activeOrg ?. id ) {
155- refetch ( ) ;
156- }
157- } , [ activeOrg ?. id , refetch ] ) ;
28+ const {
29+ user,
30+ refetch,
31+ activeNav,
32+ setActiveNav,
33+ activeOrg,
34+ hasAnyPermission,
35+ t,
36+ showLogoutDialog,
37+ filteredNavItems,
38+ handleSponsor,
39+ handleReportIssue,
40+ handleHelp,
41+ handleLogoutClick,
42+ handleLogoutConfirm,
43+ handleLogoutCancel
44+ } = useAppSidebar ( ) ;
15845
15946 if ( ! user || ! activeOrg ) {
16047 return null ;
@@ -182,9 +69,39 @@ export function AppSidebar({
18269 />
18370 </ SidebarContent >
18471 < SidebarFooter >
185- < NavUser user = { user } />
72+ < SidebarMenu >
73+ < SidebarMenuItem >
74+ < SidebarMenuButton onClick = { handleSponsor } className = "cursor-pointer" >
75+ < Heart className = "text-red-500" />
76+ < span > { t ( 'user.menu.sponsor' ) } </ span >
77+ </ SidebarMenuButton >
78+ </ SidebarMenuItem >
79+ < SidebarMenuItem >
80+ < SidebarMenuButton onClick = { handleHelp } className = "cursor-pointer" >
81+ < HelpCircle />
82+ < span > { t ( 'user.menu.help' ) } </ span >
83+ </ SidebarMenuButton >
84+ </ SidebarMenuItem >
85+ < SidebarMenuItem >
86+ < SidebarMenuButton onClick = { handleReportIssue } className = "cursor-pointer" >
87+ < AlertCircle />
88+ < span > { t ( 'user.menu.reportIssue' ) } </ span >
89+ </ SidebarMenuButton >
90+ </ SidebarMenuItem >
91+ < SidebarMenuItem >
92+ < SidebarMenuButton onClick = { handleLogoutClick } className = "cursor-pointer" >
93+ < LogOut />
94+ < span > { t ( 'user.menu.logout' ) } </ span >
95+ </ SidebarMenuButton >
96+ </ SidebarMenuItem >
97+ </ SidebarMenu >
18698 </ SidebarFooter >
18799 < SidebarRail />
100+ < LogoutDialog
101+ open = { showLogoutDialog }
102+ onConfirm = { handleLogoutConfirm }
103+ onCancel = { handleLogoutCancel }
104+ />
188105 </ Sidebar >
189106 ) ;
190107}
0 commit comments