feat(freight:backoffice): added org structure managemnet

This commit is contained in:
Michael Abebe
2026-05-25 15:50:09 +03:00
parent 2272255bef
commit 516d2c8b45
7 changed files with 1878 additions and 37 deletions

View File

@@ -16,6 +16,7 @@ import { BillingModule } from "./modules/billing/billing.module";
import { NotificationsModule } from "./modules/notifications/notifications.module";
import { FileUploadSettingsModule } from "./modules/file-upload-settings/file-upload-settings.module";
import { DropdownSettingsModule } from "./modules/dropdown-settings/dropdown-settings.module";
import { EdrOrgSeeder } from "./seed/edr-org.seeder";
@Module({
imports: [
@@ -40,11 +41,16 @@ import { DropdownSettingsModule } from "./modules/dropdown-settings/dropdown-set
FileUploadSettingsModule,
DropdownSettingsModule,
],
providers: [EdrOrgSeeder],
})
export class AppModule implements OnApplicationBootstrap {
constructor(private readonly seeder: DataSeeder) {}
constructor(
private readonly seeder: DataSeeder,
private readonly edrOrgSeeder: EdrOrgSeeder,
) {}
async onApplicationBootstrap() {
await this.seeder.run();
await this.edrOrgSeeder.run();
}
}

View File

@@ -0,0 +1,82 @@
import { Injectable, Logger } from "@nestjs/common";
import {
Organization,
OrganizationConfiguration,
Role,
} from "@tria-plc/iamapi-common";
import { DataSource } from "typeorm";
const EDR_ORG_KEY = "edr_freight";
const EDR_ORG_NAME = { en: "EDR Freight" };
const SEED_FLAG = "SEED_EDR_ORG";
const EDR_ROLES = [
{
key: "edr_employee",
name: { en: "EDR Employee" },
},
{
key: "edr_customer",
name: { en: "EDR Customer" },
},
];
@Injectable()
export class EdrOrgSeeder {
private readonly logger = new Logger(EdrOrgSeeder.name);
constructor(private readonly dataSource: DataSource) {}
async run() {
const shouldSeed = process.env[SEED_FLAG]?.trim().toLowerCase() === "true";
if (!shouldSeed) {
this.logger.log(`Skipping EDR org seed because ${SEED_FLAG} is not enabled`);
return;
}
const roleRepository = this.dataSource.getRepository(Role);
const organizationRepository = this.dataSource.getRepository(Organization);
const organizationConfigurationRepository =
this.dataSource.getRepository(OrganizationConfiguration);
await roleRepository.upsert(EDR_ROLES, {
conflictPaths: { key: true },
});
this.logger.log("Ensured EDR roles 'edr_employee' and 'edr_customer'");
let organization = await organizationRepository.findOne({
where: { key: EDR_ORG_KEY },
select: { id: true, key: true },
});
if (!organization) {
const insertResult = await organizationRepository.insert({
key: EDR_ORG_KEY,
name: EDR_ORG_NAME,
isGovernmentOrganization: true,
});
organization = {
id: insertResult.identifiers[0]?.id as string,
key: EDR_ORG_KEY,
} as Organization;
this.logger.log(`Seeded EDR organization '${EDR_ORG_KEY}'`);
} else {
this.logger.log(`Ensured EDR organization '${EDR_ORG_KEY}'`);
}
await organizationConfigurationRepository.upsert({
organizationId: organization.id,
canCreateBranchByItself: true,
canStartReceivingRecord: true,
}, {
conflictPaths: { organizationId: true },
});
this.logger.log(
`Ensured organization configuration for '${EDR_ORG_KEY}'`,
);
}
}

View File

@@ -1,6 +1,6 @@
import { Navigate, Outlet, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { DashboardLayout, type SidebarItem } from "@edr/ui-common";
import { LayoutDashboard, ShieldCheck, Users, Building2 } from "lucide-react";
import { LayoutDashboard, ShieldCheck, Users, Network } from "lucide-react";
import { useAuth } from "./auth/useAuth";
import LoginPage from "./pages/auth/LoginPage";
@@ -8,6 +8,7 @@ import OverviewPage from "./pages/dashboard/OverviewPage";
import UsersPage from "./pages/dashboard/user-management/UsersPage";
import RolesPage from "./pages/dashboard/user-management/RolesPage";
import DepartmentsPage from "./pages/dashboard/user-management/DepartmentsPage";
import OrgStructurePage from "./pages/dashboard/org-structure/OrgStructurePage";
import LoadingScreen from "./components/LoadingScreen";
const sidebarItems: SidebarItem[] = [
@@ -30,13 +31,13 @@ const sidebarItems: SidebarItem[] = [
label: "Roles",
href: "/dashboard/user-management/roles",
},
{
label: "Departments",
href: "/dashboard/user-management/departments",
icon: <Building2 />,
},
],
},
{
label: "Org structure",
href: "/dashboard/org-structure",
icon: <Network />,
},
];
const DashboardShell = () => {
@@ -90,10 +91,10 @@ const App = () => {
/>
<Route path="user-management/users" element={<UsersPage />} />
<Route path="user-management/roles" element={<RolesPage />} />
<Route
path="user-management/departments"
element={<DepartmentsPage />}
/>
<Route path="user-management/departments" element={<DepartmentsPage />} />
<Route path="org-structure" element={<OrgStructurePage />} />
<Route path="org-structure/units/:unitId" element={<OrgStructurePage />} />
<Route path="org-structure/units/:unitId/:section" element={<OrgStructurePage />} />
</Route>
<Route path="*" element={<Navigate to="/dashboard/overview" replace />} />
</Routes>