mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: update UI
This commit is contained in:
parent
12000904ee
commit
a76e02045b
@ -7,20 +7,21 @@ import { TSelectionHelper } from "@/hooks/use-multiple-select";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
id: string;
|
id: string;
|
||||||
selectionHelpers: TSelectionHelper;
|
selectionHelpers: TSelectionHelper;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MultipleSelectAction: React.FC<Props> = (props) => {
|
export const MultipleSelectAction: React.FC<Props> = (props) => {
|
||||||
const { className, groupId, id, selectionHelpers } = props;
|
const { className, disabled = false, groupId, id, selectionHelpers } = props;
|
||||||
// derived values
|
// derived values
|
||||||
const isSelected = selectionHelpers.isEntitySelected(id);
|
const isSelected = selectionHelpers.isEntitySelected(id);
|
||||||
const isActive = selectionHelpers.isEntityActive(id);
|
const isActive = selectionHelpers.isEntityActive(id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className={cn("outline-0", className)}
|
className={cn("!outline-none", className)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
selectionHelpers.handleEntityClick(e, id, groupId);
|
selectionHelpers.handleEntityClick(e, id, groupId);
|
||||||
@ -30,6 +31,7 @@ export const MultipleSelectAction: React.FC<Props> = (props) => {
|
|||||||
data-entity-id={id}
|
data-entity-id={id}
|
||||||
data-type="multiple-select-action"
|
data-type="multiple-select-action"
|
||||||
data-active={isActive}
|
data-active={isActive}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { TOAST_TYPE, setToast } from "@plane/ui";
|
|||||||
// components
|
// components
|
||||||
import { AlertModalCore, EModalPosition, EModalWidth } from "@/components/core";
|
import { AlertModalCore, EModalPosition, EModalWidth } from "@/components/core";
|
||||||
// constants
|
// constants
|
||||||
|
import { EErrorCodes, ERROR_DETAILS } from "@/constants/errors";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues } from "@/hooks/store";
|
import { useIssues } from "@/hooks/store";
|
||||||
@ -40,13 +41,14 @@ export const BulkArchiveConfirmationModal: React.FC<Props> = observer((props) =>
|
|||||||
onSubmit?.();
|
onSubmit?.();
|
||||||
handleClose();
|
handleClose();
|
||||||
})
|
})
|
||||||
.catch(() =>
|
.catch((error) => {
|
||||||
|
const errorInfo = ERROR_DETAILS[error?.error_code as EErrorCodes] ?? undefined;
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.ERROR,
|
type: TOAST_TYPE.ERROR,
|
||||||
title: "Error!",
|
title: errorInfo?.title ?? "Error!",
|
||||||
message: "Something went wrong. Please try again.",
|
message: errorInfo?.message ?? "Something went wrong. Please try again.",
|
||||||
})
|
});
|
||||||
)
|
})
|
||||||
.finally(() => setIsDeleting(false));
|
.finally(() => setIsDeleting(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,8 +2,12 @@ import { useRouter } from "next/router";
|
|||||||
import { CalendarCheck2, CalendarClock } from "lucide-react";
|
import { CalendarCheck2, CalendarClock } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { TBulkIssueProperties } from "@plane/types";
|
import { TBulkIssueProperties } from "@plane/types";
|
||||||
|
// ui
|
||||||
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { DateDropdown, MemberDropdown, PriorityDropdown, StateDropdown } from "@/components/dropdowns";
|
import { DateDropdown, MemberDropdown, PriorityDropdown, StateDropdown } from "@/components/dropdowns";
|
||||||
|
// constants
|
||||||
|
import { EErrorCodes, ERROR_DETAILS } from "@/constants/errors";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@ -29,6 +33,13 @@ export const IssueBulkOperationsProperties: React.FC<Props> = (props) => {
|
|||||||
bulkUpdateProperties(workspaceSlug.toString(), projectId.toString(), {
|
bulkUpdateProperties(workspaceSlug.toString(), projectId.toString(), {
|
||||||
issue_ids: snapshot.selectedEntityIds,
|
issue_ids: snapshot.selectedEntityIds,
|
||||||
properties: data,
|
properties: data,
|
||||||
|
}).catch((error) => {
|
||||||
|
const errorInfo = ERROR_DETAILS[error?.error_code as EErrorCodes] ?? undefined;
|
||||||
|
setToast({
|
||||||
|
type: TOAST_TYPE.ERROR,
|
||||||
|
title: errorInfo?.title ?? "Error!",
|
||||||
|
message: errorInfo?.message ?? "Something went wrong. Please try again.",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,12 +128,10 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||||||
<div
|
<div
|
||||||
ref={issueRef}
|
ref={issueRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group/list-block min-h-11 relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 p-3 pl-1.5 text-sm transition-colors issue-list-block",
|
"group/list-block min-h-11 relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 hover:bg-custom-background-90 p-3 pl-1.5 text-sm transition-colors issue-list-block border border-transparent",
|
||||||
{
|
{
|
||||||
"border border-custom-primary-70 hover:border-custom-primary-70":
|
"border-custom-primary-70": getIsIssuePeeked(issue.id) && peekIssue?.nestingLevel === nestingLevel,
|
||||||
getIsIssuePeeked(issue.id) && peekIssue?.nestingLevel === nestingLevel,
|
|
||||||
"last:border-b-transparent": !getIsIssuePeeked(issue.id),
|
"last:border-b-transparent": !getIsIssuePeeked(issue.id),
|
||||||
"hover:bg-custom-background-90": !isIssueSelected,
|
|
||||||
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
"bg-custom-background-80": isCurrentBlockDragging,
|
"bg-custom-background-80": isCurrentBlockDragging,
|
||||||
}
|
}
|
||||||
@ -153,21 +151,33 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||||||
<div className="flex items-center gap-1 group">
|
<div className="flex items-center gap-1 group">
|
||||||
<DragHandle isDragging={isCurrentBlockDragging} ref={dragHandleRef} disabled={!canDrag} />
|
<DragHandle isDragging={isCurrentBlockDragging} ref={dragHandleRef} disabled={!canDrag} />
|
||||||
{projectId && canEditIssueProperties && (
|
{projectId && canEditIssueProperties && (
|
||||||
<div className="flex-shrink-0 grid place-items-center w-3.5">
|
<Tooltip
|
||||||
<MultipleSelectAction
|
tooltipContent={
|
||||||
className={cn(
|
<>
|
||||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
Only issues within the current
|
||||||
{
|
<br />
|
||||||
"opacity-100 pointer-events-auto": isIssueSelected,
|
project can be selected.
|
||||||
}
|
</>
|
||||||
)}
|
}
|
||||||
groupId={groupId}
|
disabled={issue.project_id === projectId}
|
||||||
id={issue.id}
|
>
|
||||||
selectionHelpers={selectionHelpers}
|
<div className="flex-shrink-0 grid place-items-center w-3.5">
|
||||||
/>
|
<MultipleSelectAction
|
||||||
</div>
|
className={cn(
|
||||||
|
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||||
|
{
|
||||||
|
"opacity-100 pointer-events-auto": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
groupId={groupId}
|
||||||
|
id={issue.id}
|
||||||
|
selectionHelpers={selectionHelpers}
|
||||||
|
disabled={issue.project_id !== projectId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<div className="flex h-5 w-5 items-center justify-center">
|
<div className="size-5 flex items-center justify-center">
|
||||||
{subIssuesCount > 0 && (
|
{subIssuesCount > 0 && (
|
||||||
<button
|
<button
|
||||||
className="flex items-center justify-center h-5 w-5 cursor-pointer rounded-sm text-custom-text-400 hover:text-custom-text-300"
|
className="flex items-center justify-center h-5 w-5 cursor-pointer rounded-sm text-custom-text-400 hover:text-custom-text-300"
|
||||||
|
@ -84,9 +84,9 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="group/list-header relative w-full flex-shrink-0 flex items-center gap-2 py-1.5 pl-2.5">
|
<div className="group/list-header relative w-full flex-shrink-0 flex items-center gap-1.5 py-1.5 pl-4">
|
||||||
{canSelectIssues && (
|
{canSelectIssues && (
|
||||||
<div className="flex-shrink-0 flex items-center w-3.5">
|
<div className="flex-shrink-0 flex items-center w-3.5 pl-0.5">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className={cn(
|
className={cn(
|
||||||
"opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto outline-0",
|
"opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto outline-0",
|
||||||
@ -99,6 +99,7 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="size-5" />
|
||||||
<div className="flex-shrink-0 grid place-items-center overflow-hidden">
|
<div className="flex-shrink-0 grid place-items-center overflow-hidden">
|
||||||
{icon ?? <CircleDashed className="h-3.5 w-3.5" strokeWidth={2} />}
|
{icon ?? <CircleDashed className="h-3.5 w-3.5" strokeWidth={2} />}
|
||||||
</div>
|
</div>
|
||||||
|
@ -192,7 +192,7 @@ export const ListGroup = observer((props: Props) => {
|
|||||||
"border-custom-primary-100 ": isDraggingOverColumn,
|
"border-custom-primary-100 ": isDraggingOverColumn,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 pl-5 py-1">
|
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 py-1">
|
||||||
<HeaderGroupByCard
|
<HeaderGroupByCard
|
||||||
groupID={group.id}
|
groupID={group.id}
|
||||||
icon={group.icon}
|
icon={group.icon}
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { MemberDropdown } from "@/components/dropdowns";
|
import { MemberDropdown } from "@/components/dropdowns";
|
||||||
// types
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetAssigneeColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetAssigneeColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||||
@ -36,7 +39,9 @@ export const SpreadsheetAssigneeColumn: React.FC<Props> = observer((props: Props
|
|||||||
buttonVariant={
|
buttonVariant={
|
||||||
issue?.assignee_ids && issue.assignee_ids.length > 0 ? "transparent-without-text" : "transparent-with-text"
|
issue?.assignee_ids && issue.assignee_ids.length > 0 ? "transparent-without-text" : "transparent-with-text"
|
||||||
}
|
}
|
||||||
buttonClassName="text-left"
|
buttonClassName={cn("text-left rounded-none", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { TIssue } from "@plane/types";
|
|
||||||
// types
|
// types
|
||||||
|
import { TIssue } from "@plane/types";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetAttachmentColumn: React.FC<Props> = observer((props) => {
|
export const SpreadsheetAttachmentColumn: React.FC<Props> = observer((props) => {
|
||||||
const { issue } = props;
|
const { issue, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
{issue?.attachment_count} {issue?.attachment_count === 1 ? "attachment" : "attachments"}
|
{issue?.attachment_count} {issue?.attachment_count === 1 ? "attachment" : "attachments"}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,18 +1,28 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||||
// types
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetCreatedOnColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetCreatedOnColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue } = props;
|
const { issue, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
{renderFormattedDate(issue.created_at)}
|
{renderFormattedDate(issue.created_at)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,27 +1,29 @@
|
|||||||
import React, { useCallback } from "react";
|
import React, { useCallback } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { TIssue } from "@plane/types";
|
|
||||||
// hooks
|
|
||||||
import { CycleDropdown } from "@/components/dropdowns";
|
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
|
||||||
import { useEventTracker, useIssues } from "@/hooks/store";
|
|
||||||
// components
|
|
||||||
// types
|
// types
|
||||||
|
import { TIssue } from "@plane/types";
|
||||||
|
// components
|
||||||
|
import { CycleDropdown } from "@/components/dropdowns";
|
||||||
// constants
|
// constants
|
||||||
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
// hooks
|
||||||
|
import { useEventTracker, useIssues } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetCycleColumn: React.FC<Props> = observer((props) => {
|
export const SpreadsheetCycleColumn: React.FC<Props> = observer((props) => {
|
||||||
|
const { issue, disabled, onClose, isIssueSelected } = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// props
|
|
||||||
const { issue, disabled, onClose } = props;
|
|
||||||
// hooks
|
// hooks
|
||||||
const { captureIssueEvent } = useEventTracker();
|
const { captureIssueEvent } = useEventTracker();
|
||||||
const {
|
const {
|
||||||
@ -56,7 +58,9 @@ export const SpreadsheetCycleColumn: React.FC<Props> = observer((props) => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder="Select cycle"
|
placeholder="Select cycle"
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonContainerClassName="w-full relative flex items-center p-2"
|
buttonContainerClassName={cn("w-full relative flex items-center p-2", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonClassName="relative leading-4 h-4.5 bg-transparent"
|
buttonClassName="relative leading-4 h-4.5 bg-transparent"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,26 +1,27 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { CalendarCheck2 } from "lucide-react";
|
import { CalendarCheck2 } from "lucide-react";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// hooks
|
|
||||||
// components
|
// components
|
||||||
import { DateDropdown } from "@/components/dropdowns";
|
import { DateDropdown } from "@/components/dropdowns";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||||
import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
|
import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
|
||||||
|
// hooks
|
||||||
import { useProjectState } from "@/hooks/store";
|
import { useProjectState } from "@/hooks/store";
|
||||||
// types
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetDueDateColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetDueDateColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { getStateById } = useProjectState();
|
const { getStateById } = useProjectState();
|
||||||
// derived values
|
// derived values
|
||||||
@ -49,6 +50,7 @@ export const SpreadsheetDueDateColumn: React.FC<Props> = observer((props: Props)
|
|||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
buttonClassName={cn("rounded-none text-left", {
|
buttonClassName={cn("rounded-none text-left", {
|
||||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
})}
|
})}
|
||||||
clearIconClassName="!text-custom-text-100"
|
clearIconClassName="!text-custom-text-100"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
// components
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { TIssue } from "@plane/types";
|
|
||||||
import { EstimateDropdown } from "@/components/dropdowns";
|
|
||||||
// types
|
// types
|
||||||
|
import { TIssue } from "@plane/types";
|
||||||
|
// components
|
||||||
|
import { EstimateDropdown } from "@/components/dropdowns";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetEstimateColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetEstimateColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||||
@ -25,7 +28,9 @@ export const SpreadsheetEstimateColumn: React.FC<Props> = observer((props: Props
|
|||||||
projectId={issue.project_id}
|
projectId={issue.project_id}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonClassName="rounded-none text-left"
|
buttonClassName={cn("text-left rounded-none", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// components
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel } from "@/hooks/store";
|
import { useLabel } from "@/hooks/store";
|
||||||
// types
|
// components
|
||||||
import { IssuePropertyLabels } from "../../properties";
|
import { IssuePropertyLabels } from "../../properties";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -12,10 +14,11 @@ type Props = {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetLabelColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetLabelColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const { labelMap } = useLabel();
|
const { labelMap } = useLabel();
|
||||||
|
|
||||||
@ -28,7 +31,9 @@ export const SpreadsheetLabelColumn: React.FC<Props> = observer((props: Props) =
|
|||||||
defaultOptions={defaultLabelOptions}
|
defaultOptions={defaultLabelOptions}
|
||||||
onChange={(data) => onChange(issue, { label_ids: data }, { changed_property: "labels", change_details: data })}
|
onChange={(data) => onChange(issue, { label_ids: data }, { changed_property: "labels", change_details: data })}
|
||||||
className="h-11 w-full border-b-[0.5px] border-custom-border-200 hover:bg-custom-background-80"
|
className="h-11 w-full border-b-[0.5px] border-custom-border-200 hover:bg-custom-background-80"
|
||||||
buttonClassName="px-2.5 h-full"
|
buttonClassName={cn("px-2.5 h-full", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
maxRender={1}
|
maxRender={1}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { TIssue } from "@plane/types";
|
|
||||||
// types
|
// types
|
||||||
|
import { TIssue } from "@plane/types";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetLinkColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetLinkColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue } = props;
|
const { issue, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
{issue?.link_count} {issue?.link_count === 1 ? "link" : "links"}
|
{issue?.link_count} {issue?.link_count === 1 ? "link" : "links"}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,27 +2,29 @@ import React, { useCallback } from "react";
|
|||||||
import xor from "lodash/xor";
|
import xor from "lodash/xor";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { TIssue } from "@plane/types";
|
|
||||||
// hooks
|
|
||||||
import { ModuleDropdown } from "@/components/dropdowns";
|
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
|
||||||
import { useEventTracker, useIssues } from "@/hooks/store";
|
|
||||||
// components
|
|
||||||
// types
|
// types
|
||||||
|
import { TIssue } from "@plane/types";
|
||||||
|
// components
|
||||||
|
import { ModuleDropdown } from "@/components/dropdowns";
|
||||||
// constants
|
// constants
|
||||||
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
// hooks
|
||||||
|
import { useEventTracker, useIssues } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetModuleColumn: React.FC<Props> = observer((props) => {
|
export const SpreadsheetModuleColumn: React.FC<Props> = observer((props) => {
|
||||||
|
const { issue, disabled, onClose, isIssueSelected } = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// props
|
|
||||||
const { issue, disabled, onClose } = props;
|
|
||||||
// hooks
|
// hooks
|
||||||
const { captureIssueEvent } = useEventTracker();
|
const { captureIssueEvent } = useEventTracker();
|
||||||
const {
|
const {
|
||||||
@ -65,7 +67,9 @@ export const SpreadsheetModuleColumn: React.FC<Props> = observer((props) => {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder="Select modules"
|
placeholder="Select modules"
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonContainerClassName="w-full relative flex items-center p-2"
|
buttonContainerClassName={cn("w-full relative flex items-center p-2", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonClassName="relative leading-4 h-4.5 bg-transparent"
|
buttonClassName="relative leading-4 h-4.5 bg-transparent"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
multiple
|
multiple
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { PriorityDropdown } from "@/components/dropdowns";
|
import { PriorityDropdown } from "@/components/dropdowns";
|
||||||
// types
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetPriorityColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetPriorityColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||||
@ -22,7 +25,9 @@ export const SpreadsheetPriorityColumn: React.FC<Props> = observer((props: Props
|
|||||||
onChange={(data) => onChange(issue, { priority: data }, { changed_property: "priority", change_details: data })}
|
onChange={(data) => onChange(issue, { priority: data }, { changed_property: "priority", change_details: data })}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonClassName="rounded-none text-left"
|
buttonClassName={cn("text-left rounded-none", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { CalendarClock } from "lucide-react";
|
import { CalendarClock } from "lucide-react";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { DateDropdown } from "@/components/dropdowns";
|
import { DateDropdown } from "@/components/dropdowns";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||||
// types
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetStartDateColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetStartDateColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||||
@ -38,7 +40,9 @@ export const SpreadsheetStartDateColumn: React.FC<Props> = observer((props: Prop
|
|||||||
placeholder="Start date"
|
placeholder="Start date"
|
||||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonClassName="rounded-none text-left"
|
buttonClassName={cn("text-left rounded-none", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { StateDropdown } from "@/components/dropdowns";
|
import { StateDropdown } from "@/components/dropdowns";
|
||||||
// types
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetStateColumn: React.FC<Props> = observer((props) => {
|
export const SpreadsheetStateColumn: React.FC<Props> = observer((props) => {
|
||||||
const { issue, onChange, disabled, onClose } = props;
|
const { issue, onChange, disabled, onClose, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||||
@ -23,7 +26,9 @@ export const SpreadsheetStateColumn: React.FC<Props> = observer((props) => {
|
|||||||
onChange={(data) => onChange(issue, { state_id: data }, { changed_property: "state", change_details: data })}
|
onChange={(data) => onChange(issue, { state_id: data }, { changed_property: "state", change_details: data })}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
buttonVariant="transparent-with-text"
|
buttonVariant="transparent-with-text"
|
||||||
buttonClassName="rounded-none text-left"
|
buttonClassName={cn("text-left rounded-none", {
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
})}
|
||||||
buttonContainerClassName="w-full"
|
buttonContainerClassName="w-full"
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -10,10 +10,11 @@ import { useAppRouter } from "@/hooks/store";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue } = props;
|
const { issue, isIssueSelected } = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// hooks
|
// hooks
|
||||||
@ -37,6 +38,7 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
|||||||
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||||
{
|
{
|
||||||
"cursor-pointer": subIssueCount,
|
"cursor-pointer": subIssueCount,
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -1,18 +1,28 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||||
// types
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetUpdatedOnColumn: React.FC<Props> = observer((props: Props) => {
|
export const SpreadsheetUpdatedOnColumn: React.FC<Props> = observer((props: Props) => {
|
||||||
const { issue } = props;
|
const { issue, isIssueSelected } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
{renderFormattedDate(issue.updated_at)}
|
{renderFormattedDate(issue.updated_at)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
|
||||||
// types
|
// types
|
||||||
import { SPREADSHEET_PROPERTY_DETAILS } from "@/constants/spreadsheet";
|
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||||
import { useEventTracker } from "@/hooks/store";
|
|
||||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
|
||||||
// constants
|
// constants
|
||||||
|
import { SPREADSHEET_PROPERTY_DETAILS } from "@/constants/spreadsheet";
|
||||||
|
// hooks
|
||||||
|
import { useEventTracker } from "@/hooks/store";
|
||||||
// components
|
// components
|
||||||
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
displayProperties: IIssueDisplayProperties;
|
displayProperties: IIssueDisplayProperties;
|
||||||
@ -16,10 +17,19 @@ type Props = {
|
|||||||
property: keyof IIssueDisplayProperties;
|
property: keyof IIssueDisplayProperties;
|
||||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||||
isEstimateEnabled: boolean;
|
isEstimateEnabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueColumn = observer((props: Props) => {
|
export const IssueColumn = observer((props: Props) => {
|
||||||
const { displayProperties, issueDetail, disableUserActions, property, updateIssue, isEstimateEnabled } = props;
|
const {
|
||||||
|
displayProperties,
|
||||||
|
issueDetail,
|
||||||
|
disableUserActions,
|
||||||
|
property,
|
||||||
|
updateIssue,
|
||||||
|
isEstimateEnabled,
|
||||||
|
isIssueSelected,
|
||||||
|
} = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const tableCellRef = useRef<HTMLTableCellElement | null>(null);
|
const tableCellRef = useRef<HTMLTableCellElement | null>(null);
|
||||||
@ -58,9 +68,8 @@ export const IssueColumn = observer((props: Props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
disabled={disableUserActions}
|
disabled={disableUserActions}
|
||||||
onClose={() => {
|
onClose={() => tableCellRef?.current?.focus()}
|
||||||
tableCellRef?.current?.focus();
|
isIssueSelected={isIssueSelected}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</WithDisplayPropertiesHOC>
|
</WithDisplayPropertiesHOC>
|
||||||
|
@ -216,7 +216,12 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<td id={`issue-${issueId}`} ref={cellRef} tabIndex={0} className="sticky left-0 z-10 group/list-block">
|
<td
|
||||||
|
id={`issue-${issueId}`}
|
||||||
|
ref={cellRef}
|
||||||
|
tabIndex={0}
|
||||||
|
className="sticky left-0 z-10 group/list-block bg-custom-background-100"
|
||||||
|
>
|
||||||
<ControlLink
|
<ControlLink
|
||||||
href={`/${workspaceSlug}/projects/${issueDetail.project_id}/issues/${issueId}`}
|
href={`/${workspaceSlug}/projects/${issueDetail.project_id}/issues/${issueId}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -228,6 +233,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
"border border-custom-primary-70 hover:border-custom-primary-70":
|
"border border-custom-primary-70 hover:border-custom-primary-70":
|
||||||
getIsIssuePeeked(issueDetail.id) && nestingLevel === peekIssue?.nestingLevel,
|
getIsIssuePeeked(issueDetail.id) && nestingLevel === peekIssue?.nestingLevel,
|
||||||
"shadow-[8px_22px_22px_10px_rgba(0,0,0,0.05)]": isScrolled.current,
|
"shadow-[8px_22px_22px_10px_rgba(0,0,0,0.05)]": isScrolled.current,
|
||||||
|
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
disabled={!!issueDetail?.tempId}
|
disabled={!!issueDetail?.tempId}
|
||||||
@ -239,19 +245,31 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{/* bulk ops */}
|
{/* bulk ops */}
|
||||||
{projectId && !disableUserActions && (
|
{projectId && !disableUserActions && (
|
||||||
<div className="flex-shrink-0 grid place-items-center w-3.5">
|
<Tooltip
|
||||||
<MultipleSelectAction
|
tooltipContent={
|
||||||
className={cn(
|
<>
|
||||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
Only issues within the current
|
||||||
{
|
<br />
|
||||||
"opacity-100 pointer-events-auto": isIssueSelected,
|
project can be selected.
|
||||||
}
|
</>
|
||||||
)}
|
}
|
||||||
groupId={SPREADSHEET_SELECT_GROUP}
|
disabled={issueDetail.project_id === projectId}
|
||||||
id={issueDetail.id}
|
>
|
||||||
selectionHelpers={selectionHelpers}
|
<div className="flex-shrink-0 grid place-items-center w-3.5">
|
||||||
/>
|
<MultipleSelectAction
|
||||||
</div>
|
className={cn(
|
||||||
|
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||||
|
{
|
||||||
|
"opacity-100 pointer-events-auto": isIssueSelected,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
groupId={SPREADSHEET_SELECT_GROUP}
|
||||||
|
id={issueDetail.id}
|
||||||
|
selectionHelpers={selectionHelpers}
|
||||||
|
disabled={issueDetail.project_id !== projectId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<div className="flex size-4 items-center justify-center">
|
<div className="flex size-4 items-center justify-center">
|
||||||
{subIssuesCount > 0 && (
|
{subIssuesCount > 0 && (
|
||||||
@ -308,6 +326,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
property={property}
|
property={property}
|
||||||
updateIssue={updateIssue}
|
updateIssue={updateIssue}
|
||||||
isEstimateEnabled={isEstimateEnabled}
|
isEstimateEnabled={isEstimateEnabled}
|
||||||
|
isIssueSelected={isIssueSelected}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
25
web/constants/errors.ts
Normal file
25
web/constants/errors.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export enum EErrorCodes {
|
||||||
|
"INVALID_ARCHIVE_STATE_GROUP" = 4091,
|
||||||
|
"INVALID_ISSUE_START_DATE" = 4101,
|
||||||
|
"INVALID_ISSUE_TARGET_DATE" = 4102,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ERROR_DETAILS: {
|
||||||
|
[key in EErrorCodes]: {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
} = {
|
||||||
|
[EErrorCodes.INVALID_ARCHIVE_STATE_GROUP]: {
|
||||||
|
title: "Unable to archive issues",
|
||||||
|
message: "Only issues belonging to Completed or Canceled states can be archived.",
|
||||||
|
},
|
||||||
|
[EErrorCodes.INVALID_ISSUE_START_DATE]: {
|
||||||
|
title: "Unable to update issues",
|
||||||
|
message: "Start date selected succeeds the due date for some issues. Ensure start date to be before the due date.",
|
||||||
|
},
|
||||||
|
[EErrorCodes.INVALID_ISSUE_TARGET_DATE]: {
|
||||||
|
title: "Unable to update issues",
|
||||||
|
message: "Due date selected precedes the start date for some issues. Ensure due date to be after the start date.",
|
||||||
|
},
|
||||||
|
};
|
@ -35,6 +35,7 @@ export const SPREADSHEET_PROPERTY_DETAILS: {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
onChange: (issue: TIssue, data: Partial<TIssue>, updates: any) => void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
isIssueSelected: boolean;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
} = {
|
} = {
|
||||||
|
@ -242,7 +242,7 @@ export class IssueService extends APIService {
|
|||||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-operation-issues/`, data)
|
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-operation-issues/`, data)
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw error?.response;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import { IIssueRootStore } from "./root.store";
|
|||||||
|
|
||||||
export type IIssueBulkOperationsStore = {
|
export type IIssueBulkOperationsStore = {
|
||||||
// actions
|
// actions
|
||||||
bulkUpdateProperties: (workspaceSlug: string, projectId: string, data: TBulkOperationsPayload) => void;
|
bulkUpdateProperties: (workspaceSlug: string, projectId: string, data: TBulkOperationsPayload) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class IssueBulkOperationsStore implements IIssueBulkOperationsStore {
|
export class IssueBulkOperationsStore implements IIssueBulkOperationsStore {
|
||||||
|
Loading…
Reference in New Issue
Block a user