Merge remote-tracking branch 'origin/master' into feature/slicing
This commit is contained in:
commit
8c2298ff61
@ -21,7 +21,7 @@ export const EditorButton = (properties: TProperties) => {
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'flex h-6 w-8 cursor-pointer items-center justify-center rounded-md p-2 hover:bg-[#2E2F7C] hover:text-white disabled:cursor-not-allowed disabled:bg-[#2E2F7C]/50 disabled:text-white disabled:hover:bg-[#2E2F7C]/50',
|
'flex h-6 w-8 cursor-pointer items-center justify-center rounded-md p-2 hover:bg-[#2E2F7C] hover:text-white disabled:cursor-not-allowed disabled:bg-[#2E2F7C]/50 disabled:text-white disabled:opacity-50',
|
||||||
isActive ? 'bg-[#2E2F7C]/10' : '',
|
isActive ? 'bg-[#2E2F7C]/10' : '',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import {
|
|||||||
import { useRemixFormContext } from 'remix-hook-form'
|
import { useRemixFormContext } from 'remix-hook-form'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
|
import { useAdminContext } from '~/contexts/admin'
|
||||||
|
|
||||||
import { Button } from './button'
|
import { Button } from './button'
|
||||||
|
|
||||||
type TInputProperties<T extends FieldValues> = Omit<
|
type TInputProperties<T extends FieldValues> = Omit<
|
||||||
@ -40,6 +42,7 @@ export const InputFile = <TFormValues extends Record<string, unknown>>(
|
|||||||
labelClassName,
|
labelClassName,
|
||||||
...restProperties
|
...restProperties
|
||||||
} = properties
|
} = properties
|
||||||
|
const { setIsUploadOpen } = useAdminContext()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -59,7 +62,7 @@ export const InputFile = <TFormValues extends Record<string, unknown>>(
|
|||||||
</Label>
|
</Label>
|
||||||
<HeadlessInput
|
<HeadlessInput
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'h-[42px] w-full rounded-md border border-[#DFDFDF] p-2',
|
'h-[42px] w-full rounded-md border border-[#DFDFDF] p-2 pr-8',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -71,7 +74,9 @@ export const InputFile = <TFormValues extends Record<string, unknown>>(
|
|||||||
variant="icon"
|
variant="icon"
|
||||||
size="fit"
|
size="fit"
|
||||||
className="absolute right-3 h-[42px]"
|
className="absolute right-3 h-[42px]"
|
||||||
onClick={() => {}}
|
onClick={() => {
|
||||||
|
setIsUploadOpen('featured_image')
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<CloudArrowUpIcon className="h-4 w-4 text-gray-500/50" />
|
<CloudArrowUpIcon className="h-4 w-4 text-gray-500/50" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export const Input = <TFormValues extends Record<string, unknown>>(
|
|||||||
<HeadlessInput
|
<HeadlessInput
|
||||||
type={inputType}
|
type={inputType}
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'h-[42px] w-full rounded-md border border-[#DFDFDF] p-2',
|
'h-[42px] w-full rounded-md border border-[#DFDFDF] p-2 pr-8',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
placeholder={inputType === 'password' ? '******' : placeholder}
|
placeholder={inputType === 'password' ? '******' : placeholder}
|
||||||
|
|||||||
47
app/contexts/admin.tsx
Normal file
47
app/contexts/admin.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
useState,
|
||||||
|
useContext,
|
||||||
|
type PropsWithChildren,
|
||||||
|
type Dispatch,
|
||||||
|
type SetStateAction,
|
||||||
|
} from 'react'
|
||||||
|
|
||||||
|
type TUpload =
|
||||||
|
| 'featured_image'
|
||||||
|
| 'ads'
|
||||||
|
| 'content'
|
||||||
|
| 'profile_picture'
|
||||||
|
| undefined
|
||||||
|
|
||||||
|
type AdminContextProperties = {
|
||||||
|
isUploadOpen: TUpload
|
||||||
|
setIsUploadOpen: Dispatch<SetStateAction<TUpload>>
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminContext = createContext<AdminContextProperties | undefined>(
|
||||||
|
undefined,
|
||||||
|
)
|
||||||
|
|
||||||
|
export const AdminProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
const [isUploadOpen, setIsUploadOpen] = useState<TUpload>()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminContext.Provider
|
||||||
|
value={{
|
||||||
|
isUploadOpen,
|
||||||
|
setIsUploadOpen,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{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,10 +1,14 @@
|
|||||||
|
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
|
|
||||||
|
import { useAdminContext } from '~/contexts/admin'
|
||||||
|
|
||||||
import { Navbar } from './navbar'
|
import { Navbar } from './navbar'
|
||||||
import { Sidebar } from './sidebar'
|
import { Sidebar } from './sidebar'
|
||||||
|
|
||||||
export const AdminDashboardLayout = (properties: PropsWithChildren) => {
|
export const AdminDashboardLayout = (properties: PropsWithChildren) => {
|
||||||
const { children } = properties
|
const { children } = properties
|
||||||
|
const { isUploadOpen, setIsUploadOpen } = useAdminContext()
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
@ -12,6 +16,28 @@ export const AdminDashboardLayout = (properties: PropsWithChildren) => {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div className="min-h-[calc(100dvh-80px)] flex-1 p-8">{children}</div>
|
<div className="min-h-[calc(100dvh-80px)] flex-1 p-8">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={!!isUploadOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsUploadOpen(undefined)
|
||||||
|
}}
|
||||||
|
className="relative z-50"
|
||||||
|
transition
|
||||||
|
>
|
||||||
|
<DialogBackdrop
|
||||||
|
className="fixed inset-0 bg-black/50 duration-300 ease-out data-[closed]:opacity-0"
|
||||||
|
transition
|
||||||
|
/>
|
||||||
|
<div className="fixed inset-0 flex w-screen justify-center overflow-y-auto p-0 max-sm:bg-white sm:items-center sm:p-4">
|
||||||
|
<DialogPanel
|
||||||
|
transition
|
||||||
|
className="max-w-lg space-y-6 rounded-lg bg-white p-8 duration-300 ease-out data-[closed]:scale-95 data-[closed]:opacity-0 sm:shadow-lg"
|
||||||
|
>
|
||||||
|
Upload di mari {isUploadOpen}
|
||||||
|
</DialogPanel>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Outlet } from 'react-router'
|
|||||||
|
|
||||||
import { getCategories } from '~/apis/common/get-categories'
|
import { getCategories } from '~/apis/common/get-categories'
|
||||||
import { getTags } from '~/apis/common/get-tags'
|
import { getTags } from '~/apis/common/get-tags'
|
||||||
|
import { AdminProvider } from '~/contexts/admin'
|
||||||
import { AdminDashboardLayout } from '~/layouts/admin/dashboard'
|
import { AdminDashboardLayout } from '~/layouts/admin/dashboard'
|
||||||
|
|
||||||
import type { Route } from './+types/_admin.lg-admin._dashboard'
|
import type { Route } from './+types/_admin.lg-admin._dashboard'
|
||||||
@ -18,9 +19,11 @@ export const loader = async ({}: Route.LoaderArgs) => {
|
|||||||
|
|
||||||
const DashboardLayout = () => {
|
const DashboardLayout = () => {
|
||||||
return (
|
return (
|
||||||
|
<AdminProvider>
|
||||||
<AdminDashboardLayout>
|
<AdminDashboardLayout>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</AdminDashboardLayout>
|
</AdminDashboardLayout>
|
||||||
|
</AdminProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default DashboardLayout
|
export default DashboardLayout
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { Outlet, redirect } from 'react-router'
|
import { Outlet, redirect } from 'react-router'
|
||||||
|
import { XiorError } from 'xior'
|
||||||
|
|
||||||
import { getStaff } from '~/apis/admin/get-staff'
|
import { getStaff } from '~/apis/admin/get-staff'
|
||||||
import { AUTH_PAGES } from '~/configs/pages'
|
import { AUTH_PAGES } from '~/configs/pages'
|
||||||
import { AdminDefaultLayout } from '~/layouts/admin/default'
|
import { AdminDefaultLayout } from '~/layouts/admin/default'
|
||||||
import { handleCookie } from '~/libs/cookies'
|
import { handleCookie } from '~/libs/cookies'
|
||||||
|
import { setStaffLogoutHeaders } from '~/libs/logout-header.server'
|
||||||
|
|
||||||
import type { Route } from './+types/_admin.lg-admin'
|
import type { Route } from './+types/_admin.lg-admin'
|
||||||
|
|
||||||
@ -13,6 +15,19 @@ export const loader = async ({ request }: Route.LoaderArgs) => {
|
|||||||
const isAuthPage = AUTH_PAGES.includes(pathname)
|
const isAuthPage = AUTH_PAGES.includes(pathname)
|
||||||
let staffData
|
let staffData
|
||||||
|
|
||||||
|
if (staffToken) {
|
||||||
|
try {
|
||||||
|
const { data } = await getStaff({
|
||||||
|
accessToken: staffToken,
|
||||||
|
})
|
||||||
|
staffData = data
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof XiorError && error.response?.status === 401) {
|
||||||
|
setStaffLogoutHeaders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAuthPage && !staffToken) {
|
if (!isAuthPage && !staffToken) {
|
||||||
throw redirect('/lg-admin/login')
|
throw redirect('/lg-admin/login')
|
||||||
}
|
}
|
||||||
@ -21,13 +36,6 @@ export const loader = async ({ request }: Route.LoaderArgs) => {
|
|||||||
throw redirect('/lg-admin')
|
throw redirect('/lg-admin')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (staffToken) {
|
|
||||||
const { data } = await getStaff({
|
|
||||||
accessToken: staffToken,
|
|
||||||
})
|
|
||||||
staffData = data
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
staffData,
|
staffData,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Outlet } from 'react-router'
|
import { Outlet } from 'react-router'
|
||||||
|
import { XiorError } from 'xior'
|
||||||
|
|
||||||
import { getCategories } from '~/apis/common/get-categories'
|
import { getCategories } from '~/apis/common/get-categories'
|
||||||
import { getSubscriptions } from '~/apis/common/get-subscriptions'
|
import { getSubscriptions } from '~/apis/common/get-subscriptions'
|
||||||
@ -6,6 +7,7 @@ import { getUser } from '~/apis/news/get-user'
|
|||||||
import { NewsProvider } from '~/contexts/news'
|
import { NewsProvider } from '~/contexts/news'
|
||||||
import { NewsDefaultLayout } from '~/layouts/news/default'
|
import { NewsDefaultLayout } from '~/layouts/news/default'
|
||||||
import { handleCookie } from '~/libs/cookies'
|
import { handleCookie } from '~/libs/cookies'
|
||||||
|
import { setUserLogoutHeaders } from '~/libs/logout-header.server'
|
||||||
|
|
||||||
import type { Route } from './+types/_news'
|
import type { Route } from './+types/_news'
|
||||||
|
|
||||||
@ -13,10 +15,16 @@ export const loader = async ({ request }: Route.LoaderArgs) => {
|
|||||||
const { userToken } = await handleCookie(request)
|
const { userToken } = await handleCookie(request)
|
||||||
let userData
|
let userData
|
||||||
if (userToken) {
|
if (userToken) {
|
||||||
|
try {
|
||||||
const { data } = await getUser({
|
const { data } = await getUser({
|
||||||
accessToken: userToken,
|
accessToken: userToken,
|
||||||
})
|
})
|
||||||
userData = data
|
userData = data
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof XiorError && error.response?.status === 401) {
|
||||||
|
setUserLogoutHeaders()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const { data: subscriptionsData } = await getSubscriptions()
|
const { data: subscriptionsData } = await getSubscriptions()
|
||||||
const { data: categoriesData } = await getCategories()
|
const { data: categoriesData } = await getCategories()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user