Skip to content

Commit

Permalink
refactor(core): migrate navigationMenu config format (#2193)
Browse files Browse the repository at this point in the history
see #2066
  • Loading branch information
sleidig authored Jan 25, 2024
1 parent 048de5f commit 037873c
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 75 deletions.
22 changes: 11 additions & 11 deletions src/app/core/config/config-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,57 @@ export const defaultJsonConfig = {
"navigationMenu": {
"items": [
{
"name": $localize`:Menu item:Dashboard`,
"label": $localize`:Menu item:Dashboard`,
"icon": "home",
"link": "/"
},
{
"name": $localize`:Menu item:Children`,
"label": $localize`:Menu item:Children`,
"icon": "child",
"link": "/child"
},
{
"name": $localize`:Menu item:Schools`,
"label": $localize`:Menu item:Schools`,
"icon": "university",
"link": "/school"
},
{
"name": $localize`:Menu item:Attendance`,
"label": $localize`:Menu item:Attendance`,
"icon": "calendar-check",
"link": "/attendance"
},
{
"name": $localize`:Menu item:Notes`,
"label": $localize`:Menu item:Notes`,
"icon": "file-alt",
"link": "/note"
},
{
"name": $localize`:Menu item:Tasks`,
"label": $localize`:Menu item:Tasks`,
"icon": "tasks",
"link": "/todo"
},
{
"name": $localize`:Menu item:Import`,
"label": $localize`:Menu item:Import`,
"icon": "file-import",
"link": "/import"
},
{
"name": $localize`:Menu item:Users`,
"label": $localize`:Menu item:Users`,
"icon": "users",
"link": "/user"
},
{
"name": $localize`:Menu item:Reports`,
"label": $localize`:Menu item:Reports`,
"icon": "line-chart",
"link": "/report"
},
{
"name": $localize`:Menu item:Help`,
"label": $localize`:Menu item:Help`,
"icon": "question",
"link": "/help"
},
{
"name": $localize`:Menu item:Admin`,
"label": $localize`:Menu item:Admin`,
"icon": "wrench",
"link": "/admin"
},
Expand Down
47 changes: 47 additions & 0 deletions src/app/core/config/config.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { firstValueFrom, Subject } from "rxjs";
import { UpdatedEntity } from "../entity/model/entity-update";
import { EntityConfig } from "../entity/entity-config";
import { FieldGroup } from "../entity-details/form/field-group";
import { NavigationMenuConfig } from "../ui/navigation/menu-item";

describe("ConfigService", () => {
let service: ConfigService;
Expand Down Expand Up @@ -280,4 +281,50 @@ describe("ConfigService", () => {

expect(result2.config.columns[0]).toEqual(expectedFieldConfig);
}));

it("should migrate menu item format", fakeAsync(() => {
const config = new Config();
const oldFormat = {
items: [
{
name: "one",
icon: "child",
link: "/one",
},
{
name: "two",
icon: "child",
link: "/two",
},
],
};
const newFormat = {
items: [
{
label: "one",
icon: "child",
link: "/one",
},
{
label: "two",
icon: "child",
link: "/two",
},
],
};

config.data = { navigationMenu: oldFormat };
updateSubject.next({ entity: config, type: "update" });
tick();
const actualFromOld =
service.getConfig<NavigationMenuConfig>("navigationMenu");
expect(actualFromOld).toEqual(newFormat);

config.data = { navigationMenu: newFormat };
updateSubject.next({ entity: config, type: "update" });
tick();
const actualFromNew =
service.getConfig<NavigationMenuConfig>("navigationMenu");
expect(actualFromNew).toEqual(newFormat);
}));
});
31 changes: 31 additions & 0 deletions src/app/core/config/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LatestEntityLoader } from "../entity/latest-entity-loader";
import { shareReplay } from "rxjs/operators";
import { EntitySchemaField } from "../entity/schema/entity-schema-field";
import { FieldGroup } from "../entity-details/form/field-group";
import { MenuItem } from "../ui/navigation/menu-item";

/**
* Access dynamic app configuration retrieved from the database
Expand Down Expand Up @@ -56,6 +57,7 @@ export class ConfigService extends LatestEntityLoader<Config> {
migrateEntityAttributesWithId,
migrateFormHeadersIntoFieldGroups,
migrateFormFieldConfigView2ViewComponent,
migrateMenuItemConfig,
];

const newConfig = JSON.parse(JSON.stringify(config), (_that, rawValue) => {
Expand Down Expand Up @@ -157,3 +159,32 @@ const migrateFormFieldConfigView2ViewComponent: ConfigMigration = (
}
return configPart;
};

const migrateMenuItemConfig: ConfigMigration = (key, configPart) => {
if (key !== "navigationMenu") {
return configPart;
}

const oldItems: (
| {
name: string;
icon: string;
link: string;
}
| MenuItem
)[] = configPart.items;

configPart.items = oldItems.map((item) => {
if (item.hasOwnProperty("name")) {
return {
label: item["name"],
icon: item.icon,
link: item.link,
};
} else {
return item;
}
});

return configPart;
};
25 changes: 8 additions & 17 deletions src/app/core/ui/navigation/menu-item.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
/*
* This file is part of ndb-core.
*
* ndb-core is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ndb-core is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ndb-core. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Structure for menu items to be displayed.
*/
Expand All @@ -32,3 +15,11 @@ export interface MenuItem {
*/
link: string;
}

/**
* Object specifying overall navigation menu
* as stored in the config database
*/
export interface NavigationMenuConfig {
items: MenuItem[];
}
12 changes: 0 additions & 12 deletions src/app/core/ui/navigation/navigation-menu-config.interface.ts

This file was deleted.

30 changes: 7 additions & 23 deletions src/app/core/ui/navigation/navigation/navigation.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { UserRoleGuard } from "../../../permissions/permission-guard/user-role.g
import { Event, NavigationEnd, Router } from "@angular/router";
import { MockedTestingModule } from "../../../../utils/mocked-testing.module";
import { EntityPermissionGuard } from "../../../permissions/permission-guard/entity-permission.guard";
import { NavigationMenuConfig } from "../menu-item";

describe("NavigationComponent", () => {
let component: NavigationComponent;
Expand Down Expand Up @@ -73,28 +74,11 @@ describe("NavigationComponent", () => {
expect(component).toBeTruthy();
});

it("generates menu items from config", fakeAsync(() => {
const testConfig = {
items: [
{ name: "Dashboard", icon: "home", link: "/dashboard" },
{ name: "Children", icon: "child", link: "/child" },
],
};
mockConfigService.getConfig.and.returnValue(testConfig);
mockConfigUpdated.next(null);
tick();

expect(component.menuItems).toEqual([
{ label: "Dashboard", icon: "home", link: "/dashboard" },
{ label: "Children", icon: "child", link: "/child" },
]);
}));

it("marks items that require admin rights", fakeAsync(() => {
const testConfig = {
const testConfig: NavigationMenuConfig = {
items: [
{ name: "Dashboard", icon: "home", link: "/dashboard" },
{ name: "Children", icon: "child", link: "/child" },
{ label: "Dashboard", icon: "home", link: "/dashboard" },
{ label: "Children", icon: "child", link: "/child" },
],
};
mockRoleGuard.checkRoutePermissions.and.callFake(async (route: string) => {
Expand All @@ -118,10 +102,10 @@ describe("NavigationComponent", () => {
}));

it("should add menu items where entity permissions are missing", fakeAsync(() => {
const testConfig = {
const testConfig: NavigationMenuConfig = {
items: [
{ name: "Dashboard", icon: "home", link: "/dashboard" },
{ name: "Children", icon: "child", link: "/child" },
{ label: "Dashboard", icon: "home", link: "/dashboard" },
{ label: "Children", icon: "child", link: "/child" },
],
};
mockEntityGuard.checkRoutePermissions.and.callFake((route: string) => {
Expand Down
22 changes: 10 additions & 12 deletions src/app/core/ui/navigation/navigation/navigation.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
*/

import { Component } from "@angular/core";
import { MenuItem } from "../menu-item";
import { NavigationMenuConfig } from "../navigation-menu-config.interface";
import { MenuItem, NavigationMenuConfig } from "../menu-item";
import { ConfigService } from "../../../config/config.service";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
import { NavigationEnd, Router, RouterLink } from "@angular/router";
Expand Down Expand Up @@ -48,8 +47,10 @@ import { RoutePermissionsService } from "../../../config/dynamic-routing/route-p
export class NavigationComponent {
/** The menu-item link (not the actual router link) that is currently active */
activeLink: string;

/** name of config array in the config json file */
private readonly CONFIG_ID = "navigationMenu";

/** all menu items to be displayed */
public menuItems: MenuItem[] = [];

Expand Down Expand Up @@ -113,16 +114,13 @@ export class NavigationComponent {
* Load menu items from config file
*/
private async initMenuItemsFromConfig() {
const config: NavigationMenuConfig =
this.configService.getConfig<NavigationMenuConfig>(this.CONFIG_ID);
// TODO align interface {@link https://github.com/Aam-Digital/ndb-core/issues/2066}
const items: MenuItem[] = config.items.map(({ name, icon, link }) => ({
label: name,
icon,
link,
}));
this.menuItems =
await this.routePermissionService.filterPermittedRoutes(items);
const config = this.configService.getConfig<NavigationMenuConfig>(
this.CONFIG_ID,
);

this.menuItems = await this.routePermissionService.filterPermittedRoutes(
config.items,
);

// re-select active menu item after menu has been fully initialized
this.activeLink = this.computeActiveLink(location.pathname);
Expand Down

0 comments on commit 037873c

Please sign in to comment.