Skip to main content

Implementasi Tema Admin dengan Alurkerja UI

Requirment

  1. React v18
  2. Vite v3+
  3. Tailwind
  4. React Router DOM v6

How To

  1. 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 },
    ]
  2. 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>
    )
    }
  3. 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>
    )
    }
  4. 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</> },
    ],
    },
    ])

picture 0