mirror of
https://github.com/makeplane/plane.git
synced 2025-12-24 07:39:32 +01:00
[WEB-2628] fix: Sorting by estimates (#5988)
* fix estimates sorting in Front end side * change estimate sorting keys * - Fix estimate sorting when local db is enabled - Fix a bug with with sorting on special fields on spreadsheet layout - Cleanup logging * Add logic for order by based on layout for special cases of no load --------- Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
This commit is contained in:
4
packages/types/src/view-props.d.ts
vendored
4
packages/types/src/view-props.d.ts
vendored
@@ -40,8 +40,8 @@ export type TIssueOrderByOptions =
|
||||
| "-issue_cycle__cycle__name"
|
||||
| "target_date"
|
||||
| "-target_date"
|
||||
| "estimate_point"
|
||||
| "-estimate_point"
|
||||
| "estimate_point__key"
|
||||
| "-estimate_point__key"
|
||||
| "start_date"
|
||||
| "-start_date"
|
||||
| "link_count"
|
||||
|
||||
@@ -79,9 +79,9 @@ export const SPREADSHEET_PROPERTY_DETAILS: {
|
||||
},
|
||||
estimate: {
|
||||
title: "Estimate",
|
||||
ascendingOrderKey: "estimate_point",
|
||||
ascendingOrderKey: "estimate_point__key",
|
||||
ascendingOrderTitle: "Low",
|
||||
descendingOrderKey: "-estimate_point",
|
||||
descendingOrderKey: "-estimate_point__key",
|
||||
descendingOrderTitle: "High",
|
||||
icon: Triangle,
|
||||
Column: SpreadsheetEstimateColumn,
|
||||
|
||||
@@ -300,6 +300,7 @@ export class Storage {
|
||||
const { cursor, group_by, sub_group_by } = queries;
|
||||
|
||||
const query = issueFilterQueryConstructor(this.workspaceSlug, projectId, queries);
|
||||
log("#### Query", query);
|
||||
const countQuery = issueFilterCountQueryConstructor(this.workspaceSlug, projectId, queries);
|
||||
const start = performance.now();
|
||||
let issuesRaw: any[] = [];
|
||||
|
||||
@@ -87,10 +87,10 @@ export const getStates = async (workspaceSlug: string) => {
|
||||
export const getEstimatePoints = async (workspaceSlug: string) => {
|
||||
const estimateService = new EstimateService();
|
||||
const estimates = await estimateService.fetchWorkspaceEstimates(workspaceSlug);
|
||||
const objects: IEstimatePoint[] = [];
|
||||
let objects: IEstimatePoint[] = [];
|
||||
(estimates || []).forEach((estimate: IEstimate) => {
|
||||
if (estimate?.points) {
|
||||
objects.concat(estimate.points);
|
||||
objects = objects.concat(estimate.points);
|
||||
}
|
||||
});
|
||||
return objects;
|
||||
@@ -104,6 +104,9 @@ export const getMembers = async (workspaceSlug: string) => {
|
||||
};
|
||||
|
||||
export const loadWorkSpaceData = async (workspaceSlug: string) => {
|
||||
if (!persistence.db || !persistence.db.exec) {
|
||||
return;
|
||||
}
|
||||
log("Loading workspace data");
|
||||
const promises = [];
|
||||
promises.push(getLabels(workspaceSlug));
|
||||
@@ -112,7 +115,7 @@ export const loadWorkSpaceData = async (workspaceSlug: string) => {
|
||||
promises.push(getStates(workspaceSlug));
|
||||
promises.push(getEstimatePoints(workspaceSlug));
|
||||
promises.push(getMembers(workspaceSlug));
|
||||
const [labels, modules, cycles, states, estimates, memebers] = await Promise.all(promises);
|
||||
const [labels, modules, cycles, states, estimates, members] = await Promise.all(promises);
|
||||
|
||||
const start = performance.now();
|
||||
await persistence.db.exec("BEGIN;");
|
||||
@@ -121,7 +124,7 @@ export const loadWorkSpaceData = async (workspaceSlug: string) => {
|
||||
await batchInserts(cycles, "cycles", cycleSchema);
|
||||
await batchInserts(states, "states", stateSchema);
|
||||
await batchInserts(estimates, "estimate_points", estimatePointSchema);
|
||||
await batchInserts(memebers, "members", memberSchema);
|
||||
await batchInserts(members, "members", memberSchema);
|
||||
await persistence.db.exec("COMMIT;");
|
||||
const end = performance.now();
|
||||
log("Time taken to load workspace data", end - start);
|
||||
|
||||
@@ -18,6 +18,8 @@ export const SPECIAL_ORDER_BY = {
|
||||
"-issue_cycle__cycle__name": "cycles",
|
||||
state__name: "states",
|
||||
"-state__name": "states",
|
||||
estimate_point__key: "estimate_point",
|
||||
"-estimate_point__key": "estimate_point",
|
||||
};
|
||||
export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: string, queries: any) => {
|
||||
const {
|
||||
@@ -48,8 +50,6 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
|
||||
`;
|
||||
|
||||
log("###", sql);
|
||||
|
||||
return sql;
|
||||
}
|
||||
if (group_by) {
|
||||
@@ -64,8 +64,6 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
WHERE rank <= ${per_page}
|
||||
`;
|
||||
|
||||
log("###", sql);
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -78,8 +76,10 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
sql += `SELECT fi.* , `;
|
||||
if (order_by.includes("assignee")) {
|
||||
sql += ` s.first_name as ${name} `;
|
||||
} else if (order_by.includes("estimate")) {
|
||||
sql += ` s.key as ${name} `;
|
||||
} else {
|
||||
sql += ` s.name as ${name} `;
|
||||
sql += ` s.name as ${name} `;
|
||||
}
|
||||
sql += `FROM fi `;
|
||||
if (order_by && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) {
|
||||
@@ -87,7 +87,7 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
sql += `
|
||||
LEFT JOIN cycles s on fi.cycle_id = s.id`;
|
||||
}
|
||||
if (order_by.includes("estimate_point")) {
|
||||
if (order_by.includes("estimate_point__key")) {
|
||||
sql += `
|
||||
LEFT JOIN estimate_points s on fi.estimate_point = s.id`;
|
||||
}
|
||||
@@ -120,7 +120,6 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
`;
|
||||
sql += ` group by i.id ${orderByString} LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`;
|
||||
|
||||
log("######$$$", sql);
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -149,7 +148,6 @@ export const issueFilterQueryConstructor = (workspaceSlug: string, projectId: st
|
||||
// Add offset and paging to query
|
||||
sql += ` LIMIT ${pageSize} OFFSET ${offset * 1 + page * pageSize};`;
|
||||
|
||||
log("$$$", sql);
|
||||
return sql;
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export const translateQueryParams = (queries: any) => {
|
||||
}
|
||||
|
||||
// Fix invalid orderby when switching from spreadsheet layout
|
||||
if (layout === "spreadsheet" && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) {
|
||||
if (layout !== "spreadsheet" && Object.keys(SPECIAL_ORDER_BY).includes(order_by)) {
|
||||
otherProps.order_by = "sort_order";
|
||||
}
|
||||
// For each property value, replace None with empty string
|
||||
@@ -336,6 +336,9 @@ const getSingleFilterFields = (queries: any) => {
|
||||
if (state_group) {
|
||||
fields.add("states.'group' as state_group");
|
||||
}
|
||||
if (order_by?.includes("estimate_point__key")) {
|
||||
fields.add("estimate_point");
|
||||
}
|
||||
return Array.from(fields);
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { EIssueLayoutTypes, ISSUE_PRIORITIES } from "@/constants/issue";
|
||||
// helpers
|
||||
import { convertToISODateString } from "@/helpers/date-time.helper";
|
||||
// local-db
|
||||
import { SPECIAL_ORDER_BY } from "@/local-db/utils/query-constructor";
|
||||
import { updatePersistentLayer } from "@/local-db/utils/utils";
|
||||
// services
|
||||
import { CycleService } from "@/services/cycle.service";
|
||||
@@ -164,8 +165,8 @@ const ISSUE_ORDERBY_KEY: Record<TIssueOrderByOptions, keyof TIssue> = {
|
||||
"-issue_cycle__cycle__name": "cycle_id",
|
||||
target_date: "target_date",
|
||||
"-target_date": "target_date",
|
||||
estimate_point: "estimate_point",
|
||||
"-estimate_point": "estimate_point",
|
||||
estimate_point__key: "estimate_point",
|
||||
"-estimate_point__key": "estimate_point",
|
||||
start_date: "start_date",
|
||||
"-start_date": "start_date",
|
||||
link_count: "link_count",
|
||||
@@ -282,6 +283,19 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
const displayFilters = this.issueFilterStore?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return;
|
||||
|
||||
const layout = displayFilters.layout;
|
||||
const orderBy = displayFilters.order_by;
|
||||
|
||||
// Temporary code to fix no load order by
|
||||
if (
|
||||
this.rootIssueStore.rootStore.user.localDBEnabled &&
|
||||
layout !== EIssueLayoutTypes.SPREADSHEET &&
|
||||
orderBy &&
|
||||
Object.keys(SPECIAL_ORDER_BY).includes(orderBy)
|
||||
) {
|
||||
return "sort_order";
|
||||
}
|
||||
|
||||
return displayFilters?.order_by;
|
||||
}
|
||||
|
||||
@@ -1701,13 +1715,14 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
* @returns string | string[] of sortable fields to be used for sorting
|
||||
*/
|
||||
populateIssueDataForSorting(
|
||||
dataType: "state_id" | "label_ids" | "assignee_ids" | "module_ids" | "cycle_id",
|
||||
dataType: "state_id" | "label_ids" | "assignee_ids" | "module_ids" | "cycle_id" | "estimate_point",
|
||||
dataIds: string | string[] | null | undefined,
|
||||
projectId: string | undefined | null,
|
||||
order?: "asc" | "desc"
|
||||
) {
|
||||
if (!dataIds) return;
|
||||
|
||||
const dataValues: string[] = [];
|
||||
const dataValues: (string | number)[] = [];
|
||||
const isDataIdsArray = Array.isArray(dataIds);
|
||||
const dataIdsArray = isDataIdsArray ? dataIds : [dataIds];
|
||||
|
||||
@@ -1757,6 +1772,26 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "estimate_point": {
|
||||
// return if project Id does not exist
|
||||
if (!projectId) break;
|
||||
// get the estimate ID for the current Project
|
||||
const currentProjectEstimateId =
|
||||
this.rootIssueStore.rootStore.projectEstimate.currentActiveEstimateIdByProjectId(projectId);
|
||||
// return if current Estimate Id for the project is not available
|
||||
if (!currentProjectEstimateId) break;
|
||||
// get Estimate based on Id
|
||||
const estimate = this.rootIssueStore.rootStore.projectEstimate.estimateById(currentProjectEstimateId);
|
||||
// If Estimate is not available, then return
|
||||
if (!estimate) break;
|
||||
// Get Estimate Value
|
||||
const estimateKey = estimate?.estimatePointById(dataIds as string)?.key;
|
||||
|
||||
// If Value string i not available or empty then return
|
||||
if (estimateKey === undefined) break;
|
||||
|
||||
dataValues.push(estimateKey);
|
||||
}
|
||||
}
|
||||
|
||||
return isDataIdsArray ? (order ? orderBy(dataValues, undefined, [order]) : dataValues) : dataValues;
|
||||
@@ -1771,11 +1806,17 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
return getIssueIds(orderBy(array, "sort_order"));
|
||||
case "state__name":
|
||||
return getIssueIds(
|
||||
orderBy(array, (issue) => this.populateIssueDataForSorting("state_id", issue?.["state_id"]))
|
||||
orderBy(array, (issue) =>
|
||||
this.populateIssueDataForSorting("state_id", issue?.["state_id"], issue?.["project_id"])
|
||||
)
|
||||
);
|
||||
case "-state__name":
|
||||
return getIssueIds(
|
||||
orderBy(array, (issue) => this.populateIssueDataForSorting("state_id", issue?.["state_id"]), ["desc"])
|
||||
orderBy(
|
||||
array,
|
||||
(issue) => this.populateIssueDataForSorting("state_id", issue?.["state_id"], issue?.["project_id"]),
|
||||
["desc"]
|
||||
)
|
||||
);
|
||||
// dates
|
||||
case "created_at":
|
||||
@@ -1826,15 +1867,23 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
case "-attachment_count":
|
||||
return getIssueIds(orderBy(array, "attachment_count", ["desc"]));
|
||||
|
||||
case "estimate_point":
|
||||
case "estimate_point__key":
|
||||
return getIssueIds(
|
||||
orderBy(array, [getSortOrderToFilterEmptyValues.bind(null, "estimate_point"), "estimate_point"])
|
||||
orderBy(array, [
|
||||
getSortOrderToFilterEmptyValues.bind(null, "estimate_point"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("estimate_point", issue?.["estimate_point"], issue?.["project_id"]),
|
||||
])
|
||||
); //preferring sorting based on empty values to always keep the empty values below
|
||||
case "-estimate_point":
|
||||
case "-estimate_point__key":
|
||||
return getIssueIds(
|
||||
orderBy(
|
||||
array,
|
||||
[getSortOrderToFilterEmptyValues.bind(null, "estimate_point"), "estimate_point"], //preferring sorting based on empty values to always keep the empty values below
|
||||
[
|
||||
getSortOrderToFilterEmptyValues.bind(null, "estimate_point"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("estimate_point", issue?.["estimate_point"], issue?.["project_id"]),
|
||||
], //preferring sorting based on empty values to always keep the empty values below
|
||||
["asc", "desc"]
|
||||
)
|
||||
);
|
||||
@@ -1854,7 +1903,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
return getIssueIds(
|
||||
orderBy(array, [
|
||||
getSortOrderToFilterEmptyValues.bind(null, "label_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("label_ids", issue?.["label_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("label_ids", issue?.["label_ids"], issue?.["project_id"], "asc"),
|
||||
])
|
||||
);
|
||||
case "-labels__name":
|
||||
@@ -1863,7 +1913,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
array,
|
||||
[
|
||||
getSortOrderToFilterEmptyValues.bind(null, "label_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("label_ids", issue?.["label_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("label_ids", issue?.["label_ids"], issue?.["project_id"], "asc"),
|
||||
],
|
||||
["asc", "desc"]
|
||||
)
|
||||
@@ -1873,7 +1924,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
return getIssueIds(
|
||||
orderBy(array, [
|
||||
getSortOrderToFilterEmptyValues.bind(null, "module_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("module_ids", issue?.["module_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("module_ids", issue?.["module_ids"], issue?.["project_id"], "asc"),
|
||||
])
|
||||
);
|
||||
case "-issue_module__module__name":
|
||||
@@ -1882,7 +1934,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
array,
|
||||
[
|
||||
getSortOrderToFilterEmptyValues.bind(null, "module_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("module_ids", issue?.["module_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("module_ids", issue?.["module_ids"], issue?.["project_id"], "asc"),
|
||||
],
|
||||
["asc", "desc"]
|
||||
)
|
||||
@@ -1892,7 +1945,7 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
return getIssueIds(
|
||||
orderBy(array, [
|
||||
getSortOrderToFilterEmptyValues.bind(null, "cycle_id"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("cycle_id", issue?.["cycle_id"], "asc"),
|
||||
(issue) => this.populateIssueDataForSorting("cycle_id", issue?.["cycle_id"], issue?.["project_id"], "asc"),
|
||||
])
|
||||
);
|
||||
case "-issue_cycle__cycle__name":
|
||||
@@ -1901,7 +1954,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
array,
|
||||
[
|
||||
getSortOrderToFilterEmptyValues.bind(null, "cycle_id"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("cycle_id", issue?.["cycle_id"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("cycle_id", issue?.["cycle_id"], issue?.["project_id"], "asc"),
|
||||
],
|
||||
["asc", "desc"]
|
||||
)
|
||||
@@ -1911,7 +1965,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
return getIssueIds(
|
||||
orderBy(array, [
|
||||
getSortOrderToFilterEmptyValues.bind(null, "assignee_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("assignee_ids", issue?.["assignee_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("assignee_ids", issue?.["assignee_ids"], issue?.["project_id"], "asc"),
|
||||
])
|
||||
);
|
||||
case "-assignees__first_name":
|
||||
@@ -1920,7 +1975,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
array,
|
||||
[
|
||||
getSortOrderToFilterEmptyValues.bind(null, "assignee_ids"), //preferring sorting based on empty values to always keep the empty values below
|
||||
(issue) => this.populateIssueDataForSorting("assignee_ids", issue?.["assignee_ids"], "asc"),
|
||||
(issue) =>
|
||||
this.populateIssueDataForSorting("assignee_ids", issue?.["assignee_ids"], issue?.["project_id"], "asc"),
|
||||
],
|
||||
["asc", "desc"]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user