enhance contract and booking services with server-side search and validation improvements

- Added  parameter to  and  for server-side free-text search on contract reference, company name, and booking details.
- Introduced new validation errors in  for container clashes and space issues when creating bookings.
- Implemented paginated dropdown settings retrieval in .
- Updated  to fetch active yards using a new method that handles pagination.
- Enhanced  with a  method to fetch all records by walking through pages.
- Refactored  to support filtering and pagination in schedule listings.
- Improved  to return a paginated list of facilities.
- Updated UI components in  and  to utilize debounced search inputs for better performance.
- Added alerts in  to inform users about booking constraints related to splits and capacity.
- Enhanced  to display notifications for split bookings and capacity usage.
This commit is contained in:
Marshal
2026-07-12 10:51:31 +00:00
parent 6695c5448e
commit 4b7f6d2548
108 changed files with 3187 additions and 1368 deletions

View File

@@ -68,50 +68,63 @@ const defaultMeta = (
dataLength: number,
page = 1,
pageSize = 10,
): RuleEngineListMeta => ({
total: dataLength,
page,
pageSize,
totalPages: Math.max(1, Math.ceil(dataLength / pageSize)),
});
): RuleEngineListMeta => {
const totalPages = Math.max(1, Math.ceil(dataLength / pageSize));
return {
total: dataLength,
page,
pageSize,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
};
};
const isPaginatedListResult = <T extends RuleEngineRecord>(
/** Standard envelope from the shared pagination toolkit: `{ items, meta }`. */
const isItemsEnvelope = <T extends RuleEngineRecord>(
value: unknown,
): value is RuleEngineListResult<T> =>
Boolean(value) &&
typeof value === "object" &&
"data" in (value ?? {}) &&
Array.isArray((value as RuleEngineListResult<T>).data);
Array.isArray((value as { items?: unknown }).items);
/** Legacy envelope (`{ data, meta }`) — still returned by wagon-types. */
const isLegacyEnvelope = <T extends RuleEngineRecord>(
value: unknown,
): value is { data: T[]; meta?: RuleEngineListMeta } =>
Boolean(value) &&
typeof value === "object" &&
Array.isArray((value as { data?: unknown }).data);
const normalizeList = <T extends RuleEngineRecord>(
payload: unknown,
page = 1,
pageSize = 10,
): RuleEngineListResult<T> => {
if (isPaginatedListResult<T>(payload)) {
return {
data: payload.data,
meta: payload.meta ?? defaultMeta(payload.data.length, page, pageSize),
};
const candidates: unknown[] = [payload, unwrap(payload as { data: unknown })];
for (const body of candidates) {
if (isItemsEnvelope<T>(body)) {
return {
items: body.items,
meta: body.meta ?? defaultMeta(body.items.length, page, pageSize),
};
}
if (isLegacyEnvelope<T>(body)) {
return {
items: body.data,
meta: body.meta ?? defaultMeta(body.data.length, page, pageSize),
};
}
if (Array.isArray(body)) {
return {
items: body as T[],
meta: defaultMeta(body.length, page, pageSize),
};
}
}
const body = unwrap(payload as { data: unknown }) as unknown;
if (isPaginatedListResult<T>(body)) {
return {
data: body.data,
meta: body.meta ?? defaultMeta(body.data.length, page, pageSize),
};
}
if (Array.isArray(body)) {
return {
data: body as T[],
meta: defaultMeta(body.length, page, pageSize),
};
}
return { data: [], meta: defaultMeta(0, page, pageSize) };
return { items: [], meta: defaultMeta(0, page, pageSize) };
};
const normalizeEntity = <T extends RuleEngineRecord>(payload: unknown): T => {
@@ -140,6 +153,34 @@ export const ruleEngineService = {
return normalizeList<T>(response.data, page, pageSize);
},
/**
* Fetch every row of a resource by walking the pages. The API caps pageSize
* at 100, so option/dropdown consumers that used to ask for 200-500 rows in
* one shot go through here instead of getting silently capped (or a 400).
*/
listAll: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
params?: Omit<RuleEngineListParams, "page" | "pageSize">,
): Promise<T[]> => {
const pageSize = 100;
const first = await ruleEngineService.list<T>(resource, {
...params,
page: 1,
pageSize,
});
const items = [...first.items];
const totalPages = first.meta.totalPages ?? 1;
for (let page = 2; page <= totalPages; page += 1) {
const next = await ruleEngineService.list<T>(resource, {
...params,
page,
pageSize,
});
items.push(...next.items);
}
return items;
},
getById: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
id: string,