chore: code refactor and build fix (#6285)
* chore: code refactor and build fix * chore: code refactor * chore: code refactor
This commit is contained in:
parent
3c6bbaef3c
commit
211d5e1cd0
33 changed files with 292 additions and 101 deletions
|
|
@ -11,7 +11,6 @@ import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
|||
import { useIssues, useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||
// plane-web
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
|
|
@ -19,10 +18,11 @@ type Props = {
|
|||
data?: TIssue | TDeDupeIssue;
|
||||
isSubIssue?: boolean;
|
||||
onSubmit?: () => Promise<void>;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||
const { dataId, data, isOpen, handleClose, isSubIssue = false, onSubmit } = props;
|
||||
const { dataId, data, isOpen, handleClose, isSubIssue = false, onSubmit, isEpic = false } = props;
|
||||
// states
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
// store hooks
|
||||
|
|
@ -70,12 +70,14 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
|||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: `${isSubIssue ? "Sub-issue" : "Issue"} deleted successfully`,
|
||||
message: `${isSubIssue ? "Sub-issue" : isEpic ? "Epic" : "Issue"} deleted successfully`,
|
||||
});
|
||||
onClose();
|
||||
})
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "Only admin or creator can delete the issue";
|
||||
const isPermissionError =
|
||||
errors?.error ===
|
||||
`Only admin or creator can delete the ${isSubIssue ? "sub-issue" : isEpic ? "epic" : "issue"}`;
|
||||
const currentError = isPermissionError
|
||||
? PROJECT_ERROR_MESSAGES.permissionError
|
||||
: PROJECT_ERROR_MESSAGES.issueDeleteError;
|
||||
|
|
@ -94,14 +96,14 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
|||
handleSubmit={handleIssueDelete}
|
||||
isSubmitting={isDeleting}
|
||||
isOpen={isOpen}
|
||||
title="Delete issue"
|
||||
title={`Delete ${isEpic ? "epic" : "issue"}`}
|
||||
content={
|
||||
<>
|
||||
Are you sure you want to delete issue{" "}
|
||||
{`Are you sure you want to delete ${isEpic ? "epic" : "issue"} `}
|
||||
<span className="break-words font-medium text-custom-text-100">
|
||||
{projectDetails?.identifier}-{issue?.sequence_id}
|
||||
</span>
|
||||
{""}? All of the data related to the issue will be permanently removed. This action cannot be undone.
|
||||
{` ? All of the data related to the ${isEpic ? "epic" : "issue"} will be permanently removed. This action cannot be undone.`}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ const HeaderFilters = observer((props: Props) => {
|
|||
states={projectStates}
|
||||
cycleViewDisabled={!currentProjectDetails?.cycle_view}
|
||||
moduleViewDisabled={!currentProjectDetails?.module_view}
|
||||
isEpic={storeType === EIssuesStoreType.EPIC}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
<FiltersDropdown title="Display" placement="bottom-end">
|
||||
|
|
@ -134,6 +135,7 @@ const HeaderFilters = observer((props: Props) => {
|
|||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
cycleViewDisabled={!currentProjectDetails?.cycle_view}
|
||||
moduleViewDisabled={!currentProjectDetails?.module_view}
|
||||
isEpic={storeType === EIssuesStoreType.EPIC}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
{canUserCreateIssue ? (
|
||||
|
|
|
|||
|
|
@ -53,11 +53,10 @@ export const SubIssuesCollapsibleContent: FC<Props> = observer((props) => {
|
|||
},
|
||||
});
|
||||
// store hooks
|
||||
const { toggleCreateIssueModal, toggleDeleteIssueModal } = useIssueDetail();
|
||||
const {
|
||||
subIssues: { subIssueHelpersByIssueId, setSubIssueHelpers },
|
||||
toggleCreateIssueModal,
|
||||
toggleDeleteIssueModal,
|
||||
} = useIssueDetail();
|
||||
} = useIssueDetail(issueServiceType);
|
||||
|
||||
// helpers
|
||||
const subIssueOperations = useSubIssueOperations(issueServiceType);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const SubIssuesCollapsibleTitle: FC<Props> = observer((props) => {
|
|||
return (
|
||||
<CollapsibleButton
|
||||
isOpen={isOpen}
|
||||
title="Sub-issues"
|
||||
title={`${issueServiceType === EIssueServiceType.EPICS ? "Issues" : "Sub-issues"}`}
|
||||
indicatorElement={
|
||||
<div className="flex items-center gap-1.5 text-custom-text-300 text-sm">
|
||||
<CircularProgressIndicator size={18} percentage={percentage} strokeWidth={3} />
|
||||
|
|
|
|||
|
|
@ -9,18 +9,24 @@ import { cn } from "@/helpers/common.helper";
|
|||
export type TActivitySortRoot = {
|
||||
sortOrder: "asc" | "desc";
|
||||
toggleSort: () => void;
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
};
|
||||
export const ActivitySortRoot: FC<TActivitySortRoot> = memo((props) => (
|
||||
<div
|
||||
className={cn(getButtonStyling("neutral-primary", "sm"), "px-2 text-custom-text-300 cursor-pointer")}
|
||||
className={cn(
|
||||
getButtonStyling("neutral-primary", "sm"),
|
||||
"px-2 text-custom-text-300 cursor-pointer",
|
||||
props.className
|
||||
)}
|
||||
onClick={() => {
|
||||
props.toggleSort();
|
||||
}}
|
||||
>
|
||||
{props.sortOrder === "asc" ? (
|
||||
<ArrowUpWideNarrow className="size-4 " />
|
||||
<ArrowUpWideNarrow className={cn("size-4", props.iconClassName)} />
|
||||
) : (
|
||||
<ArrowDownWideNarrow className="size-4 " />
|
||||
<ArrowDownWideNarrow className={cn("size-4", props.iconClassName)} />
|
||||
)}
|
||||
</div>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||
const { workspaceSlug } = useParams();
|
||||
|
||||
// hooks
|
||||
const storeType = useIssueStoreType() as CalendarStoreType;
|
||||
const storeType = isEpic ? EIssuesStoreType.EPIC : (useIssueStoreType() as CalendarStoreType);
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
|||
}}
|
||||
quickAddCallback={quickAddCallback}
|
||||
addIssuesToView={addIssuesToView}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ type Props = {
|
|||
ignoreGroupedFilters?: Partial<TIssueGroupByOptions>[];
|
||||
cycleViewDisabled?: boolean;
|
||||
moduleViewDisabled?: boolean;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
||||
|
|
@ -37,6 +38,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
|||
ignoreGroupedFilters = [],
|
||||
cycleViewDisabled = false,
|
||||
moduleViewDisabled = false,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
|
||||
const isDisplayFilterEnabled = (displayFilter: keyof IIssueDisplayFilterOptions) =>
|
||||
|
|
@ -61,6 +63,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
|||
handleUpdate={handleDisplayPropertiesUpdate}
|
||||
cycleViewDisabled={cycleViewDisabled}
|
||||
moduleViewDisabled={moduleViewDisabled}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ type Props = {
|
|||
handleUpdate: (updatedDisplayProperties: Partial<IIssueDisplayProperties>) => void;
|
||||
cycleViewDisabled?: boolean;
|
||||
moduleViewDisabled?: boolean;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const FilterDisplayProperties: React.FC<Props> = observer((props) => {
|
||||
|
|
@ -25,6 +26,7 @@ export const FilterDisplayProperties: React.FC<Props> = observer((props) => {
|
|||
handleUpdate,
|
||||
cycleViewDisabled = false,
|
||||
moduleViewDisabled = false,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
// router
|
||||
const { workspaceSlug, projectId: routerProjectId } = useParams();
|
||||
|
|
@ -45,6 +47,11 @@ export const FilterDisplayProperties: React.FC<Props> = observer((props) => {
|
|||
default:
|
||||
return shouldRenderDisplayProperty({ workspaceSlug: workspaceSlug?.toString(), projectId, key: property.key });
|
||||
}
|
||||
}).map((property) => {
|
||||
if (isEpic && property.key === "sub_issue_count") {
|
||||
return { ...property, title: "Issue count" };
|
||||
}
|
||||
return property;
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ import { ISSUE_FILTER_OPTIONS } from "@/constants/issue";
|
|||
type Props = {
|
||||
selectedIssueType: TIssueGroupingFilters | undefined;
|
||||
handleUpdate: (val: TIssueGroupingFilters) => void;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const FilterIssueGrouping: React.FC<Props> = observer((props) => {
|
||||
const { selectedIssueType, handleUpdate } = props;
|
||||
const { selectedIssueType, handleUpdate, isEpic = false } = props;
|
||||
|
||||
const [previewEnabled, setPreviewEnabled] = React.useState(true);
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ export const FilterIssueGrouping: React.FC<Props> = observer((props) => {
|
|||
return (
|
||||
<>
|
||||
<FilterHeader
|
||||
title="Issue Grouping"
|
||||
title={`${isEpic ? "Epic" : "Issue"} Grouping`}
|
||||
isPreviewEnabled={previewEnabled}
|
||||
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
|
||||
/>
|
||||
|
|
@ -34,7 +35,7 @@ export const FilterIssueGrouping: React.FC<Props> = observer((props) => {
|
|||
key={issueType?.key}
|
||||
isChecked={activeIssueType === issueType?.key ? true : false}
|
||||
onClick={() => handleUpdate(issueType?.key)}
|
||||
title={issueType.title}
|
||||
title={`${issueType.title} ${isEpic ? "Epics" : "Issues"}`}
|
||||
multiple={false}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ type Props = {
|
|||
states?: IState[] | undefined;
|
||||
cycleViewDisabled?: boolean;
|
||||
moduleViewDisabled?: boolean;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const FilterSelection: React.FC<Props> = observer((props) => {
|
||||
|
|
@ -56,6 +57,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||
states,
|
||||
cycleViewDisabled = false,
|
||||
moduleViewDisabled = false,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
// hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
|
@ -234,6 +236,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||
type: val,
|
||||
})
|
||||
}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||
target_date: renderFormattedPayloadDate(targetDate),
|
||||
}}
|
||||
quickAddCallback={quickAddIssue}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
) : undefined;
|
||||
|
||||
|
|
@ -120,8 +121,8 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||
<div className="h-full w-full">
|
||||
<GanttChartRoot
|
||||
border={false}
|
||||
title="Issues"
|
||||
loaderTitle="Issues"
|
||||
title={isEpic ? "Epics" : "Issues"}
|
||||
loaderTitle={isEpic ? "Epics" : "Issues"}
|
||||
blockIds={issuesIds}
|
||||
blockUpdateHandler={updateIssueBlockStructure}
|
||||
blockToRender={(data: TIssue) => <IssueGanttBlock issueId={data.id} isEpic={isEpic} />}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ type Props = {
|
|||
dropErrorMessage?: string;
|
||||
orderBy: TIssueOrderByOptions | undefined;
|
||||
isDraggingOverColumn: boolean;
|
||||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const GroupDragOverlay = (props: Props) => {
|
||||
|
|
@ -27,6 +28,7 @@ export const GroupDragOverlay = (props: Props) => {
|
|||
dropErrorMessage,
|
||||
orderBy,
|
||||
isDraggingOverColumn,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
|
||||
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
|
||||
|
|
@ -68,7 +70,7 @@ export const GroupDragOverlay = (props: Props) => {
|
|||
The layout is ordered by <span className="font-semibold">{readableOrderBy}</span>.
|
||||
</span>
|
||||
)}
|
||||
<span>Drop here to move the issue.</span>
|
||||
<span>{`Drop here to move the ${isEpic ? "epic" : "issue"}.`}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ export const ListGroup = observer((props: Props) => {
|
|||
dropErrorMessage={group.dropErrorMessage}
|
||||
orderBy={orderBy}
|
||||
isDraggingOverColumn={isDraggingOverColumn}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
{groupIssueIds && (
|
||||
<IssueBlocksList
|
||||
|
|
@ -312,6 +313,7 @@ export const ListGroup = observer((props: Props) => {
|
|||
prePopulatedData={prePopulateQuickAddData(group_by, group.id)}
|
||||
containerClassName="border-b border-t border-custom-border-200 bg-custom-background-100 "
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback, useMemo, SyntheticEvent } from "react";
|
||||
import xor from "lodash/xor";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
|
|
@ -245,7 +245,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
|
||||
const redirectToIssueDetail = () => {
|
||||
router.push(
|
||||
`/${workspaceSlug}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${issue.id}#sub-issues`
|
||||
`/${workspaceSlug}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}${isEpic ? "epics" : "issues"}/${issue.id}#sub-issues`
|
||||
);
|
||||
// router.push({
|
||||
// pathname: `/${workspaceSlug}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${
|
||||
|
|
@ -265,7 +265,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
const maxDate = getDate(issue.target_date);
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
const handleEventPropagation = (e: React.MouseEvent) => {
|
||||
const handleEventPropagation = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
|
@ -275,7 +275,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
{/* basic properties */}
|
||||
{/* state */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="state">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<StateDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
value={issue.state_id}
|
||||
|
|
@ -291,7 +291,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
|
||||
{/* priority */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="priority">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<PriorityDropdown
|
||||
value={issue?.priority}
|
||||
onChange={handlePriority}
|
||||
|
|
@ -306,7 +306,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
|
||||
{/* start date */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="start_date">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<DateDropdown
|
||||
value={issue.start_date ?? null}
|
||||
onChange={handleStartDate}
|
||||
|
|
@ -324,7 +324,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
|
||||
{/* target/due date */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="due_date">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<DateDropdown
|
||||
value={issue?.target_date ?? null}
|
||||
onChange={handleTargetDate}
|
||||
|
|
@ -344,7 +344,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
|
||||
{/* assignee */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<MemberDropdown
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.assignee_ids}
|
||||
|
|
@ -362,52 +362,54 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
|
||||
{!isEpic && (
|
||||
<>
|
||||
{/* modules */}
|
||||
{projectDetails?.module_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="modules">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<ModuleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.module_ids ?? []}
|
||||
onChange={handleModule}
|
||||
disabled={isReadOnly}
|
||||
renderByDefault={isMobile}
|
||||
multiple
|
||||
buttonVariant="border-with-text"
|
||||
showCount
|
||||
showTooltip
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
)}
|
||||
<>
|
||||
{!isEpic && (
|
||||
<>
|
||||
{/* modules */}
|
||||
{projectDetails?.module_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="modules">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<ModuleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.module_ids ?? []}
|
||||
onChange={handleModule}
|
||||
disabled={isReadOnly}
|
||||
renderByDefault={isMobile}
|
||||
multiple
|
||||
buttonVariant="border-with-text"
|
||||
showCount
|
||||
showTooltip
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
)}
|
||||
|
||||
{/* cycles */}
|
||||
{projectDetails?.cycle_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="cycle">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<CycleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.cycle_id}
|
||||
onChange={handleCycle}
|
||||
disabled={isReadOnly}
|
||||
buttonVariant="border-with-text"
|
||||
renderByDefault={isMobile}
|
||||
showTooltip
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* cycles */}
|
||||
{projectDetails?.cycle_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="cycle">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<CycleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.cycle_id}
|
||||
onChange={handleCycle}
|
||||
disabled={isReadOnly}
|
||||
buttonVariant="border-with-text"
|
||||
renderByDefault={isMobile}
|
||||
showTooltip
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
{/* estimates */}
|
||||
{projectId && areEstimateEnabledByProjectId(projectId?.toString()) && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="estimate">
|
||||
<div className="h-5" onClick={handleEventPropagation}>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<EstimateDropdown
|
||||
value={issue.estimate_point ?? undefined}
|
||||
onChange={handleEstimate}
|
||||
|
|
@ -429,12 +431,13 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!subIssueCount}
|
||||
>
|
||||
<Tooltip
|
||||
tooltipHeading="Sub-issues"
|
||||
tooltipHeading={isEpic ? "Issues" : "Sub-issues"}
|
||||
tooltipContent={`${subIssueCount}`}
|
||||
isMobile={isMobile}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
|
@ -467,6 +470,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
>
|
||||
<div
|
||||
className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1"
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<Paperclip className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
|
|
@ -489,6 +493,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||
>
|
||||
<div
|
||||
className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1"
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<Link className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Row } from "@plane/ui";
|
|||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const GanttQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
const { onClick } = props;
|
||||
const { onClick, isEpic = false } = props;
|
||||
|
||||
return (
|
||||
<button
|
||||
|
|
@ -15,7 +15,7 @@ export const GanttQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((prop
|
|||
>
|
||||
<Row className="flex py-2 gap-2">
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2 my-auto" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
<span className="text-sm font-medium">{`New ${isEpic ? "Epic" : "Issue"}`}</span>
|
||||
</Row>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { PlusIcon } from "lucide-react";
|
|||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const KanbanQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
const { onClick } = props;
|
||||
const { onClick, isEpic = false } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -12,7 +12,7 @@ export const KanbanQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((pro
|
|||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
<span className="text-sm font-medium">{`New ${isEpic ? "Epic" : "Issue"}`}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Row } from "@plane/ui";
|
|||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const ListQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
const { onClick } = props;
|
||||
const { onClick, isEpic = false } = props;
|
||||
|
||||
return (
|
||||
<Row
|
||||
|
|
@ -13,7 +13,7 @@ export const ListQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props
|
|||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
<span className="text-sm font-medium">{`New ${isEpic ? "Epic" : "Issue"}`}</span>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { PlusIcon } from "lucide-react";
|
|||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const SpreadsheetAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
const { onClick } = props;
|
||||
const { onClick, isEpic = false } = props;
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
|
|
@ -14,7 +14,7 @@ export const SpreadsheetAddIssueButton: FC<TQuickAddIssueButton> = observer((pro
|
|||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
<span className="text-sm font-medium">{`New ${isEpic ? "Epic" : "Issue"}`}</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ interface Props {
|
|||
displayFilters: IIssueDisplayFilterOptions;
|
||||
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
||||
onClose: () => void;
|
||||
isEpic?: boolean;
|
||||
}
|
||||
|
||||
export const HeaderColumn = (props: Props) => {
|
||||
const { displayFilters, handleDisplayFilterUpdate, property, onClose } = props;
|
||||
const { displayFilters, handleDisplayFilterUpdate, property, onClose, isEpic = false } = props;
|
||||
|
||||
const { storedValue: selectedMenuItem, setValue: setSelectedMenuItem } = useLocalStorage(
|
||||
"spreadsheetViewSorting",
|
||||
|
|
@ -46,7 +47,7 @@ export const HeaderColumn = (props: Props) => {
|
|||
<Row className="flex w-full cursor-pointer items-center justify-between gap-1.5 py-2 text-sm text-custom-text-200 hover:text-custom-text-100">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{<propertyDetails.icon className="h-4 w-4 text-custom-text-400" />}
|
||||
{propertyDetails.title}
|
||||
{propertyDetails.title === "Sub-issue" && isEpic ? "Issues" : propertyDetails.title}
|
||||
</div>
|
||||
<div className="ml-3 flex">
|
||||
{activeSortingProperty === property && (
|
||||
|
|
|
|||
|
|
@ -18,16 +18,19 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
|||
// router
|
||||
const router = useAppRouter();
|
||||
// hooks
|
||||
const { workspaceSlug } = useParams();
|
||||
const { workspaceSlug, epicId } = useParams();
|
||||
// derived values
|
||||
const subIssueCount = issue?.sub_issues_count ?? 0;
|
||||
|
||||
const redirectToIssueDetail = () => {
|
||||
router.push(
|
||||
`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${issue.id}#sub-issues`
|
||||
`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}${epicId ? "epics" : "issues"}/${issue.id}#sub-issues`
|
||||
);
|
||||
};
|
||||
|
||||
const issueLabel = epicId ? "issue" : "sub-issue";
|
||||
const label = `${subIssueCount} ${issueLabel}${subIssueCount !== 1 ? "s" : ""}`;
|
||||
|
||||
return (
|
||||
<Row
|
||||
onClick={subIssueCount ? redirectToIssueDetail : () => {}}
|
||||
|
|
@ -38,7 +41,7 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
|||
}
|
||||
)}
|
||||
>
|
||||
{subIssueCount} {subIssueCount === 1 ? "sub-issue" : "sub-issues"}
|
||||
{label}
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,9 +12,17 @@ interface Props {
|
|||
isEstimateEnabled: boolean;
|
||||
displayFilters: IIssueDisplayFilterOptions;
|
||||
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
||||
isEpic?: boolean;
|
||||
}
|
||||
export const SpreadsheetHeaderColumn = observer((props: Props) => {
|
||||
const { displayProperties, displayFilters, property, isEstimateEnabled, handleDisplayFilterUpdate } = props;
|
||||
const {
|
||||
displayProperties,
|
||||
displayFilters,
|
||||
property,
|
||||
isEstimateEnabled,
|
||||
handleDisplayFilterUpdate,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
|
||||
//hooks
|
||||
const tableHeaderCellRef = useRef<HTMLTableCellElement | null>(null);
|
||||
|
|
@ -39,6 +47,7 @@ export const SpreadsheetHeaderColumn = observer((props: Props) => {
|
|||
onClose={() => {
|
||||
tableHeaderCellRef?.current?.focus();
|
||||
}}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</th>
|
||||
</WithDisplayPropertiesHOC>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ interface Props {
|
|||
isEstimateEnabled: boolean;
|
||||
spreadsheetColumnsList: (keyof IIssueDisplayProperties)[];
|
||||
selectionHelpers: TSelectionHelper;
|
||||
isEpic?: boolean;
|
||||
}
|
||||
|
||||
export const SpreadsheetHeader = observer((props: Props) => {
|
||||
|
|
@ -32,6 +33,7 @@ export const SpreadsheetHeader = observer((props: Props) => {
|
|||
isEstimateEnabled,
|
||||
spreadsheetColumnsList,
|
||||
selectionHelpers,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
// router
|
||||
const { projectId } = useParams();
|
||||
|
|
@ -62,7 +64,7 @@ export const SpreadsheetHeader = observer((props: Props) => {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="flex h-full w-full flex-grow items-center py-2.5">Issues</span>
|
||||
<span className="flex h-full w-full flex-grow items-center py-2.5">{`${isEpic ? "Epics" : "Issues"}`}</span>
|
||||
</Row>
|
||||
</th>
|
||||
|
||||
|
|
@ -74,6 +76,7 @@ export const SpreadsheetHeader = observer((props: Props) => {
|
|||
displayFilters={displayFilters}
|
||||
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
))}
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
|||
isEstimateEnabled={isEstimateEnabled}
|
||||
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||
selectionHelpers={selectionHelpers}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
<tbody>
|
||||
{issueIds.map((id) => (
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||
layout={EIssueLayoutTypes.SPREADSHEET}
|
||||
QuickAddButton={SpreadsheetAddIssueButton}
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export const ParentIssuesListModal: React.FC<Props> = ({
|
|||
projectService
|
||||
.projectIssuesSearch(workspaceSlug as string, projectId as string, {
|
||||
search: debouncedSearchTerm,
|
||||
parent: true,
|
||||
parent: searchEpic ? undefined : true,
|
||||
issue_id: issueId,
|
||||
workspace_search: false,
|
||||
epic: searchEpic ? true : undefined,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ export const IssueList: FC<IIssueList> = observer((props) => {
|
|||
disabled={disabled}
|
||||
handleIssueCrudState={handleIssueCrudState}
|
||||
subIssueOperations={subIssueOperations}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue