diff --git a/packages/types/src/view-props.d.ts b/packages/types/src/view-props.d.ts index 0c543241eef..57baa4cfdd1 100644 --- a/packages/types/src/view-props.d.ts +++ b/packages/types/src/view-props.d.ts @@ -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" diff --git a/web/core/constants/spreadsheet.ts b/web/core/constants/spreadsheet.ts index e4e8d8ee57b..e4ca07ffba4 100644 --- a/web/core/constants/spreadsheet.ts +++ b/web/core/constants/spreadsheet.ts @@ -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, diff --git a/web/core/local-db/storage.sqlite.ts b/web/core/local-db/storage.sqlite.ts index 65d5a388581..a6a9889c5c3 100644 --- a/web/core/local-db/storage.sqlite.ts +++ b/web/core/local-db/storage.sqlite.ts @@ -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[] = []; diff --git a/web/core/local-db/utils/load-workspace.ts b/web/core/local-db/utils/load-workspace.ts index 3b715735e97..b6330832689 100644 --- a/web/core/local-db/utils/load-workspace.ts +++ b/web/core/local-db/utils/load-workspace.ts @@ -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); diff --git a/web/core/local-db/utils/query-constructor.ts b/web/core/local-db/utils/query-constructor.ts index 6fd538f1e77..6c722d528d1 100644 --- a/web/core/local-db/utils/query-constructor.ts +++ b/web/core/local-db/utils/query-constructor.ts @@ -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; }; diff --git a/web/core/local-db/utils/query.utils.ts b/web/core/local-db/utils/query.utils.ts index 520338e6af6..44da98f8451 100644 --- a/web/core/local-db/utils/query.utils.ts +++ b/web/core/local-db/utils/query.utils.ts @@ -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); }; diff --git a/web/core/store/issue/helpers/base-issues.store.ts b/web/core/store/issue/helpers/base-issues.store.ts index 462643eb287..d5545db4425 100644 --- a/web/core/store/issue/helpers/base-issues.store.ts +++ b/web/core/store/issue/helpers/base-issues.store.ts @@ -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 = { "-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"] )