chore: minor fixes on pages (#557)
* feat: block sync * chore: minor fixes on pages * fix: remove dangerously set inner html * fix: pages crud operations mutation * fix: favorites mutation for recent pages * fix: remove dangerously set inner html
This commit is contained in:
parent
c0a471e916
commit
b654d30aeb
15 changed files with 139 additions and 70 deletions
|
|
@ -45,12 +45,20 @@ export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, da
|
|||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
mutate<IPage[]>(
|
||||
MY_PAGES_LIST(projectId as string),
|
||||
(prevData) => [res, ...(prevData as IPage[])],
|
||||
(prevData) => {
|
||||
if (!prevData) return undefined;
|
||||
|
||||
return [res, ...(prevData as IPage[])];
|
||||
},
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(
|
||||
ALL_PAGES_LIST(projectId as string),
|
||||
(prevData) => [res, ...(prevData as IPage[])],
|
||||
(prevData) => {
|
||||
if (!prevData) return undefined;
|
||||
|
||||
return [res, ...(prevData as IPage[])];
|
||||
},
|
||||
false
|
||||
);
|
||||
onClose();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React, { useState } from "react";
|
|||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { mutate } from "swr";
|
||||
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
|
|
@ -14,6 +16,13 @@ import { DangerButton, SecondaryButton } from "components/ui";
|
|||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import type { IPage } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
ALL_PAGES_LIST,
|
||||
FAVORITE_PAGES_LIST,
|
||||
MY_PAGES_LIST,
|
||||
RECENT_PAGES_LIST,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
type TConfirmPageDeletionProps = {
|
||||
isOpen: boolean;
|
||||
|
|
@ -45,6 +54,22 @@ export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = ({
|
|||
await pagesService
|
||||
.deletePage(workspaceSlug as string, data.project, data.id)
|
||||
.then(() => {
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
mutate<IPage[]>(
|
||||
MY_PAGES_LIST(projectId as string),
|
||||
(prevData) => (prevData ?? []).filter((page) => page.id !== data?.id),
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(
|
||||
ALL_PAGES_LIST(projectId as string),
|
||||
(prevData) => (prevData ?? []).filter((page) => page.id !== data?.id),
|
||||
false
|
||||
);
|
||||
mutate<IPage[]>(
|
||||
FAVORITE_PAGES_LIST(projectId as string),
|
||||
(prevData) => (prevData ?? []).filter((page) => page.id !== data?.id),
|
||||
false
|
||||
);
|
||||
handleClose();
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
|||
const handleAddToFavorites = (page: IPage) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
mutate<IPage[]>(
|
||||
ALL_PAGES_LIST(projectId as string),
|
||||
(prevData) =>
|
||||
|
|
@ -89,6 +88,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
|||
page: page.id,
|
||||
})
|
||||
.then(() => {
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
|
|
@ -107,7 +107,6 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
|||
const handleRemoveFromFavorites = (page: IPage) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
mutate<IPage[]>(
|
||||
ALL_PAGES_LIST(projectId as string),
|
||||
(prevData) =>
|
||||
|
|
@ -137,6 +136,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
|||
pagesService
|
||||
.removePageFromFavorites(workspaceSlug as string, projectId as string, page.id)
|
||||
.then(() => {
|
||||
mutate(RECENT_PAGES_LIST(projectId as string));
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
|
|
|
|||
|
|
@ -17,11 +17,16 @@ import useToast from "hooks/use-toast";
|
|||
import { CreateUpdateIssueModal } from "components/issues";
|
||||
import { GptAssistantModal } from "components/core";
|
||||
// ui
|
||||
import { CustomMenu, Loader, TextArea } from "components/ui";
|
||||
import { CustomMenu, Input, Loader, TextArea } from "components/ui";
|
||||
// icons
|
||||
import { LayerDiagonalIcon, WaterDropIcon } from "components/icons";
|
||||
import { LayerDiagonalIcon } from "components/icons";
|
||||
import { ArrowPathIcon } from "@heroicons/react/20/solid";
|
||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
BoltIcon,
|
||||
CheckIcon,
|
||||
CursorArrowRaysIcon,
|
||||
SparklesIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
// helpers
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
// types
|
||||
|
|
@ -163,21 +168,8 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
const handleAiAssistance = async (response: string) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
setValue("description", {
|
||||
type: "doc",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [
|
||||
{
|
||||
text: response,
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
setValue("description_html", `<p>${response}</p>`);
|
||||
setValue("description", {});
|
||||
setValue("description_html", `${watch("description_html")}<p>${response}</p>`);
|
||||
handleSubmit(updatePageBlock)()
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
|
|
@ -253,7 +245,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
}}
|
||||
/>
|
||||
<div className="-mx-3 -mt-2 flex items-center justify-between gap-2">
|
||||
<TextArea
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Block title"
|
||||
|
|
@ -261,11 +253,11 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
onBlur={handleSubmit(updatePageBlock)}
|
||||
onChange={(e) => setValue("name", e.target.value)}
|
||||
required={true}
|
||||
className="min-h-10 block w-full resize-none overflow-hidden border-none bg-transparent text-base font-medium"
|
||||
className="min-h-10 block w-full resize-none overflow-hidden border-none bg-transparent py-1 text-base font-medium ring-0 focus:ring-1 focus:ring-gray-200"
|
||||
role="textbox"
|
||||
/>
|
||||
<div className="flex flex-shrink-0 items-center gap-2">
|
||||
{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">
|
||||
{isSyncing ? (
|
||||
<ArrowPathIcon className="h-3 w-3 animate-spin" />
|
||||
|
|
@ -285,12 +277,13 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="-mr-2 rounded px-1.5 py-1 text-xs hover:bg-gray-100"
|
||||
className="-mr-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-gray-100"
|
||||
onClick={() => setGptAssistantModal((prevData) => !prevData)}
|
||||
>
|
||||
<SparklesIcon className="h-4 w-4" />
|
||||
AI
|
||||
</button>
|
||||
<CustomMenu label={<WaterDropIcon width={14} height={15} />} noBorder noChevron>
|
||||
<CustomMenu label={<BoltIcon className="h-4.5 w-3.5" />} noBorder noChevron>
|
||||
{block.issue ? (
|
||||
<>
|
||||
<CustomMenu.MenuItem onClick={handleBlockSync}>
|
||||
|
|
@ -312,7 +305,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="page-block-section relative -mx-3 -mt-5">
|
||||
<div className="page-block-section font relative -mx-3 -mt-3">
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
|
|
@ -327,8 +320,9 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
onJSONChange={(jsonValue) => setValue("description", jsonValue)}
|
||||
onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)}
|
||||
placeholder="Block description..."
|
||||
customClassName="text-gray-500"
|
||||
customClassName="border border-transparent"
|
||||
noBorder
|
||||
borderOnFocus
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -337,6 +331,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
|
|||
handleClose={() => setGptAssistantModal(false)}
|
||||
inset="top-2 left-0"
|
||||
content={block.description_stripped}
|
||||
htmlContent={block.description_html}
|
||||
onResponse={handleAiAssistance}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ import { useRouter } from "next/router";
|
|||
// ui
|
||||
import { CustomMenu, Tooltip } from "components/ui";
|
||||
// icons
|
||||
import { PencilIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { DocumentTextIcon, PencilIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
// helpers
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
import { renderShortDate, renderShortTime } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IPage } from "types";
|
||||
import { PencilScribbleIcon } from "components/icons";
|
||||
|
||||
type TSingleStatProps = {
|
||||
page: IPage;
|
||||
|
|
@ -39,7 +38,7 @@ export const SinglePageListItem: React.FC<TSingleStatProps> = ({
|
|||
<div className="relative rounded p-4 hover:bg-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<PencilScribbleIcon />
|
||||
<DocumentTextIcon className="h-4 w-4" />
|
||||
<p className="mr-2 truncate text-sm font-medium">{truncateText(page.name, 75)}</p>
|
||||
{page.label_details.length > 0 &&
|
||||
page.label_details.map((label) => (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue