mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 04:45:03 +00:00
@@ -582,9 +582,7 @@ const App = () => {
|
||||
<Route
|
||||
path="routes"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.routes.view}>
|
||||
<RoutesPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -592,12 +590,7 @@ const App = () => {
|
||||
<Route
|
||||
path="locomotives"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[
|
||||
FREIGHT_PERMS.locomotives.view,
|
||||
FREIGHT_PERMS.fleet.view,
|
||||
]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.locomotives.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -605,9 +598,7 @@ const App = () => {
|
||||
<Route
|
||||
path="trains"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.trains.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -615,9 +606,7 @@ const App = () => {
|
||||
<Route
|
||||
path="trains/:id"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.trains.view}>
|
||||
<TrainDetailPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -625,9 +614,7 @@ const App = () => {
|
||||
<Route
|
||||
path="train-builder"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.trains.view}>
|
||||
<TrainBuilderListPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -635,9 +622,7 @@ const App = () => {
|
||||
<Route
|
||||
path="train-builder/:id"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.trains.view}>
|
||||
<TrainBuilderDetailPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -645,9 +630,7 @@ const App = () => {
|
||||
<Route
|
||||
path="wagons"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.wagons.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -668,12 +651,7 @@ const App = () => {
|
||||
<Route
|
||||
path="containers"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[
|
||||
FREIGHT_PERMS.containers.view,
|
||||
FREIGHT_PERMS.fleet.view,
|
||||
]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.containers.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -681,12 +659,7 @@ const App = () => {
|
||||
<Route
|
||||
path="cargoes"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[
|
||||
FREIGHT_PERMS.cargoes.view,
|
||||
FREIGHT_PERMS.fleet.view,
|
||||
]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.cargoes.view}>
|
||||
<FleetResourcePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -774,9 +747,7 @@ const App = () => {
|
||||
<Route
|
||||
path="compliance"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.compliance.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.compliance.view}>
|
||||
<CompliancePage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -800,9 +771,7 @@ const App = () => {
|
||||
<Route
|
||||
path="procurement"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.procurement.view, FREIGHT_PERMS.fleet.view]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.procurement.view}>
|
||||
<ProcurementPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -825,9 +794,7 @@ const App = () => {
|
||||
<Route
|
||||
path="file-settings"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.settings.fileUpload.view, FREIGHT_PERMS.admin]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.settings.fileUpload.view}>
|
||||
<FileUploadSettingsPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -835,9 +802,7 @@ const App = () => {
|
||||
<Route
|
||||
path="dropdown-settings"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.settings.dropdown.view, FREIGHT_PERMS.admin]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.settings.dropdown.view}>
|
||||
<DropdownSettingsPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -854,10 +819,7 @@ const App = () => {
|
||||
path="contract-templates"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[
|
||||
FREIGHT_PERMS.settings.contractTemplates.view,
|
||||
FREIGHT_PERMS.admin,
|
||||
]}
|
||||
permission={FREIGHT_PERMS.settings.contractTemplates.view}
|
||||
>
|
||||
<ContractTemplatesPage />
|
||||
</RequirePermission>
|
||||
@@ -867,10 +829,7 @@ const App = () => {
|
||||
path="contract-templates/:code"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[
|
||||
FREIGHT_PERMS.settings.contractTemplates.view,
|
||||
FREIGHT_PERMS.admin,
|
||||
]}
|
||||
permission={FREIGHT_PERMS.settings.contractTemplates.view}
|
||||
>
|
||||
<ContractTemplateEditorPage />
|
||||
</RequirePermission>
|
||||
@@ -883,7 +842,6 @@ const App = () => {
|
||||
permission={[
|
||||
FREIGHT_PERMS.settings.supportContent.view,
|
||||
FREIGHT_PERMS.settings.supportContent.manage,
|
||||
FREIGHT_PERMS.admin,
|
||||
]}
|
||||
>
|
||||
<PortalContentPage />
|
||||
@@ -908,9 +866,7 @@ const App = () => {
|
||||
<Route
|
||||
path="configuration/trade-access"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.tradeAccess.view, FREIGHT_PERMS.admin]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.tradeAccess.view}>
|
||||
<TradeAccessPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
@@ -918,9 +874,7 @@ const App = () => {
|
||||
<Route
|
||||
path="configuration/exchange-rate"
|
||||
element={
|
||||
<RequirePermission
|
||||
permission={[FREIGHT_PERMS.settings.exchangeRate.view, FREIGHT_PERMS.admin]}
|
||||
>
|
||||
<RequirePermission permission={FREIGHT_PERMS.settings.exchangeRate.view}>
|
||||
<div className="p-4">
|
||||
<ExchangeRateSettingsCard />
|
||||
</div>
|
||||
|
||||
@@ -32,8 +32,6 @@ import { formatDate, humanize } from "./format";
|
||||
/** Friendly labels for the proposed-change snapshot keys (UpdateProfileDto). */
|
||||
export const FIELD_LABELS: Record<string, string> = {
|
||||
companyName: "Company name",
|
||||
companyEmail: "Company email",
|
||||
companyPhone: "Company phone",
|
||||
companyLocation: "Location",
|
||||
companyAddress: "Address",
|
||||
tin: "TIN",
|
||||
@@ -73,8 +71,6 @@ export function currentValue(company: Company, key: string): string {
|
||||
const attrs = (company.attributes ?? {}) as Record<string, unknown>;
|
||||
const map: Record<string, unknown> = {
|
||||
companyName: c.name,
|
||||
companyEmail: c.email,
|
||||
companyPhone: c.phone,
|
||||
companyLocation: c.country,
|
||||
companyAddress: c.address,
|
||||
tin: c.tin,
|
||||
@@ -118,7 +114,8 @@ function FaydaIdentityDiff({
|
||||
if (!subject) return null;
|
||||
const current =
|
||||
subject === "owner" ? company.identity?.owner : company.identity?.poa;
|
||||
const read = (key: string) => snapshot[`${subject}${key}`] as string | undefined;
|
||||
const read = (key: string) =>
|
||||
snapshot[`${subject}${key}`] as string | undefined;
|
||||
const verifiedAt = read("FaydaVerifiedAt");
|
||||
const fields: { label: string; from?: string | null; to?: string }[] = [
|
||||
{ label: "Name", from: current?.name, to: read("Name") },
|
||||
@@ -131,7 +128,9 @@ function FaydaIdentityDiff({
|
||||
<Stack gap={8}>
|
||||
<Group gap={8}>
|
||||
<Text size="sm" fw={600} c="edr-text">
|
||||
{subject === "owner" ? "Owner re-verification" : "PoA re-verification"}
|
||||
{subject === "owner"
|
||||
? "Owner re-verification"
|
||||
: "PoA re-verification"}
|
||||
</Text>
|
||||
{verifiedAt && (
|
||||
<Text size="xs" c="dimmed">
|
||||
@@ -283,8 +282,8 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
icon={<AlertTriangle size={16} />}
|
||||
>
|
||||
Changes were requested on an earlier round of this same
|
||||
submission: <strong>{pending.note}</strong> — check whether
|
||||
this resubmission actually addresses it before approving.
|
||||
submission: <strong>{pending.note}</strong> — check whether this
|
||||
resubmission actually addresses it before approving.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
@@ -297,8 +296,8 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
from={currentValue(company, key)}
|
||||
to={
|
||||
pending.snapshot[key] === null ||
|
||||
pending.snapshot[key] === undefined ||
|
||||
pending.snapshot[key] === ""
|
||||
pending.snapshot[key] === undefined ||
|
||||
pending.snapshot[key] === ""
|
||||
? "—"
|
||||
: String(pending.snapshot[key])
|
||||
}
|
||||
@@ -312,7 +311,10 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
) : null}
|
||||
|
||||
{faydaIdentitySnapshot && (
|
||||
<FaydaIdentityDiff company={company} snapshot={faydaIdentitySnapshot} />
|
||||
<FaydaIdentityDiff
|
||||
company={company}
|
||||
snapshot={faydaIdentitySnapshot}
|
||||
/>
|
||||
)}
|
||||
|
||||
{documentChanges.length > 0 && (
|
||||
@@ -373,9 +375,10 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
void fetchViewableFile(fileId, `Document ${i + 1}`).then(
|
||||
view,
|
||||
)
|
||||
void fetchViewableFile(
|
||||
fileId,
|
||||
`Document ${i + 1}`,
|
||||
).then(view)
|
||||
}
|
||||
>
|
||||
Document {i + 1}
|
||||
@@ -446,7 +449,10 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
variant="light"
|
||||
color="yellow"
|
||||
onClick={() => {
|
||||
setActionTarget({ id: pending.id, kind: "request-changes" });
|
||||
setActionTarget({
|
||||
id: pending.id,
|
||||
kind: "request-changes",
|
||||
});
|
||||
setNote("");
|
||||
}}
|
||||
>
|
||||
@@ -525,7 +531,11 @@ export function ChangeRequestReview({ company }: { company: Company }) {
|
||||
}
|
||||
|
||||
/** Compact "N changes pending" pill for the customer list/detail header. */
|
||||
export function ChangeRequestPendingBadge({ companyId }: { companyId: string }) {
|
||||
export function ChangeRequestPendingBadge({
|
||||
companyId,
|
||||
}: {
|
||||
companyId: string;
|
||||
}) {
|
||||
const query = useQuery(
|
||||
api.customers.changeRequests.queryOptions({ input: { id: companyId } }),
|
||||
);
|
||||
|
||||
@@ -203,22 +203,19 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
label: "Routes",
|
||||
href: "/dashboard/routes",
|
||||
icon: <Network />,
|
||||
permission: [FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view],
|
||||
permission: FREIGHT_PERMS.routes.view,
|
||||
},
|
||||
{
|
||||
label: "Locomotives",
|
||||
href: "/dashboard/locomotives",
|
||||
icon: <Train />,
|
||||
permission: [
|
||||
FREIGHT_PERMS.locomotives.view,
|
||||
FREIGHT_PERMS.fleet.view,
|
||||
],
|
||||
permission: FREIGHT_PERMS.locomotives.view,
|
||||
},
|
||||
{
|
||||
label: "Train Builder",
|
||||
href: "/dashboard/train-builder",
|
||||
icon: <Hammer />,
|
||||
permission: [FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view],
|
||||
permission: FREIGHT_PERMS.trains.view,
|
||||
},
|
||||
|
||||
// {
|
||||
@@ -230,7 +227,7 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
label: "Wagons",
|
||||
href: "/dashboard/wagons",
|
||||
icon: <Truck />,
|
||||
permission: [FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view],
|
||||
permission: FREIGHT_PERMS.wagons.view,
|
||||
},
|
||||
{
|
||||
label: "Wagon Transfers",
|
||||
@@ -287,21 +284,23 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
label: "Compliance & Alerts",
|
||||
href: "/dashboard/compliance",
|
||||
icon: <ShieldCheck />,
|
||||
permission: [FREIGHT_PERMS.compliance.view, FREIGHT_PERMS.fleet.view],
|
||||
permission: FREIGHT_PERMS.compliance.view,
|
||||
},
|
||||
{
|
||||
label: "Incidents",
|
||||
href: "/dashboard/incidents",
|
||||
icon: <FileText />,
|
||||
// No dedicated backend key exists for incidents yet — stuck on the
|
||||
// blanket fleet:view fallback until one is added.
|
||||
// No dedicated backend key exists for incidents yet. Not part of
|
||||
// the fleet.view/admin fallback cleanup — removing fleet.view
|
||||
// here with nothing to replace it would lock the page to
|
||||
// super-admin only, so it stays as the sole (if coarse) gate.
|
||||
permission: FREIGHT_PERMS.fleet.view,
|
||||
},
|
||||
{
|
||||
label: "Procurement",
|
||||
href: "/dashboard/procurement",
|
||||
icon: <Package />,
|
||||
permission: [FREIGHT_PERMS.procurement.view, FREIGHT_PERMS.fleet.view],
|
||||
permission: FREIGHT_PERMS.procurement.view,
|
||||
},
|
||||
{
|
||||
label: "Financial Reports",
|
||||
@@ -486,23 +485,20 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
label: "File settings",
|
||||
href: "/dashboard/file-settings",
|
||||
icon: <Paperclip />,
|
||||
permission: [FREIGHT_PERMS.settings.fileUpload.view, FREIGHT_PERMS.admin],
|
||||
permission: FREIGHT_PERMS.settings.fileUpload.view,
|
||||
},
|
||||
{
|
||||
label: "Dropdown settings",
|
||||
href: "/dashboard/dropdown-settings",
|
||||
icon: <Settings />,
|
||||
permission: [FREIGHT_PERMS.settings.dropdown.view, FREIGHT_PERMS.admin],
|
||||
permission: FREIGHT_PERMS.settings.dropdown.view,
|
||||
},
|
||||
{
|
||||
label: "Contract templates",
|
||||
href: "/dashboard/contract-templates",
|
||||
icon: <ScrollText />,
|
||||
// `view` opens the page; `read` alone is API-only and shows no menu.
|
||||
permission: [
|
||||
FREIGHT_PERMS.settings.contractTemplates.view,
|
||||
FREIGHT_PERMS.admin,
|
||||
],
|
||||
permission: FREIGHT_PERMS.settings.contractTemplates.view,
|
||||
},
|
||||
{
|
||||
label: "Portal content",
|
||||
@@ -511,7 +507,6 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
permission: [
|
||||
FREIGHT_PERMS.settings.supportContent.view,
|
||||
FREIGHT_PERMS.settings.supportContent.manage,
|
||||
FREIGHT_PERMS.admin,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -534,12 +529,12 @@ export const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[]
|
||||
{
|
||||
label: "Trade access",
|
||||
href: "/dashboard/configuration/trade-access",
|
||||
permission: [FREIGHT_PERMS.tradeAccess.view, FREIGHT_PERMS.admin],
|
||||
permission: FREIGHT_PERMS.tradeAccess.view,
|
||||
},
|
||||
{
|
||||
label: "Exchange rate",
|
||||
href: "/dashboard/configuration/exchange-rate",
|
||||
permission: [FREIGHT_PERMS.settings.exchangeRate.view, FREIGHT_PERMS.admin],
|
||||
permission: FREIGHT_PERMS.settings.exchangeRate.view,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -659,19 +659,13 @@ export type FleetCrudResource =
|
||||
| "vehicles"
|
||||
| "drivers";
|
||||
|
||||
/**
|
||||
* Per-resource fleet CRUD check. The legacy coarse fleet:manage key still
|
||||
* grants every action (mirrors the API's one-of guard fallback).
|
||||
*/
|
||||
/** Per-resource fleet CRUD check — each resource needs its own grant. */
|
||||
export function canFleetAction(
|
||||
user: AuthUser | null | undefined,
|
||||
resource: FleetCrudResource,
|
||||
action: "create" | "update" | "delete",
|
||||
): boolean {
|
||||
return (
|
||||
hasPermission(user, FREIGHT_PERMS[resource][action]) ||
|
||||
hasPermission(user, FREIGHT_PERMS.fleet.manage)
|
||||
);
|
||||
return hasPermission(user, FREIGHT_PERMS[resource][action]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,11 +87,7 @@ const TAB_ITEMS: Array<{
|
||||
icon: TrainFront,
|
||||
kpiKey: "operations",
|
||||
metricKey: "wagonsAvailable",
|
||||
permission: [
|
||||
FREIGHT_PERMS.fleet.view,
|
||||
FREIGHT_PERMS.wagons.view,
|
||||
FREIGHT_PERMS.trainScheduling.view,
|
||||
],
|
||||
permission: [FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.trainScheduling.view],
|
||||
},
|
||||
{
|
||||
value: "customers",
|
||||
|
||||
@@ -85,9 +85,7 @@ export default function TrainBuilderDetailPage() {
|
||||
const { user } = useAuth();
|
||||
const canUpdate = canFleetAction(user, "trains", "update");
|
||||
const canDelete = canFleetAction(user, "trains", "delete");
|
||||
const canAssign =
|
||||
hasPermission(user, FREIGHT_PERMS.trains.assignWagons) ||
|
||||
hasPermission(user, FREIGHT_PERMS.fleet.manage);
|
||||
const canAssign = hasPermission(user, FREIGHT_PERMS.trains.assignWagons);
|
||||
|
||||
const compositionQuery = useQuery(
|
||||
api.trainBuilder.composition.queryOptions({ input: { id }, enabled: Boolean(id) }),
|
||||
|
||||
Reference in New Issue
Block a user