import { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR, { mutate } from "swr"; import { Plus } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // services import { PageService } from "services/page.service"; import { ProjectMemberService } from "services/project"; // hooks import useToast from "hooks/use-toast"; // components import { CreateUpdatePageModal, DeletePageModal, SinglePageDetailedItem, SinglePageListItem } from "components/pages"; import { EmptyState } from "components/common"; // ui import { Loader } from "@plane/ui"; // images import emptyPage from "public/empty-state/page.svg"; // types import { IPage, TPageViewProps } from "types"; import { ALL_PAGES_LIST, FAVORITE_PAGES_LIST, MY_PAGES_LIST, PROJECT_MEMBERS, RECENT_PAGES_LIST, } from "constants/fetch-keys"; type Props = { pages: IPage[] | undefined; viewType: TPageViewProps; }; // services const pageService = new PageService(); const projectMemberService = new ProjectMemberService(); export const PagesView: React.FC<Props> = observer(({ pages, viewType }) => { // states const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false); const [selectedPageToUpdate, setSelectedPageToUpdate] = useState<IPage | null>(null); const [deletePageModal, setDeletePageModal] = useState(false); const [selectedPageToDelete, setSelectedPageToDelete] = useState<IPage | null>(null); const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore(); const user = userStore.currentUser ?? undefined; // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); const { data: people } = useSWR( workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null, workspaceSlug && projectId ? () => projectMemberService.fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null ); const handleEditPage = (page: IPage) => { setSelectedPageToUpdate(page); setCreateUpdatePageModal(true); }; const handleDeletePage = (page: IPage) => { setSelectedPageToDelete(page); setDeletePageModal(true); }; const handleAddToFavorites = (page: IPage) => { if (!workspaceSlug || !projectId) return; mutate<IPage[]>( ALL_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => { if (p.id === page.id) p.is_favorite = true; return p; }), false ); mutate<IPage[]>( MY_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => { if (p.id === page.id) p.is_favorite = true; return p; }), false ); mutate<IPage[]>(FAVORITE_PAGES_LIST(projectId.toString()), (prevData) => [page, ...(prevData ?? [])], false); pageService .addPageToFavorites(workspaceSlug.toString(), projectId.toString(), { page: page.id, }) .then(() => { mutate(RECENT_PAGES_LIST(projectId.toString())); setToastAlert({ type: "success", title: "Success!", message: "Successfully added the page to favorites.", }); }) .catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't add the page to favorites. Please try again.", }); }); }; const handleRemoveFromFavorites = (page: IPage) => { if (!workspaceSlug || !projectId) return; mutate<IPage[]>( ALL_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => { if (p.id === page.id) p.is_favorite = false; return p; }), false ); mutate<IPage[]>( MY_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => { if (p.id === page.id) p.is_favorite = false; return p; }), false ); mutate<IPage[]>( FAVORITE_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).filter((p) => p.id !== page.id), false ); pageService .removePageFromFavorites(workspaceSlug.toString(), projectId.toString(), page.id) .then(() => { mutate(RECENT_PAGES_LIST(projectId.toString())); setToastAlert({ type: "success", title: "Success!", message: "Successfully removed the page from favorites.", }); }) .catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't remove the page from favorites. Please try again.", }); }); }; const partialUpdatePage = (page: IPage, formData: Partial<IPage>) => { if (!workspaceSlug || !projectId || !user) return; mutate<IPage[]>( ALL_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); mutate<IPage[]>( MY_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); mutate<IPage[]>( FAVORITE_PAGES_LIST(projectId.toString()), (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); pageService.patchPage(workspaceSlug.toString(), projectId.toString(), page.id, formData, user).then(() => { mutate(RECENT_PAGES_LIST(projectId.toString())); }); }; return ( <> {workspaceSlug && projectId && ( <> <CreateUpdatePageModal isOpen={createUpdatePageModal} handleClose={() => setCreateUpdatePageModal(false)} data={selectedPageToUpdate} user={user} workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} /> <DeletePageModal isOpen={deletePageModal} setIsOpen={setDeletePageModal} data={selectedPageToDelete} user={user} /> </> )} {pages ? ( <div className="space-y-4 h-full overflow-y-auto"> {pages.length > 0 ? ( viewType === "list" ? ( <ul role="list" className="divide-y divide-custom-border-200"> {pages.map((page) => ( <SinglePageListItem key={page.id} page={page} people={people} handleEditPage={() => handleEditPage(page)} handleDeletePage={() => handleDeletePage(page)} handleAddToFavorites={() => handleAddToFavorites(page)} handleRemoveFromFavorites={() => handleRemoveFromFavorites(page)} partialUpdatePage={partialUpdatePage} /> ))} </ul> ) : viewType === "detailed" ? ( <div className="divide-y divide-custom-border-200 rounded-[10px] border border-custom-border-200 bg-custom-background-100"> {pages.map((page) => ( <SinglePageDetailedItem key={page.id} page={page} people={people} handleEditPage={() => handleEditPage(page)} handleDeletePage={() => handleDeletePage(page)} handleAddToFavorites={() => handleAddToFavorites(page)} handleRemoveFromFavorites={() => handleRemoveFromFavorites(page)} partialUpdatePage={partialUpdatePage} /> ))} </div> ) : ( <div className="rounded-[10px] border border-custom-border-200"> {pages.map((page) => ( <SinglePageDetailedItem key={page.id} page={page} people={people} handleEditPage={() => handleEditPage(page)} handleDeletePage={() => handleDeletePage(page)} handleAddToFavorites={() => handleAddToFavorites(page)} handleRemoveFromFavorites={() => handleRemoveFromFavorites(page)} partialUpdatePage={partialUpdatePage} /> ))} </div> ) ) : ( <EmptyState title="Have your thoughts in place" description="You can think of Pages as an AI-powered notepad." image={emptyPage} primaryButton={{ icon: <Plus className="h-4 w-4" />, text: "New Page", onClick: () => commandPaletteStore.toggleCreatePageModal(true), }} /> )} </div> ) : viewType === "list" ? ( <Loader className="space-y-4"> <Loader.Item height="40px" /> <Loader.Item height="40px" /> <Loader.Item height="40px" /> </Loader> ) : viewType === "detailed" ? ( <Loader className="space-y-4"> <Loader.Item height="150px" /> <Loader.Item height="150px" /> </Loader> ) : ( <Loader className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"> <Loader.Item height="150px" /> <Loader.Item height="150px" /> <Loader.Item height="150px" /> </Loader> )} </> ); });