Merge pull request #1335 from Tria-plc/freight/feat/yard-loc-ac

feat: yard scoping to position
This commit is contained in:
Nathnael Wondisha
2026-08-18 15:57:03 +03:00
committed by GitHub
27 changed files with 2657 additions and 257 deletions

View File

@@ -0,0 +1,64 @@
import { api as apiClient } from "../auth/http";
// NOTE: `auth/http`'s response interceptor already unwraps the API's
// `{ success, data }` envelope, so `response.data` IS the payload here — a
// second `.data` hop reads undefined and silently yields an empty list.
/** A desk mapped to a yard, joined to its IAM position for display. */
export interface YardPositionRow {
id: string;
yardId: string;
yardCode: string;
yardLabel: string;
positionId: string;
positionName: { am?: string; en?: string } | null;
positionTypeKey: string | null;
}
export interface SelectablePosition {
id: string;
name: { am?: string; en?: string } | null;
positionTypeKey: string | null;
unitKey: string | null;
}
export interface MyYardScope {
/** null = unrestricted (super admin or `yards:view_all`). */
yardIds: string[] | null;
unrestricted: boolean;
/** False while the backend is still shadow-logging instead of denying. */
enforced: boolean;
}
export const yardPositionsService = {
listByYard: async (yardId: string): Promise<YardPositionRow[]> => {
const { data } = await apiClient.get(`/yard-positions`, {
params: { yardId },
});
return data ?? [];
},
listPositions: async (): Promise<SelectablePosition[]> => {
const { data } = await apiClient.get(`/yard-positions/positions`);
return data ?? [];
},
myScope: async (): Promise<MyYardScope> => {
const { data } = await apiClient.get(`/yard-positions/my-yards`);
return data;
},
/**
* Replaces the yard's whole desk set — send every position that should remain
* mapped, not just the additions.
*/
setForYard: async (
yardId: string,
positionIds: string[],
): Promise<YardPositionRow[]> => {
const { data } = await apiClient.put(`/yard-positions/yard/${yardId}`, {
positionIds,
});
return data ?? [];
},
};