# approval

## Client

- Query:
  `approvalsQueryOptions`
  `approvalQueryOptions`
- Mutation:
  `createApprovalMutationOptions`
  `approveApprovalMutationOptions`
  `resubmitApprovalMutationOptions`
  `withdrawApprovalMutationOptions`
  `cancelApprovalMutationOptions`

## Server

- Fetch:
  `getApprovals`
  `getApproval`
- Mutate:
  `createApproval`
  `approveApproval`
  `resubmitApproval`
  `withdrawApproval`
  `cancelApproval`

## 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 ApprovalCoRequestorSchema = 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 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(),
  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().optional(),
});

const ApprovalCoRequestorEmployeeSchema = z.object({
  employeeId: z.string().trim().min(1),
  name: z.string().trim().min(1),
  email: z.email().optional(),
});

const ApprovalCoRequestorPrincipalSchema = z.union([
  ApprovalCoRequestorGroupSchema,
  ApprovalCoRequestorEmployeeSchema,
]);

const ApprovalSchema = z.object({
  id: z.uuid(),
  number: z.string().trim().max(30).optional(),
  applicationName: z.string().trim().min(1).max(50),
  type: ApprovalRequestType.default("Single").optional(),
  status: ApprovalStatus,
  created: z.date(),
  createdBy: z.custom<ApprovalEmployee>(),
  updated: z.date().optional(),
  updatedBy: z.custom<ApprovalEmployee>().optional(),
  approvalFlows: z.array(ApprovalFlow).min(1),
  currentApprovers: z.array(ApprovalEmployee),
  coRequestors: z
    .array(z.object({ id: z.uuid(), principal: ApprovalCoRequestorPrincipalSchema }))
    .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),
  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),
  coRequestors: z.array(ApprovalCoRequestorPrincipalSchema).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({
  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),
});

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 } from "wcz-layout/data/client";
import { approveApproval } from "wcz-layout/data/server";

// client
const { data: approval } = useQuery(approvalQueryOptions({ id }));

// server
await approveApproval({ data: { id, result: "Approved", emailBody } });
```
