wcz-layout
Version:
305 lines (259 loc) • 9.29 kB
Markdown
# approval
## Client
`import { ... } from "wcz-layout/data/client";`
- Query:
`approvalsQueryOptions(GetApprovalsSchema)` → `Approval[]` // approvals of the application, optionally filtered; `appName` is sent as `applicationName`
`approvalQueryOptions(GetApprovalSchema)` → `Approval` // single approval
- Mutation:
`createApprovalMutationOptions({ queryClient })` → `mutate(CreateApprovalSchema)` → `Approval` // create a new approval request
`approveApprovalMutationOptions({ queryClient })` → `mutate(ApproveApprovalSchema)` → `Approval` // current approver's decision (`"Approved"` / `"Rejected"`) on the active step
`resubmitApprovalMutationOptions({ queryClient })` → `mutate(ResubmitApprovalSchema)` → `Approval` // resubmit with new flows (same flow shape as create)
`withdrawApprovalMutationOptions({ queryClient })` → `mutate(WithdrawApprovalSchema)` → `void` // withdraw the request
`cancelApprovalMutationOptions({ queryClient })` → `mutate(CancelApprovalSchema)` → `void` // cancel the request
## Server
`import { ... } from "wcz-layout/data/server";`
- Fetch:
`getApprovals({ data: GetApprovalsSchema })` → `Approval[]` // approvals of the application, optionally filtered
`getApproval({ data: GetApprovalSchema })` → `Approval` // single approval
- Mutate:
`createApproval({ data: CreateApprovalSchema })` → `Approval` // create a new approval request
`approveApproval({ data: ApproveApprovalSchema })` → `Approval` // approve / reject the active step
`resubmitApproval({ data: ResubmitApprovalSchema })` → `Approval` // resubmit with new flows
`withdrawApproval({ data: WithdrawApprovalSchema })` → `void` // withdraw the request
`cancelApproval({ data: CancelApprovalSchema })` → `void` // cancel the request
## Schemas/Models
```ts
const ApprovalRequestType = z.enum(["Single", "Batch"]);
const ApprovalStatus = z.enum([
"WaitingForApproval",
"Approved",
"PartiallyApproved",
"Rejected",
"Withdrawn",
"Cancelled",
]);
const ApprovalStepResult = z.enum([
"NotAvailable",
"FutureApproval",
"WaitingForApproval",
"Approved",
"Rejected",
"Skipped",
"Withdrawn",
"Cancelled",
]);
const StepApprovalOrder = z.enum(["Serial", "Parallel", "OneOfThem"]);
const ApprovalEmployeeSchema = z.object({
employeeId: z.string().trim().min(1).max(20),
name: z.string().trim().min(1).max(50),
email: z.email().min(1).max(100),
});
const ApprovalProxySchema = z.object({
proxy: ApprovalEmployeeSchema,
});
const ApprovalFlowStepSchema = z.object({
id: z.uuid(),
order: z.number(),
approverRole: z.string().trim().min(1).max(100).default("Approver").optional(),
approver: ApprovalEmployeeSchema,
actualApprover: ApprovalEmployeeSchema.optional(),
proxies: z.array(ApprovalProxySchema),
result: ApprovalStepResult.default("NotAvailable").optional(),
resultDate: z.date().optional(),
resultComment: z.string().trim().max(2000).optional(),
});
const ApprovalFlowSchema = z.object({
id: z.uuid(),
approvalStage: z.number(),
batchSequence: z.number().optional(),
approvalSequence: z.number(),
stepApprovalOrder: StepApprovalOrder,
steps: z.array(ApprovalFlowStepSchema),
isActive: z.boolean().default(false).optional(),
});
const ApprovalCoRequestorGroupSchema = z.object({
role: z.string().trim().min(1),
email: z.email(),
});
const ApprovalCoRequestorEmployeeSchema = z.object({
employeeId: z.string().trim().min(1),
name: z.string().trim().min(1),
email: z.email(),
});
const ApprovalCoRequestorPrincipalSchema = z.union([
ApprovalCoRequestorGroupSchema,
ApprovalCoRequestorEmployeeSchema,
]);
const ApprovalCoRequestorPermissionsSchema = z.object({
withdraw: z.boolean().default(false).optional(),
cancel: z.boolean().default(false).optional(),
});
const ApprovalCoRequestorSchema = z.object({
id: z.uuid(),
principal: ApprovalCoRequestorPrincipalSchema,
permissions: ApprovalCoRequestorPermissionsSchema,
});
const HqApprovalColSchema = z.object({
title: z.string().optional(),
text: z.string().optional(),
textColor: z.string().optional(),
textSize: z.string().optional(),
});
const HqApprovalRowSchema = z.object({ col: z.array(HqApprovalColSchema) });
const HqApprovalSectionSchema = z.object({
row: z.array(HqApprovalRowSchema),
title: z.string().optional(),
type: z.string().optional(),
text: z.string().optional(),
});
const HqApprovalPageSchema = z.object({ section: z.array(HqApprovalSectionSchema) });
const HqApprovalSummarySchema = z.object({ page: z.array(HqApprovalPageSchema) });
const HqApprovalFormDataSchema = z.object({
systemId: z.string().optional(),
formType: z.string().optional(),
formId: z.string().optional(),
stepName: z.string().optional(),
userId: z.string().optional(),
title: z.string().optional(),
subject: z.string().optional(),
summary: HqApprovalSummarySchema.optional(),
messageId: z.string().optional(),
attachList: z.string().optional(),
chkResult: z.string().optional(),
apprSummary: z.string().optional(),
approvalComment: z.string().optional(),
});
const HqApprovalHeaderSchema = z.object({
serviceId: z.string().optional(),
authorizationCode: z.string().optional(),
dataCreatedTime: z.string().optional(),
characterEnable: z.boolean().default(false).optional(),
});
const HqApprovalRequestSchema = z.object({
header: HqApprovalHeaderSchema.optional(),
formData: HqApprovalFormDataSchema.optional(),
});
const ApprovalSchema = z.object({
id: z.uuid(),
number: z.string().trim().min(1).max(30),
applicationName: z.string().trim().min(1).max(50),
type: ApprovalRequestType.default("Single").optional(),
status: ApprovalStatus,
created: z.date(),
createdBy: ApprovalEmployeeSchema,
updated: z.date().optional(),
updatedBy: ApprovalEmployeeSchema.optional(),
approvalFlows: z.array(ApprovalFlowSchema).min(1),
currentApprovers: z.array(
ApprovalEmployeeSchema.extend({ proxies: z.array(ApprovalProxySchema) }),
),
coRequestors: z.array(ApprovalCoRequestorSchema).optional(),
});
const GetApprovalsSchema = z.object({
appName: z.string().min(1).max(255),
status: ApprovalStatus.optional(),
approverEmployeeId: z.string().min(1).max(20).optional(),
stepResult: ApprovalStepResult.optional(),
});
const GetApprovalSchema = z.object({
id: z.uuid(),
});
const CreateApprovalSchema = ApprovalSchema.pick({
id: true,
number: true,
applicationName: true,
type: true,
status: true,
approvalFlows: true,
}).extend({
emailBody: z.string().trim().min(1),
hqApprovalRequest: HqApprovalRequestSchema.optional(),
approvalFlows: z
.array(
ApprovalFlowSchema.pick({
approvalStage: true,
batchSequence: true,
stepApprovalOrder: true,
steps: true,
}).extend({
steps: z.array(ApprovalFlowStepSchema.pick({ approverRole: true, approver: true })).min(1),
}),
)
.min(1),
coRequestors: z.array(ApprovalCoRequestorSchema).optional(),
});
const ApproveApprovalSchema = z.object({
id: z.uuid(),
result: ApprovalStepResult,
resultComment: z.string().trim().max(2000).optional(),
emailBody: z.string().trim().min(1),
});
const ResubmitApprovalSchema = ApprovalSchema.pick({
id: true,
approvalFlows: true,
}).extend({
emailBody: z.string().trim().min(1),
approvalFlows: z
.array(
ApprovalFlowSchema.pick({
approvalStage: true,
batchSequence: true,
stepApprovalOrder: true,
steps: true,
}).extend({
steps: z.array(ApprovalFlowStepSchema.pick({ approverRole: true, approver: true })).min(1),
}),
)
.min(1),
});
const CancelApprovalSchema = z.object({
id: z.uuid(),
comment: z.string().trim().min(1).max(2000),
emailBody: z.string().trim().min(1),
});
const WithdrawApprovalSchema = z.object({
id: z.uuid(),
comment: z.string().trim().min(1).max(2000),
emailBody: z.string().trim().min(1),
});
```
## Examples
```ts
import {
approvalQueryOptions,
approvalsQueryOptions,
approveApprovalMutationOptions,
createApprovalMutationOptions,
} from "wcz-layout/data/client";
import { createApproval } from "wcz-layout/data/server";
import { uuidv7 } from "wcz-layout/utils";
const { queryClient } = Route.useRouteContext();
// fetch
const { data: approvals = [] } = useQuery(
approvalsQueryOptions({ appName: packageJson.name, status: "WaitingForApproval" }),
);
const { data: approval } = useQuery(approvalQueryOptions({ id }));
// create
const { mutate: create } = useMutation(createApprovalMutationOptions({ queryClient }));
create({
id: uuidv7(),
number: "REQ-0001",
applicationName: packageJson.name,
status: "WaitingForApproval",
emailBody: "<p>Please approve REQ-0001.</p>",
approvalFlows: [
{
approvalStage: 1,
stepApprovalOrder: "Serial",
steps: [{ approver: { employeeId, name, email } }],
},
],
});
// approve / reject
const { mutate: approve } = useMutation(approveApprovalMutationOptions({ queryClient }));
approve({ id, result: "Approved", emailBody: "<p>REQ-0001 was approved.</p>" });
// server (inside another server function)
const created = await createApproval({
data: { id, number, applicationName, status, emailBody, approvalFlows },
});
```