Compare commits

...

5 Commits

Author SHA1 Message Date
gurusainath
80937780cc chore: hanlded popover close from prop 2024-07-19 15:35:02 +05:30
gurusainath
1c46acbc66 Merge branch 'preview' of gurusainath:makeplane/plane into chore-issue_activity_store 2024-07-19 15:27:41 +05:30
gurusainath
d451cca4f3 chore: added paramenter on the issue worklog component 2024-07-19 15:02:51 +05:30
gurusainath
f0ed24647d chore: updated issue activity store and handled workspace settings order 2024-07-19 13:21:29 +05:30
gurusainath
8c69dc6609 chore: issue activity store 2024-07-18 21:34:19 +05:30
10 changed files with 113 additions and 90 deletions

View File

@@ -18,6 +18,7 @@ export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
popoverClassName = "",
keyExtractor,
render,
popoverButtonRef,
} = props;
return (
@@ -32,6 +33,7 @@ export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
panelClassName
)}
popoverClassName={popoverClassName}
popoverButtonRef={popoverButtonRef}
>
<Fragment>
{data.map((item, index) => (

View File

@@ -1,4 +1,4 @@
import React, { Fragment, useState } from "react";
import React, { Fragment, Ref, useState } from "react";
import { usePopper } from "react-popper";
import { Popover as HeadlessReactPopover, Transition } from "@headlessui/react";
// helpers
@@ -17,9 +17,10 @@ export const Popover = (props: TPopover) => {
disabled = false,
panelClassName = "",
children,
popoverButtonRef,
} = props;
// states
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
// react-popper derived values
@@ -37,19 +38,21 @@ export const Popover = (props: TPopover) => {
return (
<HeadlessReactPopover className={cn("relative flex h-full w-full items-center justify-center", popoverClassName)}>
<HeadlessReactPopover.Button
ref={setReferenceElement}
className={cn(
{
"flex justify-center items-center text-base h-6 w-6 rounded transition-all bg-custom-background-90 hover:bg-custom-background-80":
!button,
},
buttonClassName
)}
disabled={disabled}
>
{button ? button : <EllipsisVertical className="h-3 w-3" />}
</HeadlessReactPopover.Button>
<div ref={setReferenceElement} className="w-full">
<HeadlessReactPopover.Button
ref={popoverButtonRef as Ref<HTMLButtonElement>}
className={cn(
{
"flex justify-center items-center text-base h-6 w-6 rounded transition-all bg-custom-background-90 hover:bg-custom-background-80":
!button,
},
buttonClassName
)}
disabled={disabled}
>
{button ? button : <EllipsisVertical className="h-3 w-3" />}
</HeadlessReactPopover.Button>
</div>
<Transition
as={Fragment}

View File

@@ -1,4 +1,4 @@
import { ReactNode } from "react";
import { MutableRefObject, ReactNode } from "react";
import { Placement } from "@popperjs/core";
export type TPopoverButtonDefaultOptions = {
@@ -15,6 +15,7 @@ export type TPopoverDefaultOptions = TPopoverButtonDefaultOptions & {
// panel styling
panelClassName?: string;
popoverClassName?: string;
popoverButtonRef?: MutableRefObject<HTMLButtonElement | null>;
};
export type TPopover = TPopoverDefaultOptions & {

View File

@@ -8,6 +8,7 @@ type TIssueActivityWorklog = {
projectId: string;
issueId: string;
activityComment: TIssueActivityComment;
ends?: "top" | "bottom";
};
export const IssueActivityWorklog: FC<TIssueActivityWorklog> = () => <></>;

View File

@@ -4,6 +4,57 @@ import { Props } from "@/components/icons/types";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
export const WORKSPACE_SETTINGS = {
general: {
key: "general",
label: "General",
href: `/settings`,
access: EUserWorkspaceRoles.GUEST,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
Icon: SettingIcon,
},
members: {
key: "members",
label: "Members",
href: `/settings/members`,
access: EUserWorkspaceRoles.GUEST,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
Icon: SettingIcon,
},
"billing-and-plans": {
key: "billing-and-plans",
label: "Billing and plans",
href: `/settings/billing`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
Icon: SettingIcon,
},
export: {
key: "export",
label: "Exports",
href: `/settings/exports`,
access: EUserWorkspaceRoles.MEMBER,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
Icon: SettingIcon,
},
webhooks: {
key: "webhooks",
label: "Webhooks",
href: `/settings/webhooks`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
Icon: SettingIcon,
},
"api-tokens": {
key: "api-tokens",
label: "API tokens",
href: `/settings/api-tokens`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`,
Icon: SettingIcon,
},
};
export const WORKSPACE_SETTINGS_LINKS: {
key: string;
label: string;
@@ -12,52 +63,10 @@ export const WORKSPACE_SETTINGS_LINKS: {
highlight: (pathname: string, baseUrl: string) => boolean;
Icon: React.FC<Props>;
}[] = [
{
key: "general",
label: "General",
href: `/settings`,
access: EUserWorkspaceRoles.GUEST,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
Icon: SettingIcon,
},
{
key: "members",
label: "Members",
href: `/settings/members`,
access: EUserWorkspaceRoles.GUEST,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
Icon: SettingIcon,
},
{
key: "billing-and-plans",
label: "Billing and plans",
href: `/settings/billing`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
Icon: SettingIcon,
},
{
key: "export",
label: "Exports",
href: `/settings/exports`,
access: EUserWorkspaceRoles.MEMBER,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
Icon: SettingIcon,
},
{
key: "webhooks",
label: "Webhooks",
href: `/settings/webhooks`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
Icon: SettingIcon,
},
{
key: "api-tokens",
label: "API tokens",
href: `/settings/api-tokens`,
access: EUserWorkspaceRoles.ADMIN,
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`,
Icon: SettingIcon,
},
WORKSPACE_SETTINGS["general"],
WORKSPACE_SETTINGS["members"],
WORKSPACE_SETTINGS["billing-and-plans"],
WORKSPACE_SETTINGS["export"],
WORKSPACE_SETTINGS["webhooks"],
WORKSPACE_SETTINGS["api-tokens"],
];

View File

@@ -1,3 +1,5 @@
/* eslint-disable no-useless-catch */
import concat from "lodash/concat";
import set from "lodash/set";
import sortBy from "lodash/sortBy";
@@ -5,10 +7,12 @@ import uniq from "lodash/uniq";
import update from "lodash/update";
import { action, makeObservable, observable, runInAction } from "mobx";
import { TIssueActivityComment, TIssueActivity, TIssueActivityMap, TIssueActivityIdMap } from "@plane/types";
// plane web constants
import { EActivityFilterType } from "@/plane-web/constants/issues";
// plane web store types
import { RootStore } from "@/plane-web/store/root.store";
// services
import { IssueActivityService } from "@/services/issue";
// types
import { IIssueDetail } from "./root.store";
export type TActivityLoader = "fetch" | "mutate" | undefined;
@@ -38,12 +42,11 @@ export class IssueActivityStore implements IIssueActivityStore {
loader: TActivityLoader = "fetch";
activities: TIssueActivityIdMap = {};
activityMap: TIssueActivityMap = {};
// root store
rootIssueDetailStore: IIssueDetail;
// services
issueActivityService;
constructor(rootStore: IIssueDetail) {
constructor(protected store: RootStore) {
makeObservable(this, {
// observables
loader: observable.ref,
@@ -52,8 +55,6 @@ export class IssueActivityStore implements IIssueActivityStore {
// actions
fetchActivities: action,
});
// root store
this.rootIssueDetailStore = rootStore;
// services
this.issueActivityService = new IssueActivityService();
}
@@ -69,50 +70,46 @@ export class IssueActivityStore implements IIssueActivityStore {
return this.activityMap[activityId] ?? undefined;
};
getActivityCommentByIssueId = (issueId: string) => {
public getActivityCommentByIssueId(issueId: string) {
if (!issueId) return undefined;
let activityComments: TIssueActivityComment[] = [];
const activities = this.getActivitiesByIssueId(issueId) || [];
const comments = this.rootIssueDetailStore.comment.getCommentsByIssueId(issueId) || [];
const comments = this.store.issue.issueDetail.comment.getCommentsByIssueId(issueId) || [];
activities.forEach((activityId) => {
const activity = this.getActivityById(activityId);
if (!activity) return;
activityComments.push({
id: activity.id,
activity_type: "ACTIVITY",
activity_type: EActivityFilterType.ACTIVITY,
created_at: activity.created_at,
});
});
comments.forEach((commentId) => {
const comment = this.rootIssueDetailStore.comment.getCommentById(commentId);
const comment = this.store.issue.issueDetail.comment.getCommentById(commentId);
if (!comment) return;
activityComments.push({
id: comment.id,
activity_type: "COMMENT",
activity_type: EActivityFilterType.COMMENT,
created_at: comment.created_at,
});
});
activityComments = sortBy(activityComments, "created_at");
activityComments = activityComments.map((activityComment) => ({
id: activityComment.id,
activity_type: activityComment.activity_type,
}));
return activityComments;
};
}
// actions
fetchActivities = async (
public async fetchActivities(
workspaceSlug: string,
projectId: string,
issueId: string,
loaderType: TActivityLoader = "fetch"
) => {
) {
try {
this.loader = loaderType;
@@ -140,7 +137,8 @@ export class IssueActivityStore implements IIssueActivityStore {
return activities;
} catch (error) {
this.loader = undefined;
throw error;
}
};
}
}

View File

@@ -62,6 +62,7 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
projectId={projectId}
issueId={issueId}
activityComment={activityComment}
ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
/>
) : (
<></>

View File

@@ -10,8 +10,14 @@ import {
TIssueRelationTypes,
TIssueDetailWidget,
} from "@plane/types";
// plane web store
import {
IIssueActivityStore,
IssueActivityStore,
IIssueActivityStoreActions,
TActivityLoader,
} from "@/plane-web/store/issue/issue-details/activity.store";
import { IIssueRootStore } from "../root.store";
import { IIssueActivityStore, IssueActivityStore, IIssueActivityStoreActions, TActivityLoader } from "./activity.store";
import { IIssueAttachmentStore, IssueAttachmentStore, IIssueAttachmentStoreActions } from "./attachment.store";
import { IIssueCommentStore, IssueCommentStore, IIssueCommentStoreActions, TCommentLoader } from "./comment.store";
import {
@@ -187,7 +193,7 @@ export class IssueDetail implements IIssueDetail {
this.issue = new IssueStore(this);
this.reaction = new IssueReactionStore(this);
this.attachment = new IssueAttachmentStore(rootStore);
this.activity = new IssueActivityStore(this);
this.activity = new IssueActivityStore(rootStore.rootStore);
this.comment = new IssueCommentStore(this);
this.commentReaction = new IssueCommentReactionStore(this);
this.subIssues = new IssueSubIssuesStore(this);

View File

@@ -1,9 +1,10 @@
import isEmpty from "lodash/isEmpty";
import { autorun, makeObservable, observable } from "mobx";
import { ICycle, IIssueLabel, IModule, IProject, IState, IUserLite } from "@plane/types";
// plane web root store
import { RootStore } from "@/plane-web/store/root.store";
// root store
import { IWorkspaceMembership } from "@/store/member/workspace-member.store";
import { CoreRootStore } from "../root.store";
import { IStateStore, StateStore } from "../state.store";
// issues data store
import { IArchivedIssuesFilter, ArchivedIssuesFilter, IArchivedIssues, ArchivedIssues } from "./archived";
@@ -43,7 +44,7 @@ export interface IIssueRootStore {
moduleMap: Record<string, IModule> | undefined;
cycleMap: Record<string, ICycle> | undefined;
rootStore: CoreRootStore;
rootStore: RootStore;
issues: IIssueStore;
@@ -98,7 +99,7 @@ export class IssueRootStore implements IIssueRootStore {
moduleMap: Record<string, IModule> | undefined = undefined;
cycleMap: Record<string, ICycle> | undefined = undefined;
rootStore: CoreRootStore;
rootStore: RootStore;
issues: IIssueStore;
@@ -133,7 +134,7 @@ export class IssueRootStore implements IIssueRootStore {
issueKanBanView: IIssueKanBanViewStore;
issueCalendarView: ICalendarStore;
constructor(rootStore: CoreRootStore) {
constructor(rootStore: RootStore) {
makeObservable(this, {
workspaceSlug: observable.ref,
projectId: observable.ref,

View File

@@ -0,0 +1 @@
export * from "ce/store/issue/issue-details/activity.store";