Implement intercity document handling and rejection notes for contracts

This commit is contained in:
Marshal
2026-07-21 10:20:36 +00:00
226 changed files with 13854 additions and 2973 deletions

View File

@@ -10,6 +10,7 @@ import type {
CustomerBooking,
CustomerDocument,
CustomerPayment,
CustomerResetTarget,
PaginatedCompanies,
ProfileStatus,
ResetChannel,
@@ -2567,6 +2568,13 @@ export const api = {
({ id }) => QUERY_KEYS.CUSTOMERS.payments(id),
),
resetTarget: endpoint<{ companyId: string }, CustomerResetTarget>(
"customers",
"resetTarget",
({ companyId }) => customersService.resetTarget(companyId),
({ companyId }) => QUERY_KEYS.CUSTOMERS.resetTarget(companyId),
),
resetPassword: endpoint<
{ companyId: string; channel: ResetChannel },
ResetPasswordResult
@@ -2623,6 +2631,25 @@ export const api = {
],
),
/**
* Ask the customer to correct one document. Invalidates the documents list
* and the company itself, since an open request blocks role approval.
*/
requestDocumentChange: endpoint<
{ companyId: string; fileId: string; note: string },
CustomerDocument
>(
"customers",
"requestDocumentChange",
({ fileId, note }) =>
customersService.requestDocumentChange(fileId, note),
undefined,
({ companyId }) => [
QUERY_KEYS.CUSTOMERS.documents(companyId),
QUERY_KEYS.CUSTOMERS.byId(companyId),
],
),
setCompanyStatus: endpoint<{ companyId: string; status: string }, unknown>(
"customers",
"setCompanyStatus",

View File

@@ -9,6 +9,7 @@ import type {
CustomerBooking,
CustomerDocument,
CustomerPayment,
CustomerResetTarget,
PaginatedCompanies,
ProfileStatus,
ResetChannel,
@@ -89,8 +90,20 @@ export const customersService = {
},
/**
* Send a password-reset code to the company's primary contact. Staff never
* receive a credential the customer sets their own password from the code.
* The IAM account a reset link would go to. Read before offering the action
* so staff see the credentials the link actually reaches, not the company's
* business contact details.
*/
resetTarget(companyId: string): Promise<CustomerResetTarget> {
return apiClient
.get<CustomerResetTarget>(URL_CONSTANTS.COMPANIES.RESET_TARGET(companyId))
.then((r) => r.data);
},
/**
* Send a password-reset link to the company's primary contact. Staff never
* receive a credential — the customer opens the link and sets their own
* password.
*/
resetPassword(
companyId: string,
@@ -151,4 +164,21 @@ export const customersService = {
)
.then((r) => r.data);
},
/**
* Ask the customer to correct one uploaded document. Narrower than rejecting
* the whole role: the customer keeps their other documents and only re-uploads
* this one, but the role cannot be approved until they do.
*/
requestDocumentChange(
fileId: string,
note: string,
): Promise<CustomerDocument> {
return apiClient
.post<CustomerDocument>(
URL_CONSTANTS.COMPANIES.DOCUMENT_REQUEST_CHANGE(fileId),
{ note },
)
.then((r) => r.data);
},
};

View File

@@ -5,6 +5,7 @@ import { api as apiClient } from '../auth/http';
import { URL_CONSTANTS } from '@/constants/URLS';
import type {
ZoneOccupancy,
TruckOnSite,
WarehouseOpsStats,
WarehouseThroughputPoint,
WarehouseDwellStats,
@@ -404,6 +405,9 @@ export const warehouseService = {
),
opsStats: () =>
apiClient.get<WarehouseOpsStats>(URL_CONSTANTS.WAREHOUSE_INVENTORY.OPS_STATS),
/** Trucks inside the yard right now — the list behind the trucksOnSite figure. */
trucksOnSite: () =>
apiClient.get<TruckOnSite[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.TRUCKS_ON_SITE),
throughput: (granularity: 'week' | 'month' | 'year') =>
apiClient.get<WarehouseThroughputPoint[]>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.THROUGHPUT(granularity),