fix: cycle and module reordering in the gantt chart (#3570)

* fix: cycle and module reordering in the gantt chart

* chore: hide duration from sidebar if no dates are assigned

* chore: updated date helper functions to accept undefined params

* chore: update cycle sidebar condition
This commit is contained in:
Aaryan Khandelwal 2024-02-08 13:30:16 +05:30 committed by GitHub
parent c1c0297b6d
commit 9545dc77d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 75 additions and 324 deletions

View File

@ -150,7 +150,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
color: group.color, color: group.color,
})); }));
const daysLeft = findHowManyDaysLeft(activeCycle.end_date ?? new Date()); const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0;
return ( return (
<div className="grid-row-2 grid divide-y rounded-[10px] border border-custom-border-200 bg-custom-background-100 shadow"> <div className="grid-row-2 grid divide-y rounded-[10px] border border-custom-border-200 bg-custom-background-100 shadow">

View File

@ -137,7 +137,7 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
}); });
}; };
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date ?? new Date()); const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;
return ( return (
<div> <div>

View File

@ -140,7 +140,7 @@ export const CyclesListItem: FC<TCyclesListItem> = (props) => {
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date ?? new Date()); const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;
return ( return (
<> <>

View File

@ -1,11 +1,8 @@
import { FC } from "react"; import { FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { KeyedMutator } from "swr";
// hooks // hooks
import { useCycle, useUser } from "hooks/store"; import { useCycle, useUser } from "hooks/store";
// services
import { CycleService } from "services/cycle.service";
// components // components
import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/gantt-chart"; import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/gantt-chart";
import { CycleGanttBlock } from "components/cycles"; import { CycleGanttBlock } from "components/cycles";
@ -17,14 +14,10 @@ import { EUserProjectRoles } from "constants/project";
type Props = { type Props = {
workspaceSlug: string; workspaceSlug: string;
cycleIds: string[]; cycleIds: string[];
mutateCycles?: KeyedMutator<ICycle[]>;
}; };
// services
const cycleService = new CycleService();
export const CyclesListGanttChartView: FC<Props> = observer((props) => { export const CyclesListGanttChartView: FC<Props> = observer((props) => {
const { cycleIds, mutateCycles } = props; const { cycleIds } = props;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
@ -32,38 +25,15 @@ export const CyclesListGanttChartView: FC<Props> = observer((props) => {
const { const {
membership: { currentProjectRole }, membership: { currentProjectRole },
} = useUser(); } = useUser();
const { getCycleById } = useCycle(); const { getCycleById, updateCycleDetails } = useCycle();
const handleCycleUpdate = (cycle: ICycle, payload: IBlockUpdateData) => { const handleCycleUpdate = async (cycle: ICycle, data: IBlockUpdateData) => {
if (!workspaceSlug) return; if (!workspaceSlug || !cycle) return;
mutateCycles &&
mutateCycles((prevData: any) => {
if (!prevData) return prevData;
const newList = prevData.map((p: any) => ({ const payload: any = { ...data };
...p, if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
...(p.id === cycle.id
? {
start_date: payload.start_date ? payload.start_date : p.start_date,
target_date: payload.target_date ? payload.target_date : p.end_date,
sort_order: payload.sort_order ? payload.sort_order.newSortOrder : p.sort_order,
}
: {}),
}));
if (payload.sort_order) { await updateCycleDetails(workspaceSlug.toString(), cycle.project, cycle.id, payload);
const removedElement = newList.splice(payload.sort_order.sourceIndex, 1)[0];
newList.splice(payload.sort_order.destinationIndex, 0, removedElement);
}
return newList;
}, false);
const newPayload: any = { ...payload };
if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder;
cycleService.patchCycle(workspaceSlug.toString(), cycle.project, cycle.id, newPayload);
}; };
const blockFormat = (blocks: (ICycle | null)[]) => { const blockFormat = (blocks: (ICycle | null)[]) => {

View File

@ -318,6 +318,7 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
const issueCount = const issueCount =
cycleDetails.total_issues === 0 ? "0 Issue" : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; cycleDetails.total_issues === 0 ? "0 Issue" : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`;
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
@ -375,8 +376,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
backgroundColor: `${currentCycle.color}20`, backgroundColor: `${currentCycle.color}20`,
}} }}
> >
{currentCycle.value === "current" {currentCycle.value === "current" && daysLeft !== undefined
? `${findHowManyDaysLeft(cycleDetails.end_date ?? new Date())} ${currentCycle.label}` ? `${daysLeft} ${currentCycle.label}`
: `${currentCycle.label}`} : `${currentCycle.label}`}
</span> </span>
)} )}

View File

@ -83,7 +83,7 @@ export const AssignedOverdueIssueListItem: React.FC<IssueListItemProps> = observ
const blockedByIssueProjectDetails = const blockedByIssueProjectDetails =
blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null; blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null;
const dueBy = findTotalDaysInRange(new Date(issueDetails.target_date ?? ""), new Date(), false); const dueBy = findTotalDaysInRange(new Date(issueDetails.target_date ?? ""), new Date(), false) ?? 0;
return ( return (
<ControlLink <ControlLink
@ -212,7 +212,7 @@ export const CreatedOverdueIssueListItem: React.FC<IssueListItemProps> = observe
const projectDetails = getProjectById(issue.project_id); const projectDetails = getProjectById(issue.project_id);
const dueBy = findTotalDaysInRange(new Date(issue.target_date ?? ""), new Date(), false); const dueBy = findTotalDaysInRange(new Date(issue.target_date ?? ""), new Date(), false) ?? 0;
return ( return (
<ControlLink <ControlLink

View File

@ -3,8 +3,7 @@ import { TIssue } from "@plane/types";
import { IGanttBlock } from "components/gantt-chart"; import { IGanttBlock } from "components/gantt-chart";
export const renderIssueBlocksStructure = (blocks: TIssue[]): IGanttBlock[] => export const renderIssueBlocksStructure = (blocks: TIssue[]): IGanttBlock[] =>
blocks && blocks?.map((block) => ({
blocks.map((block) => ({
data: block, data: block,
id: block.id, id: block.id,
sort_order: block.sort_order, sort_order: block.sort_order,

View File

@ -93,7 +93,7 @@ export const CycleGanttSidebar: React.FC<Props> = (props) => {
<> <>
{blocks ? ( {blocks ? (
blocks.map((block, index) => { blocks.map((block, index) => {
const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); const duration = findTotalDaysInRange(block.start_date, block.target_date);
return ( return (
<Draggable <Draggable
@ -130,9 +130,11 @@ export const CycleGanttSidebar: React.FC<Props> = (props) => {
<div className="flex-grow truncate"> <div className="flex-grow truncate">
<CycleGanttSidebarBlock data={block.data} /> <CycleGanttSidebarBlock data={block.data} />
</div> </div>
<div className="flex-shrink-0 text-sm text-custom-text-200"> {duration !== undefined && (
{duration} day{duration > 1 ? "s" : ""} <div className="flex-shrink-0 text-sm text-custom-text-200">
</div> {duration} day{duration > 1 ? "s" : ""}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -93,7 +93,7 @@ export const ModuleGanttSidebar: React.FC<Props> = (props) => {
<> <>
{blocks ? ( {blocks ? (
blocks.map((block, index) => { blocks.map((block, index) => {
const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); const duration = findTotalDaysInRange(block.start_date, block.target_date);
return ( return (
<Draggable <Draggable
@ -130,9 +130,11 @@ export const ModuleGanttSidebar: React.FC<Props> = (props) => {
<div className="flex-grow truncate"> <div className="flex-grow truncate">
<ModuleGanttSidebarBlock data={block.data} /> <ModuleGanttSidebarBlock data={block.data} />
</div> </div>
<div className="flex-shrink-0 text-sm text-custom-text-200"> {duration !== undefined && (
{duration} day{duration > 1 ? "s" : ""} <div className="flex-shrink-0 text-sm text-custom-text-200">
</div> {duration} day{duration > 1 ? "s" : ""}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -94,7 +94,7 @@ export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
<> <>
{blocks ? ( {blocks ? (
blocks.map((block, index) => { blocks.map((block, index) => {
const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); const duration = findTotalDaysInRange(block.start_date, block.target_date);
return ( return (
<Draggable <Draggable
@ -131,9 +131,11 @@ export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
<div className="flex-grow truncate"> <div className="flex-grow truncate">
<IssueGanttSidebarBlock data={block.data} /> <IssueGanttSidebarBlock data={block.data} />
</div> </div>
<div className="flex-shrink-0 text-sm text-custom-text-200"> {duration !== undefined && (
{duration} day{duration > 1 ? "s" : ""} <div className="flex-shrink-0 text-sm text-custom-text-200">
</div> {duration} day{duration > 1 ? "s" : ""}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -119,10 +119,7 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
// hide the block if it doesn't have start and target dates and showAllBlocks is false // hide the block if it doesn't have start and target dates and showAllBlocks is false
if (!showAllBlocks && !isBlockVisibleOnSidebar) return; if (!showAllBlocks && !isBlockVisibleOnSidebar) return;
const duration = const duration = findTotalDaysInRange(block.start_date, block.target_date);
!block.start_date || !block.target_date
? null
: findTotalDaysInRange(block.start_date, block.target_date);
return ( return (
<Draggable <Draggable
@ -166,13 +163,13 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
<div className="flex-grow truncate"> <div className="flex-grow truncate">
<IssueGanttSidebarBlock data={block.data} /> <IssueGanttSidebarBlock data={block.data} />
</div> </div>
<div className="flex-shrink-0 text-sm text-custom-text-200"> {duration !== undefined && (
{duration && ( <div className="flex-shrink-0 text-sm text-custom-text-200">
<span> <span>
{duration} day{duration > 1 ? "s" : ""} {duration} day{duration > 1 ? "s" : ""}
</span> </span>
)} </div>
</div> )}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,5 @@
export * from "./attachment"; export * from "./attachment";
export * from "./issue-modal"; export * from "./issue-modal";
export * from "./view-select";
export * from "./delete-issue-modal"; export * from "./delete-issue-modal";
export * from "./description-form"; export * from "./description-form";
export * from "./issue-layouts"; export * from "./issue-layouts";

View File

@ -1,81 +0,0 @@
// ui
import { CustomDatePicker } from "components/ui";
import { Tooltip } from "@plane/ui";
import { CalendarCheck } from "lucide-react";
// helpers
import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper";
// types
import { TIssue } from "@plane/types";
type Props = {
issue: TIssue;
onChange: (date: string | null) => void;
handleOnOpen?: () => void;
handleOnClose?: () => void;
tooltipPosition?: "top" | "bottom";
className?: string;
noBorder?: boolean;
disabled: boolean;
};
export const ViewDueDateSelect: React.FC<Props> = ({
issue,
onChange,
handleOnOpen,
handleOnClose,
tooltipPosition = "top",
className = "",
noBorder = false,
disabled,
}) => {
const minDate = issue.start_date ? new Date(issue.start_date) : null;
minDate?.setDate(minDate.getDate());
return (
<Tooltip
tooltipHeading="Due date"
tooltipContent={issue.target_date ? renderFormattedDate(issue.target_date) ?? "N/A" : "N/A"}
position={tooltipPosition}
>
<div
className={`group max-w-[6.5rem] flex-shrink-0 ${className} ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
? "text-red-600"
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CustomDatePicker
value={issue?.target_date}
onChange={onChange}
className={`bg-transparent ${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"}`}
customInput={
<div
className={`flex items-center justify-center gap-2 rounded border border-custom-border-200 px-2 py-1 text-xs shadow-sm duration-200 hover:bg-custom-background-80 ${
issue.target_date ? "pr-6 text-custom-text-300" : "text-custom-text-400"
} ${disabled ? "cursor-not-allowed" : "cursor-pointer"}`}
>
{issue.target_date ? (
<>
<CalendarCheck className="h-3.5 w-3.5 flex-shrink-0" />
<span>{renderFormattedDate(issue.target_date) ?? "_ _"}</span>
</>
) : (
<>
<CalendarCheck className="h-3.5 w-3.5 flex-shrink-0" />
<span>Due Date</span>
</>
)}
</div>
}
minDate={minDate ?? undefined}
noBorder={noBorder}
handleOnOpen={handleOnOpen}
handleOnClose={handleOnClose}
disabled={disabled}
/>
</div>
</Tooltip>
);
};

View File

@ -1,64 +0,0 @@
import React from "react";
import { observer } from "mobx-react-lite";
import { Triangle } from "lucide-react";
import sortBy from "lodash/sortBy";
// store hooks
import { useEstimate } from "hooks/store";
// ui
import { CustomSelect, Tooltip } from "@plane/ui";
// types
import { TIssue } from "@plane/types";
type Props = {
issue: TIssue;
onChange: (data: number) => void;
tooltipPosition?: "top" | "bottom";
customButton?: boolean;
disabled: boolean;
};
export const ViewEstimateSelect: React.FC<Props> = observer((props) => {
const { issue, onChange, tooltipPosition = "top", customButton = false, disabled } = props;
const { areEstimatesEnabledForCurrentProject, activeEstimateDetails, getEstimatePointValue } = useEstimate();
const estimateValue = getEstimatePointValue(issue.estimate_point, issue.project_id);
const estimateLabels = (
<Tooltip tooltipHeading="Estimate" tooltipContent={estimateValue} position={tooltipPosition}>
<div className="flex items-center gap-1 text-custom-text-200">
<Triangle className="h-3 w-3" />
{estimateValue ?? "None"}
</div>
</Tooltip>
);
if (!areEstimatesEnabledForCurrentProject) return null;
return (
<CustomSelect
value={issue.estimate_point}
onChange={onChange}
{...(customButton ? { customButton: estimateLabels } : { label: estimateLabels })}
maxHeight="md"
noChevron
disabled={disabled}
>
<CustomSelect.Option value={null}>
<>
<span>
<Triangle className="h-3 w-3" />
</span>
None
</>
</CustomSelect.Option>
{sortBy(activeEstimateDetails?.points, "key")?.map((estimate) => (
<CustomSelect.Option key={estimate.id} value={estimate.key}>
<>
<Triangle className="h-3 w-3" />
{estimate.value}
</>
</CustomSelect.Option>
))}
</CustomSelect>
);
});

View File

@ -1,3 +0,0 @@
export * from "./due-date";
export * from "./estimate";
export * from "./start-date";

View File

@ -1,72 +0,0 @@
// ui
import { CustomDatePicker } from "components/ui";
import { Tooltip } from "@plane/ui";
import { CalendarClock } from "lucide-react";
// helpers
import { renderFormattedDate } from "helpers/date-time.helper";
// types
import { TIssue } from "@plane/types";
type Props = {
issue: TIssue;
onChange: (date: string | null) => void;
handleOnOpen?: () => void;
handleOnClose?: () => void;
tooltipPosition?: "top" | "bottom";
className?: string;
noBorder?: boolean;
disabled: boolean;
};
export const ViewStartDateSelect: React.FC<Props> = ({
issue,
onChange,
handleOnOpen,
handleOnClose,
tooltipPosition = "top",
className = "",
noBorder = false,
disabled,
}) => {
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
maxDate?.setDate(maxDate.getDate());
return (
<Tooltip
tooltipHeading="Start date"
tooltipContent={issue.start_date ? renderFormattedDate(issue.start_date) ?? "N/A" : "N/A"}
position={tooltipPosition}
>
<div className={`group max-w-[6.5rem] flex-shrink-0 ${className}`}>
<CustomDatePicker
value={issue?.start_date}
onChange={onChange}
maxDate={maxDate ?? undefined}
noBorder={noBorder}
handleOnOpen={handleOnOpen}
customInput={
<div
className={`flex items-center justify-center gap-2 rounded border border-custom-border-200 px-2 py-1 text-xs shadow-sm duration-200 hover:bg-custom-background-80 ${
issue?.start_date ? "pr-6 text-custom-text-300" : "text-custom-text-400"
} ${disabled ? "cursor-not-allowed" : "cursor-pointer"}`}
>
{issue?.start_date ? (
<>
<CalendarClock className="h-3.5 w-3.5 flex-shrink-0" />
<span>{renderFormattedDate(issue?.start_date ?? "_ _")}</span>
</>
) : (
<>
<CalendarClock className="h-3.5 w-3.5 flex-shrink-0" />
<span>Start Date</span>
</>
)}
</div>
}
handleOnClose={handleOnClose}
disabled={disabled}
/>
</div>
</Tooltip>
);
};

View File

@ -13,37 +13,32 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
// store // store
const { projectModuleIds, moduleMap } = useModule();
const { currentProjectDetails } = useProject(); const { currentProjectDetails } = useProject();
const { projectModuleIds, moduleMap, updateModuleDetails } = useModule();
const handleModuleUpdate = (module: IModule, payload: IBlockUpdateData) => { const handleModuleUpdate = async (module: IModule, data: IBlockUpdateData) => {
if (!workspaceSlug) return; if (!workspaceSlug || !module) return;
// FIXME
//updateModuleGanttStructure(workspaceSlug.toString(), module.project, module, payload); const payload: any = { ...data };
if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
await updateModuleDetails(workspaceSlug.toString(), module.project, module.id, payload);
}; };
const blockFormat = (blocks: string[]) => const blockFormat = (blocks: string[]) =>
blocks && blocks.length > 0 blocks?.map((blockId) => {
? blocks const block = moduleMap[blockId];
.filter((blockId) => { return {
const block = moduleMap[blockId]; data: block,
return block.start_date && block.target_date && new Date(block.start_date) <= new Date(block.target_date); id: block.id,
}) sort_order: block.sort_order,
.map((blockId) => { start_date: block.start_date ? new Date(block.start_date) : null,
const block = moduleMap[blockId]; target_date: block.target_date ? new Date(block.target_date) : null,
return { };
data: block, });
id: block.id,
sort_order: block.sort_order,
start_date: new Date(block.start_date ?? ""),
target_date: new Date(block.target_date ?? ""),
};
})
: [];
const isAllowed = currentProjectDetails?.member_role === 20 || currentProjectDetails?.member_role === 15; const isAllowed = currentProjectDetails?.member_role === 20 || currentProjectDetails?.member_role === 15;
const modules = projectModuleIds;
return ( return (
<div className="h-full w-full overflow-y-auto"> <div className="h-full w-full overflow-y-auto">
<GanttChartRoot <GanttChartRoot
@ -57,6 +52,7 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
enableBlockRightResize={isAllowed} enableBlockRightResize={isAllowed}
enableBlockMove={isAllowed} enableBlockMove={isAllowed}
enableReorder={isAllowed} enableReorder={isAllowed}
showAllBlocks
/> />
</div> </div>
); );

View File

@ -87,11 +87,11 @@ export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" |
* @example checkIfStringIsDate("2021-01-01", "2021-01-08") // 8 * @example checkIfStringIsDate("2021-01-01", "2021-01-08") // 8
*/ */
export const findTotalDaysInRange = ( export const findTotalDaysInRange = (
startDate: Date | string, startDate: Date | string | undefined | null,
endDate: Date | string, endDate: Date | string | undefined | null,
inclusive: boolean = true inclusive: boolean = true
): number => { ): number | undefined => {
if (!startDate || !endDate) return 0; if (!startDate || !endDate) return undefined;
// Parse the dates to check if they are valid // Parse the dates to check if they are valid
const parsedStartDate = new Date(startDate); const parsedStartDate = new Date(startDate);
const parsedEndDate = new Date(endDate); const parsedEndDate = new Date(endDate);
@ -110,8 +110,11 @@ export const findTotalDaysInRange = (
* @param {boolean} inclusive (optional) // default true * @param {boolean} inclusive (optional) // default true
* @example findHowManyDaysLeft("2024-01-01") // 3 * @example findHowManyDaysLeft("2024-01-01") // 3
*/ */
export const findHowManyDaysLeft = (date: string | Date, inclusive: boolean = true): number => { export const findHowManyDaysLeft = (
if (!date) return 0; date: Date | string | undefined | null,
inclusive: boolean = true
): number | undefined => {
if (!date) return undefined;
// Pass the date to findTotalDaysInRange function to find the total number of days in range from today // Pass the date to findTotalDaysInRange function to find the total number of days in range from today
return findTotalDaysInRange(new Date(), date, inclusive); return findTotalDaysInRange(new Date(), date, inclusive);
}; };

View File

@ -103,7 +103,7 @@ export class CycleStore implements ICycleStore {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.fetchedMap[projectId]) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId); let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId);
allCycles = sortBy(allCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); allCycles = sortBy(allCycles, [(c) => c.sort_order]);
const allCycleIds = allCycles.map((c) => c.id); const allCycleIds = allCycles.map((c) => c.id);
return allCycleIds; return allCycleIds;
} }
@ -118,7 +118,7 @@ export class CycleStore implements ICycleStore {
const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && hasEndDatePassed; return c.project === projectId && hasEndDatePassed;
}); });
completedCycles = sortBy(completedCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); completedCycles = sortBy(completedCycles, [(c) => c.sort_order]);
const completedCycleIds = completedCycles.map((c) => c.id); const completedCycleIds = completedCycles.map((c) => c.id);
return completedCycleIds; return completedCycleIds;
} }
@ -133,7 +133,7 @@ export class CycleStore implements ICycleStore {
const isStartDateUpcoming = isFuture(new Date(c.start_date ?? "")); const isStartDateUpcoming = isFuture(new Date(c.start_date ?? ""));
return c.project === projectId && isStartDateUpcoming; return c.project === projectId && isStartDateUpcoming;
}); });
upcomingCycles = sortBy(upcomingCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); upcomingCycles = sortBy(upcomingCycles, [(c) => c.sort_order]);
const upcomingCycleIds = upcomingCycles.map((c) => c.id); const upcomingCycleIds = upcomingCycles.map((c) => c.id);
return upcomingCycleIds; return upcomingCycleIds;
} }
@ -148,7 +148,7 @@ export class CycleStore implements ICycleStore {
const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && !hasEndDatePassed; return c.project === projectId && !hasEndDatePassed;
}); });
incompleteCycles = sortBy(incompleteCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); incompleteCycles = sortBy(incompleteCycles, [(c) => c.sort_order]);
const incompleteCycleIds = incompleteCycles.map((c) => c.id); const incompleteCycleIds = incompleteCycles.map((c) => c.id);
return incompleteCycleIds; return incompleteCycleIds;
} }
@ -162,7 +162,7 @@ export class CycleStore implements ICycleStore {
let draftCycles = Object.values(this.cycleMap ?? {}).filter( let draftCycles = Object.values(this.cycleMap ?? {}).filter(
(c) => c.project === projectId && !c.start_date && !c.end_date (c) => c.project === projectId && !c.start_date && !c.end_date
); );
draftCycles = sortBy(draftCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); draftCycles = sortBy(draftCycles, [(c) => c.sort_order]);
const draftCycleIds = draftCycles.map((c) => c.id); const draftCycleIds = draftCycles.map((c) => c.id);
return draftCycleIds; return draftCycleIds;
} }
@ -203,7 +203,7 @@ export class CycleStore implements ICycleStore {
if (!this.fetchedMap[projectId]) return null; if (!this.fetchedMap[projectId]) return null;
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId); let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); cycles = sortBy(cycles, [(c) => c.sort_order]);
const cycleIds = cycles.map((c) => c.id); const cycleIds = cycles.map((c) => c.id);
return cycleIds || null; return cycleIds || null;
}); });

View File

@ -100,7 +100,7 @@ export class ModulesStore implements IModuleStore {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.fetchedMap[projectId]) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); projectModules = sortBy(projectModules, [(m) => m.sort_order]);
const projectModuleIds = projectModules.map((m) => m.id); const projectModuleIds = projectModules.map((m) => m.id);
return projectModuleIds || null; return projectModuleIds || null;
} }
@ -120,7 +120,7 @@ export class ModulesStore implements IModuleStore {
if (!this.fetchedMap[projectId]) return null; if (!this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); projectModules = sortBy(projectModules, [(m) => m.sort_order]);
const projectModuleIds = projectModules.map((m) => m.id); const projectModuleIds = projectModules.map((m) => m.id);
return projectModuleIds; return projectModuleIds;
}); });