[WEB-1883] chore: moved workspace settings to respective folders for CE and EE (#5151)

* chore: moved workspace settings to respective folders for ce and ee

* chore: updated imports

* chore: updated imports for ee

* chore: resolved import error

* chore: resolved import error

* chore: ee imports in the issue sidebar

* chore: updated file structure

* chore: table UI

* chore: resolved build errors

* chore: added worklog on issue peekoverview
This commit is contained in:
guru_sainath
2024-07-18 14:45:30 +05:30
committed by GitHub
parent fff27c60e4
commit 482b363045
44 changed files with 440 additions and 285 deletions

View File

@@ -55,4 +55,9 @@ export type TIssueActivityComment =
id: string;
activity_type: "ACTIVITY";
created_at?: string;
}
| {
id: string;
activity_type: "WORKLOG";
created_at?: string;
};

View File

@@ -1,199 +1,199 @@
import {EUserWorkspaceRoles} from "@/constants/workspace";
import { EUserWorkspaceRoles } from "@/constants/workspace";
import type {
IProjectMember,
IUser,
IUserLite,
IWorkspaceViewProps,
IProjectMember,
IUser,
IUserLite,
IWorkspaceViewProps,
} from "@plane/types";
export interface IWorkspace {
readonly id: string;
readonly owner: IUser;
readonly created_at: Date;
readonly updated_at: Date;
name: string;
url: string;
logo: string | null;
slug: string;
readonly total_members: number;
readonly slug: string;
readonly created_by: string;
readonly updated_by: string;
organization_size: string;
total_issues: number;
readonly id: string;
readonly owner: IUser;
readonly created_at: Date;
readonly updated_at: Date;
name: string;
url: string;
logo: string | null;
slug: string;
readonly total_members: number;
readonly slug: string;
readonly created_by: string;
readonly updated_by: string;
organization_size: string;
total_issues: number;
}
export interface IWorkspaceLite {
readonly id: string;
name: string;
slug: string;
readonly id: string;
name: string;
slug: string;
}
export interface IWorkspaceMemberInvitation {
accepted: boolean;
email: string;
id: string;
message: string;
responded_at: Date;
role: EUserWorkspaceRoles;
token: string;
workspace: {
id: string;
logo: string;
name: string;
slug: string;
};
accepted: boolean;
email: string;
id: string;
message: string;
responded_at: Date;
role: EUserWorkspaceRoles;
token: string;
workspace: {
id: string;
logo: string;
name: string;
slug: string;
};
}
export interface IWorkspaceBulkInviteFormData {
emails: {email: string; role: EUserWorkspaceRoles}[];
emails: { email: string; role: EUserWorkspaceRoles }[];
}
export type Properties = {
assignee: boolean;
start_date: boolean;
due_date: boolean;
labels: boolean;
key: boolean;
priority: boolean;
state: boolean;
sub_issue_count: boolean;
link: boolean;
attachment_count: boolean;
estimate: boolean;
created_on: boolean;
updated_on: boolean;
assignee: boolean;
start_date: boolean;
due_date: boolean;
labels: boolean;
key: boolean;
priority: boolean;
state: boolean;
sub_issue_count: boolean;
link: boolean;
attachment_count: boolean;
estimate: boolean;
created_on: boolean;
updated_on: boolean;
};
export interface IWorkspaceMember {
id: string;
member: IUserLite;
role: EUserWorkspaceRoles;
created_at: string;
avatar?: string;
email?: string;
first_name?: string;
last_name?: string;
joining_date: string;
display_name?: string;
id: string;
member: IUserLite;
role: EUserWorkspaceRoles;
created_at?: string;
avatar?: string;
email?: string;
first_name?: string;
last_name?: string;
joining_date?: string;
display_name?: string;
}
export interface IWorkspaceMemberMe {
company_role: string | null;
created_at: Date;
created_by: string;
default_props: IWorkspaceViewProps;
id: string;
member: string;
role: EUserWorkspaceRoles;
updated_at: Date;
updated_by: string;
view_props: IWorkspaceViewProps;
workspace: string;
company_role: string | null;
created_at: Date;
created_by: string;
default_props: IWorkspaceViewProps;
id: string;
member: string;
role: EUserWorkspaceRoles;
updated_at: Date;
updated_by: string;
view_props: IWorkspaceViewProps;
workspace: string;
}
export interface ILastActiveWorkspaceDetails {
workspace_details: IWorkspace;
project_details?: IProjectMember[];
workspace_details: IWorkspace;
project_details?: IProjectMember[];
}
export interface IWorkspaceDefaultSearchResult {
id: string;
name: string;
project_id: string;
project__identifier: string;
workspace__slug: string;
id: string;
name: string;
project_id: string;
project__identifier: string;
workspace__slug: string;
}
export interface IWorkspaceSearchResult {
id: string;
name: string;
slug: string;
id: string;
name: string;
slug: string;
}
export interface IWorkspaceIssueSearchResult {
id: string;
name: string;
project__identifier: string;
project_id: string;
sequence_id: number;
workspace__slug: string;
id: string;
name: string;
project__identifier: string;
project_id: string;
sequence_id: number;
workspace__slug: string;
}
export interface IWorkspacePageSearchResult {
id: string;
name: string;
project_ids: string[];
project__identifiers: string[];
workspace__slug: string;
id: string;
name: string;
project_ids: string[];
project__identifiers: string[];
workspace__slug: string;
}
export interface IWorkspaceProjectSearchResult {
id: string;
identifier: string;
name: string;
workspace__slug: string;
id: string;
identifier: string;
name: string;
workspace__slug: string;
}
export interface IWorkspaceSearchResults {
results: {
workspace: IWorkspaceSearchResult[];
project: IWorkspaceProjectSearchResult[];
issue: IWorkspaceIssueSearchResult[];
cycle: IWorkspaceDefaultSearchResult[];
module: IWorkspaceDefaultSearchResult[];
issue_view: IWorkspaceDefaultSearchResult[];
page: IWorkspacePageSearchResult[];
};
results: {
workspace: IWorkspaceSearchResult[];
project: IWorkspaceProjectSearchResult[];
issue: IWorkspaceIssueSearchResult[];
cycle: IWorkspaceDefaultSearchResult[];
module: IWorkspaceDefaultSearchResult[];
issue_view: IWorkspaceDefaultSearchResult[];
page: IWorkspacePageSearchResult[];
};
}
export interface IProductUpdateResponse {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
author: {
login: string;
id: string;
node_id: string;
avatar_url: string;
gravatar_id: "";
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: false;
};
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: true;
created_at: string;
published_at: string;
assets: [];
tarball_url: string;
zipball_url: string;
body: string;
reactions: {
url: string;
total_count: number;
"+1": number;
"-1": number;
laugh: number;
hooray: number;
confused: number;
heart: number;
rocket: number;
eyes: number;
};
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
author: {
login: string;
id: string;
node_id: string;
avatar_url: string;
gravatar_id: "";
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: false;
};
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: true;
created_at: string;
published_at: string;
assets: [];
tarball_url: string;
zipball_url: string;
body: string;
reactions: {
url: string;
total_count: number;
"+1": number;
"-1": number;
laugh: number;
hooray: number;
confused: number;
heart: number;
rocket: number;
eyes: number;
};
}

