mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 14:08:11 +00:00
Merge pull request #1143 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
This commit is contained in:
@@ -23,7 +23,13 @@ interface AuthEmployeePosition {
|
||||
permissions?: AuthPermission[];
|
||||
/** Some IAM payloads nest the position record instead of flattening its key. */
|
||||
position?: { id?: string; key?: string; name?: LocaleText };
|
||||
positionType?: { id?: string; key?: string; name?: LocaleText } | null;
|
||||
positionType?: {
|
||||
id?: string;
|
||||
key?: string;
|
||||
name?: LocaleText;
|
||||
/** Grants held by the TYPE — where admin-created positions keep theirs. */
|
||||
permissions?: AuthPermission[];
|
||||
} | null;
|
||||
}
|
||||
|
||||
interface AuthEmployeeRecord {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Alert, Button, Group, Paper, Stack, Text } from "@mantine/core";
|
||||
import { DateInput } from "@mantine/dates";
|
||||
import { AlertTriangle, Send } from "lucide-react";
|
||||
import { AlertTriangle, Pencil, Send } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -16,6 +16,9 @@ export interface BookingChangesRequestedAlertProps {
|
||||
scheduledDate?: string | null;
|
||||
/** GL Ethiopia owns customs bookings, so only they get the resubmit control. */
|
||||
canResubmit: boolean;
|
||||
/** Completion-form route for editing the cargo before resubmitting —
|
||||
* rendered only for resubmit-capable users when provided. */
|
||||
editHref?: string;
|
||||
onResubmitted?: () => void;
|
||||
}
|
||||
|
||||
@@ -33,6 +36,7 @@ export function BookingChangesRequestedAlert({
|
||||
note,
|
||||
scheduledDate,
|
||||
canResubmit,
|
||||
editHref,
|
||||
onResubmitted,
|
||||
}: BookingChangesRequestedAlertProps) {
|
||||
const [day, setDay] = useState<Date | null>(
|
||||
@@ -122,6 +126,18 @@ export function BookingChangesRequestedAlert({
|
||||
>
|
||||
Resubmit to Operations
|
||||
</Button>
|
||||
{editHref ? (
|
||||
<Button
|
||||
component={Link}
|
||||
to={editHref}
|
||||
variant="default"
|
||||
radius="md"
|
||||
size="sm"
|
||||
leftSection={<Pencil size={15} />}
|
||||
>
|
||||
Edit cargo & resubmit
|
||||
</Button>
|
||||
) : null}
|
||||
</Group>
|
||||
) : null}
|
||||
</Stack>
|
||||
|
||||
@@ -348,6 +348,14 @@ export function getPermissionKeys(user: AuthUser | null | undefined): string[] {
|
||||
for (const p of pos.permissions ?? []) {
|
||||
if (p.key) keys.add(p.key);
|
||||
}
|
||||
// Positions created through the admin UI keep their grants on the
|
||||
// position TYPE, not the position — miss these and such staff resolve to
|
||||
// zero permissions and every gated route rejects them. `/api/me` folds
|
||||
// them into the position's permission list, but older payloads may still
|
||||
// carry them separately.
|
||||
for (const p of pos.positionType?.permissions ?? []) {
|
||||
if (p.key) keys.add(p.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...keys];
|
||||
|
||||
@@ -311,6 +311,7 @@ export default function ContractClearanceDetailPage() {
|
||||
note={clearance.linkedBookingReviewNote}
|
||||
scheduledDate={clearance.linkedBookingScheduledDate}
|
||||
canResubmit={canResubmitBooking}
|
||||
editHref={`/dashboard/contracts/${id}/bookings/${linkedBookingId}/complete?copyFrom=${linkedBookingId}`}
|
||||
onResubmitted={() => {
|
||||
void refetch();
|
||||
void refetchContract();
|
||||
|
||||
@@ -916,17 +916,57 @@ export default function TrainScheduleV2DetailPage() {
|
||||
<Title order={2} fw={700} style={{ color: "#0f172a" }}>
|
||||
{schedule.route?.name ?? "Train schedule"}
|
||||
</Title>
|
||||
{schedule.trainNumber ? (
|
||||
<Badge variant="light" color="#F2A516" radius="sm" style={{ fontWeight: 600 }}>
|
||||
{schedule.trainNumber}
|
||||
</Badge>
|
||||
) : null}
|
||||
{schedule.train ? (
|
||||
<Text size="xs" c="dimmed" ff="monospace">
|
||||
Train {schedule.train.code}
|
||||
</Text>
|
||||
) : null}
|
||||
</Group>
|
||||
|
||||
{/* Voyage (train) number and trade direction — the two things
|
||||
operations identify a run by, so they read at a glance
|
||||
rather than as small badges among the rest. */}
|
||||
<Group gap="lg" align="center" wrap="wrap">
|
||||
{schedule.trainNumber ? (
|
||||
<Box>
|
||||
<Text size="xs" c="dimmed" fw={600} tt="uppercase" lh={1.2}>
|
||||
Voyage No.
|
||||
</Text>
|
||||
<Text
|
||||
ff="monospace"
|
||||
fw={800}
|
||||
lh={1.1}
|
||||
style={{ fontSize: 32, color: "#0f172a" }}
|
||||
>
|
||||
{schedule.trainNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
) : null}
|
||||
{schedule.direction ? (
|
||||
<Box>
|
||||
<Text size="xs" c="dimmed" fw={600} tt="uppercase" lh={1.2}>
|
||||
Direction
|
||||
</Text>
|
||||
<Text
|
||||
fw={800}
|
||||
lh={1.1}
|
||||
tt="uppercase"
|
||||
style={{
|
||||
fontSize: 32,
|
||||
letterSpacing: 0.5,
|
||||
color:
|
||||
schedule.direction === "IMPORT"
|
||||
? "#2E5B96"
|
||||
: schedule.direction === "EXPORT"
|
||||
? "#0A6F4D"
|
||||
: "#0f172a",
|
||||
}}
|
||||
>
|
||||
{schedule.direction}
|
||||
</Text>
|
||||
</Box>
|
||||
) : null}
|
||||
</Group>
|
||||
{(schedule.stops?.length ?? 0) >= 3 ||
|
||||
(schedule.bookings ?? []).some(
|
||||
(b) => b.tradeDirection === "DOMESTIC",
|
||||
|
||||
Reference in New Issue
Block a user