feat: page improvement (#797)
* feat: remove label icon added * feat: block menu dropdown state added * feat: page info icon added and style: overflow title and label fix
This commit is contained in:
parent
f2e8add29d
commit
d6dbfdc731
5 changed files with 84 additions and 16 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import useSWR, { mutate } from "swr";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
import pagesService from "services/pages.service";
|
import pagesService from "services/pages.service";
|
||||||
|
import projectService from "services/project.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
|
|
@ -23,9 +25,9 @@ import {
|
||||||
ALL_PAGES_LIST,
|
ALL_PAGES_LIST,
|
||||||
FAVORITE_PAGES_LIST,
|
FAVORITE_PAGES_LIST,
|
||||||
MY_PAGES_LIST,
|
MY_PAGES_LIST,
|
||||||
|
PROJECT_MEMBERS,
|
||||||
RECENT_PAGES_LIST,
|
RECENT_PAGES_LIST,
|
||||||
} from "constants/fetch-keys";
|
} from "constants/fetch-keys";
|
||||||
import { mutate } from "swr";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
pages: IPage[] | undefined;
|
pages: IPage[] | undefined;
|
||||||
|
|
@ -44,6 +46,13 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
|
const { data: people } = useSWR(
|
||||||
|
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
||||||
|
workspaceSlug && projectId
|
||||||
|
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
const handleEditPage = (page: IPage) => {
|
const handleEditPage = (page: IPage) => {
|
||||||
setSelectedPageToUpdate(page);
|
setSelectedPageToUpdate(page);
|
||||||
setCreateUpdatePageModal(true);
|
setCreateUpdatePageModal(true);
|
||||||
|
|
@ -198,6 +207,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||||
<SinglePageListItem
|
<SinglePageListItem
|
||||||
key={page.id}
|
key={page.id}
|
||||||
page={page}
|
page={page}
|
||||||
|
people={people}
|
||||||
handleEditPage={() => handleEditPage(page)}
|
handleEditPage={() => handleEditPage(page)}
|
||||||
handleDeletePage={() => handleDeletePage(page)}
|
handleDeletePage={() => handleDeletePage(page)}
|
||||||
handleAddToFavorites={() => handleAddToFavorites(page)}
|
handleAddToFavorites={() => handleAddToFavorites(page)}
|
||||||
|
|
@ -212,6 +222,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||||
<SinglePageDetailedItem
|
<SinglePageDetailedItem
|
||||||
key={page.id}
|
key={page.id}
|
||||||
page={page}
|
page={page}
|
||||||
|
people={people}
|
||||||
handleEditPage={() => handleEditPage(page)}
|
handleEditPage={() => handleEditPage(page)}
|
||||||
handleDeletePage={() => handleDeletePage(page)}
|
handleDeletePage={() => handleDeletePage(page)}
|
||||||
handleAddToFavorites={() => handleAddToFavorites(page)}
|
handleAddToFavorites={() => handleAddToFavorites(page)}
|
||||||
|
|
@ -226,6 +237,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||||
<SinglePageDetailedItem
|
<SinglePageDetailedItem
|
||||||
key={page.id}
|
key={page.id}
|
||||||
page={page}
|
page={page}
|
||||||
|
people={people}
|
||||||
handleEditPage={() => handleEditPage(page)}
|
handleEditPage={() => handleEditPage(page)}
|
||||||
handleDeletePage={() => handleDeletePage(page)}
|
handleDeletePage={() => handleDeletePage(page)}
|
||||||
handleAddToFavorites={() => handleAddToFavorites(page)}
|
handleAddToFavorites={() => handleAddToFavorites(page)}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
@ -16,6 +16,7 @@ import issuesService from "services/issues.service";
|
||||||
import aiService from "services/ai.service";
|
import aiService from "services/ai.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// components
|
// components
|
||||||
import { GptAssistantModal } from "components/core";
|
import { GptAssistantModal } from "components/core";
|
||||||
import { CreateUpdateBlockInline } from "components/pages";
|
import { CreateUpdateBlockInline } from "components/pages";
|
||||||
|
|
@ -61,6 +62,9 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||||
|
|
||||||
const [gptAssistantModal, setGptAssistantModal] = useState(false);
|
const [gptAssistantModal, setGptAssistantModal] = useState(false);
|
||||||
|
|
||||||
|
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||||
|
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, pageId } = router.query;
|
const { workspaceSlug, projectId, pageId } = router.query;
|
||||||
|
|
||||||
|
|
@ -274,6 +278,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||||
reset({ ...block });
|
reset({ ...block });
|
||||||
}, [reset, block]);
|
}, [reset, block]);
|
||||||
|
|
||||||
|
useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false));
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={block.id} index={index} isDragDisabled={createBlockForm}>
|
<Draggable draggableId={block.id} index={index} isDragDisabled={createBlockForm}>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
|
|
@ -308,7 +313,12 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||||
<EllipsisVerticalIcon className="h-[18px]" />
|
<EllipsisVerticalIcon className="h-[18px]" />
|
||||||
<EllipsisVerticalIcon className="h-[18px] -ml-3" />
|
<EllipsisVerticalIcon className="h-[18px] -ml-3" />
|
||||||
</button>
|
</button>
|
||||||
<div className="absolute top-4 right-0 items-center gap-2 hidden group-hover:!flex bg-white pl-4">
|
<div
|
||||||
|
ref={actionSectionRef}
|
||||||
|
className={`absolute top-4 right-0 items-center gap-2 hidden group-hover:!flex bg-white pl-4 ${
|
||||||
|
isMenuActive ? "!flex" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{block.issue && block.sync && (
|
{block.issue && block.sync && (
|
||||||
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded bg-gray-100 py-1 px-1.5 text-xs">
|
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded bg-gray-100 py-1 px-1.5 text-xs">
|
||||||
{isSyncing ? (
|
{isSyncing ? (
|
||||||
|
|
@ -350,7 +360,16 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||||
>
|
>
|
||||||
<PencilIcon className="h-3.5 w-3.5" />
|
<PencilIcon className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<CustomMenu label={<BoltIcon className="h-4.5 w-3.5" />} noBorder noChevron>
|
<CustomMenu
|
||||||
|
customButton={
|
||||||
|
<button
|
||||||
|
className="flex cursor-pointer items-center justify-between gap-1 px-2.5 py-1 text-xs duration-300 hover:bg-gray-100 text-left rounded w-full"
|
||||||
|
onClick={() => setIsMenuActive(!isMenuActive)}
|
||||||
|
>
|
||||||
|
<BoltIcon className="h-4.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
{block.issue ? (
|
{block.issue ? (
|
||||||
<>
|
<>
|
||||||
<CustomMenu.MenuItem onClick={handleBlockSync}>
|
<CustomMenu.MenuItem onClick={handleBlockSync}>
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,16 @@ import {
|
||||||
StarIcon,
|
StarIcon,
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
|
import { ExclamationIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
import { renderShortTime, renderShortDate } from "helpers/date-time.helper";
|
import { renderShortTime, renderShortDate, renderLongDateFormat } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { IPage } from "types";
|
import { IPage, IProjectMember } from "types";
|
||||||
|
|
||||||
type TSingleStatProps = {
|
type TSingleStatProps = {
|
||||||
page: IPage;
|
page: IPage;
|
||||||
|
people: IProjectMember[] | undefined;
|
||||||
handleEditPage: () => void;
|
handleEditPage: () => void;
|
||||||
handleDeletePage: () => void;
|
handleDeletePage: () => void;
|
||||||
handleAddToFavorites: () => void;
|
handleAddToFavorites: () => void;
|
||||||
|
|
@ -32,6 +34,7 @@ type TSingleStatProps = {
|
||||||
|
|
||||||
export const SinglePageDetailedItem: React.FC<TSingleStatProps> = ({
|
export const SinglePageDetailedItem: React.FC<TSingleStatProps> = ({
|
||||||
page,
|
page,
|
||||||
|
people,
|
||||||
handleEditPage,
|
handleEditPage,
|
||||||
handleDeletePage,
|
handleDeletePage,
|
||||||
handleAddToFavorites,
|
handleAddToFavorites,
|
||||||
|
|
@ -132,6 +135,18 @@ export const SinglePageDetailedItem: React.FC<TSingleStatProps> = ({
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
<Tooltip
|
||||||
|
theme="dark"
|
||||||
|
position="top-right"
|
||||||
|
tooltipContent={`Created by ${
|
||||||
|
people?.find((person) => person.member.id === page.created_by)?.member
|
||||||
|
.first_name ?? ""
|
||||||
|
} on ${renderLongDateFormat(`${page.created_at}`)}`}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<ExclamationIcon className="h-4 w-4 text-gray-400" />
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
<CustomMenu verticalEllipsis>
|
<CustomMenu verticalEllipsis>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
onClick={(e: any) => {
|
onClick={(e: any) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
|
@ -9,22 +11,22 @@ import useUser from "hooks/use-user";
|
||||||
import { CustomMenu, Tooltip } from "components/ui";
|
import { CustomMenu, Tooltip } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import {
|
import {
|
||||||
DocumentTextIcon,
|
|
||||||
LockClosedIcon,
|
LockClosedIcon,
|
||||||
LockOpenIcon,
|
LockOpenIcon,
|
||||||
PencilIcon,
|
PencilIcon,
|
||||||
StarIcon,
|
StarIcon,
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
import { PencilScribbleIcon } from "components/icons";
|
import { ExclamationIcon, PencilScribbleIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
import { renderShortDate, renderShortTime } from "helpers/date-time.helper";
|
import { renderLongDateFormat, renderShortDate, renderShortTime } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { IPage } from "types";
|
import { IPage, IProjectMember } from "types";
|
||||||
|
|
||||||
type TSingleStatProps = {
|
type TSingleStatProps = {
|
||||||
page: IPage;
|
page: IPage;
|
||||||
|
people: IProjectMember[] | undefined;
|
||||||
handleEditPage: () => void;
|
handleEditPage: () => void;
|
||||||
handleDeletePage: () => void;
|
handleDeletePage: () => void;
|
||||||
handleAddToFavorites: () => void;
|
handleAddToFavorites: () => void;
|
||||||
|
|
@ -34,6 +36,7 @@ type TSingleStatProps = {
|
||||||
|
|
||||||
export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
||||||
page,
|
page,
|
||||||
|
people,
|
||||||
handleEditPage,
|
handleEditPage,
|
||||||
handleDeletePage,
|
handleDeletePage,
|
||||||
handleAddToFavorites,
|
handleAddToFavorites,
|
||||||
|
|
@ -49,11 +52,11 @@ export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
||||||
<li>
|
<li>
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${page.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${page.id}`}>
|
||||||
<a>
|
<a>
|
||||||
<div className="relative rounded p-4 hover:bg-[#E9ECEF]">
|
<div className="relative rounded p-4 hover:bg-gray-200">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<PencilScribbleIcon className="h-4 w-4" />
|
<PencilScribbleIcon className="h-4 w-4" />
|
||||||
<p className="mr-2 truncate text-base text-[#212529] font-medium">
|
<p className="mr-2 truncate text-base text-gray-800 font-medium">
|
||||||
{truncateText(page.name, 75)}
|
{truncateText(page.name, 75)}
|
||||||
</p>
|
</p>
|
||||||
{page.label_details.length > 0 &&
|
{page.label_details.length > 0 &&
|
||||||
|
|
@ -81,8 +84,9 @@ export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
||||||
<div className="ml-2 flex flex-shrink-0">
|
<div className="ml-2 flex flex-shrink-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
tooltipContent={`Last updated at ${
|
tooltipContent={`Last updated at ${renderShortTime(
|
||||||
renderShortTime(page.updated_at)} on ${renderShortDate(page.updated_at)}`}
|
page.updated_at
|
||||||
|
)} on ${renderShortDate(page.updated_at)}`}
|
||||||
>
|
>
|
||||||
<p className="text-sm text-gray-400">{renderShortTime(page.updated_at)}</p>
|
<p className="text-sm text-gray-400">{renderShortTime(page.updated_at)}</p>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -134,6 +138,18 @@ export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
<Tooltip
|
||||||
|
theme="dark"
|
||||||
|
position="top-right"
|
||||||
|
tooltipContent={`Created by ${
|
||||||
|
people?.find((person) => person.member.id === page.created_by)?.member
|
||||||
|
.first_name ?? ""
|
||||||
|
} on ${renderLongDateFormat(`${page.created_at}`)}`}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<ExclamationIcon className="h-4 w-4 text-gray-400" />
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<CustomMenu width="auto" verticalEllipsis>
|
<CustomMenu width="auto" verticalEllipsis>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import {
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
StarIcon,
|
StarIcon,
|
||||||
LinkIcon,
|
LinkIcon,
|
||||||
|
XMarkIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
import { ColorPalletteIcon, ClipboardIcon } from "components/icons";
|
import { ColorPalletteIcon, ClipboardIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
|
|
@ -330,7 +331,11 @@ const SinglePage: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={label.id}
|
key={label.id}
|
||||||
className="group flex items-center gap-1 rounded-2xl border px-2 py-0.5 text-xs"
|
className="group flex items-center gap-1 cursor-pointer rounded-2xl border px-2 py-0.5 text-xs hover:border-red-500 hover:bg-red-50"
|
||||||
|
onClick={() => {
|
||||||
|
const updatedLabels = pageDetails.labels.filter((l) => l !== labelId);
|
||||||
|
partialUpdatePage({ labels_list: updatedLabels });
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: `${
|
backgroundColor: `${
|
||||||
label?.color && label.color !== "" ? label.color : "#000000"
|
label?.color && label.color !== "" ? label.color : "#000000"
|
||||||
|
|
@ -345,6 +350,7 @@ const SinglePage: NextPage = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{label.name}
|
{label.name}
|
||||||
|
<XMarkIcon className="h-2.5 w-2.5 group-hover:text-red-500" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue