fix(auth): resolve position-type permissions so GL staff can open clearance pages

This commit is contained in:
Marshal
2026-08-06 13:24:42 +00:00
parent 5933795116
commit 3a2f1a46d6
14 changed files with 466 additions and 11 deletions

View File

@@ -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 {

View File

@@ -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>

View File

@@ -342,6 +342,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];

View File

@@ -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();