Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"graphql": "^16.9.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"lucide-react": "^0.516.0",
"next": "^14.2.14",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
89 changes: 89 additions & 0 deletions frontend/src/components/AdminLayout/AdminLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { usePathname, useRouter } from 'next/navigation';
import { useMemo, useState, useEffect } from 'react';
import clsx from 'clsx';

import Sidebar from '@/components/AdminLayout/SideBar';
import MobileOverlay from '@/components/AdminLayout/MobileOverlay';
import ToggleButton from '@/components/AdminLayout/ToggleButton';
import TopbarMobile from '@/components/AdminLayout/TopbarMobile';
import navigation, { NavItem } from './Navigation';
import { useUser } from '@/context/UserContext/UserContext';


const AdminLayout = ({ children }: { children: React.ReactNode }) : React.ReactElement => {
const pathname = usePathname();
const router = useRouter();
const user = useUser();
// console.log('user dans AdminLayout', user.user?.role)
const role = user.user?.role || 'view';

const currentTab = useMemo(() => {
const segments = pathname?.split('/') || []
const index = segments.findIndex((segment) => segment === 'admin')
return segments.slice(index + 1).join('/') || 'dashboard'
}, [pathname])

const [sidebarOpen, setSidebarOpen] = useState(false)
const [openMenus, setOpenMenus] = useState<string[]>([])

useEffect(() => {
const parent = navigation.find((item) => item.children?.some(child => child.key === currentTab))
if (parent && !openMenus.includes(parent.key)) {
setOpenMenus((prev) => [...prev, parent.key])
}
}, [currentTab, openMenus])

const filteredNavigation = useMemo<NavItem[]>(() => {
const isAllowed = (roles?: string[]) => !roles || roles.includes(role)

return navigation.map((item) => {
const children = (item.children || []).map((child) => ({
...child,
disabled: !isAllowed(child.roles),
}))

const disabled = !isAllowed(item.roles)

return {
...item,
children,
disabled,
}
})
}, [role])

return (
<div className="flex h-screen bg-body overflow-hidden">
<Sidebar
navigation={filteredNavigation}
sidebarOpen={sidebarOpen}
setSidebarOpen={setSidebarOpen}
activeTab={currentTab}
setActiveTab={(key) => {
setOpenMenus((prev) => {
const parent = filteredNavigation.find(item => item.children?.some(child => child.key === key))
if (parent && !prev.includes(parent.key)) {
return [...prev, parent.key]
}
return prev
})
router.push(`/admin/${key}`)
}}
openMenus={openMenus}
setOpenMenus={setOpenMenus}
/>

{sidebarOpen && <MobileOverlay setSidebarOpen={setSidebarOpen} />}

<div className={clsx('flex flex-col flex-1 pt-[80px] overflow-hidden', 'md:ml-80')}>
<ToggleButton sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
<TopbarMobile activeTab={currentTab} navigation={filteredNavigation} />
<div className="flex-1 overflow-y-auto p-6">
{children}
</div>
</div>
</div>
)
}

export default AdminLayout
17 changes: 17 additions & 0 deletions frontend/src/components/AdminLayout/MobileOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Dispatch, SetStateAction } from 'react'

type MobileOverlayProps = {
setSidebarOpen: Dispatch<SetStateAction<boolean>>
}

const MobileOverlay = ({ setSidebarOpen }: MobileOverlayProps): React.ReactElement => {
return (
<div
className="fixed top-[80px] left-0 right-0 bottom-0 z-30 bg-black bg-opacity-40 md:hidden"
onClick={() => setSidebarOpen(false)}
aria-hidden="true"
/>
)
}

export default MobileOverlay;
121 changes: 121 additions & 0 deletions frontend/src/components/AdminLayout/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
LayoutDashboard,
User,
Settings,
FolderPlus,
Eye,
DatabaseBackup,
PanelsTopLeft,
FileUser,
} from 'lucide-react'

export type NavItem = {
name: string
key: string
icon: React.ComponentType<{ className?: string }>
children?: NavItem[]
roles?: ('admin' | 'editor' | 'view')[]
access?: boolean
parentKey?: string
disabled?: boolean
}

