Merge branch 'develop' of gurusainath:makeplane/plane into feat/mobx-global-views

This commit is contained in:
gurusainath 2024-02-02 12:11:01 +05:30
commit 90bcdeccf4
59 changed files with 647 additions and 530 deletions

View File

@ -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)

View File

@ -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}"

View File

@ -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">

View File

@ -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 its 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());
},
}),
];
},
});

View File

@ -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,

View File

@ -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: {

View File

@ -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();
}, },

View File

@ -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,

View File

@ -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 }) => [

View File

@ -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),
];

View File

@ -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;

View File

@ -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;

View File

@ -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>

View 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>
);
};

View File

@ -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";

View File

@ -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 !== "" && (

View File

@ -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 && (

View File

@ -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>
) : ( ) : (
"" ""

View File

@ -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";

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
))} ))}

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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(() => {

View File

@ -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 }

View File

@ -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",

View File

@ -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 },

View File

@ -97,6 +97,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
portalElement={portalElement} portalElement={portalElement}
/> />
), ),
// eslint-disable-next-line react-hooks/exhaustive-deps
[handleIssues] [handleIssues]
); );

View File

@ -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");

View File

@ -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)}

View File

@ -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>
)} )}

View File

@ -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>
)} )}

View File

@ -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>

View File

@ -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
}); });

View File

@ -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>

View File

@ -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 });

View File

@ -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);

View File

@ -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;