forked from github/plane
chore: peek overview authorization (#2632)
* chore: peek overview authorization * chore: comment access specifier validation
This commit is contained in:
parent
d48f13416f
commit
992cf79031
@ -1,7 +1,37 @@
|
|||||||
import { BoldIcon, Heading1, CheckSquare, Heading2, Heading3, QuoteIcon, ImageIcon, TableIcon, ListIcon, ListOrderedIcon, ItalicIcon, UnderlineIcon, StrikethroughIcon, CodeIcon } from "lucide-react";
|
import {
|
||||||
|
BoldIcon,
|
||||||
|
Heading1,
|
||||||
|
CheckSquare,
|
||||||
|
Heading2,
|
||||||
|
Heading3,
|
||||||
|
QuoteIcon,
|
||||||
|
ImageIcon,
|
||||||
|
TableIcon,
|
||||||
|
ListIcon,
|
||||||
|
ListOrderedIcon,
|
||||||
|
ItalicIcon,
|
||||||
|
UnderlineIcon,
|
||||||
|
StrikethroughIcon,
|
||||||
|
CodeIcon,
|
||||||
|
} from "lucide-react";
|
||||||
import { Editor } from "@tiptap/react";
|
import { Editor } from "@tiptap/react";
|
||||||
import { UploadImage } from "../../../types/upload-image";
|
import { UploadImage } from "../../../types/upload-image";
|
||||||
import { insertImageCommand, insertTableCommand, toggleBlockquote, toggleBold, toggleBulletList, toggleCode, toggleHeadingOne, toggleHeadingThree, toggleHeadingTwo, toggleItalic, toggleOrderedList, toggleStrike, toggleTaskList, toggleUnderline, } from "../../../lib/editor-commands";
|
import {
|
||||||
|
insertImageCommand,
|
||||||
|
insertTableCommand,
|
||||||
|
toggleBlockquote,
|
||||||
|
toggleBold,
|
||||||
|
toggleBulletList,
|
||||||
|
toggleCode,
|
||||||
|
toggleHeadingOne,
|
||||||
|
toggleHeadingThree,
|
||||||
|
toggleHeadingTwo,
|
||||||
|
toggleItalic,
|
||||||
|
toggleOrderedList,
|
||||||
|
toggleStrike,
|
||||||
|
toggleTaskList,
|
||||||
|
toggleUnderline,
|
||||||
|
} from "../../../lib/editor-commands";
|
||||||
|
|
||||||
export interface EditorMenuItem {
|
export interface EditorMenuItem {
|
||||||
name: string;
|
name: string;
|
||||||
@ -15,95 +45,101 @@ export const HeadingOneItem = (editor: Editor): EditorMenuItem => ({
|
|||||||
isActive: () => editor.isActive("heading", { level: 1 }),
|
isActive: () => editor.isActive("heading", { level: 1 }),
|
||||||
command: () => toggleHeadingOne(editor),
|
command: () => toggleHeadingOne(editor),
|
||||||
icon: Heading1,
|
icon: Heading1,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const HeadingTwoItem = (editor: Editor): EditorMenuItem => ({
|
export const HeadingTwoItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "H2",
|
name: "H2",
|
||||||
isActive: () => editor.isActive("heading", { level: 2 }),
|
isActive: () => editor.isActive("heading", { level: 2 }),
|
||||||
command: () => toggleHeadingTwo(editor),
|
command: () => toggleHeadingTwo(editor),
|
||||||
icon: Heading2,
|
icon: Heading2,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const HeadingThreeItem = (editor: Editor): EditorMenuItem => ({
|
export const HeadingThreeItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "H3",
|
name: "H3",
|
||||||
isActive: () => editor.isActive("heading", { level: 3 }),
|
isActive: () => editor.isActive("heading", { level: 3 }),
|
||||||
command: () => toggleHeadingThree(editor),
|
command: () => toggleHeadingThree(editor),
|
||||||
icon: Heading3,
|
icon: Heading3,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const BoldItem = (editor: Editor): EditorMenuItem => ({
|
export const BoldItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "bold",
|
name: "bold",
|
||||||
isActive: () => editor?.isActive("bold"),
|
isActive: () => editor?.isActive("bold"),
|
||||||
command: () => toggleBold(editor),
|
command: () => toggleBold(editor),
|
||||||
icon: BoldIcon,
|
icon: BoldIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const ItalicItem = (editor: Editor): EditorMenuItem => ({
|
export const ItalicItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "italic",
|
name: "italic",
|
||||||
isActive: () => editor?.isActive("italic"),
|
isActive: () => editor?.isActive("italic"),
|
||||||
command: () => toggleItalic(editor),
|
command: () => toggleItalic(editor),
|
||||||
icon: ItalicIcon,
|
icon: ItalicIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const UnderLineItem = (editor: Editor): EditorMenuItem => ({
|
export const UnderLineItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "underline",
|
name: "underline",
|
||||||
isActive: () => editor?.isActive("underline"),
|
isActive: () => editor?.isActive("underline"),
|
||||||
command: () => toggleUnderline(editor),
|
command: () => toggleUnderline(editor),
|
||||||
icon: UnderlineIcon,
|
icon: UnderlineIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const StrikeThroughItem = (editor: Editor): EditorMenuItem => ({
|
export const StrikeThroughItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "strike",
|
name: "strike",
|
||||||
isActive: () => editor?.isActive("strike"),
|
isActive: () => editor?.isActive("strike"),
|
||||||
command: () => toggleStrike(editor),
|
command: () => toggleStrike(editor),
|
||||||
icon: StrikethroughIcon,
|
icon: StrikethroughIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const CodeItem = (editor: Editor): EditorMenuItem => ({
|
export const CodeItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "code",
|
name: "code",
|
||||||
isActive: () => editor?.isActive("code"),
|
isActive: () => editor?.isActive("code"),
|
||||||
command: () => toggleCode(editor),
|
command: () => toggleCode(editor),
|
||||||
icon: CodeIcon,
|
icon: CodeIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const BulletListItem = (editor: Editor): EditorMenuItem => ({
|
export const BulletListItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "bullet-list",
|
name: "bullet-list",
|
||||||
isActive: () => editor?.isActive("bulletList"),
|
isActive: () => editor?.isActive("bulletList"),
|
||||||
command: () => toggleBulletList(editor),
|
command: () => toggleBulletList(editor),
|
||||||
icon: ListIcon,
|
icon: ListIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const TodoListItem = (editor: Editor): EditorMenuItem => ({
|
export const TodoListItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "To-do List",
|
name: "To-do List",
|
||||||
isActive: () => editor.isActive("taskItem"),
|
isActive: () => editor.isActive("taskItem"),
|
||||||
command: () => toggleTaskList(editor),
|
command: () => toggleTaskList(editor),
|
||||||
icon: CheckSquare,
|
icon: CheckSquare,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const NumberedListItem = (editor: Editor): EditorMenuItem => ({
|
export const NumberedListItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "ordered-list",
|
name: "ordered-list",
|
||||||
isActive: () => editor?.isActive("orderedList"),
|
isActive: () => editor?.isActive("orderedList"),
|
||||||
command: () => toggleOrderedList(editor),
|
command: () => toggleOrderedList(editor),
|
||||||
icon: ListOrderedIcon
|
icon: ListOrderedIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const QuoteItem = (editor: Editor): EditorMenuItem => ({
|
export const QuoteItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "quote",
|
name: "quote",
|
||||||
isActive: () => editor?.isActive("quote"),
|
isActive: () => editor?.isActive("quote"),
|
||||||
command: () => toggleBlockquote(editor),
|
command: () => toggleBlockquote(editor),
|
||||||
icon: QuoteIcon
|
icon: QuoteIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const TableItem = (editor: Editor): EditorMenuItem => ({
|
export const TableItem = (editor: Editor): EditorMenuItem => ({
|
||||||
name: "quote",
|
name: "table",
|
||||||
isActive: () => editor?.isActive("table"),
|
isActive: () => editor?.isActive("table"),
|
||||||
command: () => insertTableCommand(editor),
|
command: () => insertTableCommand(editor),
|
||||||
icon: TableIcon
|
icon: TableIcon,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const ImageItem = (editor: Editor, uploadFile: UploadImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void): EditorMenuItem => ({
|
export const ImageItem = (
|
||||||
|
editor: Editor,
|
||||||
|
uploadFile: UploadImage,
|
||||||
|
setIsSubmitting?: (
|
||||||
|
isSubmitting: "submitting" | "submitted" | "saved",
|
||||||
|
) => void,
|
||||||
|
): EditorMenuItem => ({
|
||||||
name: "image",
|
name: "image",
|
||||||
isActive: () => editor?.isActive("image"),
|
isActive: () => editor?.isActive("image"),
|
||||||
command: () => insertImageCommand(editor, uploadFile, setIsSubmitting),
|
command: () => insertImageCommand(editor, uploadFile, setIsSubmitting),
|
||||||
icon: ImageIcon,
|
icon: ImageIcon,
|
||||||
})
|
});
|
||||||
|
@ -72,12 +72,10 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
|
|||||||
props.commentAccessSpecifier?.onAccessChange(accessKey);
|
props.commentAccessSpecifier?.onAccessChange(accessKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(complexItems);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-stretch gap-1.5 w-full h-9">
|
<div className="flex items-stretch gap-1.5 w-full h-9 overflow-x-scroll">
|
||||||
{props.commentAccessSpecifier && (
|
{props.commentAccessSpecifier && (
|
||||||
<div className="flex-shrink-0 flex items-stretch gap-0.5 border border-custom-border-200 rounded p-1">
|
<div className="flex-shrink-0 flex items-stretch gap-0.5 border-[0.5px] border-custom-border-200 rounded p-1">
|
||||||
{props?.commentAccessSpecifier.commentAccess?.map((access) => (
|
{props?.commentAccessSpecifier.commentAccess?.map((access) => (
|
||||||
<Tooltip key={access.key} tooltipContent={access.label}>
|
<Tooltip key={access.key} tooltipContent={access.label}>
|
||||||
<button
|
<button
|
||||||
@ -102,102 +100,118 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-stretch justify-between gap-2 w-full border border-custom-border-200 bg-custom-background-90 rounded p-1">
|
<div className="flex items-stretch justify-between gap-2 w-full border-[0.5px] border-custom-border-200 bg-custom-background-90 rounded p-1">
|
||||||
<div className="flex items-stretch">
|
<div className="flex items-stretch">
|
||||||
<div className="flex items-stretch gap-0.5 pr-2.5 border-r border-custom-border-200">
|
<div className="flex items-stretch gap-0.5 pr-2.5 border-r border-custom-border-200">
|
||||||
{basicMarkItems.map((item, index) => (
|
{basicMarkItems.map((item, index) => (
|
||||||
<button
|
<Tooltip
|
||||||
key={index}
|
key={index}
|
||||||
type="button"
|
tooltipContent={<span className="capitalize">{item.name}</span>}
|
||||||
onClick={item.command}
|
|
||||||
className={cn(
|
|
||||||
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
|
||||||
{
|
|
||||||
"text-custom-text-100 bg-custom-background-80":
|
|
||||||
item.isActive(),
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<item.icon
|
<button
|
||||||
className={cn("h-3.5 w-3.5", {
|
type="button"
|
||||||
"text-custom-text-100": item.isActive(),
|
onClick={item.command}
|
||||||
})}
|
className={cn(
|
||||||
strokeWidth={2.5}
|
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
||||||
/>
|
{
|
||||||
</button>
|
"text-custom-text-100 bg-custom-background-80":
|
||||||
|
item.isActive(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={cn("h-3.5 w-3.5", {
|
||||||
|
"text-custom-text-100": item.isActive(),
|
||||||
|
})}
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-stretch gap-0.5 px-2.5 border-r border-custom-border-200">
|
<div className="flex items-stretch gap-0.5 px-2.5 border-r border-custom-border-200">
|
||||||
{listItems.map((item, index) => (
|
{listItems.map((item, index) => (
|
||||||
<button
|
<Tooltip
|
||||||
key={index}
|
key={index}
|
||||||
type="button"
|
tooltipContent={<span className="capitalize">{item.name}</span>}
|
||||||
onClick={item.command}
|
|
||||||
className={cn(
|
|
||||||
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
|
||||||
{
|
|
||||||
"text-custom-text-100 bg-custom-background-80":
|
|
||||||
item.isActive(),
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<item.icon
|
<button
|
||||||
className={cn("h-3.5 w-3.5", {
|
type="button"
|
||||||
"text-custom-text-100": item.isActive(),
|
onClick={item.command}
|
||||||
})}
|
className={cn(
|
||||||
strokeWidth={2.5}
|
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
||||||
/>
|
{
|
||||||
</button>
|
"text-custom-text-100 bg-custom-background-80":
|
||||||
|
item.isActive(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={cn("h-3.5 w-3.5", {
|
||||||
|
"text-custom-text-100": item.isActive(),
|
||||||
|
})}
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-stretch gap-0.5 px-2.5 border-r border-custom-border-200">
|
<div className="flex items-stretch gap-0.5 px-2.5 border-r border-custom-border-200">
|
||||||
{userActionItems.map((item, index) => (
|
{userActionItems.map((item, index) => (
|
||||||
<button
|
<Tooltip
|
||||||
key={index}
|
key={index}
|
||||||
type="button"
|
tooltipContent={<span className="capitalize">{item.name}</span>}
|
||||||
onClick={item.command}
|
|
||||||
className={cn(
|
|
||||||
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
|
||||||
{
|
|
||||||
"text-custom-text-100 bg-custom-background-80":
|
|
||||||
item.isActive(),
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<item.icon
|
<button
|
||||||
className={cn("h-3.5 w-3.5", {
|
type="button"
|
||||||
"text-custom-text-100": item.isActive(),
|
onClick={item.command}
|
||||||
})}
|
className={cn(
|
||||||
strokeWidth={2.5}
|
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
||||||
/>
|
{
|
||||||
</button>
|
"text-custom-text-100 bg-custom-background-80":
|
||||||
|
item.isActive(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={cn("h-3.5 w-3.5", {
|
||||||
|
"text-custom-text-100": item.isActive(),
|
||||||
|
})}
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-stretch gap-0.5 pl-2.5">
|
<div className="flex items-stretch gap-0.5 pl-2.5">
|
||||||
{complexItems.map((item, index) => (
|
{complexItems.map((item, index) => (
|
||||||
<button
|
<Tooltip
|
||||||
key={index}
|
key={index}
|
||||||
type="button"
|
tooltipContent={<span className="capitalize">{item.name}</span>}
|
||||||
onClick={item.command}
|
|
||||||
className={cn(
|
|
||||||
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
|
||||||
{
|
|
||||||
"text-custom-text-100 bg-custom-background-80":
|
|
||||||
item.isActive(),
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<item.icon
|
<button
|
||||||
className={cn("h-3.5 w-3.5", {
|
type="button"
|
||||||
"text-custom-text-100": item.isActive(),
|
onClick={item.command}
|
||||||
})}
|
className={cn(
|
||||||
strokeWidth={2.5}
|
"p-1 aspect-square text-custom-text-400 hover:bg-custom-background-80 rounded-sm grid place-items-center",
|
||||||
/>
|
{
|
||||||
</button>
|
"text-custom-text-100 bg-custom-background-80":
|
||||||
|
item.isActive(),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={cn("h-3.5 w-3.5", {
|
||||||
|
"text-custom-text-100": item.isActive(),
|
||||||
|
})}
|
||||||
|
strokeWidth={2.5}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{props.submitButton}
|
<div className="sticky right-1">{props.submitButton}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -52,7 +52,7 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit, showAc
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined)
|
const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@ -74,33 +74,35 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit, showAc
|
|||||||
<div>
|
<div>
|
||||||
<form onSubmit={handleSubmit(handleAddComment)}>
|
<form onSubmit={handleSubmit(handleAddComment)}>
|
||||||
<div>
|
<div>
|
||||||
<div className="relative">
|
<Controller
|
||||||
<Controller
|
name="access"
|
||||||
name="access"
|
control={control}
|
||||||
control={control}
|
render={({ field: { onChange: onAccessChange, value: accessValue } }) => (
|
||||||
render={({ field: { onChange: onAccessChange, value: accessValue } }) => (
|
<Controller
|
||||||
<Controller
|
name="comment_html"
|
||||||
name="comment_html"
|
control={control}
|
||||||
control={control}
|
render={({ field: { onChange: onCommentChange, value: commentValue } }) => (
|
||||||
render={({ field: { onChange: onCommentChange, value: commentValue } }) => (
|
<LiteTextEditorWithRef
|
||||||
<LiteTextEditorWithRef
|
onEnterKeyPress={handleSubmit(handleAddComment)}
|
||||||
onEnterKeyPress={handleSubmit(handleAddComment)}
|
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
deleteFile={fileService.deleteImage}
|
||||||
deleteFile={fileService.deleteImage}
|
ref={editorRef}
|
||||||
ref={editorRef}
|
value={!commentValue || commentValue === "" ? "<p></p>" : commentValue}
|
||||||
value={!commentValue || commentValue === "" ? "<p></p>" : commentValue}
|
customClassName="p-3 min-h-[100px] shadow-sm"
|
||||||
customClassName="p-3 min-h-[100px] shadow-sm"
|
debouncedUpdatesEnabled={false}
|
||||||
debouncedUpdatesEnabled={false}
|
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
||||||
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
commentAccessSpecifier={
|
||||||
commentAccessSpecifier={{ accessValue, onAccessChange, showAccessSpecifier, commentAccess }}
|
showAccessSpecifier
|
||||||
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
? { accessValue, onAccessChange, showAccessSpecifier, commentAccess }
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
: undefined
|
||||||
/>
|
}
|
||||||
)}
|
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
||||||
/>
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
)}
|
/>
|
||||||
/>
|
)}
|
||||||
</div>
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button variant="neutral-primary" type="submit" disabled={isSubmitting || disabled}>
|
<Button variant="neutral-primary" type="submit" disabled={isSubmitting || disabled}>
|
||||||
{isSubmitting ? "Adding..." : "Comment"}
|
{isSubmitting ? "Adding..." : "Comment"}
|
||||||
|
@ -96,6 +96,7 @@ export const KanBanProperties: FC<IKanBanProperties> = observer((props) => {
|
|||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
onChange={handleAssignee}
|
onChange={handleAssignee}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
|
multiple
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
import { Globe2, Lock } from "lucide-react";
|
||||||
// services
|
// services
|
||||||
import { FileService } from "services/file.service";
|
import { FileService } from "services/file.service";
|
||||||
|
// hooks
|
||||||
|
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||||
// components
|
// components
|
||||||
import { LiteTextEditorWithRef } from "@plane/lite-text-editor";
|
import { LiteTextEditorWithRef } from "@plane/lite-text-editor";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Tooltip } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
import { Globe2, Lock } from "lucide-react";
|
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueComment } from "types";
|
||||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueComment> = {
|
const defaultValues: Partial<IIssueComment> = {
|
||||||
access: "INTERNAL",
|
access: "INTERNAL",
|
||||||
@ -75,36 +74,7 @@ export const IssueCommentEditor: React.FC<IIssueCommentEditor> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(handleAddComment)}>
|
<form onSubmit={handleSubmit(handleAddComment)}>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="relative h-full">
|
<div className="h-full">
|
||||||
{showAccessSpecifier && (
|
|
||||||
<div className="absolute bottom-2 left-3 z-[1]">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="access"
|
|
||||||
render={({ field: { onChange, value } }) => (
|
|
||||||
<div className="flex border border-custom-border-300 divide-x divide-custom-border-300 rounded overflow-hidden">
|
|
||||||
{commentAccess.map((access) => (
|
|
||||||
<Tooltip key={access.key} tooltipContent={access.label}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onChange(access.key)}
|
|
||||||
className={`grid place-items-center p-1 hover:bg-custom-background-80 ${
|
|
||||||
value === access.key ? "bg-custom-background-80" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<access.icon
|
|
||||||
className={`w-4 h-4 -mt-1 ${
|
|
||||||
value === access.key ? "!text-custom-text-100" : "!text-custom-text-400"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Controller
|
<Controller
|
||||||
name="access"
|
name="access"
|
||||||
control={control}
|
control={control}
|
||||||
@ -124,7 +94,11 @@ export const IssueCommentEditor: React.FC<IIssueCommentEditor> = (props) => {
|
|||||||
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
||||||
commentAccessSpecifier={{ accessValue, onAccessChange, showAccessSpecifier, commentAccess }}
|
commentAccessSpecifier={
|
||||||
|
showAccessSpecifier
|
||||||
|
? { accessValue, onAccessChange, showAccessSpecifier, commentAccess }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
submitButton={
|
submitButton={
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
@ -14,6 +14,7 @@ interface IIssueComment {
|
|||||||
issueCommentRemove: (commentId: string) => void;
|
issueCommentRemove: (commentId: string) => void;
|
||||||
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
||||||
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
||||||
|
showCommentAccessSpecifier: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IssueComment: FC<IIssueComment> = (props) => {
|
export const IssueComment: FC<IIssueComment> = (props) => {
|
||||||
@ -28,6 +29,7 @@ export const IssueComment: FC<IIssueComment> = (props) => {
|
|||||||
issueCommentRemove,
|
issueCommentRemove,
|
||||||
issueCommentReactionCreate,
|
issueCommentReactionCreate,
|
||||||
issueCommentReactionRemove,
|
issueCommentReactionRemove,
|
||||||
|
showCommentAccessSpecifier,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const handleAddComment = async (formData: any) => {
|
const handleAddComment = async (formData: any) => {
|
||||||
@ -40,10 +42,7 @@ export const IssueComment: FC<IIssueComment> = (props) => {
|
|||||||
<div className="font-medium text-lg">Activity</div>
|
<div className="font-medium text-lg">Activity</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<IssueCommentEditor
|
<IssueCommentEditor onSubmit={handleAddComment} showAccessSpecifier={showCommentAccessSpecifier} />
|
||||||
onSubmit={handleAddComment}
|
|
||||||
// showAccessSpecifier={projectDetails && projectDetails.is_deployed}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<IssueActivityCard
|
<IssueActivityCard
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
|
@ -32,13 +32,14 @@ import { IssueService } from "services/issue";
|
|||||||
interface IPeekOverviewProperties {
|
interface IPeekOverviewProperties {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
issueUpdate: (issue: Partial<IIssue>) => void;
|
issueUpdate: (issue: Partial<IIssue>) => void;
|
||||||
user: any;
|
disableUserActions: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const issueService = new IssueService();
|
const issueService = new IssueService();
|
||||||
|
|
||||||
export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((props) => {
|
export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((props) => {
|
||||||
const { issue, issueUpdate, user } = props;
|
const { issue, issueUpdate, disableUserActions } = props;
|
||||||
|
// states
|
||||||
const [linkModal, setLinkModal] = useState(false);
|
const [linkModal, setLinkModal] = useState(false);
|
||||||
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
|
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
|
||||||
|
|
||||||
@ -172,8 +173,6 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
|
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
|
||||||
maxDate?.setDate(maxDate.getDate());
|
maxDate?.setDate(maxDate.getDate());
|
||||||
|
|
||||||
const isNotAllowed = user?.memberRole?.isGuest || user?.memberRole?.isViewer;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LinkModal
|
<LinkModal
|
||||||
@ -196,7 +195,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>State</p>
|
<p>State</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarStateSelect value={issue?.state || ""} onChange={handleState} disabled={isNotAllowed} />
|
<SidebarStateSelect value={issue?.state || ""} onChange={handleState} disabled={disableUserActions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -207,7 +206,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Assignees</p>
|
<p>Assignees</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarAssigneeSelect value={issue.assignees || []} onChange={handleAssignee} disabled={isNotAllowed} />
|
<SidebarAssigneeSelect
|
||||||
|
value={issue.assignees || []}
|
||||||
|
onChange={handleAssignee}
|
||||||
|
disabled={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -218,7 +221,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Priority</p>
|
<p>Priority</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarPrioritySelect value={issue.priority || ""} onChange={handlePriority} disabled={isNotAllowed} />
|
<SidebarPrioritySelect
|
||||||
|
value={issue.priority || ""}
|
||||||
|
onChange={handlePriority}
|
||||||
|
disabled={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -229,7 +236,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Estimate</p>
|
<p>Estimate</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarEstimateSelect value={issue.estimate_point} onChange={handleEstimate} disabled={isNotAllowed} />
|
<SidebarEstimateSelect
|
||||||
|
value={issue.estimate_point}
|
||||||
|
onChange={handleEstimate}
|
||||||
|
disabled={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -246,7 +257,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
onChange={handleStartDate}
|
onChange={handleStartDate}
|
||||||
className="bg-custom-background-80 border-none !px-2.5 !py-0.5"
|
className="bg-custom-background-80 border-none !px-2.5 !py-0.5"
|
||||||
maxDate={maxDate ?? undefined}
|
maxDate={maxDate ?? undefined}
|
||||||
disabled={isNotAllowed}
|
disabled={disableUserActions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -264,7 +275,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
onChange={handleTargetDate}
|
onChange={handleTargetDate}
|
||||||
className="bg-custom-background-80 border-none !px-2.5 !py-0.5"
|
className="bg-custom-background-80 border-none !px-2.5 !py-0.5"
|
||||||
minDate={minDate ?? undefined}
|
minDate={minDate ?? undefined}
|
||||||
disabled={isNotAllowed}
|
disabled={disableUserActions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -276,7 +287,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Parent</p>
|
<p>Parent</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarParentSelect onChange={handleParent} issueDetails={issue} disabled={isNotAllowed} />
|
<SidebarParentSelect onChange={handleParent} issueDetails={issue} disabled={disableUserActions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -290,7 +301,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Cycle</p>
|
<p>Cycle</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarCycleSelect issueDetail={issue} handleCycleChange={addIssueToCycle} disabled={isNotAllowed} />
|
<SidebarCycleSelect
|
||||||
|
issueDetail={issue}
|
||||||
|
handleCycleChange={addIssueToCycle}
|
||||||
|
disabled={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -300,7 +315,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Module</p>
|
<p>Module</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<SidebarModuleSelect issueDetail={issue} handleModuleChange={addIssueToModule} disabled={isNotAllowed} />
|
<SidebarModuleSelect
|
||||||
|
issueDetail={issue}
|
||||||
|
handleModuleChange={addIssueToModule}
|
||||||
|
disabled={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2 w-full">
|
<div className="flex items-start gap-2 w-full">
|
||||||
@ -313,8 +332,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
issueDetails={issue}
|
issueDetails={issue}
|
||||||
labelList={issue.labels}
|
labelList={issue.labels}
|
||||||
submitChanges={handleLabels}
|
submitChanges={handleLabels}
|
||||||
isNotAllowed={isNotAllowed}
|
isNotAllowed={disableUserActions}
|
||||||
uneditable={isNotAllowed}
|
uneditable={disableUserActions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -330,11 +349,11 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
<p>Links</p>
|
<p>Links</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{!isNotAllowed && (
|
{!disableUserActions && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex ${
|
className={`flex ${
|
||||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-90"
|
disableUserActions ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-90"
|
||||||
} items-center gap-1 rounded-2xl border border-custom-border-100 px-2 py-0.5 text-xs hover:text-custom-text-200 text-custom-text-300`}
|
} items-center gap-1 rounded-2xl border border-custom-border-100 px-2 py-0.5 text-xs hover:text-custom-text-200 text-custom-text-300`}
|
||||||
onClick={() => setLinkModal(true)}
|
onClick={() => setLinkModal(true)}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
|
@ -6,7 +6,6 @@ import { IssueView } from "./view";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { RootStore } from "store/root";
|
|
||||||
|
|
||||||
interface IIssuePeekOverview {
|
interface IIssuePeekOverview {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -19,7 +18,7 @@ interface IIssuePeekOverview {
|
|||||||
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, handleIssue, children } = props;
|
const { workspaceSlug, projectId, issueId, handleIssue, children } = props;
|
||||||
|
|
||||||
const { issueDetail: issueDetailStore }: RootStore = useMobxStore();
|
const { issueDetail: issueDetailStore, project: projectStore, user: userStore } = useMobxStore();
|
||||||
|
|
||||||
const issueUpdate = (_data: Partial<IIssue>) => {
|
const issueUpdate = (_data: Partial<IIssue>) => {
|
||||||
handleIssue(_data);
|
handleIssue(_data);
|
||||||
@ -52,6 +51,8 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
|
|
||||||
const handleDeleteIssue = () => issueDetailStore.deleteIssue(workspaceSlug, projectId, issueId);
|
const handleDeleteIssue = () => issueDetailStore.deleteIssue(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
|
const userRole = userStore.currentProjectRole ?? 5;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IssueView
|
<IssueView
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
@ -68,6 +69,8 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
issueSubscriptionCreate={issueSubscriptionCreate}
|
issueSubscriptionCreate={issueSubscriptionCreate}
|
||||||
issueSubscriptionRemove={issueSubscriptionRemove}
|
issueSubscriptionRemove={issueSubscriptionRemove}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
|
disableUserActions={[5, 10].includes(userRole)}
|
||||||
|
showCommentAccessSpecifier={projectStore.currentProjectDetails?.is_deployed}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</IssueView>
|
</IssueView>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { FC, ReactNode, useState } from "react";
|
import { FC, ReactNode, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { PeekOverviewIssueDetails } from "./issue-detail";
|
import { PeekOverviewIssueDetails } from "./issue-detail";
|
||||||
import { PeekOverviewProperties } from "./properties";
|
import { PeekOverviewProperties } from "./properties";
|
||||||
@ -13,7 +15,6 @@ import { DeleteIssueModal } from "../delete-issue-modal";
|
|||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { RootStore } from "store/root";
|
import { RootStore } from "store/root";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||||
@ -34,6 +35,8 @@ interface IIssueView {
|
|||||||
issueSubscriptionRemove: () => void;
|
issueSubscriptionRemove: () => void;
|
||||||
handleDeleteIssue: () => Promise<void>;
|
handleDeleteIssue: () => Promise<void>;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
disableUserActions?: boolean;
|
||||||
|
showCommentAccessSpecifier?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TPeekModes = "side-peek" | "modal" | "full-screen";
|
type TPeekModes = "side-peek" | "modal" | "full-screen";
|
||||||
@ -73,6 +76,8 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issueSubscriptionRemove,
|
issueSubscriptionRemove,
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
children,
|
children,
|
||||||
|
disableUserActions = false,
|
||||||
|
showCommentAccessSpecifier = false,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -239,9 +244,11 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
<button onClick={handleCopyText}>
|
<button onClick={handleCopyText}>
|
||||||
<Link2 className="h-4 w-4 text-custom-text-400 hover:text-custom-text-200 -rotate-45" />
|
<Link2 className="h-4 w-4 text-custom-text-400 hover:text-custom-text-200 -rotate-45" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => setDeleteIssueModal(true)}>
|
{!disableUserActions && (
|
||||||
<Trash2 className="h-4 w-4 text-custom-text-400 hover:text-custom-text-200" />
|
<button onClick={() => setDeleteIssueModal(true)}>
|
||||||
</button>
|
<Trash2 className="h-4 w-4 text-custom-text-400 hover:text-custom-text-200" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -264,7 +271,11 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issueReactionRemove={issueReactionRemove}
|
issueReactionRemove={issueReactionRemove}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PeekOverviewProperties issue={issue} issueUpdate={issueUpdate} user={user} />
|
<PeekOverviewProperties
|
||||||
|
issue={issue}
|
||||||
|
issueUpdate={issueUpdate}
|
||||||
|
disableUserActions={disableUserActions}
|
||||||
|
/>
|
||||||
|
|
||||||
<IssueComment
|
<IssueComment
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
@ -277,6 +288,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issueCommentRemove={issueCommentRemove}
|
issueCommentRemove={issueCommentRemove}
|
||||||
issueCommentReactionCreate={issueCommentReactionCreate}
|
issueCommentReactionCreate={issueCommentReactionCreate}
|
||||||
issueCommentReactionRemove={issueCommentReactionRemove}
|
issueCommentReactionRemove={issueCommentReactionRemove}
|
||||||
|
showCommentAccessSpecifier={showCommentAccessSpecifier}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -305,10 +317,15 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issueCommentRemove={issueCommentRemove}
|
issueCommentRemove={issueCommentRemove}
|
||||||
issueCommentReactionCreate={issueCommentReactionCreate}
|
issueCommentReactionCreate={issueCommentReactionCreate}
|
||||||
issueCommentReactionRemove={issueCommentReactionRemove}
|
issueCommentReactionRemove={issueCommentReactionRemove}
|
||||||
|
showCommentAccessSpecifier={showCommentAccessSpecifier}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-shrink-0 !w-[400px] h-full border-l border-custom-border-200 p-4 py-5">
|
<div className="flex-shrink-0 !w-[400px] h-full border-l border-custom-border-200 p-4 py-5">
|
||||||
<PeekOverviewProperties issue={issue} issueUpdate={issueUpdate} user={user} />
|
<PeekOverviewProperties
|
||||||
|
issue={issue}
|
||||||
|
issueUpdate={issueUpdate}
|
||||||
|
disableUserActions={disableUserActions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { useState, Fragment } from "react";
|
import { useState, Fragment } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { Transition, Dialog } from "@headlessui/react";
|
import { Transition, Dialog } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
@ -17,10 +18,12 @@ type TJoinProjectModalProps = {
|
|||||||
|
|
||||||
export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
||||||
const { handleClose, isOpen, project, workspaceSlug } = props;
|
const { handleClose, isOpen, project, workspaceSlug } = props;
|
||||||
// store
|
|
||||||
const { project: projectStore } = useMobxStore();
|
|
||||||
// states
|
// states
|
||||||
const [isJoiningLoading, setIsJoiningLoading] = useState(false);
|
const [isJoiningLoading, setIsJoiningLoading] = useState(false);
|
||||||
|
// store
|
||||||
|
const { project: projectStore } = useMobxStore();
|
||||||
|
// router
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const handleJoin = () => {
|
const handleJoin = () => {
|
||||||
setIsJoiningLoading(true);
|
setIsJoiningLoading(true);
|
||||||
@ -29,6 +32,8 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
|||||||
.joinProject(workspaceSlug, [project.id])
|
.joinProject(workspaceSlug, [project.id])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsJoiningLoading(false);
|
setIsJoiningLoading(false);
|
||||||
|
|
||||||
|
router.push(`/${workspaceSlug}/projects/${project.id}/issues`);
|
||||||
handleClose();
|
handleClose();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@ -17,11 +17,17 @@ export const UserAuthWrapper: FC<IUserAuthWrapper> = (props) => {
|
|||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// fetching user information
|
// fetching user information
|
||||||
const { data: currentUser, error } = useSWR("CURRENT_USER_DETAILS", () => userStore.fetchCurrentUser());
|
const { data: currentUser, error } = useSWR("CURRENT_USER_DETAILS", () => userStore.fetchCurrentUser(), {
|
||||||
|
shouldRetryOnError: false,
|
||||||
|
});
|
||||||
// fetching user settings
|
// fetching user settings
|
||||||
useSWR("CURRENT_USER_SETTINGS", () => userStore.fetchCurrentUserSettings());
|
useSWR("CURRENT_USER_SETTINGS", () => userStore.fetchCurrentUserSettings(), {
|
||||||
|
shouldRetryOnError: false,
|
||||||
|
});
|
||||||
// fetching all workspaces
|
// fetching all workspaces
|
||||||
useSWR(`USER_WORKSPACES_LIST`, () => workspaceStore.fetchWorkspaces());
|
useSWR(`USER_WORKSPACES_LIST`, () => workspaceStore.fetchWorkspaces(), {
|
||||||
|
shouldRetryOnError: false,
|
||||||
|
});
|
||||||
|
|
||||||
if (!currentUser && !error) {
|
if (!currentUser && !error) {
|
||||||
return (
|
return (
|
||||||
|
1
web/types/projects.d.ts
vendored
1
web/types/projects.d.ts
vendored
@ -43,7 +43,6 @@ export interface IProject {
|
|||||||
page_view: boolean;
|
page_view: boolean;
|
||||||
project_lead: IUserLite | string | null;
|
project_lead: IUserLite | string | null;
|
||||||
sort_order: number | null;
|
sort_order: number | null;
|
||||||
slug: string;
|
|
||||||
total_cycles: number;
|
total_cycles: number;
|
||||||
total_members: number;
|
total_members: number;
|
||||||
total_modules: number;
|
total_modules: number;
|
||||||
|
Loading…
Reference in New Issue
Block a user