const navigation: NavItem[] = [
{
name: 'Dashboard',
key: 'dashboard',
icon: LayoutDashboard,
roles: ['admin', 'editor', 'view'],
},
{
name: 'Projets',
key: 'projects',
icon: PanelsTopLeft,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir les projets', key: 'projects/list', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'projects' },
{ name: 'Créer un projet', key: 'projects/create', icon: FolderPlus, roles: ['admin', 'editor'], parentKey: 'projects' },
],
},
{
name: 'Utilisateurs',
key: 'users',
icon: User,
roles: ['admin'],
children: [
{ name: 'Voir les utilisateurs', key: 'users/list', icon: Eye, roles: ['admin'], parentKey: 'users' },
{ name: 'Créer un utilisateur', key: 'users/create', icon: FolderPlus, roles: ['admin'], parentKey: 'users' },
],
},
{
name: 'Expériences',
key: 'experiences',
icon: PanelsTopLeft,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir les expériences', key: 'experiences/list', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'experiences' },
{ name: 'Créer une expérience', key: 'experiences/create', icon: FolderPlus, roles: ['admin', 'editor'], parentKey: 'experiences' },
],
},
{
name: 'Éducation',
key: 'educations',
icon: PanelsTopLeft,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir les éducations', key: 'educations/list', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'educations' },
{ name: 'Créer une éducation', key: 'educations/create', icon: FolderPlus, roles: ['admin', 'editor'], parentKey: 'educations' },
],
},
{
name: 'Skills',
key: 'skills',
icon: PanelsTopLeft,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir les skills', key: 'skills/list', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'skills' },
{ name: 'Créer un skill', key: 'skills/create', icon: FolderPlus, roles: ['admin', 'editor'], parentKey: 'skills' },
],
},
{
name: 'Thème',
key: 'theme-colors',
icon: Settings,
roles: ['admin'],
children: [
{ name: 'Voir les couleurs', key: 'theme-colors/list', icon: Eye, roles: ['admin'], parentKey: 'theme-colors' },
{ name: 'Créer une nouvelle couleur', key: 'theme-colors/create', icon: FolderPlus, roles: ['admin'], parentKey: 'theme-colors' },
],
},
{
name: 'Sauvegarde',
key: 'backup',
icon: DatabaseBackup,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir les backup', key: 'backup/list', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'backup' },
{ name: 'Nouvelle backup', key: 'backup/create', icon: FolderPlus, roles: ['admin'], parentKey: 'backup' },
],
},
{
name: 'CV',
key: 'cv',
icon: FileUser,
roles: ['admin', 'editor', 'view'],
children: [
{ name: 'Voir le CV', key: 'cv/view', icon: Eye, roles: ['admin', 'editor', 'view'], parentKey: 'cv' },
{ name: 'Modifier le CV', key: 'cv/update', icon: FolderPlus, roles: ['admin'], parentKey: 'cv' },
],
},
// {
// name: 'Paramètres',
// key: 'settings',
// icon: Settings,
// roles: ['admin'],
// children: [
// { name: 'Changer mot de passe', key: 'settings/change-password', icon: Lock, roles: ['admin'], parentKey: 'settings' },
// ],
// },
]

export default navigation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const BackUpCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page BackUpCreate</p>
)
}

export default BackUpCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const BackUpList = (): React.ReactElement => {
return (
<p className="text-primary">Page BackUpList</p>
)
}

export default BackUpList;
7 changes: 7 additions & 0 deletions frontend/src/components/AdminLayout/Pages/CV/CVUpdate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const CVUpdate = (): React.ReactElement => {
return (
<p className="text-primary">Page CVUpdate</p>
)
}

export default CVUpdate;
7 changes: 7 additions & 0 deletions frontend/src/components/AdminLayout/Pages/CV/CVView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const CVView = (): React.ReactElement => {
return (
<p className="text-primary">Page CVView</p>
)
}

export default CVView;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const Dashboard = (): React.ReactElement => {
return (
<p className="text-primary">Page Dashboard</p>
)
}

export default Dashboard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const EducationCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page EducationCreate</p>
)
}

export default EducationCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const EducationsList = (): React.ReactElement => {
return (
<p className="text-primary">Page EducationsList</p>
)
}

export default EducationsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ExperienceCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page ExperienceCreate</p>
)
}

export default ExperienceCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ExperiencesList = (): React.ReactElement => {
return (
<p className="text-primary">Page ExperiencesList</p>
)
}

export default ExperiencesList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ProjectCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page ProjectCreate</p>
)
}

export default ProjectCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ProjectsList = (): React.ReactElement => {
return (
<p className="text-primary">Page ProjectsList</p>
)
}

export default ProjectsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const SkillCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page SkillCreate</p>
)
}

export default SkillCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const SkillsList = (): React.ReactElement => {
return (
<p className="text-primary">Page SkillsList</p>
)
}

export default SkillsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ThemeCreate = (): React.ReactElement => {
return (
<p className="text-primary">Page ThemeCreate</p>
)
}

export default ThemeCreate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ThemesList = (): React.ReactElement => {
return (
<p className="text-primary">Page ThemesList</p>
)
}

export default ThemesList;
Loading
Loading