View File

@@ -12,6 +12,7 @@ export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
popperPadding = 0,
buttonClassName = "",
button,
disabled,
panelClassName = "",
data,
popoverClassName = "",
@@ -25,6 +26,7 @@ export const PopoverMenu = <T,>(props: TPopoverMenu<T>) => {
popperPadding={popperPadding}
buttonClassName={buttonClassName}
button={button}
disabled={disabled}
panelClassName={cn(
"my-1 w-48 rounded border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2 text-xs shadow-custom-shadow-rg focus:outline-none",
panelClassName

View File

@@ -14,6 +14,7 @@ export const Popover = (props: TPopover) => {
buttonClassName = "",
popoverClassName = "",
button,
disabled = false,
panelClassName = "",
children,
} = props;
@@ -36,19 +37,18 @@ 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="flex justify-center items-center">
{button ? (
button
) : (
<div
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",
buttonClassName
)}
>
<EllipsisVertical className="h-3 w-3" />
</div>
<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>
<Transition

View File

@@ -5,6 +5,7 @@ export type TPopoverButtonDefaultOptions = {
// button and button styling
button?: ReactNode;
buttonClassName?: string;
disabled?: boolean;
};
export type TPopoverDefaultOptions = TPopoverButtonDefaultOptions & {

View File

@@ -1,8 +1,8 @@
import { useParams, usePathname } from "next/navigation";
// constants
import { WORKSPACE_SETTINGS_LINKS } from "@/constants/workspace";
// hooks
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
export const MobileWorkspaceSettingsTabs = () => {
const router = useAppRouter();
@@ -12,10 +12,11 @@ export const MobileWorkspaceSettingsTabs = () => {
<div className="flex-shrink-0 md:hidden sticky inset-0 flex overflow-x-auto bg-custom-background-100 z-10">
{WORKSPACE_SETTINGS_LINKS.map((item, index) => (
<div
className={`${item.highlight(pathname, `/${workspaceSlug}`)
className={`${
item.highlight(pathname, `/${workspaceSlug}`)
? "text-custom-primary-100 text-sm py-2 px-3 whitespace-nowrap flex flex-grow cursor-pointer justify-around border-b border-custom-primary-200"
: "text-custom-text-200 flex flex-grow cursor-pointer justify-around border-b border-custom-border-200 text-sm py-2 px-3 whitespace-nowrap"
}`}
}`}
key={index}
onClick={() => router.push(`/${workspaceSlug}${item.href}`)}
>

View File

@@ -5,9 +5,11 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// constants
import { EUserWorkspaceRoles, WORKSPACE_SETTINGS_LINKS } from "@/constants/workspace";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
// plane web constants
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
export const WorkspaceSettingsSidebar = observer(() => {
// router
@@ -31,10 +33,11 @@ export const WorkspaceSettingsSidebar = observer(() => {
<Link key={link.key} href={`/${workspaceSlug}${link.href}`}>
<span>
<div
className={`rounded-md px-4 py-2 text-sm font-medium ${link.highlight(pathname, `/${workspaceSlug}`)
? "bg-custom-primary-100/10 text-custom-primary-100"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
}`}
className={`rounded-md px-4 py-2 text-sm font-medium ${
link.highlight(pathname, `/${workspaceSlug}`)
? "bg-custom-primary-100/10 text-custom-primary-100"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
}`}
>
{link.label}
</div>

View File

@@ -1 +1,2 @@
export * from "./bulk-operations";
export * from "./bulk-operations";
export * from "./worklog";

View File

@@ -0,0 +1,2 @@
export * from "./root";
export * from "./worklog-create-button";

View File

@@ -0,0 +1,13 @@
"use client";
import { FC } from "react";
import { TIssueActivityComment } from "@plane/types";
type TIssueActivityWorklog = {
workspaceSlug: string;
projectId: string;
issueId: string;
activityComment: TIssueActivityComment;
};
export const IssueActivityWorklog: FC<TIssueActivityWorklog> = () => <></>;

View File

@@ -0,0 +1,12 @@
"use client";
import { FC } from "react";
type TIssueActivityWorklogCreateButton = {
workspaceSlug: string;
projectId: string;
issueId: string;
disabled: boolean;
};
export const IssueActivityWorklogCreateButton: FC<TIssueActivityWorklogCreateButton> = () => <></>;

View File

@@ -0,0 +1,2 @@
export * from "./property";
export * from "./activity";

View File

@@ -0,0 +1 @@
export * from "./root";

View File

@@ -0,0 +1,12 @@
"use client";
import { FC } from "react";
type TIssueWorklogProperty = {
workspaceSlug: string;
projectId: string;
issueId: string;
disabled: boolean;
};
export const IssueWorklogProperty: FC<TIssueWorklogProperty> = () => <></>;

View File

@@ -66,7 +66,7 @@ const useProjectColumns = () => {
{
key: "Joining Date",
content: "Joining Date",
tdRender: (rowData: RowData) => <div>{getFormattedDate(rowData.member.joining_date)}</div>,
tdRender: (rowData: RowData) => <div>{getFormattedDate(rowData?.member?.joining_date || "")}</div>,
},
];
return { columns, workspaceSlug, projectId, removeMemberModal, setRemoveMemberModal };

View File

@@ -60,7 +60,7 @@ const useMemberColumns = () => {
{
key: "Joining Date",
content: "Joining Date",
tdRender: (rowData: RowData) => <div>{getFormattedDate(rowData.member.joining_date)}</div>,
tdRender: (rowData: RowData) => <div>{getFormattedDate(rowData?.member?.joining_date || "")}</div>,
},
];
return { columns, workspaceSlug, removeMemberModal, setRemoveMemberModal };

View File

@@ -0,0 +1,23 @@
import { TIssueActivityComment } from "@plane/types";
export enum EActivityFilterType {
ACTIVITY = "activity",
COMMENT = "comment",
}
export type TActivityFilters = EActivityFilterType;
export const ACTIVITY_FILTER_TYPE_OPTIONS: Record<EActivityFilterType, { label: string }> = {
[EActivityFilterType.ACTIVITY]: {
label: "Updates",
},
[EActivityFilterType.COMMENT]: {
label: "Comments",
},
};
export const filterActivityOnSelectedFilters = (
activity: TIssueActivityComment[],
filter: TActivityFilters[]
): TIssueActivityComment[] =>
activity.filter((activity) => filter.includes(activity.activity_type as TActivityFilters));

View File

@@ -0,0 +1,63 @@
// icons
import { SettingIcon } from "@/components/icons/attachment";
import { Props } from "@/components/icons/types";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
export const WORKSPACE_SETTINGS_LINKS: {
key: string;
label: string;
href: string;
access: EUserWorkspaceRoles;
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,
},
];

View File

@@ -5,10 +5,12 @@ import { Command } from "cmdk";
import Link from "next/link";
import { useParams } from "next/navigation";
// constants
import { EUserWorkspaceRoles, WORKSPACE_SETTINGS_LINKS } from "@/constants/workspace";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane wev constants
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
type Props = {
closePalette: () => void;

View File

@@ -1,8 +1,11 @@
import { FC } from "react";
import { observer } from "mobx-react";
// hooks
import { EActivityFilterType } from "@/constants/issue";
import { useIssueDetail } from "@/hooks/store";
// plane wev components
import { IssueActivityWorklog } from "@/plane-web/components/issues/worklog/activity/root";
// plane web constants
import { TActivityFilters, filterActivityOnSelectedFilters } from "@/plane-web/constants/issues";
// components
import { IssueActivityItem } from "./activity/activity-list";
import { IssueCommentCard } from "./comments/comment-card";
@@ -13,7 +16,7 @@ type TIssueActivityCommentRoot = {
workspaceSlug: string;
projectId: string;
issueId: string;
selectedFilters: EActivityFilterType[];
selectedFilters: TActivityFilters[];
activityOperations: TActivityOperations;
showAccessSpecifier?: boolean;
disabled?: boolean;
@@ -32,14 +35,7 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
if (!activityComments || (activityComments && activityComments.length <= 0)) return <></>;
const isCommentFilterSelected = selectedFilters.includes(EActivityFilterType.COMMENT);
const isActivityFilterSelected = selectedFilters.includes(EActivityFilterType.ACTIVITY);
const filteredActivityComments = activityComments.filter(
(activityComment) =>
(activityComment.activity_type === "COMMENT" && isCommentFilterSelected) ||
(activityComment.activity_type === "ACTIVITY" && isActivityFilterSelected)
);
const filteredActivityComments = filterActivityOnSelectedFilters(activityComments, selectedFilters);
return (
<div>
@@ -60,6 +56,13 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
activityId={activityComment.id}
ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
/>
) : activityComment.activity_type === "WORKLOG" ? (
<IssueActivityWorklog
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
activityComment={activityComment}
/>
) : (
<></>
)

View File

@@ -4,14 +4,14 @@ import { Check, ListFilter } from "lucide-react";
import { Popover, Transition } from "@headlessui/react";
// ui
import { Button } from "@plane/ui";
// constants
import { ACTIVITY_FILTER_TYPE_OPTIONS, EActivityFilterType } from "@/constants/issue";
// helper
import { cn } from "@/helpers/common.helper";
// constants
import { TActivityFilters, ACTIVITY_FILTER_TYPE_OPTIONS } from "@/plane-web/constants/issues";
type Props = {
selectedFilters: EActivityFilterType[];
toggleFilter: (filter: EActivityFilterType) => void;
selectedFilters: TActivityFilters[];
toggleFilter: (filter: TActivityFilters) => void;
};
export const ActivityFilter: FC<Props> = observer((props) => {
@@ -42,13 +42,15 @@ export const ActivityFilter: FC<Props> = observer((props) => {
>
<Popover.Panel className="absolute mt-2 right-0 z-10 min-w-40">
<div className="p-2 rounded-md border border-custom-border-200 bg-custom-background-100">
{ACTIVITY_FILTER_TYPE_OPTIONS.map((filter) => {
const isSelected = selectedFilters.includes(filter.value);
{Object.keys(ACTIVITY_FILTER_TYPE_OPTIONS).map((key) => {
const filterKey = key as TActivityFilters;
const filter = ACTIVITY_FILTER_TYPE_OPTIONS[filterKey];
const isSelected = selectedFilters.includes(filterKey);
return (
<div
key={filter.value}
key={filterKey}
className="flex items-center gap-2 text-sm cursor-pointer px-2 p-1 transition-all hover:bg-custom-background-80 rounded-sm"
onClick={() => toggleFilter(filter.value)}
onClick={() => toggleFilter(filterKey)}
>
<div
className={cn(

View File

@@ -7,11 +7,14 @@ import { TIssueComment } from "@plane/types";
// ui
import { TOAST_TYPE, setToast } from "@plane/ui";
// components
import { ActivityFilter, IssueActivityCommentRoot, IssueCommentCreate } from "@/components/issues";
// constants
import { EActivityFilterType } from "@/constants/issue";
import { ActivityFilter, IssueCommentCreate } from "@/components/issues";
import { IssueActivityCommentRoot } from "@/components/issues/issue-detail";
// hooks
import { useIssueDetail, useProject } from "@/hooks/store";
// plane web components
import { IssueActivityWorklogCreateButton } from "@/plane-web/components/issues/worklog";
// plane web constants
import { TActivityFilters, EActivityFilterType } from "@/plane-web/constants/issues";
type TIssueActivity = {
workspaceSlug: string;
@@ -32,9 +35,12 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
const { createComment, updateComment, removeComment } = useIssueDetail();
const { getProjectById } = useProject();
// state
const [selectedFilters, setSelectedFilters] = useState([EActivityFilterType.COMMENT, EActivityFilterType.ACTIVITY]);
const [selectedFilters, setSelectedFilters] = useState<TActivityFilters[]>([
EActivityFilterType.ACTIVITY,
EActivityFilterType.COMMENT,
]);
// toggle filter
const toggleFilter = (filter: EActivityFilterType) => {
const toggleFilter = (filter: TActivityFilters) => {
setSelectedFilters((prevFilters) => {
if (prevFilters.includes(filter)) {
if (prevFilters.length === 1) return prevFilters; // Ensure at least one filter is applied
@@ -110,7 +116,15 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
{/* header */}
<div className="flex items-center justify-between">
<div className="text-lg text-custom-text-100">Activity</div>
<ActivityFilter selectedFilters={selectedFilters} toggleFilter={toggleFilter} />
<div className="flex items-center gap-2">
<IssueActivityWorklogCreateButton
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
disabled={disabled}
/>
<ActivityFilter selectedFilters={selectedFilters} toggleFilter={toggleFilter} />
</div>
</div>
{/* rendering activity */}

View File

@@ -21,6 +21,8 @@ import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"
import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
// hooks
import { useProjectEstimates, useIssueDetail, useProject, useProjectState, useMember } from "@/hooks/store";
// plane web components
import { IssueWorklogProperty } from "@/plane-web/components/issues";
// components
import type { TIssueOperations } from "./root";
@@ -279,6 +281,13 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
/>
</div>
</div>
<IssueWorklogProperty
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
disabled={!isEditable}
/>
</div>
</div>
</div>

View File

@@ -27,6 +27,8 @@ import { cn } from "@/helpers/common.helper";
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
import { useIssueDetail, useMember, useProject, useProjectState } from "@/hooks/store";
// plane web components
import { IssueWorklogProperty } from "@/plane-web/components/issues";
interface IPeekOverviewProperties {
workspaceSlug: string;
@@ -279,6 +281,13 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
<IssueLabel workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} disabled={disabled} />
</div>
</div>
<IssueWorklogProperty
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
disabled={disabled}
/>
</div>
</div>
);

View File

@@ -14,7 +14,7 @@ import { Button } from "@plane/ui";
import { useProject, useUser } from "@/hooks/store";
type Props = {
data: IUserLite;
data: Partial<IUserLite>;
onSubmit: () => Promise<void>;
isOpen: boolean;
onClose: () => void;

View File

@@ -80,7 +80,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
)}
<Table
columns={columns}
data={memberDetails?.filter((member): member is IProjectMemberDetails => member !== null) ?? []}
data={(memberDetails?.filter((member): member is IProjectMemberDetails => member !== null) ?? []) as any}
keyExtractor={(rowData) => rowData?.member.id ?? ""}
thClassName="text-left font-medium divide-x-0 border-b border-t divide-custom-border-200"
tBodyClassName="divide-y-0"

View File

@@ -12,7 +12,6 @@ import { WORKSPACE_MEMBER_LEAVE } from "@/constants/event-tracker";
// hooks
import { useEventTracker, useMember, useUser } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os";
import useMemberColumns from "@/plane-web/components/workspace/settings/useMemberColumns";
type Props = {
@@ -33,7 +32,6 @@ export const WorkspaceMembersListItem: FC<Props> = observer((props) => {
workspace: { removeMemberFromWorkspace },
} = useMember();
const { captureEvent } = useEventTracker();
const { isMobile } = usePlatformOS();
// derived values
const handleLeaveWorkspace = async () => {
@@ -96,9 +94,10 @@ export const WorkspaceMembersListItem: FC<Props> = observer((props) => {
)}
<Table
columns={columns}
data={memberDetails?.filter((member): member is IWorkspaceMember => member !== null) ?? []}
data={(memberDetails?.filter((member): member is IWorkspaceMember => member !== null) ?? []) as any}
keyExtractor={(rowData) => rowData?.member.id ?? ""}
thClassName="text-left font-medium divide-x-0 border-b border-t divide-custom-border-200"
tHeadClassName="border-b border-custom-border-100"
thClassName="text-left font-medium divide-x-0"
tBodyClassName="divide-y-0"
tBodyTrClassName="divide-x-0"
tHeadTrClassName="divide-x-0"

View File

@@ -456,19 +456,3 @@ export const groupReactionEmojis = (reactions: any) => {
return groupedEmojis;
};
export enum EActivityFilterType {
COMMENT = "COMMENT",
ACTIVITY = "ACTIVITY",
}
export const ACTIVITY_FILTER_TYPE_OPTIONS = [
{
value: EActivityFilterType.COMMENT,
label: "Comments",
},
{
value: EActivityFilterType.ACTIVITY,
label: "Updates",
},
];

View File

@@ -1,8 +1,5 @@
// types
import { TStaticViewTypes } from "@plane/types";
// icons
import { SettingIcon } from "@/components/icons/attachment";
import { Props } from "@/components/icons/types";
// services images
import CSVLogo from "@/public/services/csv.svg";
import ExcelLogo from "@/public/services/excel.svg";
@@ -135,61 +132,3 @@ export const RESTRICTED_URLS = [
"spaces",
"workspace-invitations",
];
export const WORKSPACE_SETTINGS_LINKS: {
key: string;
label: string;
href: string;
access: EUserWorkspaceRoles;
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,
},
];

View File

@@ -0,0 +1,2 @@
export * from "./root";
export * from "./worklog-create-button";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/activity/root";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/activity/worklog-create-button";

View File

@@ -0,0 +1 @@
export * from "./root";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/property/root";

View File

@@ -0,0 +1 @@
export * from "ce/constants/issues";

View File

@@ -0,0 +1 @@
export * from "ce/constants/workspace";

2
web/ee/issues/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./worklog/activity";
export * from "./worklog/property";

View File

@@ -0,0 +1,2 @@
export * from "./root";
export * from "./worklog-create-button";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/activity/root";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/activity/worklog-create-button";

View File

@@ -0,0 +1,2 @@
export * from "./property";
export * from "./activity";

View File

@@ -0,0 +1 @@
export * from "./root";

View File

@@ -0,0 +1 @@
export * from "@/plane-web/components/issues/worklog/property/root";

View File

@@ -299,3 +299,38 @@ export const getCurrentDateTimeInISO = () => {
const date = new Date();
return date.toISOString();
};
/**
* @description converts hours and minutes to minutes
* @param { number } hours
* @param { number } minutes
* @returns { number } minutes
* @example convertHoursMinutesToMinutes(2, 30) // Output: 150
*/
export const convertHoursMinutesToMinutes = (hours: number, minutes: number): number => hours * 60 + minutes;
/**
* @description converts minutes to hours and minutes
* @param { number } mins
* @returns { number, number } hours and minutes
* @example convertMinutesToHoursAndMinutes(150) // Output: { hours: 2, minutes: 30 }
*/
export const convertMinutesToHoursAndMinutes = (mins: number): { hours: number; minutes: number } => {
const hours = Math.floor(mins / 60);
const minutes = Math.floor(mins % 60);
return { hours: hours, minutes: minutes };
};
/**
* @description converts minutes to days, hours and minutes
* @param { number } totalMinutes
* @returns { string } days, hours and minutes
*/
export const convertMinutesToDaysHoursMinutes = (totalMinutes: number): string => {
const days = Math.floor(totalMinutes / (60 * 24));
const hours = Math.floor((totalMinutes % (60 * 24)) / 60);
const minutes = totalMinutes % 60;
return `${days ? `${days}d ` : ``}${hours ? `${hours}h ` : ``}${minutes ? `${minutes}m ` : ``} `;
};