fix: minor refactoring changes for state dropdowns
This commit is contained in:
parent
da469dac18
commit
952eee8d55
39 changed files with 291 additions and 266 deletions
|
|
@ -1,16 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { Command } from "cmdk";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// hooks
|
||||
import { Check } from "lucide-react";
|
||||
// plane imports
|
||||
import { TIssue } from "@plane/types";
|
||||
import { Spinner, StateGroupIcon } from "@plane/ui";
|
||||
import { useProjectState, useIssueDetail } from "@/hooks/store";
|
||||
// ui
|
||||
// icons
|
||||
// types
|
||||
// store hooks
|
||||
import { useIssueDetail } from "@/hooks/store";
|
||||
// plane web imports
|
||||
import { ChangeWorkItemStateList } from "@/plane-web/components/command-palette/actions/work-item-actions";
|
||||
|
||||
type Props = { closePalette: () => void; issue: TIssue };
|
||||
|
||||
|
|
@ -20,10 +17,9 @@ export const ChangeIssueState: React.FC<Props> = observer((props) => {
|
|||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const { updateIssue } = useIssueDetail();
|
||||
const { getProjectStates } = useProjectState();
|
||||
// derived values
|
||||
const projectId = issue?.project_id;
|
||||
const projectStates = getProjectStates(projectId);
|
||||
const currentStateId = issue?.state_id;
|
||||
|
||||
const submitChanges = async (formData: Partial<TIssue>) => {
|
||||
if (!workspaceSlug || !projectId || !issue) return;
|
||||
|
|
@ -40,24 +36,10 @@ export const ChangeIssueState: React.FC<Props> = observer((props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{projectStates ? (
|
||||
projectStates.length > 0 ? (
|
||||
projectStates.map((state) => (
|
||||
<Command.Item key={state.id} onSelect={() => handleIssueState(state.id)} className="focus:outline-none">
|
||||
<div className="flex items-center space-x-3">
|
||||
<StateGroupIcon stateGroup={state.group} color={state.color} height="16px" width="16px" />
|
||||
<p>{state.name}</p>
|
||||
</div>
|
||||
<div>{state.id === issue.state_id && <Check className="h-3 w-3" />}</div>
|
||||
</Command.Item>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center">No states found</div>
|
||||
)
|
||||
) : (
|
||||
<Spinner />
|
||||
)}
|
||||
</>
|
||||
<ChangeWorkItemStateList
|
||||
projectId={projectId}
|
||||
currentStateId={currentStateId}
|
||||
handleStateChange={handleIssueState}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,15 +29,17 @@ export const ActivityBlockComponent: FC<TActivityBlockComponent> = (props) => {
|
|||
if (!activity) return <></>;
|
||||
return (
|
||||
<div
|
||||
className={`relative flex items-center gap-2 text-xs ${
|
||||
className={`relative flex items-start gap-2 text-xs ${
|
||||
ends === "top" ? `pb-3` : ends === "bottom" ? `pt-3` : `py-3`
|
||||
}`}
|
||||
>
|
||||
<div className="flex-shrink-0 ring-6 w-7 h-7 rounded-full overflow-hidden flex justify-center items-start mt-0.5 z-[4] text-custom-text-200">
|
||||
{icon ? icon : <Network className="w-3.5 h-3.5" />}
|
||||
</div>
|
||||
<div className="w-full truncate text-custom-text-200">
|
||||
<User activity={activity} customUserName={customUserName} /> {children}
|
||||
<div className="w-full text-custom-text-200">
|
||||
<div className="line-clamp-2">
|
||||
<User activity={activity} customUserName={customUserName} /> {children}
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ type Props = TDropdownProps & {
|
|||
renderByDefault?: boolean;
|
||||
stateIds?: string[];
|
||||
filterAvailableStateIds?: boolean;
|
||||
isForWorkItemCreation?: boolean;
|
||||
};
|
||||
|
||||
export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||
|
|
@ -59,6 +60,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||
renderByDefault = true,
|
||||
stateIds,
|
||||
filterAvailableStateIds = true,
|
||||
isForWorkItemCreation = false,
|
||||
} = props;
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
|
|
@ -98,7 +100,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} className="h-3 w-3 flex-shrink-0" />
|
||||
<span className="flex-grow truncate">{state?.name}</span>
|
||||
<span className="flex-grow truncate text-left">{state?.name}</span>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
|
@ -182,7 +184,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||
/>
|
||||
)}
|
||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||
<span className="flex-grow truncate">{selectedState?.name ?? t("state")}</span>
|
||||
<span className="flex-grow truncate text-left">{selectedState?.name ?? t("state")}</span>
|
||||
)}
|
||||
{dropdownArrow && (
|
||||
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||
|
|
@ -239,6 +241,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||
filterAvailableStateIds={filterAvailableStateIds}
|
||||
selectedValue={value}
|
||||
className="flex w-full cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5"
|
||||
isForWorkItemCreation={isForWorkItemCreation}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useRef } from "react";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
// Plane
|
||||
// plane imports
|
||||
import { ISSUE_ORDER_BY_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssueOrderByOptions } from "@plane/types";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// Plane-web
|
||||
import { WorkFlowDisabledMessage } from "@/plane-web/components/workflow";
|
||||
// plane web imports
|
||||
import { WorkFlowDisabledOverlay } from "@/plane-web/components/workflow";
|
||||
|
||||
type Props = {
|
||||
dragColumnOrientation: "justify-start" | "justify-center" | "justify-end";
|
||||
|
|
@ -30,9 +31,10 @@ export const GroupDragOverlay = (props: Props) => {
|
|||
isDraggingOverColumn,
|
||||
isEpic = false,
|
||||
} = props;
|
||||
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
// refs
|
||||
const messageContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
|
||||
const readableOrderBy = t(
|
||||
|
|
@ -41,27 +43,28 @@ export const GroupDragOverlay = (props: Props) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
ref={messageContainerRef}
|
||||
className={cn(
|
||||
`absolute top-0 left-0 h-full w-full items-center text-sm font-medium text-custom-text-300 rounded bg-custom-background-overlay ${dragColumnOrientation}`,
|
||||
`absolute top-0 left-0 h-full w-full items-center text-sm font-medium text-custom-text-300 rounded bg-custom-background-80/85 ${dragColumnOrientation}`,
|
||||
{
|
||||
"flex flex-col border-[1px] border-custom-border-300 z-[2]": shouldOverlayBeVisible,
|
||||
"bg-red-200/60": workflowDisabledSource && isDropDisabled,
|
||||
},
|
||||
{ hidden: !shouldOverlayBeVisible }
|
||||
)}
|
||||
>
|
||||
{workflowDisabledSource ? (
|
||||
<WorkFlowDisabledMessage parentStateId={workflowDisabledSource} className="my-2" />
|
||||
<WorkFlowDisabledOverlay
|
||||
messageContainerRef={messageContainerRef}
|
||||
workflowDisabledSource={workflowDisabledSource}
|
||||
shouldOverlayBeVisible={shouldOverlayBeVisible}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"p-3 my-8 flex flex-col rounded items-center",
|
||||
{
|
||||
"text-custom-text-200": shouldOverlayBeVisible,
|
||||
},
|
||||
{
|
||||
"text-custom-text-error": isDropDisabled,
|
||||
}
|
||||
)}
|
||||
className={cn("p-3 my-8 flex flex-col rounded items-center", {
|
||||
"text-custom-text-200": shouldOverlayBeVisible,
|
||||
"text-custom-text-error": isDropDisabled,
|
||||
})}
|
||||
>
|
||||
{dropErrorMessage ? (
|
||||
<div className="flex items-center">
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { useKanbanView } from "@/hooks/store";
|
|||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
// types
|
||||
// parent components
|
||||
import { useWorkFlowFDragNDrop } from "@/plane-web/components/workflow";
|
||||
import { TRenderQuickActions } from "../list/list-view-types";
|
||||
import { getGroupByColumns, isWorkspaceLevel, GroupDropLocation, getApproximateCardHeight } from "../utils";
|
||||
// components
|
||||
|
|
@ -98,6 +99,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||
const issueKanBanView = useKanbanView();
|
||||
// derived values
|
||||
const isDragDisabled = !issueKanBanView?.getCanUserDragDrop(group_by, sub_group_by);
|
||||
|
||||
const { getIsWorkflowWorkItemCreationDisabled } = useWorkFlowFDragNDrop(group_by, sub_group_by);
|
||||
|
||||
const list = getGroupByColumns({
|
||||
groupBy: group_by as GroupByColumnTypes,
|
||||
includeNone: true,
|
||||
|
|
@ -167,7 +171,11 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||
title={subList.name}
|
||||
count={getGroupIssueCount(subList.id, undefined, false) ?? 0}
|
||||
issuePayload={subList.payload}
|
||||
disableIssueCreation={disableIssueCreation || isGroupByCreatedBy}
|
||||
disableIssueCreation={
|
||||
disableIssueCreation ||
|
||||
isGroupByCreatedBy ||
|
||||
getIsWorkflowWorkItemCreationDisabled(subList.id, sub_group_id)
|
||||
}
|
||||
addIssuesToView={addIssuesToView}
|
||||
collapsedGroups={collapsedGroups}
|
||||
handleCollapsedGroups={handleCollapsedGroups}
|
||||
|
|
|
|||
|
|
@ -113,10 +113,8 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
|||
);
|
||||
const [isDraggingOverColumn, setIsDraggingOverColumn] = useState(false);
|
||||
|
||||
const { workflowDisabledSource, isWorkflowDropDisabled, handleWorkFlowState } = useWorkFlowFDragNDrop(
|
||||
group_by,
|
||||
sub_group_by
|
||||
);
|
||||
const { workflowDisabledSource, isWorkflowDropDisabled, handleWorkFlowState, getIsWorkflowWorkItemCreationDisabled } =
|
||||
useWorkFlowFDragNDrop(group_by, sub_group_by);
|
||||
|
||||
// Enable Kanban Columns as Drop Targets
|
||||
useEffect(() => {
|
||||
|
|
@ -306,19 +304,21 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
|||
|
||||
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
|
||||
|
||||
{enableQuickIssueCreate && !disableIssueCreation && (
|
||||
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.KANBAN}
|
||||
QuickAddButton={KanbanQuickAddIssueButton}
|
||||
prePopulatedData={{
|
||||
...(group_by && prePopulateQuickAddData(group_by, sub_group_by, groupId, sub_group_id)),
|
||||
}}
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{enableQuickIssueCreate &&
|
||||
!disableIssueCreation &&
|
||||
!getIsWorkflowWorkItemCreationDisabled(groupId, sub_group_id) && (
|
||||
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.KANBAN}
|
||||
QuickAddButton={KanbanQuickAddIssueButton}
|
||||
prePopulatedData={{
|
||||
...(group_by && prePopulateQuickAddData(group_by, sub_group_by, groupId, sub_group_id)),
|
||||
}}
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { MutableRefObject } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import {
|
||||
GroupByColumnTypes,
|
||||
IGroupByColumn,
|
||||
|
|
@ -18,6 +17,7 @@ import { Row } from "@plane/ui";
|
|||
// hooks
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
// components
|
||||
import { useWorkFlowFDragNDrop } from "@/plane-web/components/workflow";
|
||||
import { TRenderQuickActions } from "../list/list-view-types";
|
||||
import { getGroupByColumns, isWorkspaceLevel, GroupDropLocation } from "../utils";
|
||||
import { KanBan } from "./default";
|
||||
|
|
@ -53,35 +53,40 @@ const visibilitySubGroupByGroupCount = (subGroupIssueCount: number, showEmptyGro
|
|||
};
|
||||
|
||||
const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = observer(
|
||||
({ getGroupIssueCount, sub_group_by, group_by, list, collapsedGroups, handleCollapsedGroups, showEmptyGroup }) => (
|
||||
<div className="relative flex h-max min-h-full w-full items-center gap-4">
|
||||
{list &&
|
||||
list.length > 0 &&
|
||||
list.map((_list: IGroupByColumn) => {
|
||||
const groupCount = getGroupIssueCount(_list?.id, undefined, false) ?? 0;
|
||||
({ getGroupIssueCount, sub_group_by, group_by, list, collapsedGroups, handleCollapsedGroups, showEmptyGroup }) => {
|
||||
const { getIsWorkflowWorkItemCreationDisabled } = useWorkFlowFDragNDrop(group_by, sub_group_by);
|
||||
|
||||
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount, showEmptyGroup);
|
||||
return (
|
||||
<div className="relative flex h-max min-h-full w-full items-center gap-4">
|
||||
{list &&
|
||||
list.length > 0 &&
|
||||
list.map((_list: IGroupByColumn) => {
|
||||
const groupCount = getGroupIssueCount(_list?.id, undefined, false) ?? 0;
|
||||
|
||||
if (subGroupByVisibilityToggle === false) return <></>;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div key={`${sub_group_by}_${_list.id}`} className="flex w-[350px] flex-shrink-0 flex-col">
|
||||
<HeaderGroupByCard
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
column_id={_list.id}
|
||||
icon={_list.icon}
|
||||
title={_list.name}
|
||||
count={groupCount}
|
||||
collapsedGroups={collapsedGroups}
|
||||
handleCollapsedGroups={handleCollapsedGroups}
|
||||
issuePayload={_list.payload}
|
||||
/>{" "}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount, showEmptyGroup);
|
||||
|
||||
if (subGroupByVisibilityToggle === false) return <></>;
|
||||
|
||||
return (
|
||||
<div key={`${sub_group_by}_${_list.id}`} className="flex w-[350px] flex-shrink-0 flex-col">
|
||||
<HeaderGroupByCard
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
column_id={_list.id}
|
||||
icon={_list.icon}
|
||||
title={_list.name}
|
||||
count={groupCount}
|
||||
collapsedGroups={collapsedGroups}
|
||||
handleCollapsedGroups={handleCollapsedGroups}
|
||||
issuePayload={_list.payload}
|
||||
disableIssueCreation={getIsWorkflowWorkItemCreationDisabled(_list.id)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
||||
|
|
@ -156,7 +161,6 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||
{list &&
|
||||
list.length > 0 &&
|
||||
list.map((_list: IGroupByColumn, subGroupIndex) => {
|
||||
const { t } = useTranslation();
|
||||
const issueCount = getGroupIssueCount(undefined, _list.id, true) ?? 0;
|
||||
const subGroupByVisibilityToggle = visibilitySubGroupBy(_list, issueCount);
|
||||
if (subGroupByVisibilityToggle.showGroup === false) return <></>;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,9 @@ export const ListGroup = observer((props: Props) => {
|
|||
|
||||
const [intersectionElement, setIntersectionElement] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const { workflowDisabledSource, isWorkflowDropDisabled, handleWorkFlowState } = useWorkFlowFDragNDrop(group_by);
|
||||
const { workflowDisabledSource, isWorkflowDropDisabled, handleWorkFlowState, getIsWorkflowWorkItemCreationDisabled } =
|
||||
useWorkFlowFDragNDrop(group_by);
|
||||
const isWorkflowIssueCreationDisabled = getIsWorkflowWorkItemCreationDisabled(group.id);
|
||||
|
||||
const groupIssueCount = getGroupIssueCount(group.id, undefined, false) ?? 0;
|
||||
const nextPageResults = getPaginationData(group.id, undefined)?.nextPageResults;
|
||||
|
|
@ -267,7 +269,9 @@ export const ListGroup = observer((props: Props) => {
|
|||
count={groupIssueCount}
|
||||
issuePayload={group.payload}
|
||||
canEditProperties={canEditProperties}
|
||||
disableIssueCreation={disableIssueCreation || isGroupByCreatedBy || isCompletedCycle}
|
||||
disableIssueCreation={
|
||||
disableIssueCreation || isGroupByCreatedBy || isCompletedCycle || isWorkflowIssueCreationDisabled
|
||||
}
|
||||
addIssuesToView={addIssuesToView}
|
||||
selectionHelpers={selectionHelpers}
|
||||
handleCollapsedGroups={handleCollapsedGroups}
|
||||
|
|
@ -305,18 +309,22 @@ export const ListGroup = observer((props: Props) => {
|
|||
|
||||
{shouldLoadMore && (group_by ? <>{loadMore}</> : <ListLoaderItemRow ref={setIntersectionElement} />)}
|
||||
|
||||
{enableIssueQuickAdd && !disableIssueCreation && !isGroupByCreatedBy && !isCompletedCycle && (
|
||||
<div className="sticky bottom-0 z-[1] w-full flex-shrink-0">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.LIST}
|
||||
QuickAddButton={ListQuickAddIssueButton}
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, group.id)}
|
||||
containerClassName="border-b border-t border-custom-border-200 bg-custom-background-100 "
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{enableIssueQuickAdd &&
|
||||
!disableIssueCreation &&
|
||||
!isGroupByCreatedBy &&
|
||||
!isCompletedCycle &&
|
||||
!isWorkflowIssueCreationDisabled && (
|
||||
<div className="sticky bottom-0 z-[1] w-full flex-shrink-0">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.LIST}
|
||||
QuickAddButton={ListQuickAddIssueButton}
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, group.id)}
|
||||
containerClassName="border-b border-t border-custom-border-200 bg-custom-background-100 "
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ export const IssueDefaultProperties: React.FC<TIssueDefaultPropertiesProps> = ob
|
|||
projectId={projectId ?? undefined}
|
||||
buttonVariant="border-with-text"
|
||||
tabIndex={getIndex("state_id")}
|
||||
isForWorkItemCreation={!id}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export const StateMarksAsDefault: FC<TStateMarksAsDefault> = observer((props) =>
|
|||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"text-sm whitespace-nowrap transition-colors",
|
||||
"text-xs whitespace-nowrap transition-colors",
|
||||
isDefault ? "text-custom-text-300" : "text-custom-text-200 hover:text-custom-text-100"
|
||||
)}
|
||||
disabled={isDefault || isLoading}
|
||||
|
|
|
|||
|
|
@ -16,16 +16,11 @@ type TProjectState = {
|
|||
export const ProjectStateRoot: FC<TProjectState> = observer((props) => {
|
||||
const { workspaceSlug, projectId } = props;
|
||||
// hooks
|
||||
const { groupedProjectStates, fetchProjectStates, fetchProjectStateTransitions } = useProjectState();
|
||||
const { groupedProjectStates, fetchProjectStates } = useProjectState();
|
||||
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId
|
||||
? () => {
|
||||
fetchProjectStates(workspaceSlug.toString(), projectId.toString());
|
||||
fetchProjectStateTransitions(workspaceSlug.toString(), projectId.toString());
|
||||
}
|
||||
: null,
|
||||
workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import { SetStateAction } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { GripVertical, Pencil } from "lucide-react";
|
||||
// Plane
|
||||
// plane imports
|
||||
import { IState } from "@plane/types";
|
||||
import { StateGroupIcon } from "@plane/ui";
|
||||
// Plane-web
|
||||
import { StateTransitionCount } from "@/plane-web/components/workflow";
|
||||
import { IStateWorkFlow } from "@/plane-web/types";
|
||||
//
|
||||
// local imports
|
||||
import { StateDelete, StateMarksAsDefault } from "./options";
|
||||
|
||||
export type StateItemTitleProps = {
|
||||
|
|
@ -17,31 +14,37 @@ export type StateItemTitleProps = {
|
|||
stateCount: number;
|
||||
disabled: boolean;
|
||||
state: IState;
|
||||
currentTransitionMap?: IStateWorkFlow;
|
||||
shouldShowDescription?: boolean;
|
||||
};
|
||||
|
||||
export const StateItemTitle = observer((props: StateItemTitleProps) => {
|
||||
const { workspaceSlug, projectId, stateCount, setUpdateStateModal, disabled, state, currentTransitionMap } = props;
|
||||
const {
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
stateCount,
|
||||
setUpdateStateModal,
|
||||
disabled,
|
||||
state,
|
||||
shouldShowDescription = true,
|
||||
} = props;
|
||||
return (
|
||||
<div className="flex items-center gap-2 w-full justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1 px-1">
|
||||
{/* draggable indicator */}
|
||||
{!disabled && stateCount != 1 && (
|
||||
<div className="flex-shrink-0 w-3 h-3 rounded-sm absolute left-0 hidden group-hover:flex justify-center items-center transition-colors bg-custom-background-90 cursor-pointer text-custom-text-200 hover:text-custom-text-100">
|
||||
<div className="flex-shrink-0 w-3 h-3 rounded-sm absolute -left-1.5 hidden group-hover:flex justify-center items-center transition-colors bg-custom-background-90 cursor-pointer text-custom-text-200 hover:text-custom-text-100">
|
||||
<GripVertical className="w-3 h-3" />
|
||||
</div>
|
||||
)}
|
||||
{/* state icon */}
|
||||
<div className="flex-shrink-0">
|
||||
<StateGroupIcon stateGroup={state.group} color={state.color} height="16px" width="16px" />
|
||||
<StateGroupIcon stateGroup={state.group} color={state.color} className={"size-3.5"} />
|
||||
</div>
|
||||
{/* state title and description */}
|
||||
<div className="text-sm px-2 min-h-5">
|
||||
<h6 className="text-sm font-medium">{state.name}</h6>
|
||||
<p className="text-xs text-custom-text-200">{state.description}</p>
|
||||
{shouldShowDescription && <p className="text-xs text-custom-text-200">{state.description}</p>}
|
||||
</div>
|
||||
{/* Transition count */}
|
||||
<StateTransitionCount currentTransitionMap={currentTransitionMap} />
|
||||
</div>
|
||||
|
||||
{!disabled && (
|
||||
|
|
|
|||
|
|
@ -10,14 +10,12 @@ import { TDraggableData } from "@plane/constants";
|
|||
import { IState, TStateGroups } from "@plane/types";
|
||||
import { DropIndicator } from "@plane/ui";
|
||||
// components
|
||||
import { StateUpdate } from "@/components/project-states";
|
||||
import { StateItemTitle, StateUpdate } from "@/components/project-states";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { getCurrentStateSequence } from "@/helpers/state.helper";
|
||||
// hooks
|
||||
import { useProjectState } from "@/hooks/store";
|
||||
// Plane-web
|
||||
import { StateItemChild } from "@/plane-web/components/workflow";
|
||||
|
||||
type TStateItem = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -133,7 +131,7 @@ export const StateItem: FC<TStateItem> = observer((props) => {
|
|||
totalStates === 1 ? `cursor-auto` : `cursor-grab`
|
||||
)}
|
||||
>
|
||||
<StateItemChild
|
||||
<StateItemTitle
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
setUpdateStateModal={setUpdateStateModal}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import useSWR from "swr";
|
||||
import { useCycle, useProjectEstimates, useLabel, useModule, useProjectState } from "./store";
|
||||
// plane web imports
|
||||
import { useWorkspaceIssuePropertiesExtended } from "@/plane-web/hooks/use-workspace-issue-properties-extended";
|
||||
// plane imports
|
||||
import { useCycle, useProjectEstimates, useLabel, useModule } from "./store";
|
||||
|
||||
export const useWorkspaceIssueProperties = (workspaceSlug: string | string[] | undefined) => {
|
||||
const { fetchWorkspaceLabels } = useLabel();
|
||||
|
|
@ -37,4 +40,7 @@ export const useWorkspaceIssueProperties = (workspaceSlug: string | string[] | u
|
|||
workspaceSlug ? () => getWorkspaceEstimates(workspaceSlug.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
// fetch extended issue properties
|
||||
useWorkspaceIssuePropertiesExtended(workspaceSlug);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
const {
|
||||
project: { fetchProjectMembers },
|
||||
} = useMember();
|
||||
const { fetchProjectStates, fetchProjectStateTransitions } = useProjectState();
|
||||
const { fetchProjectStates } = useProjectState();
|
||||
const { fetchProjectLabels } = useLabel();
|
||||
const { getProjectEstimates } = useProjectEstimates();
|
||||
|
||||
|
|
@ -118,12 +118,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
// fetching project states
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId
|
||||
? () => {
|
||||
fetchProjectStates(workspaceSlug.toString(), projectId.toString());
|
||||
fetchProjectStateTransitions(workspaceSlug.toString(), projectId.toString());
|
||||
}
|
||||
: null,
|
||||
workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetching project estimates
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { computedFn } from "mobx-utils";
|
|||
import { STATE_GROUPS } from "@plane/constants";
|
||||
import { IState } from "@plane/types";
|
||||
// helpers
|
||||
import { convertStringArrayToBooleanObject } from "@/helpers/array.helper";
|
||||
import { sortStates } from "@/helpers/state.helper";
|
||||
// plane web
|
||||
import { syncIssuesWithDeletedStates } from "@/local-db/utils/load-workspace";
|
||||
|
|
@ -25,10 +24,6 @@ export interface IStateStore {
|
|||
// computed actions
|
||||
getStateById: (stateId: string | null | undefined) => IState | undefined;
|
||||
getProjectStates: (projectId: string | null | undefined) => IState[] | undefined;
|
||||
getAvailableProjectStateIdMap: (
|
||||
projectId: string | null | undefined,
|
||||
currStateId: string | null | undefined
|
||||
) => { [key: string]: boolean };
|
||||
// fetch actions
|
||||
fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise<IState[]>;
|
||||
fetchWorkspaceStates: (workspaceSlug: string) => Promise<IState[]>;
|
||||
|
|
@ -48,8 +43,6 @@ export interface IStateStore {
|
|||
stateId: string,
|
||||
payload: Partial<IState>
|
||||
) => Promise<void>;
|
||||
//Dummy method
|
||||
fetchProjectStateTransitions: (workspaceSlug: string, projectId: string) => void;
|
||||
}
|
||||
|
||||
export class StateStore implements IStateStore {
|
||||
|
|
@ -143,20 +136,6 @@ export class StateStore implements IStateStore {
|
|||
return sortStates(Object.values(this.stateMap).filter((state) => state.project_id === projectId));
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns an object linking state permissions as boolean values
|
||||
* @param projectId
|
||||
*/
|
||||
getAvailableProjectStateIdMap = computedFn(
|
||||
(projectId: string | null | undefined, currStateId: string | null | undefined) => {
|
||||
const projectStates = this.getProjectStates(projectId);
|
||||
|
||||
if (!projectStates) return {};
|
||||
|
||||
return convertStringArrayToBooleanObject(projectStates.map((projectState) => projectState.id));
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* fetches the stateMap of a project
|
||||
* @param workspaceSlug
|
||||
|
|
@ -292,14 +271,11 @@ export class StateStore implements IStateStore {
|
|||
});
|
||||
// updating using api
|
||||
await this.stateService.patchState(workspaceSlug, projectId, stateId, payload);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// reverting back to old state group if api fails
|
||||
runInAction(() => {
|
||||
this.stateMap = originalStates;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Dummy method
|
||||
fetchProjectStateTransitions = (workspaceSlug: string, projectId: string) => {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue