Skip to content

Commit

Permalink
♻️ refactor(sorting): improve and centralize sorting logic
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 8, 2023
1 parent 429fd8c commit bdba322
Show file tree
Hide file tree
Showing 16 changed files with 92 additions and 204 deletions.
4 changes: 2 additions & 2 deletions src/backend/dashboard-widgets/dashboard-widgets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
import {
listOrderApiService,
ListOrderApiService,
sortListByOrder,
} from "backend/list-order/list-order.service";
import { rolesApiService, RolesApiService } from "backend/roles/roles.service";
import { userFriendlyCase } from "shared/lib/strings/friendly-case";
Expand All @@ -30,6 +29,7 @@ import {
import { GranularEntityPermissions, IAccountProfile } from "shared/types/user";
import { relativeDateNotationToActualDate } from "backend/data/data-access/time.constants";
import { ILabelValue } from "shared/types/options";
import { sortListByOrder } from "shared/lib/array/sort";
import {
mutateGeneratedDashboardWidgets,
PORTAL_DASHBOARD_PERMISSION,
Expand Down Expand Up @@ -183,7 +183,7 @@ return [actual[0], relative[0]];
await this._dashboardWidgetsPersistenceService.getAllItemsIn(widgetList)
);

return sortListByOrder(widgetList, widgets);
return sortListByOrder(widgetList, widgets, "id");
}

async listDashboardWidgets(
Expand Down
10 changes: 5 additions & 5 deletions src/backend/entities/entities.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { rolesApiService, RolesApiService } from "backend/roles/roles.service";
import { IApplicationService } from "backend/types";
import { noop } from "shared/lib/noop";
import { IDBSchema, IEntityField, IEntityRelation } from "shared/types/db";
import { sortByList } from "shared/logic/entities/sort.utils";
import { DataCrudKeys } from "shared/types/data";
import { CRUD_KEY_CONFIG } from "shared/configurations/permissions";
import { sortListByOrder } from "shared/lib/array/sort";
import { SchemasApiService, schemasApiService } from "../schema/schema.service";
import { PortalFieldsFilterService } from "./portal";

Expand Down Expand Up @@ -51,9 +51,9 @@ export class EntitiesApiService implements IApplicationService {
this._configurationApiService.show("entity_fields_orders", entity),
]);

