- Implemented PayrollPage for managing payroll runs with CRUD operations. - Created TimeClocksPage for tracking employee clock-ins and clock-outs. - Developed TimeSheetsPage for displaying employee timesheets. - Added EmployeeCertificationForm and EmployeeDocumentForm for managing employee certifications and documents. - Introduced LeaveRequestForm for submitting and managing leave requests. - Added usePermissions hook for UI-side permission checks. - Created LeaveRequestsClient and PayrollClient for API interactions.
93 lines
3.2 KiB
TypeScript
93 lines
3.2 KiB
TypeScript
import { z } from "zod"
|
|
import { EmployeeType, WageType } from "@garage/api"
|
|
|
|
const relationFieldSchema = z
|
|
.object({ value: z.string(), label: z.string() })
|
|
.nullable()
|
|
|
|
const optionalNumericField = z.preprocess(
|
|
(value) => {
|
|
if (value === "" || value === null || value === undefined) {
|
|
return null
|
|
}
|
|
return Number(value)
|
|
},
|
|
z.number().finite().nullable(),
|
|
)
|
|
|
|
const optionalIntegerIdField = z.preprocess(
|
|
(value) => {
|
|
if (value === "" || value === null || value === undefined) {
|
|
return null
|
|
}
|
|
return Number(value)
|
|
},
|
|
z.number().int().positive().nullable(),
|
|
)
|
|
|
|
const optionalTrimmedString = (max: number, msg?: string) =>
|
|
z.preprocess(
|
|
(value) => (typeof value !== "string" ? value : value.trim() === "" ? undefined : value.trim()),
|
|
z.string().max(max, msg ?? `Must be at most ${max} characters`).optional(),
|
|
)
|
|
|
|
const STATUS_OPTIONS = ["active", "inactive"] as const
|
|
const TYPE_OPTIONS = EmployeeType
|
|
const WAGE_TYPE_OPTIONS = WageType
|
|
const GENDER_OPTIONS = ["male", "female"] as const
|
|
const MARITAL_OPTIONS = ["single", "married"] as const
|
|
|
|
const optionalDate = z.preprocess(
|
|
(value) => (typeof value !== "string" || value.trim() === "" ? undefined : value),
|
|
z.string().optional(),
|
|
)
|
|
|
|
const employeeFormSchema = z.object({
|
|
department: relationFieldSchema,
|
|
country: relationFieldSchema,
|
|
shop_calender: relationFieldSchema,
|
|
shop_timing: relationFieldSchema,
|
|
role_id: optionalIntegerIdField,
|
|
first_name: z.string().trim().min(1, "First name is required").max(50, "First name must be at most 50 characters"),
|
|
last_name: z.string().trim().min(1, "Last name is required").max(50, "Last name must be at most 50 characters"),
|
|
email: z.string().trim().min(1, "Email is required").email("Enter a valid email address").max(100, "Email must be at most 100 characters"),
|
|
password: z.string().min(6, "Password must be at least 6 characters"),
|
|
phone: optionalTrimmedString(30),
|
|
designation: optionalTrimmedString(50),
|
|
salary: optionalNumericField,
|
|
wage_type: z.enum(WAGE_TYPE_OPTIONS).nullable(),
|
|
status: z.enum(STATUS_OPTIONS),
|
|
type: z.enum(TYPE_OPTIONS),
|
|
track_attendance: z.boolean(),
|
|
notify_owner_when_punch_in_out: z.boolean(),
|
|
geo_fence_radius: optionalNumericField,
|
|
|
|
// Extended profile fields
|
|
address: optionalTrimmedString(500),
|
|
date_of_birth: optionalDate,
|
|
gender: z.enum(GENDER_OPTIONS).nullable().optional(),
|
|
marital_status: z.enum(MARITAL_OPTIONS).nullable().optional(),
|
|
national_id: optionalTrimmedString(50),
|
|
hire_date: optionalDate,
|
|
emergency_contact_name: optionalTrimmedString(100),
|
|
emergency_contact_phone: optionalTrimmedString(30),
|
|
bank_name: optionalTrimmedString(100),
|
|
bank_account_number: optionalTrimmedString(50),
|
|
bank_iban: optionalTrimmedString(50),
|
|
hourly_rate: optionalNumericField,
|
|
commission_rate: optionalNumericField,
|
|
})
|
|
|
|
type EmployeeFormValues = z.infer<typeof employeeFormSchema>
|
|
|
|
export {
|
|
employeeFormSchema,
|
|
relationFieldSchema,
|
|
STATUS_OPTIONS,
|
|
TYPE_OPTIONS,
|
|
WAGE_TYPE_OPTIONS,
|
|
GENDER_OPTIONS,
|
|
MARITAL_OPTIONS,
|
|
}
|
|
export type { EmployeeFormValues }
|