mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'develop' of gurusainath:makeplane/plane into feat/mobx-global-views
This commit is contained in:
commit
90bcdeccf4
@ -33,7 +33,6 @@ class CycleWriteSerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class CycleSerializer(BaseSerializer):
|
class CycleSerializer(BaseSerializer):
|
||||||
owned_by = UserLiteSerializer(read_only=True)
|
|
||||||
is_favorite = serializers.BooleanField(read_only=True)
|
is_favorite = serializers.BooleanField(read_only=True)
|
||||||
total_issues = serializers.IntegerField(read_only=True)
|
total_issues = serializers.IntegerField(read_only=True)
|
||||||
cancelled_issues = serializers.IntegerField(read_only=True)
|
cancelled_issues = serializers.IntegerField(read_only=True)
|
||||||
|
@ -186,7 +186,7 @@ def send_email_notification(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
summary = "updates were made to the issue by"
|
summary = "Updates were made to the issue by"
|
||||||
|
|
||||||
# Send the mail
|
# Send the mail
|
||||||
subject = f"{issue.project.identifier}-{issue.sequence_id} {issue.name}"
|
subject = f"{issue.project.identifier}-{issue.sequence_id} {issue.name}"
|
||||||
|
@ -108,14 +108,33 @@
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
{% if actors_involved > 0 %}
|
{% if actors_involved == 1 %}
|
||||||
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
|
{{summary}}
|
||||||
|
<span style="font-size: 1rem; font-weight: 700; line-height: 28px">
|
||||||
|
{{ data.0.actor_detail.first_name}}
|
||||||
|
{{data.0.actor_detail.last_name}}
|
||||||
|
</span>.
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
|
{{summary}}
|
||||||
|
<span style="font-size: 1rem; font-weight: 700; line-height: 28px">
|
||||||
|
{{ data.0.actor_detail.first_name}}
|
||||||
|
{{data.0.actor_detail.last_name }}
|
||||||
|
</span>and others.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- {% if actors_involved == 1 %}
|
||||||
{% if data|length > 0 and comments|length == 0 %}
|
{% if data|length > 0 and comments|length == 0 %}
|
||||||
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
<span style="font-size: 1rem; font-weight: 700; line-height: 28px">
|
<span style="font-size: 1rem; font-weight: 700; line-height: 28px">
|
||||||
{{ data.0.actor_detail.first_name}}
|
{{ data.0.actor_detail.first_name}}
|
||||||
{{data.0.actor_detail.last_name }}
|
{{data.0.actor_detail.last_name }}
|
||||||
</span>
|
</span>
|
||||||
made {{data|length}} {% if data|length > 1 %}updates{% else %}update{% endif %} to the issue.
|
made {{total_updates}} {% if total_updates > 1 %}updates{% else %}update{% endif %} to the issue.
|
||||||
</p>
|
</p>
|
||||||
{% elif data|length == 0 and comments|length > 0 %}
|
{% elif data|length == 0 and comments|length > 0 %}
|
||||||
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
@ -123,7 +142,7 @@
|
|||||||
{{ comments.0.actor_detail.first_name}}
|
{{ comments.0.actor_detail.first_name}}
|
||||||
{{comments.0.actor_detail.last_name }}
|
{{comments.0.actor_detail.last_name }}
|
||||||
</span>
|
</span>
|
||||||
added {{comments|length}} new {% if comments|length > 1 %}comments{% else %}comment{% endif %}.
|
added {{total_comments}} new {% if total_comments > 1 %}comments{% else %}comment{% endif %}.
|
||||||
</p>
|
</p>
|
||||||
{% elif data|length > 0 and comments|length > 0 %}
|
{% elif data|length > 0 and comments|length > 0 %}
|
||||||
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
@ -131,14 +150,14 @@
|
|||||||
{{ data.0.actor_detail.first_name}}
|
{{ data.0.actor_detail.first_name}}
|
||||||
{{data.0.actor_detail.last_name }}
|
{{data.0.actor_detail.last_name }}
|
||||||
</span>
|
</span>
|
||||||
made {{data|length}} {% if data|length > 1 %}updates{% else %}update{% endif %} and added {{comments|length}} new {% if comments|length > 1 %}comments{% else %}comment{% endif %} on the issue.
|
made {{total_updates}} {% if total_updates > 1 %}updates{% else %}update{% endif %} and added {{total_comments}} new {% if total_comments > 1 %}comments{% else %}comment{% endif %} on the issue.
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px">
|
||||||
There are {{ data|length }} new updates and {{comments|length}} new comments on the issue
|
There are {{ total_updates }} new updates and {{total_comments}} new comments on the issue.
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %} -->
|
||||||
{% for update in data %} {% if update.changes.name %}
|
{% for update in data %} {% if update.changes.name %}
|
||||||
<!-- Issue title updated -->
|
<!-- Issue title updated -->
|
||||||
<p style="font-size: 1rem; line-height: 28px; color: #1f2d5c">
|
<p style="font-size: 1rem; line-height: 28px; color: #1f2d5c">
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
import { TextSelection } from "prosemirror-state";
|
|
||||||
|
|
||||||
import { InputRule, mergeAttributes, Node, nodeInputRule, wrappingInputRule } from "@tiptap/core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension based on:
|
|
||||||
* - Tiptap HorizontalRule extension (https://tiptap.dev/api/nodes/horizontal-rule)
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface HorizontalRuleOptions {
|
|
||||||
HTMLAttributes: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "@tiptap/core" {
|
|
||||||
interface Commands<ReturnType> {
|
|
||||||
horizontalRule: {
|
|
||||||
/**
|
|
||||||
* Add a horizontal rule
|
|
||||||
*/
|
|
||||||
setHorizontalRule: () => ReturnType;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const HorizontalRule = Node.create<HorizontalRuleOptions>({
|
|
||||||
name: "horizontalRule",
|
|
||||||
|
|
||||||
addOptions() {
|
|
||||||
return {
|
|
||||||
HTMLAttributes: {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
group: "block",
|
|
||||||
|
|
||||||
addAttributes() {
|
|
||||||
return {
|
|
||||||
color: {
|
|
||||||
default: "#dddddd",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
parseHTML() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
tag: `div[data-type="${this.name}"]`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
renderHTML({ HTMLAttributes }) {
|
|
||||||
return [
|
|
||||||
"div",
|
|
||||||
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
||||||
"data-type": this.name,
|
|
||||||
}),
|
|
||||||
["div", {}],
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
addCommands() {
|
|
||||||
return {
|
|
||||||
setHorizontalRule:
|
|
||||||
() =>
|
|
||||||
({ chain }) => {
|
|
||||||
return (
|
|
||||||
chain()
|
|
||||||
.insertContent({ type: this.name })
|
|
||||||
// set cursor after horizontal rule
|
|
||||||
.command(({ tr, dispatch }) => {
|
|
||||||
if (dispatch) {
|
|
||||||
const { $to } = tr.selection;
|
|
||||||
const posAfter = $to.end();
|
|
||||||
|
|
||||||
if ($to.nodeAfter) {
|
|
||||||
tr.setSelection(TextSelection.create(tr.doc, $to.pos));
|
|
||||||
} else {
|
|
||||||
// add node after horizontal rule if it’s the end of the document
|
|
||||||
const node = $to.parent.type.contentMatch.defaultType?.create();
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
tr.insert(posAfter, node);
|
|
||||||
tr.setSelection(TextSelection.create(tr.doc, posAfter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.scrollIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.run()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
addInputRules() {
|
|
||||||
return [
|
|
||||||
new InputRule({
|
|
||||||
find: /^(?:---|—-|___\s|\*\*\*\s)$/,
|
|
||||||
handler: ({ state, range, match }) => {
|
|
||||||
state.tr.replaceRangeWith(range.from, range.to, this.type.create());
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,26 +1,25 @@
|
|||||||
import StarterKit from "@tiptap/starter-kit";
|
|
||||||
import TiptapUnderline from "@tiptap/extension-underline";
|
|
||||||
import TextStyle from "@tiptap/extension-text-style";
|
|
||||||
import { Color } from "@tiptap/extension-color";
|
import { Color } from "@tiptap/extension-color";
|
||||||
import TaskItem from "@tiptap/extension-task-item";
|
import TaskItem from "@tiptap/extension-task-item";
|
||||||
import TaskList from "@tiptap/extension-task-list";
|
import TaskList from "@tiptap/extension-task-list";
|
||||||
|
import TextStyle from "@tiptap/extension-text-style";
|
||||||
|
import TiptapUnderline from "@tiptap/extension-underline";
|
||||||
|
import StarterKit from "@tiptap/starter-kit";
|
||||||
import { Markdown } from "tiptap-markdown";
|
import { Markdown } from "tiptap-markdown";
|
||||||
|
|
||||||
import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
|
|
||||||
import { Table } from "src/ui/extensions/table/table";
|
import { Table } from "src/ui/extensions/table/table";
|
||||||
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
||||||
|
import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
|
||||||
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
||||||
import { HorizontalRule } from "src/ui/extensions/horizontal-rule";
|
|
||||||
|
|
||||||
import { ImageExtension } from "src/ui/extensions/image";
|
import { ImageExtension } from "src/ui/extensions/image";
|
||||||
|
|
||||||
import { isValidHttpUrl } from "src/lib/utils";
|
import { isValidHttpUrl } from "src/lib/utils";
|
||||||
import { Mentions } from "src/ui/mentions";
|
import { Mentions } from "src/ui/mentions";
|
||||||
|
|
||||||
import { CustomKeymap } from "src/ui/extensions/keymap";
|
|
||||||
import { CustomCodeBlockExtension } from "src/ui/extensions/code";
|
import { CustomCodeBlockExtension } from "src/ui/extensions/code";
|
||||||
import { CustomQuoteExtension } from "src/ui/extensions/quote";
|
|
||||||
import { ListKeymap } from "src/ui/extensions/custom-list-keymap";
|
import { ListKeymap } from "src/ui/extensions/custom-list-keymap";
|
||||||
|
import { CustomKeymap } from "src/ui/extensions/keymap";
|
||||||
|
import { CustomQuoteExtension } from "src/ui/extensions/quote";
|
||||||
|
|
||||||
import { DeleteImage } from "src/types/delete-image";
|
import { DeleteImage } from "src/types/delete-image";
|
||||||
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||||
@ -55,7 +54,9 @@ export const CoreEditorExtensions = (
|
|||||||
},
|
},
|
||||||
code: false,
|
code: false,
|
||||||
codeBlock: false,
|
codeBlock: false,
|
||||||
horizontalRule: false,
|
horizontalRule: {
|
||||||
|
HTMLAttributes: { class: "mt-4 mb-4" },
|
||||||
|
},
|
||||||
blockquote: false,
|
blockquote: false,
|
||||||
dropcursor: {
|
dropcursor: {
|
||||||
color: "rgba(var(--color-text-100))",
|
color: "rgba(var(--color-text-100))",
|
||||||
@ -104,7 +105,6 @@ export const CoreEditorExtensions = (
|
|||||||
transformCopiedText: true,
|
transformCopiedText: true,
|
||||||
transformPastedText: true,
|
transformPastedText: true,
|
||||||
}),
|
}),
|
||||||
HorizontalRule,
|
|
||||||
Table,
|
Table,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableCell,
|
TableCell,
|
||||||
|
@ -10,6 +10,11 @@ export interface CustomMentionOptions extends MentionOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CustomMention = Mention.extend<CustomMentionOptions>({
|
export const CustomMention = Mention.extend<CustomMentionOptions>({
|
||||||
|
addStorage(this) {
|
||||||
|
return {
|
||||||
|
mentionsOpen: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
id: {
|
id: {
|
||||||
|
@ -14,6 +14,7 @@ export const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
|
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
|
||||||
|
props.editor.storage.mentionsOpen = true;
|
||||||
reactRenderer = new ReactRenderer(MentionList, {
|
reactRenderer = new ReactRenderer(MentionList, {
|
||||||
props,
|
props,
|
||||||
editor: props.editor,
|
editor: props.editor,
|
||||||
@ -45,10 +46,18 @@ export const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
|
||||||
|
|
||||||
|
if (navigationKeys.includes(props.event.key)) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return reactRenderer?.ref?.onKeyDown(props);
|
reactRenderer?.ref?.onKeyDown(props);
|
||||||
|
event?.stopPropagation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
onExit: () => {
|
onExit: (props: { editor: Editor; event: KeyboardEvent }) => {
|
||||||
|
props.editor.storage.mentionsOpen = false;
|
||||||
popup?.[0].destroy();
|
popup?.[0].destroy();
|
||||||
reactRenderer?.destroy();
|
reactRenderer?.destroy();
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,6 @@ import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
|
|||||||
import { Table } from "src/ui/extensions/table/table";
|
import { Table } from "src/ui/extensions/table/table";
|
||||||
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
|
||||||
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
|
||||||
import { HorizontalRule } from "src/ui/extensions/horizontal-rule";
|
|
||||||
|
|
||||||
import { ReadOnlyImageExtension } from "src/ui/extensions/image/read-only-image";
|
import { ReadOnlyImageExtension } from "src/ui/extensions/image/read-only-image";
|
||||||
import { isValidHttpUrl } from "src/lib/utils";
|
import { isValidHttpUrl } from "src/lib/utils";
|
||||||
@ -51,7 +50,9 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
codeBlock: false,
|
codeBlock: false,
|
||||||
horizontalRule: false,
|
horizontalRule: {
|
||||||
|
HTMLAttributes: { class: "mt-4 mb-4" },
|
||||||
|
},
|
||||||
dropcursor: {
|
dropcursor: {
|
||||||
color: "rgba(var(--color-text-100))",
|
color: "rgba(var(--color-text-100))",
|
||||||
width: 2,
|
width: 2,
|
||||||
@ -72,7 +73,6 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: {
|
|||||||
class: "rounded-lg border border-custom-border-300",
|
class: "rounded-lg border border-custom-border-300",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
HorizontalRule,
|
|
||||||
TiptapUnderline,
|
TiptapUnderline,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
Color,
|
Color,
|
||||||
|
@ -4,13 +4,16 @@ export const EnterKeyExtension = (onEnterKeyPress?: () => void) =>
|
|||||||
Extension.create({
|
Extension.create({
|
||||||
name: "enterKey",
|
name: "enterKey",
|
||||||
|
|
||||||
addKeyboardShortcuts() {
|
addKeyboardShortcuts(this) {
|
||||||
return {
|
return {
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
|
if (!this.editor.storage.mentionsOpen) {
|
||||||
if (onEnterKeyPress) {
|
if (onEnterKeyPress) {
|
||||||
onEnterKeyPress();
|
onEnterKeyPress();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
"Shift-Enter": ({ editor }) =>
|
"Shift-Enter": ({ editor }) =>
|
||||||
editor.commands.first(({ commands }) => [
|
editor.commands.first(({ commands }) => [
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { EnterKeyExtension } from "src/ui/extensions/enter-key-extension";
|
import { EnterKeyExtension } from "src/ui/extensions/enter-key-extension";
|
||||||
|
|
||||||
export const LiteTextEditorExtensions = (onEnterKeyPress?: () => void) => [
|
export const LiteTextEditorExtensions = (onEnterKeyPress?: () => void) => [EnterKeyExtension(onEnterKeyPress)];
|
||||||
// EnterKeyExtension(onEnterKeyPress),
|
|
||||||
];
|
|
||||||
|
2
packages/types/src/cycles.d.ts
vendored
2
packages/types/src/cycles.d.ts
vendored
@ -30,7 +30,7 @@ export interface ICycle {
|
|||||||
is_favorite: boolean;
|
is_favorite: boolean;
|
||||||
issue: string;
|
issue: string;
|
||||||
name: string;
|
name: string;
|
||||||
owned_by: IUser;
|
owned_by: string;
|
||||||
project: string;
|
project: string;
|
||||||
project_detail: IProjectLite;
|
project_detail: IProjectLite;
|
||||||
status: TCycleGroups;
|
status: TCycleGroups;
|
||||||
|
@ -2,8 +2,6 @@ import * as React from "react";
|
|||||||
|
|
||||||
// icons
|
// icons
|
||||||
import { ChevronRight } from "lucide-react";
|
import { ChevronRight } from "lucide-react";
|
||||||
// components
|
|
||||||
import { Tooltip } from "../tooltip";
|
|
||||||
|
|
||||||
type BreadcrumbsProps = {
|
type BreadcrumbsProps = {
|
||||||
children: any;
|
children: any;
|
||||||
@ -25,42 +23,11 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => (
|
|||||||
type Props = {
|
type Props = {
|
||||||
type?: "text" | "component";
|
type?: "text" | "component";
|
||||||
component?: React.ReactNode;
|
component?: React.ReactNode;
|
||||||
label?: string;
|
link?: JSX.Element;
|
||||||
icon?: React.ReactNode;
|
|
||||||
link?: string;
|
|
||||||
};
|
};
|
||||||
const BreadcrumbItem: React.FC<Props> = (props) => {
|
const BreadcrumbItem: React.FC<Props> = (props) => {
|
||||||
const { type = "text", component, label, icon, link } = props;
|
const { type = "text", component, link } = props;
|
||||||
return (
|
return <>{type != "text" ? <div className="flex items-center space-x-2">{component}</div> : link}</>;
|
||||||
<>
|
|
||||||
{type != "text" ? (
|
|
||||||
<div className="flex items-center space-x-2">{component}</div>
|
|
||||||
) : (
|
|
||||||
<Tooltip tooltipContent={label} position="bottom">
|
|
||||||
<li className="flex items-center space-x-2">
|
|
||||||
<div className="flex flex-wrap items-center gap-2.5">
|
|
||||||
{link ? (
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-1 text-sm font-medium text-custom-text-300 hover:text-custom-text-100"
|
|
||||||
href={link}
|
|
||||||
>
|
|
||||||
{icon && (
|
|
||||||
<div className="flex h-5 w-5 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>
|
|
||||||
)}
|
|
||||||
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<div className="flex cursor-default items-center gap-1 text-sm font-medium text-custom-text-100">
|
|
||||||
{icon && <div className="flex h-5 w-5 items-center justify-center overflow-hidden">{icon}</div>}
|
|
||||||
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Breadcrumbs.BreadcrumbItem = BreadcrumbItem;
|
Breadcrumbs.BreadcrumbItem = BreadcrumbItem;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useModule, useProject } from "hooks/store";
|
import { useCycle, useMember, useModule, useProject } from "hooks/store";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||||
@ -15,10 +15,12 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
|
|||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
const { getCycleById } = useCycle();
|
const { getCycleById } = useCycle();
|
||||||
const { getModuleById } = useModule();
|
const { getModuleById } = useModule();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
||||||
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
||||||
const projectDetails = projectId ? getProjectById(projectId.toString()) : undefined;
|
const projectDetails = projectId ? getProjectById(projectId.toString()) : undefined;
|
||||||
|
const cycleOwnerDetails = cycleDetails ? getUserDetails(cycleDetails.owned_by) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -29,7 +31,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
|
|||||||
<div className="mt-4 space-y-4">
|
<div className="mt-4 space-y-4">
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<h6 className="text-custom-text-200">Lead</h6>
|
<h6 className="text-custom-text-200">Lead</h6>
|
||||||
<span>{cycleDetails.owned_by?.display_name}</span>
|
<span>{cycleOwnerDetails?.display_name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<h6 className="text-custom-text-200">Start Date</h6>
|
<h6 className="text-custom-text-200">Start Date</h6>
|
||||||
|
36
web/components/common/breadcrumb-link.tsx
Normal file
36
web/components/common/breadcrumb-link.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Tooltip } from "@plane/ui";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label?: string;
|
||||||
|
href?: string;
|
||||||
|
icon?: React.ReactNode | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BreadcrumbLink: React.FC<Props> = (props) => {
|
||||||
|
const { href, label, icon } = props;
|
||||||
|
return (
|
||||||
|
<Tooltip tooltipContent={label} position="bottom">
|
||||||
|
<li className="flex items-center space-x-2">
|
||||||
|
<div className="flex flex-wrap items-center gap-2.5">
|
||||||
|
{href ? (
|
||||||
|
<Link
|
||||||
|
className="flex items-center gap-1 text-sm font-medium text-custom-text-300 hover:text-custom-text-100"
|
||||||
|
href={href}
|
||||||
|
>
|
||||||
|
{icon && (
|
||||||
|
<div className="flex h-5 w-5 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>
|
||||||
|
)}
|
||||||
|
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<div className="flex cursor-default items-center gap-1 text-sm font-medium text-custom-text-100">
|
||||||
|
{icon && <div className="flex h-5 w-5 items-center justify-center overflow-hidden">{icon}</div>}
|
||||||
|
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./product-updates-modal";
|
export * from "./product-updates-modal";
|
||||||
export * from "./empty-state";
|
export * from "./empty-state";
|
||||||
export * from "./latest-feature-block";
|
export * from "./latest-feature-block";
|
||||||
|
export * from "./breadcrumb-link";
|
||||||
|
@ -157,6 +157,7 @@ export const GptAssistantPopover: React.FC<Props> = (props) => {
|
|||||||
window.removeEventListener("keydown", handleEnterKeyPress);
|
window.removeEventListener("keydown", handleEnterKeyPress);
|
||||||
window.removeEventListener("keydown", handleEscapeKeyPress);
|
window.removeEventListener("keydown", handleEscapeKeyPress);
|
||||||
};
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isOpen, handleSubmit, onClose]);
|
}, [isOpen, handleSubmit, onClose]);
|
||||||
|
|
||||||
const responseActionButton = response !== "" && (
|
const responseActionButton = response !== "" && (
|
||||||
|
@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useIssues, useProject, useUser } from "hooks/store";
|
import { useCycle, useIssues, useMember, useProject, useUser } from "hooks/store";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { SingleProgressStats } from "components/core";
|
import { SingleProgressStats } from "components/core";
|
||||||
@ -58,6 +58,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
|||||||
removeCycleFromFavorites,
|
removeCycleFromFavorites,
|
||||||
} = useCycle();
|
} = useCycle();
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
// toast alert
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
|||||||
);
|
);
|
||||||
|
|
||||||
const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null;
|
const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null;
|
||||||
|
const cycleOwnerDetails = activeCycle ? getUserDetails(activeCycle.owned_by) : undefined;
|
||||||
|
|
||||||
const { data: activeCycleIssues } = useSWR(
|
const { data: activeCycleIssues } = useSWR(
|
||||||
workspaceSlug && projectId && currentProjectActiveCycleId
|
workspaceSlug && projectId && currentProjectActiveCycleId
|
||||||
@ -203,20 +205,20 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
|||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center gap-2.5 text-custom-text-200">
|
<div className="flex items-center gap-2.5 text-custom-text-200">
|
||||||
{activeCycle.owned_by.avatar && activeCycle.owned_by.avatar !== "" ? (
|
{cycleOwnerDetails?.avatar && cycleOwnerDetails?.avatar !== "" ? (
|
||||||
<img
|
<img
|
||||||
src={activeCycle.owned_by.avatar}
|
src={cycleOwnerDetails?.avatar}
|
||||||
height={16}
|
height={16}
|
||||||
width={16}
|
width={16}
|
||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
alt={activeCycle.owned_by.display_name}
|
alt={cycleOwnerDetails?.display_name}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-custom-background-100 capitalize">
|
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-custom-background-100 capitalize">
|
||||||
{activeCycle.owned_by.display_name.charAt(0)}
|
{cycleOwnerDetails?.display_name.charAt(0)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="text-custom-text-200">{activeCycle.owned_by.display_name}</span>
|
<span className="text-custom-text-200">{cycleOwnerDetails?.display_name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeCycle.assignees.length > 0 && (
|
{activeCycle.assignees.length > 0 && (
|
||||||
|
@ -6,7 +6,7 @@ import { Disclosure, Popover, Transition } from "@headlessui/react";
|
|||||||
// services
|
// services
|
||||||
import { CycleService } from "services/cycle.service";
|
import { CycleService } from "services/cycle.service";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useCycle, useUser } from "hooks/store";
|
import { useApplication, useCycle, useMember, useUser } from "hooks/store";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { SidebarProgressStats } from "components/core";
|
import { SidebarProgressStats } from "components/core";
|
||||||
@ -73,8 +73,10 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { getCycleById, updateCycleDetails } = useCycle();
|
const { getCycleById, updateCycleDetails } = useCycle();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
const cycleDetails = getCycleById(cycleId);
|
const cycleDetails = getCycleById(cycleId);
|
||||||
|
const cycleOwnerDetails = cycleDetails ? getUserDetails(cycleDetails.owned_by) : undefined;
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -518,8 +520,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex w-1/2 items-center rounded-sm">
|
<div className="flex w-1/2 items-center rounded-sm">
|
||||||
<div className="flex items-center gap-2.5">
|
<div className="flex items-center gap-2.5">
|
||||||
<Avatar name={cycleDetails.owned_by.display_name} src={cycleDetails.owned_by.avatar} />
|
<Avatar name={cycleOwnerDetails?.display_name} src={cycleOwnerDetails?.avatar} />
|
||||||
<span className="text-sm text-custom-text-200">{cycleDetails.owned_by.display_name}</span>
|
<span className="text-sm text-custom-text-200">{cycleOwnerDetails?.display_name}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -587,6 +589,7 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{cycleDetails && cycleDetails.distribution && (
|
||||||
<div className="relative h-40 w-80">
|
<div className="relative h-40 w-80">
|
||||||
<ProgressChart
|
<ProgressChart
|
||||||
distribution={cycleDetails.distribution?.completion_chart ?? {}}
|
distribution={cycleDetails.distribution?.completion_chart ?? {}}
|
||||||
@ -595,6 +598,7 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||||||
totalIssues={cycleDetails.total_issues}
|
totalIssues={cycleDetails.total_issues}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
@ -7,7 +7,6 @@ import { useDashboard } from "hooks/store";
|
|||||||
import { WidgetLoader } from "components/dashboard/widgets";
|
import { WidgetLoader } from "components/dashboard/widgets";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||||
import { cn } from "helpers/common.helper";
|
|
||||||
// types
|
// types
|
||||||
import { TOverviewStatsWidgetResponse } from "@plane/types";
|
import { TOverviewStatsWidgetResponse } from "@plane/types";
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import useLocalStorage from "hooks/use-local-storage";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, ContrastIcon, CustomMenu } from "@plane/ui";
|
import { Breadcrumbs, Button, ContrastIcon, CustomMenu } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -151,6 +152,10 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -162,14 +167,18 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label="Cycles"
|
label="Cycles"
|
||||||
link={`/${workspaceSlug}/projects/${projectId}/cycles`}
|
href={`/${workspaceSlug}/projects/${projectId}/cycles`}
|
||||||
|
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="component"
|
type="component"
|
||||||
|
@ -11,6 +11,7 @@ import { renderEmoji } from "helpers/emoji.helper";
|
|||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const CyclesHeader: FC = observer(() => {
|
export const CyclesHeader: FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -32,12 +33,15 @@ export const CyclesHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -49,12 +53,12 @@ export const CyclesHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />}
|
link={<BreadcrumbLink label="Cycles" icon={<ContrastIcon className="h-4 w-4 text-custom-text-300" />} />}
|
||||||
label="Cycles"
|
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { useLabel, useMember, useUser, useIssues } from "hooks/store";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection } from "components/issues";
|
||||||
import { CreateUpdateWorkspaceViewModal } from "components/workspace";
|
import { CreateUpdateWorkspaceViewModal } from "components/workspace";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, LayersIcon, PhotoFilterIcon, Tooltip } from "@plane/ui";
|
import { Breadcrumbs, Button, LayersIcon, PhotoFilterIcon, Tooltip } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -108,10 +109,13 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="relative flex gap-2">
|
<div className="relative flex gap-2">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
label={`All ${activeLayout === "spreadsheet" ? "Issues" : "Views"}`}
|
||||||
icon={
|
icon={
|
||||||
activeLayout === "spreadsheet" ? (
|
activeLayout === "spreadsheet" ? (
|
||||||
<LayersIcon className="h-4 w-4 text-custom-text-300" />
|
<LayersIcon className="h-4 w-4 text-custom-text-300" />
|
||||||
@ -119,7 +123,8 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />
|
<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={`All ${activeLayout === "spreadsheet" ? "Issues" : "Views"}`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,7 @@ import useLocalStorage from "hooks/use-local-storage";
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, CustomMenu, DiceIcon } from "@plane/ui";
|
import { Breadcrumbs, Button, CustomMenu, DiceIcon } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -154,6 +155,10 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -165,14 +170,18 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${projectId}/modules`}
|
||||||
label="Modules"
|
label="Modules"
|
||||||
link={`/${workspaceSlug}/projects/${projectId}/modules`}
|
icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="component"
|
type="component"
|
||||||
|
@ -13,6 +13,7 @@ import { MODULE_VIEW_LAYOUTS } from "constants/module";
|
|||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const ModulesListHeader: React.FC = observer(() => {
|
export const ModulesListHeader: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -33,11 +34,14 @@ export const ModulesListHeader: React.FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -50,12 +54,12 @@ export const ModulesListHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />}
|
link={<BreadcrumbLink label="Modules" icon={<DiceIcon className="h-4 w-4 text-custom-text-300" />} />}
|
||||||
label="Modules"
|
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,6 +10,7 @@ import { Breadcrumbs, Button } from "@plane/ui";
|
|||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export interface IPagesHeaderProps {
|
export interface IPagesHeaderProps {
|
||||||
showButton?: boolean;
|
showButton?: boolean;
|
||||||
@ -29,11 +30,14 @@ export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -46,18 +50,27 @@ export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages`}
|
||||||
label="Pages"
|
label="Pages"
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages`}
|
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label={pageDetails?.name ?? "Page"}
|
label={pageDetails?.name ?? "Page"}
|
||||||
|
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import { renderEmoji } from "helpers/emoji.helper";
|
|||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const PagesHeader = observer(() => {
|
export const PagesHeader = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -31,11 +32,14 @@ export const PagesHeader = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -48,12 +52,12 @@ export const PagesHeader = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<FileText className="h-4 w-4 text-custom-text-300" />}
|
link={<BreadcrumbLink label="Pages" icon={<FileText className="h-4 w-4 text-custom-text-300" />} />}
|
||||||
label="Pages"
|
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// components
|
// components
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const ProfilePreferencesHeader = () => (
|
export const ProfilePreferencesHeader = () => (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label="My Profile Preferences" />
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink label="My Profile Preferences" />} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,7 @@ import { FC } from "react";
|
|||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
import { Settings } from "lucide-react";
|
import { Settings } from "lucide-react";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
interface IProfileSettingHeader {
|
interface IProfileSettingHeader {
|
||||||
title: string;
|
title: string;
|
||||||
@ -17,11 +18,15 @@ export const ProfileSettingsHeader: FC<IProfileSettingHeader> = (props) => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href="/profile"
|
||||||
label="My Profile"
|
label="My Profile"
|
||||||
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
||||||
link="/profile"
|
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink label={title} />} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,7 @@ import { IssueArchiveService } from "services/issue";
|
|||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
const issueArchiveService = new IssueArchiveService();
|
const issueArchiveService = new IssueArchiveService();
|
||||||
|
|
||||||
@ -41,11 +42,15 @@ export const ProjectArchivedIssueDetailsHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -57,21 +62,30 @@ export const ProjectArchivedIssueDetailsHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${projectId}/archived-issues`}
|
||||||
label="Archived Issues"
|
label="Archived Issues"
|
||||||
link={`/${workspaceSlug}/projects/${projectId}/archived-issues`}
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label={
|
label={
|
||||||
`${getProjectById(issueDetails?.project_id || "")?.identifier}-${issueDetails?.sequence_id}` ?? "..."
|
`${getProjectById(issueDetails?.project_id || "")?.identifier}-${issueDetails?.sequence_id}` ??
|
||||||
|
"..."
|
||||||
|
}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
|
@ -11,6 +11,7 @@ import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
|||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown } from "components/issues";
|
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown } from "components/issues";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// types
|
// types
|
||||||
@ -71,7 +72,7 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-14 w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-14 w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div className="block md:hidden">
|
<div className="block md:hidden">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -85,6 +86,10 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -96,14 +101,18 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label="Archived Issues"
|
label="Archived Issues"
|
||||||
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,7 @@ import { useIssues, useLabel, useMember, useProject, useProjectState } from "hoo
|
|||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
@ -75,11 +76,15 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -91,14 +96,15 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
label="Draft Issues"
|
<BreadcrumbLink label="Inbox Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,6 +9,7 @@ import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
|
|||||||
// components
|
// components
|
||||||
import { CreateInboxIssueModal } from "components/inbox";
|
import { CreateInboxIssueModal } from "components/inbox";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// helper
|
// helper
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
@ -24,11 +25,15 @@ export const ProjectInboxHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -40,14 +45,15 @@ export const ProjectInboxHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
label="Inbox Issues"
|
<BreadcrumbLink label="Inbox Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@ import { IssueService } from "services/issue";
|
|||||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const issueService = new IssueService();
|
const issueService = new IssueService();
|
||||||
@ -35,11 +36,15 @@ export const ProjectIssueDetailsHeader: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
renderEmoji(currentProjectDetails.emoji)
|
renderEmoji(currentProjectDetails.emoji)
|
||||||
@ -51,21 +56,30 @@ export const ProjectIssueDetailsHeader: FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${projectId}/issues`}
|
||||||
label="Issues"
|
label="Issues"
|
||||||
link={`/${workspaceSlug}/projects/${projectId}/issues`}
|
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label={
|
label={
|
||||||
`${getProjectById(issueDetails?.project_id || "")?.identifier}-${issueDetails?.sequence_id}` ?? "..."
|
`${getProjectById(issueDetails?.project_id || "")?.identifier}-${issueDetails?.sequence_id}` ??
|
||||||
|
"..."
|
||||||
|
}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
|
@ -9,6 +9,7 @@ import { useApplication, useLabel, useProject, useProjectState, useUser, useInbo
|
|||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { ProjectAnalyticsModal } from "components/analytics";
|
import { ProjectAnalyticsModal } from "components/analytics";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
|
import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
@ -106,7 +107,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div className="block md:hidden">
|
<div className="block md:hidden">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -120,6 +121,10 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects`}
|
||||||
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails ? (
|
currentProjectDetails ? (
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -141,14 +146,13 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
/>
|
||||||
link={`/${workspaceSlug}/projects`}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
link={<BreadcrumbLink label="Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />}
|
||||||
label="Issues"
|
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import { useProject, useUser } from "hooks/store";
|
|||||||
import { EUserProjectRoles, PROJECT_SETTINGS_LINKS } from "constants/project";
|
import { EUserProjectRoles, PROJECT_SETTINGS_LINKS } from "constants/project";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export interface IProjectSettingHeader {
|
export interface IProjectSettingHeader {
|
||||||
title: string;
|
title: string;
|
||||||
@ -38,6 +39,9 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -50,10 +54,11 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="hidden sm:hidden md:block lg:block">
|
<div className="hidden sm:hidden md:block lg:block">
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink label={title} />} />
|
||||||
</div>
|
</div>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
@ -62,13 +67,18 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
|||||||
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
||||||
maxHeight="lg"
|
maxHeight="lg"
|
||||||
customButton={
|
customButton={
|
||||||
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">{title}</span>
|
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
closeOnSelect
|
closeOnSelect
|
||||||
>
|
>
|
||||||
{PROJECT_SETTINGS_LINKS.map((item) => (
|
{PROJECT_SETTINGS_LINKS.map((item) => (
|
||||||
<CustomMenu.MenuItem key={item.key} onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}>
|
<CustomMenu.MenuItem
|
||||||
|
key={item.key}
|
||||||
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}
|
||||||
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, Button, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
import { Breadcrumbs, Button, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
@ -112,6 +113,9 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -128,13 +132,18 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/views`}
|
||||||
label="Views"
|
label="Views"
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/views`}
|
icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="component"
|
type="component"
|
||||||
|
@ -6,6 +6,7 @@ import { useApplication, useProject, useUser } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui";
|
import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
// constants
|
// constants
|
||||||
@ -31,11 +32,14 @@ export const ProjectViewsHeader: React.FC = observer(() => {
|
|||||||
<>
|
<>
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||||
label={currentProjectDetails?.name ?? "Project"}
|
label={currentProjectDetails?.name ?? "Project"}
|
||||||
icon={
|
icon={
|
||||||
currentProjectDetails?.emoji ? (
|
currentProjectDetails?.emoji ? (
|
||||||
@ -52,12 +56,14 @@ export const ProjectViewsHeader: React.FC = observer(() => {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
label="Views"
|
<BreadcrumbLink label="Views" icon={<PhotoFilterIcon className="h-4 w-4 text-custom-text-300" />} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { Breadcrumbs, Button } from "@plane/ui";
|
|||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const ProjectsHeader = observer(() => {
|
export const ProjectsHeader = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
@ -25,13 +26,12 @@ export const ProjectsHeader = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<Briefcase className="h-4 w-4 text-custom-text-300" />}
|
link={<BreadcrumbLink label="Projects" icon={<Briefcase className="h-4 w-4 text-custom-text-300" />} />}
|
||||||
label="Projects"
|
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
|
||||||
export const UserProfileHeader = () => (
|
export const UserProfileHeader = () => (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||||
<SidebarHamburgerToggle/>
|
<SidebarHamburgerToggle />
|
||||||
<div>
|
<div>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label="Activity Overview" link="/profile" />
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink href="/profile" label="Activity Overview" />} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, ContrastIcon } from "@plane/ui";
|
import { Breadcrumbs, ContrastIcon } from "@plane/ui";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
// icons
|
// icons
|
||||||
import { Crown } from "lucide-react";
|
import { Crown } from "lucide-react";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
|
||||||
|
|
||||||
export const WorkspaceActiveCycleHeader = observer(() => (
|
export const WorkspaceActiveCycleHeader = observer(() => (
|
||||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
@ -13,8 +14,12 @@ export const WorkspaceActiveCycleHeader = observer(() => (
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300 rotate-180" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
label="Active Cycles"
|
label="Active Cycles"
|
||||||
|
icon={<ContrastIcon className="h-4 w-4 text-custom-text-300 rotate-180" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
<Crown className="h-3.5 w-3.5 text-amber-400" />
|
<Crown className="h-3.5 w-3.5 text-amber-400" />
|
||||||
|
@ -4,6 +4,7 @@ import { ArrowLeft, BarChart2 } from "lucide-react";
|
|||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export const WorkspaceAnalyticsHeader = () => {
|
export const WorkspaceAnalyticsHeader = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -28,8 +29,9 @@ export const WorkspaceAnalyticsHeader = () => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<BarChart2 className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
label="Analytics"
|
<BreadcrumbLink label="Analytics" icon={<BarChart2 className="h-4 w-4 text-custom-text-300" />} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import { useTheme } from "next-themes";
|
|||||||
import githubBlackImage from "/public/logos/github-black.png";
|
import githubBlackImage from "/public/logos/github-black.png";
|
||||||
import githubWhiteImage from "/public/logos/github-white.png";
|
import githubWhiteImage from "/public/logos/github-white.png";
|
||||||
// components
|
// components
|
||||||
import { ProductUpdatesModal } from "components/common";
|
import { BreadcrumbLink, ProductUpdatesModal } from "components/common";
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
|
|
||||||
@ -25,8 +25,9 @@ export const WorkspaceDashboardHeader = () => {
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<LayoutGrid className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
label="Dashboard"
|
<BreadcrumbLink label="Dashboard" icon={<LayoutGrid className="h-4 w-4 text-custom-text-300" />} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
// components
|
// components
|
||||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||||
import { WORKSPACE_SETTINGS_LINKS } from "constants/workspace";
|
import { WORKSPACE_SETTINGS_LINKS } from "constants/workspace";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export interface IWorkspaceSettingHeader {
|
export interface IWorkspaceSettingHeader {
|
||||||
title: string;
|
title: string;
|
||||||
@ -27,12 +28,16 @@ export const WorkspaceSettingHeader: FC<IWorkspaceSettingHeader> = observer((pro
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href={`/${workspaceSlug}/settings`}
|
||||||
label="Settings"
|
label="Settings"
|
||||||
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
||||||
link={`/${workspaceSlug}/settings`}
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<div className="hidden sm:hidden md:block lg:block">
|
<div className="hidden sm:hidden md:block lg:block">
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink label={title} />} />
|
||||||
</div>
|
</div>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +45,9 @@ export const WorkspaceSettingHeader: FC<IWorkspaceSettingHeader> = observer((pro
|
|||||||
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
||||||
maxHeight="lg"
|
maxHeight="lg"
|
||||||
customButton={
|
customButton={
|
||||||
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">{title}</span>
|
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
closeOnSelect
|
closeOnSelect
|
||||||
|
@ -7,6 +7,8 @@ import { Plus } from "lucide-react";
|
|||||||
import { useApplication, useProject } from "hooks/store";
|
import { useApplication, useProject } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { CustomSelect, Input } from "@plane/ui";
|
import { CustomSelect, Input } from "@plane/ui";
|
||||||
|
// helpers
|
||||||
|
import { checkEmailValidity } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IJiraImporterForm } from "@plane/types";
|
import { IJiraImporterForm } from "@plane/types";
|
||||||
|
|
||||||
@ -46,17 +48,18 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
render={({ field: { value, onChange, ref } }) => (
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
<Input
|
<Input
|
||||||
id="metadata.api_token"
|
id="metadata.api_token"
|
||||||
name="metadata.api_token"
|
|
||||||
type="text"
|
type="text"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
hasError={Boolean(errors.metadata?.api_token)}
|
||||||
placeholder="XXXXXXXX"
|
placeholder="XXXXXXXX"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{errors.metadata?.api_token && <p className="text-red-500 text-xs">{errors.metadata.api_token.message}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -75,7 +78,6 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
render={({ field: { value, onChange, ref } }) => (
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
<Input
|
<Input
|
||||||
id="metadata.project_key"
|
id="metadata.project_key"
|
||||||
name="metadata.project_key"
|
|
||||||
type="text"
|
type="text"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@ -86,6 +88,9 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{errors.metadata?.project_key && (
|
||||||
|
<p className="text-red-500 text-xs">{errors.metadata.project_key.message}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -100,11 +105,11 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
name="metadata.email"
|
name="metadata.email"
|
||||||
rules={{
|
rules={{
|
||||||
required: "Please enter email address.",
|
required: "Please enter email address.",
|
||||||
|
validate: (value) => checkEmailValidity(value) || "Please enter a valid email address",
|
||||||
}}
|
}}
|
||||||
render={({ field: { value, onChange, ref } }) => (
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
<Input
|
<Input
|
||||||
id="metadata.email"
|
id="metadata.email"
|
||||||
name="metadata.email"
|
|
||||||
type="email"
|
type="email"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@ -115,6 +120,7 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{errors.metadata?.email && <p className="text-red-500 text-xs">{errors.metadata.email.message}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -129,12 +135,11 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
name="metadata.cloud_hostname"
|
name="metadata.cloud_hostname"
|
||||||
rules={{
|
rules={{
|
||||||
required: "Please enter your cloud host name.",
|
required: "Please enter your cloud host name.",
|
||||||
|
validate: (value) => !/^https?:\/\//.test(value) || "Hostname should not begin with http:// or https://",
|
||||||
}}
|
}}
|
||||||
render={({ field: { value, onChange, ref } }) => (
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
<Input
|
<Input
|
||||||
id="metadata.cloud_hostname"
|
id="metadata.cloud_hostname"
|
||||||
name="metadata.cloud_hostname"
|
|
||||||
type="email"
|
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -144,6 +149,9 @@ export const JiraGetImportDetail: React.FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{errors.metadata?.cloud_hostname && (
|
||||||
|
<p className="text-red-500 text-xs">{errors.metadata.cloud_hostname.message}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -170,17 +170,6 @@ export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
|
|||||||
// handleClose();
|
// handleClose();
|
||||||
// };
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isOpen || data) return;
|
|
||||||
|
|
||||||
setLocalStorageValue(
|
|
||||||
JSON.stringify({
|
|
||||||
...payload,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [JSON.stringify(payload), isOpen, data]);
|
|
||||||
|
|
||||||
// const onClose = () => {
|
// const onClose = () => {
|
||||||
// handleClose();
|
// handleClose();
|
||||||
// };
|
// };
|
||||||
@ -258,13 +247,7 @@ export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFocus("name");
|
setFocus("name");
|
||||||
|
}, [setFocus]);
|
||||||
reset({
|
|
||||||
...defaultValues,
|
|
||||||
...(prePopulatedData ?? {}),
|
|
||||||
...(data ?? {}),
|
|
||||||
});
|
|
||||||
}, [setFocus, prePopulatedData, reset, data]);
|
|
||||||
|
|
||||||
// update projectId in form when projectId changes
|
// update projectId in form when projectId changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -10,7 +10,7 @@ import { TActivityOperations } from "../root";
|
|||||||
import { TIssueComment } from "@plane/types";
|
import { TIssueComment } from "@plane/types";
|
||||||
// icons
|
// icons
|
||||||
import { Globe2, Lock } from "lucide-react";
|
import { Globe2, Lock } from "lucide-react";
|
||||||
import { useWorkspace } from "hooks/store";
|
import { useMention, useWorkspace } from "hooks/store";
|
||||||
|
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
@ -43,6 +43,8 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
|||||||
const workspaceStore = useWorkspace();
|
const workspaceStore = useWorkspace();
|
||||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
||||||
|
|
||||||
|
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||||
|
|
||||||
// refs
|
// refs
|
||||||
const editorRef = useRef<any>(null);
|
const editorRef = useRef<any>(null);
|
||||||
// react hook form
|
// react hook form
|
||||||
@ -61,7 +63,14 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
// onKeyDown={(e) => {
|
||||||
|
// if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
// e.preventDefault();
|
||||||
|
// // handleSubmit(onSubmit)(e);
|
||||||
|
// }
|
||||||
|
// }}
|
||||||
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="access"
|
name="access"
|
||||||
control={control}
|
control={control}
|
||||||
@ -72,6 +81,7 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
|||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<LiteTextEditorWithRef
|
<LiteTextEditorWithRef
|
||||||
onEnterKeyPress={(e) => {
|
onEnterKeyPress={(e) => {
|
||||||
|
console.log("yo");
|
||||||
handleSubmit(onSubmit)(e);
|
handleSubmit(onSubmit)(e);
|
||||||
}}
|
}}
|
||||||
cancelUploadImage={fileService.cancelUpload}
|
cancelUploadImage={fileService.cancelUpload}
|
||||||
@ -86,6 +96,8 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
|||||||
onChange={(comment_json: Object, comment_html: string) => {
|
onChange={(comment_json: Object, comment_html: string) => {
|
||||||
onChange(comment_html);
|
onChange(comment_html);
|
||||||
}}
|
}}
|
||||||
|
mentionSuggestions={mentionSuggestions}
|
||||||
|
mentionHighlights={mentionHighlights}
|
||||||
commentAccessSpecifier={
|
commentAccessSpecifier={
|
||||||
showAccessSpecifier
|
showAccessSpecifier
|
||||||
? { accessValue: accessValue ?? "INTERNAL", onAccessChange, showAccessSpecifier, commentAccess }
|
? { accessValue: accessValue ?? "INTERNAL", onAccessChange, showAccessSpecifier, commentAccess }
|
||||||
|
@ -61,7 +61,8 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||||||
projectId?.toString(),
|
projectId?.toString(),
|
||||||
issueStore,
|
issueStore,
|
||||||
issueMap,
|
issueMap,
|
||||||
groupedIssueIds
|
groupedIssueIds,
|
||||||
|
viewId
|
||||||
).catch((err) => {
|
).catch((err) => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
|
@ -2,7 +2,7 @@ import { useState } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useIssueDetail, useIssues, useUser } from "hooks/store";
|
import { useApplication, useIssues, useUser } from "hooks/store";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
@ -29,7 +29,6 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
|
|||||||
const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false);
|
const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { issues } = useIssues(EIssuesStoreType.MODULE);
|
const { issues } = useIssues(EIssuesStoreType.MODULE);
|
||||||
const { updateIssue, fetchIssue } = useIssueDetail();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
commandPalette: { toggleCreateIssueModal },
|
commandPalette: { toggleCreateIssueModal },
|
||||||
|
@ -97,6 +97,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
|||||||
portalElement={portalElement}
|
portalElement={portalElement}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[handleIssues]
|
[handleIssues]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
parent_id: formData.parent_id,
|
parent_id: formData.parent_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [projectId]);
|
}, [projectId]);
|
||||||
|
|
||||||
const issueName = watch("name");
|
const issueName = watch("name");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FC, useEffect } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useProject, useWorkspace } from "hooks/store";
|
import { useApplication, useProject, useWorkspace } from "hooks/store";
|
||||||
@ -29,6 +29,8 @@ const projectService = new ProjectService();
|
|||||||
|
|
||||||
export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
||||||
const { project, workspaceSlug, isAdmin } = props;
|
const { project, workspaceSlug, isAdmin } = props;
|
||||||
|
// states
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
eventTracker: { postHogEventTracker },
|
eventTracker: { postHogEventTracker },
|
||||||
@ -45,7 +47,7 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
|||||||
setValue,
|
setValue,
|
||||||
setError,
|
setError,
|
||||||
reset,
|
reset,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors },
|
||||||
} = useForm<IProject>({
|
} = useForm<IProject>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
...project,
|
...project,
|
||||||
@ -114,6 +116,7 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
|||||||
|
|
||||||
const onSubmit = async (formData: IProject) => {
|
const onSubmit = async (formData: IProject) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
const payload: Partial<IProject> = {
|
const payload: Partial<IProject> = {
|
||||||
name: formData.name,
|
name: formData.name,
|
||||||
@ -139,6 +142,10 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
|||||||
else await handleUpdateChange(payload);
|
else await handleUpdateChange(payload);
|
||||||
});
|
});
|
||||||
else await handleUpdateChange(payload);
|
else await handleUpdateChange(payload);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentNetwork = NETWORK_CHOICES.find((n) => n.key === project?.network);
|
const currentNetwork = NETWORK_CHOICES.find((n) => n.key === project?.network);
|
||||||
@ -308,8 +315,8 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
|||||||
|
|
||||||
<div className="flex items-center justify-between py-2">
|
<div className="flex items-center justify-between py-2">
|
||||||
<>
|
<>
|
||||||
<Button variant="primary" type="submit" loading={isSubmitting} disabled={!isAdmin}>
|
<Button variant="primary" type="submit" loading={isLoading} disabled={!isAdmin}>
|
||||||
{isSubmitting ? "Updating" : "Update project"}
|
{isLoading ? "Updating..." : "Update project"}
|
||||||
</Button>
|
</Button>
|
||||||
<span className="text-sm italic text-custom-sidebar-text-400">
|
<span className="text-sm italic text-custom-sidebar-text-400">
|
||||||
Created on {renderFormattedDate(project?.created_at)}
|
Created on {renderFormattedDate(project?.created_at)}
|
||||||
|
@ -32,6 +32,7 @@ const fileService = new FileService();
|
|||||||
|
|
||||||
export const WorkspaceDetails: FC = observer(() => {
|
export const WorkspaceDetails: FC = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false);
|
const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false);
|
||||||
const [isImageRemoving, setIsImageRemoving] = useState(false);
|
const [isImageRemoving, setIsImageRemoving] = useState(false);
|
||||||
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
||||||
@ -51,7 +52,7 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
watch,
|
watch,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors },
|
||||||
} = useForm<IWorkspace>({
|
} = useForm<IWorkspace>({
|
||||||
defaultValues: { ...defaultValues, ...currentWorkspace },
|
defaultValues: { ...defaultValues, ...currentWorkspace },
|
||||||
});
|
});
|
||||||
@ -59,6 +60,8 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||||||
const onSubmit = async (formData: IWorkspace) => {
|
const onSubmit = async (formData: IWorkspace) => {
|
||||||
if (!currentWorkspace) return;
|
if (!currentWorkspace) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
const payload: Partial<IWorkspace> = {
|
const payload: Partial<IWorkspace> = {
|
||||||
logo: formData.logo,
|
logo: formData.logo,
|
||||||
name: formData.name,
|
name: formData.name,
|
||||||
@ -83,6 +86,9 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||||||
});
|
});
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveLogo = () => {
|
const handleRemoveLogo = () => {
|
||||||
@ -289,8 +295,8 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div className="flex items-center justify-between py-2">
|
<div className="flex items-center justify-between py-2">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isLoading}>
|
||||||
{isSubmitting ? "Updating..." : "Update Workspace"}
|
{isLoading ? "Updating..." : "Update Workspace"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { Settings } from "lucide-react";
|
import { Settings } from "lucide-react";
|
||||||
|
import { BreadcrumbLink } from "components/common";
|
||||||
|
|
||||||
export interface IInstanceAdminHeader {
|
export interface IInstanceAdminHeader {
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -21,11 +22,15 @@ export const InstanceAdminHeader: FC<IInstanceAdminHeader> = observer((props) =>
|
|||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
link={
|
||||||
|
<BreadcrumbLink
|
||||||
|
href="/god-mode"
|
||||||
label="Settings"
|
label="Settings"
|
||||||
link="/god-mode"
|
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
||||||
/>
|
/>
|
||||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
}
|
||||||
|
/>
|
||||||
|
<Breadcrumbs.BreadcrumbItem type="text" link={<BreadcrumbLink label={title} />} />
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -47,7 +47,7 @@ export const AppProvider: FC<IAppProvider> = observer((props) => {
|
|||||||
<InstanceLayout>
|
<InstanceLayout>
|
||||||
<StoreWrapper>
|
<StoreWrapper>
|
||||||
<CrispWrapper user={currentUser}>
|
<CrispWrapper user={currentUser}>
|
||||||
{/* <PosthogWrapper
|
<PosthogWrapper
|
||||||
user={currentUser}
|
user={currentUser}
|
||||||
workspaceRole={currentWorkspaceRole}
|
workspaceRole={currentWorkspaceRole}
|
||||||
projectRole={currentProjectRole}
|
projectRole={currentProjectRole}
|
||||||
@ -55,8 +55,7 @@ export const AppProvider: FC<IAppProvider> = observer((props) => {
|
|||||||
posthogHost={envConfig?.posthog_host || null}
|
posthogHost={envConfig?.posthog_host || null}
|
||||||
>
|
>
|
||||||
<SWRConfig value={SWR_CONFIG}>{children}</SWRConfig>
|
<SWRConfig value={SWR_CONFIG}>{children}</SWRConfig>
|
||||||
</PosthogWrapper> */}
|
</PosthogWrapper>
|
||||||
<SWRConfig value={SWR_CONFIG}>{children}</SWRConfig>
|
|
||||||
</CrispWrapper>
|
</CrispWrapper>
|
||||||
</StoreWrapper>
|
</StoreWrapper>
|
||||||
</InstanceLayout>
|
</InstanceLayout>
|
||||||
|
@ -40,9 +40,9 @@ const PosthogWrapper: FC<IPosthogWrapper> = (props) => {
|
|||||||
posthog.init(posthogAPIKey, {
|
posthog.init(posthogAPIKey, {
|
||||||
api_host: posthogHost || "https://app.posthog.com",
|
api_host: posthogHost || "https://app.posthog.com",
|
||||||
// Enable debug mode in development
|
// Enable debug mode in development
|
||||||
loaded: (posthog) => {
|
// loaded: (posthog) => {
|
||||||
if (process.env.NODE_ENV === "development") posthog.debug();
|
// if (process.env.NODE_ENV === "development") posthog.debug();
|
||||||
},
|
// },
|
||||||
autocapture: false,
|
autocapture: false,
|
||||||
capture_pageview: false, // Disable automatic pageview capture, as we capture manually
|
capture_pageview: false, // Disable automatic pageview capture, as we capture manually
|
||||||
});
|
});
|
||||||
|
@ -39,6 +39,7 @@ const fileService = new FileService();
|
|||||||
|
|
||||||
const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isRemoving, setIsRemoving] = useState(false);
|
const [isRemoving, setIsRemoving] = useState(false);
|
||||||
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
||||||
const [deactivateAccountModal, setDeactivateAccountModal] = useState(false);
|
const [deactivateAccountModal, setDeactivateAccountModal] = useState(false);
|
||||||
@ -48,7 +49,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
reset,
|
reset,
|
||||||
watch,
|
watch,
|
||||||
control,
|
control,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors },
|
||||||
} = useForm<IUser>({ defaultValues });
|
} = useForm<IUser>({ defaultValues });
|
||||||
// toast alert
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
@ -62,6 +63,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
}, [myProfile, reset]);
|
}, [myProfile, reset]);
|
||||||
|
|
||||||
const onSubmit = async (formData: IUser) => {
|
const onSubmit = async (formData: IUser) => {
|
||||||
|
setIsLoading(true);
|
||||||
const payload: Partial<IUser> = {
|
const payload: Partial<IUser> = {
|
||||||
first_name: formData.first_name,
|
first_name: formData.first_name,
|
||||||
last_name: formData.last_name,
|
last_name: formData.last_name,
|
||||||
@ -87,6 +89,9 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
message: "There was some error in updating your profile. Please try again.",
|
message: "There was some error in updating your profile. Please try again.",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (url: string | null | undefined, updateUser: boolean = false) => {
|
const handleDelete = (url: string | null | undefined, updateUser: boolean = false) => {
|
||||||
@ -388,8 +393,8 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between py-2">
|
<div className="flex items-center justify-between py-2">
|
||||||
<Button variant="primary" type="submit" loading={isSubmitting}>
|
<Button variant="primary" type="submit" loading={isLoading}>
|
||||||
{isSubmitting ? "Saving..." : "Save changes"}
|
{isLoading ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -200,6 +200,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
|||||||
if (!cycleId) throw new Error("Cycle Id is required");
|
if (!cycleId) throw new Error("Cycle Id is required");
|
||||||
|
|
||||||
const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||||
|
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||||
@ -267,9 +268,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
|||||||
});
|
});
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
update(this.issues, cycleId, (cycleIssueIds = []) => {
|
update(this.issues, cycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, issueIds)));
|
||||||
return uniq(concat(cycleIssueIds, issueIds));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
issueIds.forEach((issueId) => {
|
issueIds.forEach((issueId) => {
|
||||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
||||||
|
@ -205,6 +205,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
|||||||
if (!moduleId) throw new Error("Module Id is required");
|
if (!moduleId) throw new Error("Module Id is required");
|
||||||
|
|
||||||
const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||||
|
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchIssues(workspaceSlug, projectId, "mutation", moduleId);
|
this.fetchIssues(workspaceSlug, projectId, "mutation", moduleId);
|
||||||
|
@ -38,6 +38,8 @@ export interface IIssueRootStore {
|
|||||||
members: string[] | undefined;
|
members: string[] | undefined;
|
||||||
projects: string[] | undefined;
|
projects: string[] | undefined;
|
||||||
|
|
||||||
|
rootStore: RootStore;
|
||||||
|
|
||||||
issues: IIssueStore;
|
issues: IIssueStore;
|
||||||
|
|
||||||
state: IStateStore;
|
state: IStateStore;
|
||||||
@ -87,6 +89,8 @@ export class IssueRootStore implements IIssueRootStore {
|
|||||||
members: string[] | undefined = undefined;
|
members: string[] | undefined = undefined;
|
||||||
projects: string[] | undefined = undefined;
|
projects: string[] | undefined = undefined;
|
||||||
|
|
||||||
|
rootStore: RootStore;
|
||||||
|
|
||||||
issues: IIssueStore;
|
issues: IIssueStore;
|
||||||
|
|
||||||
state: IStateStore;
|
state: IStateStore;
|
||||||
@ -136,6 +140,8 @@ export class IssueRootStore implements IIssueRootStore {
|
|||||||
projects: observable,
|
projects: observable,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.rootStore = rootStore;
|
||||||
|
|
||||||
autorun(() => {
|
autorun(() => {
|
||||||
if (rootStore.user.currentUser?.id) this.currentUserId = rootStore.user.currentUser?.id;
|
if (rootStore.user.currentUser?.id) this.currentUserId = rootStore.user.currentUser?.id;
|
||||||
if (rootStore.app.router.workspaceSlug) this.workspaceSlug = rootStore.app.router.workspaceSlug;
|
if (rootStore.app.router.workspaceSlug) this.workspaceSlug = rootStore.app.router.workspaceSlug;
|
||||||
|
Loading…
Reference in New Issue
Block a user