sortByList(
entityFields as unknown as Record<string, unknown>[],
sortListByOrder(
entityFieldsOrder,
entityFields as unknown as Record<string, unknown>[],
"name" as keyof IEntityField
);

Expand Down Expand Up @@ -166,9 +166,9 @@ export class EntitiesApiService implements IApplicationService {
"table"
);

sortByList(
allowedEntityRelation as unknown[] as Record<string, unknown>[],
sortListByOrder(
entityOrders,
allowedEntityRelation as unknown[] as Record<string, unknown>[],
"table"
);

Expand Down
2 changes: 0 additions & 2 deletions src/backend/list-order/list-order.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
} from "backend/lib/config-persistence";
import { IApplicationService } from "backend/types";

export { sortListByOrder } from "./utils";

export class ListOrderApiService implements IApplicationService {
constructor(
private readonly _listOrderPersistenceService: AbstractConfigDataPersistenceService<
Expand Down
23 changes: 0 additions & 23 deletions src/backend/list-order/utils.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/backend/menu/menu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import {
ConfigurationApiService,
configurationApiService,
} from "backend/configuration/configuration.service";
import { sortByList } from "shared/logic/entities/sort.utils";
import { RolesApiService, rolesApiService } from "backend/roles/roles.service";
import { ILabelValue } from "shared/types/options";
import { ISingularPlural } from "shared/types/config";
import { sortListByOrder } from "shared/lib/array/sort";
import { portalCheckIfIsMenuAllowed, getPortalMenuItems } from "./portal";
import { IBaseNavigationMenuApiService } from "./types";

Expand Down Expand Up @@ -148,7 +148,7 @@ export class NavigationMenuApiService
.filter(({ value }) => !hiddenMenuEntities.includes(value))
.sort((a, b) => a.value.localeCompare(b.value));

sortByList(menuEntities, entitiesOrder, "value");
sortListByOrder(entitiesOrder, menuEntities, "value");

return menuEntities;
}
Expand Down
18 changes: 1 addition & 17 deletions src/frontend/design-system/components/SortList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,14 @@ import { Stack } from "frontend/design-system/primitives/Stack";
import { pluralize } from "shared/lib/strings";
import { Typo } from "frontend/design-system/primitives/Typo";
import { Spacer } from "frontend/design-system/primitives/Spacer";
import { arrayMoveImmutable } from "shared/lib/array/move";
import { ErrorAlert } from "../Alert";
import { EmptyWrapper } from "../EmptyWrapper";
import { FormButton } from "../Button/FormButton";
import { defaultToEmptyArray } from "./utils";
import { ListSkeleton } from "../Skeleton/List";
import { SHADOW_CSS } from "../Card";

function arrayMoveMutable<T>(array: T[], fromIndex: number, toIndex: number) {
const startIndex = fromIndex < 0 ? array.length + fromIndex : fromIndex;

if (startIndex >= 0 && startIndex < array.length) {
const endIndex = toIndex < 0 ? array.length + toIndex : toIndex;

const [item] = array.splice(fromIndex, 1);
array.splice(endIndex, 0, item);
}
}

function arrayMoveImmutable<T>(array: T[], fromIndex: number, toIndex: number) {
const newArray = [...array];
arrayMoveMutable(newArray, fromIndex, toIndex);
return newArray;
}

export interface IProps<T> {
data: DataStateKeys<T[]>;
onSave: (data: string[]) => Promise<void | string[]>;
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/views/Dashboard/Manage/_BaseManageDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import styled from "styled-components";
import { Check, Plus } from "react-feather";
import { USER_PERMISSIONS } from "shared/constants/user";
import { ViewStateMachine } from "frontend/components/ViewStateMachine";
import arrayMove from "array-move";
import SortableList, { SortableItem } from "react-easy-sort";
import { useRouter } from "next/router";
import { useSetPageDetails } from "frontend/lib/routing/usePageDetails";
import { NAVIGATION_LINKS } from "frontend/lib/routing/links";
import { AppLayout } from "frontend/_layouts/app";
import { arrayMoveImmutable } from "shared/lib/array/move";
import {
useArrangeDashboardWidgetMutation,
useDashboardWidgets,
Expand Down Expand Up @@ -41,7 +41,7 @@ export function BaseManageDashboard({ dashboardId, doneLink, title }: IProps) {
useArrangeDashboardWidgetMutation(dashboardId);

const onSortEnd = (oldIndex: number, newIndex: number) => {
const newOrder = arrayMove(widgets.data, oldIndex, newIndex);
const newOrder = arrayMoveImmutable(widgets.data, oldIndex, newIndex);
arrangeDashboardWidgetMutation.mutate(newOrder.map(({ id }) => id));
};

Expand Down
16 changes: 15 additions & 1 deletion src/frontend/views/data/_BaseEntityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ButtonIconTypes } from "frontend/design-system/components/Button/consta
import { buildAppliedSchemaFormConfig } from "./buildAppliedSchemaFormConfig";
import { useEntityViewStateMachine } from "./useEntityViewStateMachine";
import { filterOutHiddenScalarColumns } from "./utils";
import { usePortalExtendEntityFormConfig } from "./portal";

type IProps = {
entity: string;
Expand Down Expand Up @@ -57,6 +58,9 @@ export function BaseEntityForm({
"entity_columns_types",
entity
);

const extendEntityFormConfig = usePortalExtendEntityFormConfig(crudAction);

const entityFormExtension = useEntityConfiguration(
"entity_form_extension",
entity
Expand All @@ -78,6 +82,7 @@ export function BaseEntityForm({
entityFormExtension.isLoading ||
entity === SLUG_LOADING_VALUE ||
entityFieldTypesMap.isLoading ||
extendEntityFormConfig === "loading" ||
additionalDataState?.isLoading;

const viewState = useEntityViewStateMachine(isLoading, error, crudAction);
Expand Down Expand Up @@ -113,6 +118,11 @@ export function BaseEntityForm({
fields,
};

const formConfig = buildAppliedSchemaFormConfig(
formSchemaConfig,
allOptional
);

return (
<ViewStateMachine
loading={viewState.type === DataStates.Loading}
Expand All @@ -137,7 +147,11 @@ export function BaseEntityForm({
action={crudAction}
icon={icon}
initialValues={fieldsInitialValues}
fields={buildAppliedSchemaFormConfig(formSchemaConfig, allOptional)}
fields={
typeof extendEntityFormConfig === "string"
? formConfig
: extendEntityFormConfig(formConfig)
}
formExtension={entityFormExtension.data}
/>
</ViewStateMachine>
Expand Down
1 change: 1 addition & 0 deletions src/frontend/views/data/portal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { usePortalExtendEntityFormConfig } from "./main";
17 changes: 17 additions & 0 deletions src/frontend/views/data/portal/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IAppliedSchemaFormConfig } from "shared/form-schemas/types";
import { noop } from "shared/lib/noop";

export const usePortalExtendEntityFormConfig = (
crudAction: "update" | "create"
):
| "loading"
| ((
formConfig: IAppliedSchemaFormConfig<any>
) => IAppliedSchemaFormConfig<any>) => {
noop(crudAction);
return (
formConfig: IAppliedSchemaFormConfig<any>
): IAppliedSchemaFormConfig<any> => {
return formConfig;
};
};
4 changes: 2 additions & 2 deletions src/frontend/views/settings/Menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import {
} from "frontend/hooks/entity/entity.store";
import { loadedDataState } from "frontend/lib/data/constants/loadedDataState";
import { NAVIGATION_MENU_ENDPOINT } from "frontend/_layouts/app/LayoutImpl/constants";
import { sortByList } from "shared/logic/entities/sort.utils";
import { AppLayout } from "frontend/_layouts/app";
import { sortListByOrder } from "shared/lib/array/sort";
import { SETTINGS_VIEW_KEY } from "../constants";
import { EntitiesSelection } from "../Entities/Selection";

Expand Down Expand Up @@ -49,7 +49,7 @@ export function MenuSettings() {
.filter(({ value }) => !menuEntitiesToHide.data.includes(value))
.sort((a, b) => a.value.localeCompare(b.value));

sortByList(menuEntities, menuEntitiesOrder.data, "value");
sortListByOrder(menuEntitiesOrder.data, menuEntities, "value");

const upsertHideFromMenuMutation = useUpsertConfigurationMutation(
"disabled_menu_entities",
Expand Down
20 changes: 20 additions & 0 deletions src/shared/lib/array/move.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function arrayMoveMutable<T>(array: T[], fromIndex: number, toIndex: number) {
const startIndex = fromIndex < 0 ? array.length + fromIndex : fromIndex;

if (startIndex >= 0 && startIndex < array.length) {
const endIndex = toIndex < 0 ? array.length + toIndex : toIndex;

const [item] = array.splice(fromIndex, 1);
array.splice(endIndex, 0, item);
}
}

export function arrayMoveImmutable<T>(
array: T[],
fromIndex: number,
toIndex: number
) {
const newArray = [...array];
arrayMoveMutable(newArray, fromIndex, toIndex);
return newArray;
}
17 changes: 17 additions & 0 deletions src/shared/lib/array/sort/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function sortListByOrder<T, K extends keyof T>(
order: string[],
itemsToOrder: T[],
key: K
): T[] {
const indexOf = (entry: T) => {
const index = order.indexOf(entry[key] as unknown as string);
if (index === -1) {
return Infinity;
}
return index;
};

return itemsToOrder.sort((a, b) => {
return indexOf(a) - indexOf(b);
});
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { sortListByOrder } from "./utils";
import { sortListByOrder } from ".";

describe("sortListByOrder", () => {
it("should return the same list if the order is empty", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }];
const order = [];
expect(sortListByOrder(order, list)).toEqual(list);
expect(sortListByOrder(order, list, "id")).toEqual(list);
});

it("should return only items in the list even if present in the order", () => {
const list = [{ id: "1" }, { id: "3" }];
const order = ["3", "2", "1"];
expect(sortListByOrder(order, list)).toEqual([{ id: "3" }, { id: "1" }]);
expect(sortListByOrder(order, list, "id")).toEqual([
{ id: "3" },
{ id: "1" },
]);
});

it("should order the list based on the order", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }];
const order = ["3", "2", "1"];
expect(sortListByOrder(order, list)).toEqual([
expect(sortListByOrder(order, list, "id")).toEqual([
{ id: "3" },
{ id: "2" },
{ id: "1" },
Expand All @@ -26,7 +29,7 @@ describe("sortListByOrder", () => {
it("should append the remaining items at the end of the list", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }, { id: "4" }];
const order = ["3", "2", "1"];
expect(sortListByOrder(order, list)).toEqual([
expect(sortListByOrder(order, list, "id")).toEqual([
{ id: "3" },
{ id: "2" },
{ id: "1" },
Expand Down
Loading

0 comments on commit bdba322

Please sign in to comment.