forked from github/plane
fix: pages layout and alignment issues (#1152)
This commit is contained in:
parent
dce3c3f008
commit
022960d7e3
@ -25,9 +25,5 @@ export const AllPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
: null
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 space-y-4">
|
||||
<PagesView pages={pages} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
return <PagesView pages={pages} viewType={viewType} />;
|
||||
};
|
||||
|
@ -25,9 +25,5 @@ export const FavoritePagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
: null
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 space-y-4">
|
||||
<PagesView pages={pages} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
return <PagesView pages={pages} viewType={viewType} />;
|
||||
};
|
||||
|
@ -25,9 +25,5 @@ export const MyPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
: null
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 space-y-4">
|
||||
<PagesView pages={pages} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
return <PagesView pages={pages} viewType={viewType} />;
|
||||
};
|
||||
|
@ -25,9 +25,5 @@ export const OtherPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
: null
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 space-y-4">
|
||||
<PagesView pages={pages} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
return <PagesView pages={pages} viewType={viewType} />;
|
||||
};
|
||||
|
@ -37,37 +37,28 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
|
||||
<>
|
||||
{pages ? (
|
||||
Object.keys(pages).length > 0 && !isEmpty ? (
|
||||
<div className="mt-4 space-y-4 flex flex-col gap-5">
|
||||
{Object.keys(pages).map((key) => {
|
||||
if (pages[key].length === 0) return null;
|
||||
Object.keys(pages).map((key) => {
|
||||
if (pages[key].length === 0) return null;
|
||||
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold capitalize mb-4">
|
||||
{replaceUnderscoreIfSnakeCase(key)}
|
||||
</h2>
|
||||
<PagesView
|
||||
pages={pages[key as keyof RecentPagesResponse]}
|
||||
viewType={viewType}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<div key={key}>
|
||||
<h2 className="text-xl font-semibold capitalize mb-2">
|
||||
{replaceUnderscoreIfSnakeCase(key)}
|
||||
</h2>
|
||||
<PagesView pages={pages[key as keyof RecentPagesResponse]} viewType={viewType} />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<EmptyState
|
||||
type="page"
|
||||
title="Create New Page"
|
||||
description="Create and document issues effortlessly in one place with Plane Notes, AI-powered for ease."
|
||||
imgURL={emptyPage}
|
||||
/>
|
||||
</div>
|
||||
<EmptyState
|
||||
type="page"
|
||||
title="Create New Page"
|
||||
description="Create and document issues effortlessly in one place with Plane Notes, AI-powered for ease."
|
||||
imgURL={emptyPage}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<Loader className="mt-8 space-y-4">
|
||||
<Loader className="space-y-4">
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
|
@ -200,61 +200,63 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
data={selectedPageToDelete}
|
||||
/>
|
||||
{pages ? (
|
||||
pages.length > 0 ? (
|
||||
viewType === "list" ? (
|
||||
<ul role="list" className="divide-y divide-brand-base">
|
||||
{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-brand-base rounded-[10px] border border-brand-base bg-brand-base">
|
||||
{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="space-y-4 h-full overflow-y-auto">
|
||||
{pages.length > 0 ? (
|
||||
viewType === "list" ? (
|
||||
<ul role="list" className="divide-y divide-brand-base">
|
||||
{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-brand-base rounded-[10px] border border-brand-base bg-brand-base">
|
||||
{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-brand-base">
|
||||
{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-brand-base">
|
||||
{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
|
||||
type="page"
|
||||
title="Create New Page"
|
||||
description="Create and document issues effortlessly in one place with Plane Notes, AI-powered for ease."
|
||||
imgURL={emptyPage}
|
||||
/>
|
||||
)
|
||||
<EmptyState
|
||||
type="page"
|
||||
title="Create New Page"
|
||||
description="Create and document issues effortlessly in one place with Plane Notes, AI-powered for ease."
|
||||
imgURL={emptyPage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : viewType === "list" ? (
|
||||
<Loader className="space-y-4">
|
||||
<Loader.Item height="40px" />
|
||||
|
@ -1,19 +1,15 @@
|
||||
import { useState } from "react";
|
||||
import { useState, Fragment } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
import useSWR from "swr";
|
||||
|
||||
// react-hook-form
|
||||
import { useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
import { Tab } from "@headlessui/react";
|
||||
// services
|
||||
import projectService from "services/project.service";
|
||||
import pagesService from "services/pages.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
// icons
|
||||
import { PlusIcon } from "components/icons";
|
||||
@ -22,20 +18,15 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
// components
|
||||
import { RecentPagesList, CreateUpdatePageModal, TPagesListProps } from "components/pages";
|
||||
// ui
|
||||
import { Input, PrimaryButton } from "components/ui";
|
||||
import { PrimaryButton } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// icons
|
||||
import { ListBulletIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IPage, TPageViewProps } from "types";
|
||||
import { TPageViewProps } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import {
|
||||
ALL_PAGES_LIST,
|
||||
MY_PAGES_LIST,
|
||||
PROJECT_DETAILS,
|
||||
RECENT_PAGES_LIST,
|
||||
} from "constants/fetch-keys";
|
||||
import { PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
|
||||
const AllPagesList = dynamic<TPagesListProps>(
|
||||
() => import("components/pages").then((a) => a.AllPagesList),
|
||||
@ -65,6 +56,8 @@ const OtherPagesList = dynamic<TPagesListProps>(
|
||||
}
|
||||
);
|
||||
|
||||
const tabsList = ["Recent", "All", "Favorites", "Created by me", "Created by others"];
|
||||
|
||||
const ProjectPages: NextPage = () => {
|
||||
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
|
||||
|
||||
@ -73,22 +66,8 @@ const ProjectPages: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { storedValue: pageTab, setValue: setPageTab } = useLocalStorage("pageTab", "Recent");
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
watch,
|
||||
reset,
|
||||
formState: { isSubmitting },
|
||||
} = useForm<Partial<IPage>>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
|
||||
const { data: projectDetails } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
@ -96,58 +75,6 @@ const ProjectPages: NextPage = () => {
|
||||
: null
|
||||
);
|
||||
|
||||
const createPage = async (formData: Partial<IPage>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
if (formData.name === "") {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Page name is required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await pagesService
|
||||
.createPage(workspaceSlug as string, projectId as string, formData)
|
||||
.then((res) => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "Page created successfully.",
|
||||
});
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/pages/${res.id}`);
|
||||
reset();
|
||||
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
mutate<IPage[]>(
|
||||
MY_PAGES_LIST(projectId as string),
|
||||
(prevData) => {
|
||||
if (!prevData) return undefined;
|
||||
|
||||
return [res, ...(prevData as IPage[])];
|
||||
},
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(
|
||||
ALL_PAGES_LIST(projectId as string),
|
||||
(prevData) => {
|
||||
if (!prevData) return undefined;
|
||||
|
||||
return [res, ...(prevData as IPage[])];
|
||||
},
|
||||
false
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Page could not be created. Please try again",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const currentTabValue = (tab: string | null) => {
|
||||
switch (tab) {
|
||||
case "Recent":
|
||||
@ -195,114 +122,85 @@ const ProjectPages: NextPage = () => {
|
||||
</PrimaryButton>
|
||||
}
|
||||
>
|
||||
<div className="space-y-5 p-8">
|
||||
<div className="space-y-5 p-8 h-full overflow-hidden flex flex-col">
|
||||
<h3 className="text-2xl font-semibold text-brand-base">Pages</h3>
|
||||
{/* <form
|
||||
onSubmit={handleSubmit(createPage)}
|
||||
className="relative mb-12 flex items-center justify-between gap-2 rounded-[6px] border border-brand-base p-2 shadow"
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
register={register}
|
||||
className="flex break-all border-none text-xl font-medium outline-none focus:ring-0"
|
||||
placeholder="Title"
|
||||
/>
|
||||
{watch("name") !== "" && (
|
||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
||||
{isSubmitting ? "Creating..." : "Create"}
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</form> */}
|
||||
<div>
|
||||
<Tab.Group
|
||||
defaultIndex={currentTabValue(pageTab)}
|
||||
onChange={(i) => {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return setPageTab("Recent");
|
||||
case 1:
|
||||
return setPageTab("All");
|
||||
case 2:
|
||||
return setPageTab("Favorites");
|
||||
case 3:
|
||||
return setPageTab("Created by me");
|
||||
case 4:
|
||||
return setPageTab("Created by others");
|
||||
<Tab.Group
|
||||
as={Fragment}
|
||||
defaultIndex={currentTabValue(pageTab)}
|
||||
onChange={(i) => {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return setPageTab("Recent");
|
||||
case 1:
|
||||
return setPageTab("All");
|
||||
case 2:
|
||||
return setPageTab("Favorites");
|
||||
case 3:
|
||||
return setPageTab("Created by me");
|
||||
case 4:
|
||||
return setPageTab("Created by others");
|
||||
|
||||
default:
|
||||
return setPageTab("Recent");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Tab.List as="div" className="mb-6 flex items-center justify-between">
|
||||
<div className="flex gap-4">
|
||||
{["Recent", "All", "Favorites", "Created by me", "Created by others"].map(
|
||||
(tab, index) => (
|
||||
<Tab
|
||||
key={index}
|
||||
className={({ selected }) =>
|
||||
`rounded-full border px-5 py-1.5 text-sm outline-none ${
|
||||
selected
|
||||
? "border-brand-accent bg-brand-accent text-white"
|
||||
: "border-brand-base bg-brand-base hover:bg-brand-surface-1"
|
||||
}`
|
||||
}
|
||||
>
|
||||
{tab}
|
||||
</Tab>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-x-1">
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
|
||||
viewType === "list" ? "bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={() => setViewType("list")}
|
||||
default:
|
||||
return setPageTab("Recent");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Tab.List as="div" className="mb-6 flex items-center justify-between">
|
||||
<div className="flex gap-4">
|
||||
{tabsList.map((tab, index) => (
|
||||
<Tab
|
||||
key={`${tab}-${index}`}
|
||||
className={({ selected }) =>
|
||||
`rounded-full border px-5 py-1.5 text-sm outline-none ${
|
||||
selected
|
||||
? "border-brand-accent bg-brand-accent text-white"
|
||||
: "border-brand-base bg-brand-base hover:bg-brand-surface-1"
|
||||
}`
|
||||
}
|
||||
>
|
||||
<ListBulletIcon className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
|
||||
viewType === "detailed" ? "bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={() => setViewType("detailed")}
|
||||
>
|
||||
<Squares2X2Icon className="h-4 w-4" />
|
||||
</button>
|
||||
{/* <button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
|
||||
viewType === "masonry" ? "bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={() => setViewType("masonry")}
|
||||
>
|
||||
<RectangleGroupIcon className="h-4 w-4" />
|
||||
</button> */}
|
||||
</div>
|
||||
</Tab.List>
|
||||
<Tab.Panels>
|
||||
<Tab.Panel>
|
||||
<RecentPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<AllPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<FavoritePagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<MyPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<OtherPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
{tab}
|
||||
</Tab>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-x-1">
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
|
||||
viewType === "list" ? "bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={() => setViewType("list")}
|
||||
>
|
||||
<ListBulletIcon className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-2 ${
|
||||
viewType === "detailed" ? "bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={() => setViewType("detailed")}
|
||||
>
|
||||
<Squares2X2Icon className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</Tab.List>
|
||||
<Tab.Panels as={Fragment}>
|
||||
<Tab.Panel as="div" className="h-full overflow-y-auto space-y-5">
|
||||
<RecentPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel as="div" className="h-full overflow-hidden">
|
||||
<AllPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel as="div" className="h-full overflow-hidden">
|
||||
<FavoritePagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel as="div" className="h-full overflow-hidden">
|
||||
<MyPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel as="div" className="h-full overflow-hidden">
|
||||
<OtherPagesList viewType={viewType} />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
</ProjectAuthorizationWrapper>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user