xLog/src/components/dashboard/PagesManager.tsx

264 lines
8.3 KiB
TypeScript

import { Fragment, useEffect, useMemo, useState } from "react"
import { getPageVisibility } from "~/lib/page-helpers"
import { formatDate } from "~/lib/date"
import { TabItem, Tabs } from "../ui/Tabs"
import { Menu } from "@headlessui/react"
import clsx from "clsx"
import { PageVisibilityEnum } from "~/lib/types"
import { DashboardMain } from "./DashboardMain"
import { useRouter } from "next/router"
import Link from "next/link"
import toast from "react-hot-toast"
import { EmptyState } from "../ui/EmptyState"
import type { Note } from "unidata.js"
import { getPagesBySite, deletePage } from "~/models/page.model"
export const PagesManager: React.FC<{
isPost: boolean
}> = ({ isPost }) => {
const router = useRouter()
const subdomain = router.query.subdomain as string
const visibility = useMemo<PageVisibilityEnum>(
() =>
router.query.visibility
? (router.query.visibility as PageVisibilityEnum)
: PageVisibilityEnum.All,
[router.query.visibility]
)
let [pages, setPages] = useState<Note[] | null>(null)
const pageDelete = (id: string) => {
deletePage({
site: subdomain,
id: id,
}).then(() => {
toast.success("Deleted!")
getPagesBySite({
type: isPost ? "post" : "page",
site: subdomain,
take: 1000,
visibility,
render: true,
}).then((pages) => {
setPages(pages?.list || [])
})
})
}
useEffect(() => {
if (subdomain) {
getPagesBySite({
type: isPost ? "post" : "page",
site: subdomain!,
take: 1000,
visibility,
render: true,
}).then((pages) => {
setPages(pages?.list || [])
})
}
}, [subdomain, isPost, visibility])
const tabItems: TabItem[] = [
{
value: PageVisibilityEnum.All,
text: `All ${isPost ? "Posts" : "Pages"}`,
},
{
value: PageVisibilityEnum.Published,
text: "Published",
},
{
value: PageVisibilityEnum.Draft,
text: "Draft",
},
{
value: PageVisibilityEnum.Scheduled,
text: "Scheduled",
},
].map((item) => ({
text: item.text,
onClick: () => {
const newQuery: Record<string, any> = {
...router.query,
visibility: item.value,
}
if (item.value === PageVisibilityEnum.All) {
delete newQuery["visibility"]
}
const search = new URLSearchParams(newQuery).toString()
router.push({
search,
})
},
active: item.value === visibility,
}))
const getPageEditLink = (page: { id: string }) => {
return `/dashboard/${subdomain}/editor?id=${page.id}&type=${
isPost ? "post" : "page"
}`
}
const getPageMenuItems = (page: { id: string }) => {
return [
{
text: "Edit",
icon: (
<svg
className="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
</svg>
),
onClick() {
router.push(getPageEditLink(page))
},
},
{
text: "Delete",
icon: (
<svg
className="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
),
onClick() {
pageDelete(page.id)
},
},
]
}
const title = isPost ? "Posts" : "Pages"
return (
<DashboardMain>
<header className="mb-8 flex justify-between items-center">
<h2 className="text-2xl font-bold">{title}</h2>
<button
type="button"
className={clsx(
`space-x-2 px-3 h-9 inline-flex items-center justify-center text-white rounded-lg text-sm`,
isPost ? `bg-pink-500` : `bg-blue-500`
)}
onClick={() =>
router.push(
`/dashboard/${subdomain}/editor?type=${isPost ? "post" : "page"}`
)
}
>
<svg
className="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
</svg>
<span>New {isPost ? "Post" : "Page"}</span>
</button>
</header>
<Tabs items={tabItems} />
<div className="-mt-3">
{pages && pages.length === 0 && (
<EmptyState resource={isPost ? "posts" : "pages"} />
)}
{pages?.map((page) => {
return (
<Link key={page.id} href={getPageEditLink(page)}>
<a className="group relative hover:bg-zinc-100 rounded-lg py-3 px-3 transition-colors -mx-3 flex justify-between">
<div>
<div className="flex items-center">
<span>{page.title || 'Untitled'}</span>
</div>
<div className="text-zinc-400 text-xs mt-1">
<span className="capitalize">
{getPageVisibility(page).toLowerCase()}
</span>
<span className="mx-2">·</span>
<span>{getPageVisibility(page) === PageVisibilityEnum.Draft ? formatDate(page.date_updated) : formatDate(page.date_published)}</span>
</div>
</div>
<div className="w-10 flex-shrink-0">
<Menu>
{({ open }) => (
<>
<Menu.Button as={Fragment}>
<button
className={clsx(
`text-gray-400 relative z-50 w-8 h-8 rounded inline-flex invisible group-hover:visible justify-center items-center`,
open ? `bg-gray-200` : `hover:bg-gray-200`
)}
onClick={(e) => {
e.stopPropagation()
}}
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" />
</svg>
</button>
</Menu.Button>
<Menu.Items className="text-sm absolute z-20 right-0 bg-white shadow-modal rounded-lg overflow-hidden py-2 w-64">
{getPageMenuItems(page).map((item) => {
return (
<Menu.Item key={item.text}>
<button
type="button"
className="h-10 flex w-full space-x-2 items-center px-3 hover:bg-gray-100"
onClick={(e) => {
e.preventDefault()
item.onClick()
}}
>
<span>{item.icon}</span>
<span>{item.text}</span>
</button>
</Menu.Item>
)
})}
</Menu.Items>
</>
)}
</Menu>
</div>
</a>
</Link>
)
})}
</div>
</DashboardMain>
)
}