Implementasi Tema Admin dengan Alurkerja UI
Requirment
- React v18
- Vite v3+
- Tailwind
- React Router DOM v6
How To
Buat config file file ini merupakan config file untuk menentukan akan ada menu apa saja di sidebar
// ./utils/configs/menu.tsx
import type { MenuConfig } from 'alurkerja-ui'
const icon = {
home: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M2.5 7.49984L10 1.6665L17.5 7.49984V16.6665C17.5 17.1085 17.3244 17.5325 17.0118 17.845C16.6993 18.1576 16.2754 18.3332 15.8333 18.3332H4.16667C3.72464 18.3332 3.30072 18.1576 2.98816 17.845C2.67559 17.5325 2.5 17.1085 2.5 16.6665V7.49984Z"
stroke="#494B74"
strokeWidth="1.66667"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.5 18.3333V10H12.5V18.3333"
stroke="#494B74"
strokeWidth="1.66667"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
),
}
export const menu: MenuConfig[] = [
{ href: '/', label: 'Home', icon: icon.home },
]Buat Komponen Sidebar
// ./components/Sidebar/Sidebar.tsx
import { FC, Dispatch, SetStateAction } from 'react'
import { MenuConfig, Sidebar } from 'alurkerja-ui'
import { menu } from '@/utils/configs'
import { useLocation } from 'react-router-dom'
interface MenuWrapperProps {
children: JSX.Element
menu: MenuConfig
}
interface ExtendedSidebarProps {
toggled: boolean
setToggled: Dispatch<SetStateAction<boolean>>
menuWrapper: (props: MenuWrapperProps) => JSX.Element
}
export const ExtendedSidebar: FC<ExtendedSidebarProps> = ({ setToggled, toggled, menuWrapper }) => {
const { pathname } = useLocation()
return (
<div className="fixed">
<Sidebar
logo={<div className="text-main-blue-alurkerja font-bold">Alurkerja</div>}
toggled={toggled}
setToggled={setToggled}
menuConfig={menu}
currentPathName={pathname}
menuWrapper={menuWrapper}
/>
</div>
)
}Buat komponen layout
// ./layouts/AdminLayout.tsx
import { createContext, useContext, useEffect, useState } from 'react'
import { AuthContext, Header, MenuConfig } from 'alurkerja-ui'
import { Link, Outlet, useNavigate } from 'react-router-dom'
import { twJoin } from 'tailwind-merge'
import { Sidebar } from '@/components'
import { useAuth } from '@/hooks'
import { jwtDecode } from 'jwt-decode'
type JwtClaim = {
email: string
nama: string
userId: string
}
const MenuWrapper = (props: { children: JSX.Element; menu: MenuConfig }) => {
const { children, menu } = props
return <Link to={menu.href}>{children}</Link>
}
export const UserContext = createContext<JwtClaim | null>(null)
export const AdminLayout = () => {
const navigate = useNavigate()
const { token, initialize, logout } = useAuth()
const axiosInstance = useContext(AuthContext)
const [toggled, setToggled] = useState(false)
const [currentUser, setCurrentUser] = useState<JwtClaim | null>(null)
useEffect(() => {
if (token) {
axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`
axiosInstance.interceptors.response.use(
(response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
return response
},
(error) => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if (error.response.status === 401) {
navigate('/login')
}
return Promise.reject(error)
}
)
// axiosInstance
// .get<AxiosResponse<UserType>>('/auth/info')
// .then((res) => {
// setCurrentUser(res.data.data)
// })
// .finally(() => {
// setIsAppReady(true)
// })
const logined = jwtDecode(token) as JwtClaim
setCurrentUser(logined)
} else {
axiosInstance.defaults.headers.common['Authorization'] = null
navigate('/login')
}
}, [token])
if (!initialize) {
return <>Loading...</>
}
const AvatarContent = () => (
<div className="p-4">
<div className="cursor-pointer p-2 hover:bg-gray-100 rounded" onClick={logout}>
Keluar
</div>
</div>
)
return (
<div className="max-w-screen">
<Sidebar toggled={toggled} setToggled={setToggled} menuWrapper={MenuWrapper} />
<div
className={twJoin('transition-[margin] ease-in-out duration-400', toggled ? 'sm:ml-[80px]' : 'sm:ml-[270px]')}
>
<div className="sticky top-0">
<Header avatarContent={<AvatarContent />} role={currentUser?.nama} />
</div>
<main className="px-4 py-4 md:py-8">
<UserContext.Provider value={currentUser}>
<Outlet />
</UserContext.Provider>
</main>
</div>
</div>
)
}Gunakan komponen layout yang sudah dibuat ketika pembuatan routing
// ./routes/root.tsx
import { createBrowserRouter } from 'react-router-dom'
// layouts
import { AdminLayout } from '@/layouts'
export const router = createBrowserRouter([
{
path: '/',
element: <AdminLayout />,
children: [
{ index: true, element: <>Ini Halamaman Home</> },
],
},
])
