Files
emaui/libs/api/src/lib/features/licensing/licensing-api.ts

818 lines
29 KiB
TypeScript

import { baseApi } from '../../base-api';
import type {
AppNotification,
ApplicationDetail,
ApplicationKind,
ApplicationPayment,
ApplicationStaff,
Attachment,
InitiatePaymentResult,
IssuedLicense,
Inspection,
LicenseApplication,
LicenseCategoryDefinition,
LicenseStatus,
LicenseType,
LicenseTypeRequirements,
OperatorType,
AssignableOfficer,
DocumentDecision,
DocumentReview,
ExportResult,
LicenseTemplate,
Paginated,
QueueCounts,
QueueFilter,
RemarkTargetType,
SavedQueueView,
TemplatePageOptions,
TemplateVariable,
} from './licensing.types';
/**
* Drops empty filters and flattens the status array.
*
* The grid keeps every facet in one object and serialises it to the URL, so
* cleared facets arrive as undefined/[]; sending those verbatim would put
* `status=` and `assignee=` on the wire and fail DTO validation.
*/
function serialiseQueueFilter(
filter: QueueFilter | void,
): Record<string, unknown> {
if (!filter) return {};
const params: Record<string, unknown> = {};
for (const [key, value] of Object.entries(filter)) {
if (value === undefined || value === null || value === '') continue;
if (Array.isArray(value)) {
if (value.length === 0) continue;
params[key] = value.join(',');
continue;
}
params[key] = value;
}
return params;
}
const TAGS = [
'LicenseType',
'OperatorType',
'LicenseApplication',
'ApplicationQueue',
'Attachment',
'Notification',
'Inspection',
'License',
'SavedView',
'LicenseTemplate',
] as const;
const listTag = (type: (typeof TAGS)[number]) => ({ type, id: 'LIST' }) as const;
const itemTag = (type: (typeof TAGS)[number], id: string) => ({ type, id }) as const;
/**
* Typed licensing endpoints, shared by the portal and the backoffice.
*
* Each resource gets its own cache tag so a transition invalidates the one
* application and its queue, rather than the whole store.
*/
export const licensingApi = baseApi
.enhanceEndpoints({ addTagTypes: TAGS })
.injectEndpoints({
endpoints: (builder) => ({
// ------------------------------------------------------------- config
/** Active licence types an applicant can choose from. */
getLicenseTypes: builder.query<Paginated<LicenseType>, void>({
query: () => ({ url: '/license-types' }),
providesTags: () => [listTag('LicenseType')],
}),
/**
* The signed-in applicant's declared modes of operation.
*
* Separate from the licence-type catalogue on purpose: the catalogue is
* the same for everyone and heavily cached, while this is per-user and
* changes the moment they edit their profile.
*/
getMyOperatorTypes: builder.query<{ items: OperatorType[] }, void>({
query: () => ({ url: '/profiles/me/operations' }),
providesTags: () => [listTag('OperatorType')],
}),
/** Replaces the set — see the Operations tab in the portal profile. */
updateMyOperatorTypes: builder.mutation<
{ items: OperatorType[] },
{ licenseTypeIds: string[] }
>({
query: (body) => ({
url: '/profiles/me/operations',
method: 'PUT',
body,
}),
// The catalogue is filtered by this, so it has to refetch too.
invalidatesTags: (_r, error) =>
error ? [] : [listTag('OperatorType'), listTag('LicenseType')],
}),
/**
* Category catalogue used to group the licence cards. Static on the
* server, so it is cached under the licence-type list tag.
*/
getLicenseCategories: builder.query<
Paginated<LicenseCategoryDefinition>,
void
>({
query: () => ({ url: '/license-categories' }),
providesTags: () => [listTag('LicenseType')],
}),
/**
* Fee configuration, for licensing administrators.
*
* `null` is a meaningful value for both amounts and must survive the
* round trip: null on the new-application fee means the licence is free,
* null on the renewal fee means it is charged at the new-application
* rate. Sending `undefined` instead would leave the column untouched.
*/
updateLicenseFees: builder.mutation<
LicenseType,
{
id: string;
feeNewApplication?: number | null;
feeRenewal?: number | null;
feeCurrency?: string;
}
>({
query: ({ id, ...body }) => ({
url: `/license-types/${id}/fees`,
method: 'PATCH',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [listTag('LicenseType'), itemTag('LicenseType', id)],
}),
/** Validity is edited beside the certificate design, not with the fees. */
updateLicenseValidity: builder.mutation<
LicenseType,
{ id: string; validityMonths: number }
>({
query: ({ id, validityMonths }) => ({
url: `/license-types/${id}/validity`,
method: 'PATCH',
body: { validityMonths },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseType', id), listTag('LicenseType')],
}),
getLicenseTypeRequirements: builder.query<
LicenseTypeRequirements,
{ idOrKey: string; kind?: ApplicationKind }
>({
query: ({ idOrKey, kind = 'NEW' }) => ({
url: `/license-types/requirements/${idOrKey}`,
params: { kind },
}),
providesTags: (_r, _e, arg) => [itemTag('LicenseType', arg.idOrKey)],
}),
// -------------------------------------------------------- application
createApplication: builder.mutation<
LicenseApplication,
{ licenseType: string; kind?: ApplicationKind; previousLicenseId?: string }
>({
query: (body) => ({ url: '/license-applications', method: 'POST', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseApplication')]),
}),
getMyApplications: builder.query<Paginated<LicenseApplication>, void>({
query: () => ({ url: '/license-applications/mine' }),
providesTags: () => [listTag('LicenseApplication')],
}),
getApplication: builder.query<ApplicationDetail, string>({
query: (id) => ({ url: `/license-applications/${id}` }),
providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
}),
patchSection: builder.mutation<
LicenseApplication,
{ id: string; sectionKey: string; values: Record<string, unknown> }
>({
query: ({ id, sectionKey, values }) => ({
url: `/license-applications/${id}/sections/${sectionKey}`,
method: 'PATCH',
body: { values },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
addStaff: builder.mutation<
ApplicationStaff,
{ id: string; roleKey: string; fullName: string; position?: string; yearsOfExperience?: number }
>({
query: ({ id, ...body }) => ({
url: `/license-applications/${id}/staff`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
removeStaff: builder.mutation<unknown, { id: string; staffId: string }>({
query: ({ id, staffId }) => ({
url: `/license-applications/${id}/staff/${staffId}`,
method: 'DELETE',
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
submitApplication: builder.mutation<LicenseApplication, string>({
query: (id) => ({ url: `/license-applications/${id}/submit`, method: 'POST' }),
invalidatesTags: (_r, error, id) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication'), listTag('ApplicationQueue')],
}),
resolveRemark: builder.mutation<unknown, { id: string; remarkId: string }>({
query: ({ id, remarkId }) => ({
url: `/license-applications/${id}/remarks/${remarkId}/resolve`,
method: 'PATCH',
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
resubmitApplication: builder.mutation<LicenseApplication, string>({
query: (id) => ({ url: `/license-applications/${id}/resubmit`, method: 'POST' }),
invalidatesTags: (_r, error, id) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication')],
}),
// -------------------------------------------------------- attachments
getAttachments: builder.query<Attachment[], { ownerType: string; ownerId: string }>({
query: ({ ownerType, ownerId }) => ({
url: '/attachments',
params: { ownerType, ownerId, withUrls: true },
}),
providesTags: (_r, _e, arg) => [itemTag('Attachment', arg.ownerId)],
}),
deleteAttachment: builder.mutation<unknown, { attachmentId: string; ownerId: string }>({
query: ({ attachmentId }) => ({
url: `/attachments/${attachmentId}`,
method: 'DELETE',
}),
invalidatesTags: (_r, error, { ownerId }) =>
error ? [] : [itemTag('Attachment', ownerId)],
}),
// ----------------------------------------------------------- payments
initiatePayment: builder.mutation<
InitiatePaymentResult,
{ id: string; provider?: string; platform?: 'web' | 'mobile'; payerAccount?: string }
>({
query: ({ id, ...body }) => ({
url: `/license-applications/${id}/payments/initiate`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
getApplicationPayment: builder.query<ApplicationPayment, string>({
query: (id) => ({ url: `/license-applications/${id}/payments` }),
providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
}),
/** Testing shortcut — only offered when the API reports it enabled. */
bypassPayment: builder.mutation<
{ status: LicenseStatus; certificateIssued: boolean },
string
>({
query: (id) => ({
url: `/license-applications/${id}/payments/bypass`,
method: 'POST',
}),
invalidatesTags: (_r, error, id) =>
error
? []
: [itemTag('LicenseApplication', id), listTag('LicenseApplication'), listTag('License')],
}),
getPaymentCapabilities: builder.query<{ bypassEnabled: boolean }, void>({
query: () => ({ url: '/license-applications/payments/capabilities' }),
}),
// ------------------------------------------------------------ licences
/** The licence register for enforcement officers. */
getLicenses: builder.query<
Paginated<IssuedLicense>,
{ search?: string } | void
>({
query: (args) => ({
url: '/licenses',
params: args?.search ? { search: args.search } : undefined,
}),
providesTags: () => [listTag('License')],
}),
getMyLicenses: builder.query<Paginated<IssuedLicense>, void>({
query: () => ({ url: '/licenses/mine' }),
providesTags: () => [listTag('License')],
}),
suspendLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
query: ({ id, reason }) => ({
url: `/licenses/${id}/suspend`,
method: 'POST',
body: { reason },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('License', id), listTag('License')],
}),
revokeLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
query: ({ id, reason }) => ({
url: `/licenses/${id}/revoke`,
method: 'POST',
body: { reason },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('License', id), listTag('License')],
}),
reinstateLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
query: ({ id, reason }) => ({
url: `/licenses/${id}/reinstate`,
method: 'POST',
body: { reason },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('License', id), listTag('License')],
}),
getCertificateUrl: builder.mutation<{ url: string }, string>({
query: (id) => ({ url: `/licenses/${id}/certificate` }),
}),
// ------------------------------------------------------------- review
getQueue: builder.query<Paginated<LicenseApplication>, QueueFilter | void>({
query: (params) => ({
url: '/license-application-review/queue',
params: serialiseQueueFilter(params),
}),
providesTags: () => [listTag('ApplicationQueue')],
}),
getAssignedToMe: builder.query<
Paginated<LicenseApplication>,
QueueFilter | void
>({
query: (params) => ({
url: '/license-application-review/assigned-to-me',
params: serialiseQueueFilter(params),
}),
providesTags: () => [listTag('ApplicationQueue')],
}),
/** Every non-draft application — backs the grid's "All" view. */
getAllApplications: builder.query<
Paginated<LicenseApplication>,
QueueFilter | void
>({
query: (params) => ({
url: '/license-application-review/all',
params: serialiseQueueFilter(params),
}),
providesTags: () => [listTag('ApplicationQueue')],
}),
/**
* Counts for the saved-view tabs. Shares the ApplicationQueue tag, so a
* claim or a decision refreshes the badges along with the list and the
* numbers can never drift from what the grid is showing.
*/
getQueueCounts: builder.query<QueueCounts, void>({
query: () => ({ url: '/license-application-review/counts' }),
providesTags: () => [listTag('ApplicationQueue')],
}),
getApplicationForReview: builder.query<ApplicationDetail, string>({
query: (id) => ({ url: `/license-application-review/${id}` }),
providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
}),
claimApplication: builder.mutation<LicenseApplication, string>({
query: (id) => ({ url: `/license-application-review/${id}/claim`, method: 'POST' }),
invalidatesTags: (_r, error, id) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
completeReview: builder.mutation<
LicenseApplication,
{ id: string; capitalAmountVerified?: number; remark?: string }
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/complete-review`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
requestAdjustment: builder.mutation<
LicenseApplication,
{
id: string;
generalRemark?: string;
items: { targetType: RemarkTargetType; targetKey: string; remark: string }[];
/** Officer-edited wording for the applicant notification. */
notificationBody?: string;
}
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/request-adjustment`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
approveDocuments: builder.mutation<LicenseApplication, { id: string; remark?: string }>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/approve-documents`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
/**
* `capitalAmountVerified` is what the officer confirms against the bank
* letter. Licence types with a capital threshold reject the approval
* without it, so it is part of the contract, not an optional extra.
*/
finalApprove: builder.mutation<
LicenseApplication,
{ id: string; remark?: string; capitalAmountVerified?: number }
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/final-approve`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
rejectApplication: builder.mutation<
LicenseApplication,
{ id: string; reason: string; notificationBody?: string }
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/reject`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
// ------------------------------------------------- certificate designs
getLicenseTemplates: builder.query<LicenseTemplate[], string | void>({
query: (licenseTypeId) => ({
url: '/license-templates',
params: licenseTypeId ? { licenseTypeId } : {},
}),
providesTags: () => [listTag('LicenseTemplate')],
}),
getTemplateVariables: builder.query<TemplateVariable[], void>({
query: () => ({ url: '/license-templates/variables' }),
}),
getBuiltInTemplate: builder.query<{ hbsSource: string }, void>({
query: () => ({ url: '/license-templates/built-in' }),
}),
createLicenseTemplate: builder.mutation<
LicenseTemplate,
{
licenseTypeId: string;
name: string;
hbsSource?: string;
pageOptions?: TemplatePageOptions;
}
>({
query: (body) => ({ url: '/license-templates', method: 'POST', body }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
}),
updateLicenseTemplate: builder.mutation<
LicenseTemplate,
{
id: string;
name?: string;
hbsSource?: string;
pageOptions?: TemplatePageOptions;
}
>({
query: ({ id, ...body }) => ({
url: `/license-templates/${id}`,
method: 'PATCH',
body,
}),
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
}),
publishLicenseTemplate: builder.mutation<LicenseTemplate, string>({
query: (id) => ({ url: `/license-templates/${id}/publish`, method: 'POST' }),
// The previously published design is archived by the same call, so the
// whole list is refetched rather than patching two rows by hand.
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
}),
archiveLicenseTemplate: builder.mutation<LicenseTemplate, string>({
query: (id) => ({ url: `/license-templates/${id}/archive`, method: 'POST' }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
}),
deleteLicenseTemplate: builder.mutation<unknown, string>({
query: (id) => ({ url: `/license-templates/${id}`, method: 'DELETE' }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
}),
// ------------------------------------------------------ document review
getDocumentReviews: builder.query<DocumentReview[], string>({
query: (id) => ({ url: `/license-application-review/${id}/document-reviews` }),
providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
}),
reviewDocument: builder.mutation<
DocumentReview,
{
id: string;
documentKey: string;
decision: DocumentDecision;
reason?: string;
attachmentId?: string;
}
>({
query: ({ id, documentKey, ...body }) => ({
url: `/license-application-review/${id}/documents/${encodeURIComponent(documentKey)}/review`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
clearDocumentReview: builder.mutation<unknown, { id: string; documentKey: string }>({
query: ({ id, documentKey }) => ({
url: `/license-application-review/${id}/documents/${encodeURIComponent(documentKey)}/review`,
method: 'DELETE',
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id)],
}),
// ----------------------------------------------------------- saved views
getSavedViews: builder.query<SavedQueueView[], void>({
query: () => ({ url: '/license-application-review/saved-views' }),
providesTags: () => [listTag('SavedView')],
}),
createSavedView: builder.mutation<
SavedQueueView,
{ name: string; queryString: string; isShared?: boolean; isDefault?: boolean }
>({
query: (body) => ({
url: '/license-application-review/saved-views',
method: 'POST',
body,
}),
invalidatesTags: (_r, error) => (error ? [] : [listTag('SavedView')]),
}),
deleteSavedView: builder.mutation<unknown, string>({
query: (viewId) => ({
url: `/license-application-review/saved-views/${viewId}`,
method: 'DELETE',
}),
invalidatesTags: (_r, error) => (error ? [] : [listTag('SavedView')]),
}),
// ------------------------------------------------------------- export
/**
* The whole filtered result set, not the current page. Lazy so it only
* runs when the officer actually asks for a file.
*/
exportApplications: builder.query<ExportResult, QueueFilter | void>({
query: (params) => ({
url: '/license-application-review/export',
params: serialiseQueueFilter(params),
}),
}),
/** Officers the Assign and Escalate dialogs can offer. */
getAssignableOfficers: builder.query<AssignableOfficer[], void>({
query: () => ({ url: '/license-application-review/officers' }),
}),
// ----------------------------------------------------- workflow controls
assignApplication: builder.mutation<
LicenseApplication,
{ id: string; officerId: string; remark?: string }
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/assign`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
holdApplication: builder.mutation<
LicenseApplication,
{ id: string; reason: string }
>({
query: ({ id, reason }) => ({
url: `/license-application-review/${id}/hold`,
method: 'POST',
body: { reason },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
resumeApplication: builder.mutation<
LicenseApplication,
{ id: string; remark?: string }
>({
query: ({ id, remark }) => ({
url: `/license-application-review/${id}/resume`,
method: 'POST',
body: { remark },
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
escalateApplication: builder.mutation<
LicenseApplication,
{ id: string; supervisorId: string; reason: string }
>({
query: ({ id, ...body }) => ({
url: `/license-application-review/${id}/escalate`,
method: 'POST',
body,
}),
invalidatesTags: (_r, error, { id }) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
confirmPayment: builder.mutation<LicenseApplication, string>({
query: (id) => ({
url: `/license-application-review/${id}/confirm-payment`,
method: 'POST',
}),
invalidatesTags: (_r, error, id) =>
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
}),
// --------------------------------------------------------- inspection
scheduleInspection: builder.mutation<
Inspection,
{ applicationId: string; scheduledDate: string; location?: string }
>({
query: (body) => ({ url: '/inspections', method: 'POST', body }),
invalidatesTags: (_r, error, { applicationId }) =>
error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
}),
getInspections: builder.query<Inspection[], string>({
query: (applicationId) => ({ url: `/inspections/application/${applicationId}` }),
providesTags: () => [listTag('Inspection')],
}),
recordInspectionResult: builder.mutation<
Inspection,
{
inspectionId: string;
applicationId: string;
result: 'PASSED' | 'FAILED';
findings: string;
/** Structured per-item outcomes (office, warehouse, vehicles, …). */
checklist?: {
key: string;
label: string;
outcome: 'PASS' | 'FAIL' | 'NEEDS_CORRECTION';
note?: string;
}[];
}
>({
query: ({ inspectionId, result, findings, checklist }) => ({
url: `/inspections/${inspectionId}/result`,
method: 'PATCH',
body: { result, findings, ...(checklist?.length ? { checklist } : {}) },
}),
invalidatesTags: (_r, error, { applicationId }) =>
error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
}),
// ------------------------------------------------------ notifications
getNotifications: builder.query<{ count: number; items: AppNotification[] }, void>({
query: () => ({ url: '/notifications' }),
providesTags: () => [listTag('Notification')],
}),
getUnseenNotifications: builder.query<{ count: number; items: AppNotification[] }, void>({
query: () => ({ url: '/notifications/unseen' }),
providesTags: () => [listTag('Notification')],
}),
markNotificationRead: builder.mutation<unknown, string>({
query: (id) => ({ url: `/notifications/${id}/read`, method: 'PATCH' }),
invalidatesTags: (_r, error) => (error ? [] : [listTag('Notification')]),
}),
}),
overrideExisting: false,
});
export const {
useGetLicenseTypesQuery,
useGetLicenseCategoriesQuery,
useUpdateLicenseFeesMutation,
useUpdateLicenseValidityMutation,
useGetLicenseTypeRequirementsQuery,
useCreateApplicationMutation,
useGetMyApplicationsQuery,
useGetApplicationQuery,
useInitiatePaymentMutation,
useBypassPaymentMutation,
useGetPaymentCapabilitiesQuery,
useGetMyLicensesQuery,
useGetLicensesQuery,
useGetCertificateUrlMutation,
useGetApplicationPaymentQuery,
usePatchSectionMutation,
useAddStaffMutation,
useRemoveStaffMutation,
useSubmitApplicationMutation,
useResolveRemarkMutation,
useResubmitApplicationMutation,
useGetAttachmentsQuery,
useDeleteAttachmentMutation,
useGetQueueQuery,
useGetAssignedToMeQuery,
useGetAllApplicationsQuery,
useGetQueueCountsQuery,
useGetLicenseTemplatesQuery,
useGetTemplateVariablesQuery,
useGetBuiltInTemplateQuery,
useCreateLicenseTemplateMutation,
useUpdateLicenseTemplateMutation,
usePublishLicenseTemplateMutation,
useArchiveLicenseTemplateMutation,
useDeleteLicenseTemplateMutation,
useGetDocumentReviewsQuery,
useReviewDocumentMutation,
useClearDocumentReviewMutation,
useGetSavedViewsQuery,
useCreateSavedViewMutation,
useDeleteSavedViewMutation,
useLazyExportApplicationsQuery,
useGetAssignableOfficersQuery,
useSuspendLicenseMutation,
useRevokeLicenseMutation,
useReinstateLicenseMutation,
useAssignApplicationMutation,
useHoldApplicationMutation,
useResumeApplicationMutation,
useEscalateApplicationMutation,
useGetApplicationForReviewQuery,
useClaimApplicationMutation,
useCompleteReviewMutation,
useRequestAdjustmentMutation,
useApproveDocumentsMutation,
useFinalApproveMutation,
useRejectApplicationMutation,
useConfirmPaymentMutation,
useScheduleInspectionMutation,
useGetInspectionsQuery,
useRecordInspectionResultMutation,
useGetNotificationsQuery,
useGetUnseenNotificationsQuery,
useMarkNotificationReadMutation,
useGetMyOperatorTypesQuery,
useUpdateMyOperatorTypesMutation,
} = licensingApi;