feat: add admin and dashboard layouts with context for improved structure and functionality
This commit is contained in:
parent
2435f9a5d5
commit
8b1af335ec
@ -1,9 +1,13 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
@import 'tailwindcss';
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-admin: 'Poppins', ui-sans-serif, system-ui, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
html,
|
||||
|
||||
41
app/contexts/admin.tsx
Normal file
41
app/contexts/admin.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import {
|
||||
createContext,
|
||||
useState,
|
||||
useContext,
|
||||
type PropsWithChildren,
|
||||
type SetStateAction,
|
||||
type Dispatch,
|
||||
} from 'react'
|
||||
|
||||
type AdminProfile = {
|
||||
name: string
|
||||
}
|
||||
|
||||
type AdminContextProperties = {
|
||||
adminProfile: AdminProfile
|
||||
setAdminProfile: Dispatch<SetStateAction<AdminProfile>>
|
||||
}
|
||||
|
||||
const AdminContext = createContext<AdminContextProperties | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
export const AdminProvider = ({ children }: PropsWithChildren) => {
|
||||
const [adminProfile, setAdminProfile] = useState<AdminProfile>({
|
||||
name: '',
|
||||
})
|
||||
|
||||
return (
|
||||
<AdminContext.Provider value={{ adminProfile, setAdminProfile }}>
|
||||
{children}
|
||||
</AdminContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAdminContext = (): AdminContextProperties => {
|
||||
const context = useContext(AdminContext)
|
||||
if (!context) {
|
||||
throw new Error('useAdminContext must be used within a AdminProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
@ -1,15 +1,17 @@
|
||||
import React, {
|
||||
import {
|
||||
createContext,
|
||||
useState,
|
||||
useContext,
|
||||
type PropsWithChildren,
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
} from 'react'
|
||||
|
||||
type NewsContextProperties = {
|
||||
isLoginOpen: boolean
|
||||
setIsLoginOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
setIsLoginOpen: Dispatch<SetStateAction<boolean>>
|
||||
isRegisterOpen: boolean
|
||||
setIsRegisterOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
setIsRegisterOpen: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
const NewsContext = createContext<NewsContextProperties | undefined>(undefined)
|
||||
|
||||
10
app/layouts/admin/default.tsx
Normal file
10
app/layouts/admin/default.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
export const AdminDefaultLayout = (properties: PropsWithChildren) => {
|
||||
const { children } = properties
|
||||
return (
|
||||
<main className="font-admin relative min-h-dvh bg-[#F7F8FC]">
|
||||
{children}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import type { JSX } from 'react'
|
||||
import type { JSX, SVGProps } from 'react'
|
||||
|
||||
import { FacebookIcon } from '~/components/icons/facebook'
|
||||
import { InstagramIcon } from '~/components/icons/instagram'
|
||||
@ -42,7 +42,7 @@ type FooterMenu = {
|
||||
title: string
|
||||
url: string
|
||||
icon?: (
|
||||
properties: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>,
|
||||
properties: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
|
||||
) => JSX.Element
|
||||
}>
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import {
|
||||
isRouteErrorResponse,
|
||||
Links,
|
||||
@ -18,10 +19,6 @@ export const links: Route.LinksFunction = () => [
|
||||
href: 'https://fonts.gstatic.com',
|
||||
crossOrigin: 'anonymous',
|
||||
},
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
|
||||
},
|
||||
]
|
||||
|
||||
export const meta = ({ location }: Route.MetaArgs) => {
|
||||
@ -39,7 +36,7 @@ export const meta = ({ location }: Route.MetaArgs) => {
|
||||
]
|
||||
}
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
export function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
106
app/routes/_layout.admin.auth.login.tsx
Normal file
106
app/routes/_layout.admin.auth.login.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { useState } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { EyeIcon } from '~/components/icons/eye'
|
||||
import { Button } from '~/components/ui/button'
|
||||
import { APP } from '~/data/meta'
|
||||
|
||||
const AuthLayout = () => {
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex min-h-dvh min-w-dvw flex-col items-center justify-center space-y-8">
|
||||
<div className="grid max-w-lg items-center justify-center space-y-7 rounded-[20px] border border-[#E6E6E6] bg-white p-8">
|
||||
<div className="flex flex-col items-center">
|
||||
<img
|
||||
src={APP.logo}
|
||||
alt={APP.title}
|
||||
className="h-[80px]"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-center">
|
||||
Selamat Datang, silakan masukkan akun Anda untuk melanjutkan!
|
||||
</p>
|
||||
<div>
|
||||
<form>
|
||||
{/* Input Email / No Telepon */}
|
||||
<div className="mb-4">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="mb-1 block text-gray-700"
|
||||
>
|
||||
Email/No. Telepon
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Contoh: legal@legalgo.id"
|
||||
className="focus:inheriten w-full rounded-md border border-[#DFDFDF] p-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Input Password */}
|
||||
<div className="relative mb-4">
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="mb-1 block text-gray-700 focus:outline-[#2E2F7C]"
|
||||
>
|
||||
Kata Sandi
|
||||
</label>
|
||||
<input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
placeholder="Masukkan Kata Sandi"
|
||||
className="w-full rounded-md border border-[#DFDFDF] p-2 pr-10 focus:outline-[#2E2F7C]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-9 right-3 text-gray-500"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeIcon
|
||||
width={15}
|
||||
height={15}
|
||||
/>
|
||||
) : (
|
||||
<EyeIcon
|
||||
width={15}
|
||||
height={15}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Lupa Kata Sandi */}
|
||||
<div className="mb-4 flex justify-between">
|
||||
<span className="text-gray-600">Lupa Kata Sandi?</span>
|
||||
<Link
|
||||
to="/admin/auth/reset-password"
|
||||
className="font-semibold text-[#2E2F7C]"
|
||||
>
|
||||
Reset Kata Sandi
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Tombol Masuk */}
|
||||
<Button className="w-full rounded-md bg-[#2E2F7C] py-2 text-white transition hover:bg-blue-800">
|
||||
Masuk
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/* Link Daftar */}
|
||||
<div className="mt-4 text-center text-sm">
|
||||
Belum punya akun?{' '}
|
||||
<Button
|
||||
onClick={() => {}}
|
||||
className="font-semibold text-[#2E2F7C]"
|
||||
variant="link"
|
||||
size="fit"
|
||||
>
|
||||
Daftar Disini
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default AuthLayout
|
||||
@ -1,4 +1,4 @@
|
||||
const AuthLayout = () => {
|
||||
const DashboardLayout = () => {
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="flex min-h-screen items-center justify-center bg-gray-100">
|
||||
@ -7,4 +7,4 @@ const AuthLayout = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default AuthLayout
|
||||
export default DashboardLayout
|
||||
16
app/routes/_layout.admin.tsx
Normal file
16
app/routes/_layout.admin.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { Outlet } from 'react-router'
|
||||
|
||||
import { AdminProvider } from '~/contexts/admin'
|
||||
import { AdminDefaultLayout } from '~/layouts/admin/default'
|
||||
|
||||
const AdminLayout = () => {
|
||||
return (
|
||||
<AdminProvider>
|
||||
<AdminDefaultLayout>
|
||||
<Outlet />
|
||||
</AdminDefaultLayout>
|
||||
</AdminProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminLayout
|
||||
@ -1,5 +0,0 @@
|
||||
const DashboardLayout = () => {
|
||||
return <div>Dashboard</div>
|
||||
}
|
||||
|
||||
export default DashboardLayout
|
||||
Loading…
x
Reference in New Issue
Block a user