Skip to content

Commit

Permalink
[WEB-1680] dev: issue detail activity revamp and issue detail page im…
Browse files Browse the repository at this point in the history
…provement (#5075)

* chore: issue link activity message updated

* chore: activity filter type constant added

* dev: issue activity revamp and code refactor

* chore: issue detail widget oreder updated in peek overview

* chore: issue detail page padding improvement

* fix: relation widget toast alert

* fix: relation widget toast alert

* fix: peek overview attachment delete modal

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: code refactor

* chore: issue detail sidebar parent field
  • Loading branch information
anmolsinghbhatia authored Jul 8, 2024
1 parent fd61079 commit 53e5d4b
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 131 deletions.
14 changes: 7 additions & 7 deletions web/core/components/issues/attachment/attachment-list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { FC, useState } from "react";
import { FC } from "react";
import { observer } from "mobx-react";
import { Trash } from "lucide-react";
// ui
Expand Down Expand Up @@ -33,9 +33,9 @@ export const IssueAttachmentsListItem: FC<TIssueAttachmentsListItem> = observer(
const { getUserDetails } = useMember();
const {
attachment: { getAttachmentById },
isDeleteAttachmentModalOpen,
toggleDeleteAttachmentModal,
} = useIssueDetail();
// state
const [isDeleteIssueAttachmentModalOpen, setIsDeleteIssueAttachmentModalOpen] = useState(false);

// derived values
const attachment = attachmentId ? getAttachmentById(attachmentId) : undefined;
Expand All @@ -46,10 +46,10 @@ export const IssueAttachmentsListItem: FC<TIssueAttachmentsListItem> = observer(

return (
<>
{isDeleteIssueAttachmentModalOpen && (
{isDeleteAttachmentModalOpen && (
<IssueAttachmentDeleteModal
isOpen={isDeleteIssueAttachmentModalOpen}
onClose={() => setIsDeleteIssueAttachmentModalOpen(false)}
isOpen={!!isDeleteAttachmentModalOpen}
onClose={() => toggleDeleteAttachmentModal(null)}
handleAttachmentOperations={handleAttachmentOperations}
data={attachment}
/>
Expand Down Expand Up @@ -95,7 +95,7 @@ export const IssueAttachmentsListItem: FC<TIssueAttachmentsListItem> = observer(
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setIsDeleteIssueAttachmentModalOpen(true);
toggleDeleteAttachmentModal(attachmentId);
}}
>
<div className="flex items-center gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ export const RelationsCollapsibleContent: FC<Props> = observer((props) => {
onSubmit={async () =>
await issueOperations.remove(workspaceSlug, projectId, issueCrudState?.delete?.issue?.id as string)
}
isSubIssue
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,12 @@ export const useRelationOperations = (): TRelationIssueOperations => {
remove: async (workspaceSlug: string, projectId: string, issueId: string) => {
try {
await removeIssue(workspaceSlug, projectId, issueId);
setToast({
title: "Success!",
type: TOAST_TYPE.SUCCESS,
message: "Issue deleted successfully",
});
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
} catch (error) {
setToast({
title: "Error!",
type: TOAST_TYPE.ERROR,
message: "Issue delete failed",
});
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: issueId, state: "FAILED", element: "Issue detail page" },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { FC } from "react";
import { observer } from "mobx-react";
// hooks
import { EActivityFilterType } from "@/constants/issue";
import { useIssueDetail } from "@/hooks/store";
// components
import { IssueActivityList } from "./activity/activity-list";
import { IssueActivityItem } from "./activity/activity-list";
import { IssueCommentCard } from "./comments/comment-card";
// types
import { TActivityOperations } from "./root";
Expand All @@ -12,13 +13,15 @@ type TIssueActivityCommentRoot = {
workspaceSlug: string;
projectId: string;
issueId: string;
selectedFilters: EActivityFilterType[];
activityOperations: TActivityOperations;
showAccessSpecifier?: boolean;
disabled?: boolean;
};

export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer((props) => {
const { workspaceSlug, issueId, activityOperations, showAccessSpecifier, projectId, disabled } = props;
const { workspaceSlug, issueId, selectedFilters, activityOperations, showAccessSpecifier, projectId, disabled } =
props;
// hooks
const {
activity: { getActivityCommentByIssueId },
Expand All @@ -28,24 +31,34 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
const activityComments = getActivityCommentByIssueId(issueId);

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

return (
<div>
{activityComments.map((activityComment, index) =>
{filteredActivityComments.map((activityComment, index) =>
activityComment.activity_type === "COMMENT" ? (
<IssueCommentCard
projectId={projectId}
key={activityComment.id}
workspaceSlug={workspaceSlug}
commentId={activityComment.id}
activityOperations={activityOperations}
ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined}
ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
showAccessSpecifier={showAccessSpecifier}
disabled={disabled}
/>
) : activityComment.activity_type === "ACTIVITY" ? (
<IssueActivityList
<IssueActivityItem
activityId={activityComment.id}
ends={index === 0 ? "top" : index === activityComments.length - 1 ? "bottom" : undefined}
ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
/>
) : (
<></>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { FC, Fragment } from "react";
import { observer } from "mobx-react";
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";

type Props = {
selectedFilters: EActivityFilterType[];
toggleFilter: (filter: EActivityFilterType) => void;
};

export const ActivityFilter: FC<Props> = observer((props) => {
const { selectedFilters, toggleFilter } = props;
return (
<Popover as="div" className="relative">
{({ open }) => (
<>
<Popover.Button as={React.Fragment}>
<Button
variant="neutral-primary"
size="sm"
prependIcon={<ListFilter className="h-3 w-3" />}
className="relative"
>
<span className={`${open ? "text-custom-text-100" : "text-custom-text-200"}`}>Filters</span>
</Button>
</Popover.Button>

<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<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);
return (
<div
key={filter.value}
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)}
>
<div
className={cn(
"flex-shrink-0 w-3 h-3 flex justify-center items-center rounded-sm transition-all bg-custom-background-90",
{
"bg-custom-primary text-white": isSelected,
"bg-custom-background-80 text-custom-text-400": isSelected && selectedFilters.length === 1,
"bg-custom-background-90": !isSelected,
}
)}
>
{isSelected && <Check className="h-2.5 w-2.5" />}
</div>
<div
className={cn(
"whitespace-nowrap",
isSelected ? "text-custom-text-100" : "text-custom-text-200"
)}
>
{filter.label}
</div>
</div>
);
})}
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const IssueLinkActivity: FC<TIssueLinkActivity> = observer((props) => {
<>
{activity.verb === "created" ? (
<>
<span>added this </span>
<span>added </span>
<a
href={`${activity.new_value}`}
target="_blank"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import {
IssueInboxActivity,
} from "./actions";

type TIssueActivityList = {
type TIssueActivityItem = {
activityId: string;
ends: "top" | "bottom" | undefined;
};

export const IssueActivityList: FC<TIssueActivityList> = observer((props) => {
export const IssueActivityItem: FC<TIssueActivityItem> = observer((props) => {
const { activityId, ends } = props;
// hooks
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./activity-comment-root";

// activity
export * from "./activity/activity-list";
export * from "./activity-filter";

// issue comment
export * from "./comments";
Loading

0 comments on commit 53e5d4b

Please sign in to comment.