logo

Navigation

Explore our products and services

employee and task management
oxymart oxymart

employee and task management

SazzadTanim

SazzadTanim

Blogger

2 min

import { z } from 'zod';


// ============ ENUMS & CONSTANTS ============

export const UserRole = z.enum(['admin', 'manager', 'visa_consultant', 'travel_consultant', 'booking_agent']);

export const AttendanceStatus = z.enum(['present', 'absent', 'late', 'half_day', 'remote']);

export const TaskStatus = z.enum(['pending', 'in_progress', 'completed', 'cancelled']);

export const TaskPriority = z.enum(['low', 'medium', 'high', 'urgent']);

export const LeaveType = z.enum(['sick', 'casual', 'vacation']);

export const LeaveStatus = z.enum(['pending', 'approved', 'rejected']);

export const PaymentStatus = z.enum(['pending', 'partial', 'paid', 'refunded']);

export const BookingStatus = z.enum(['inquiry', 'quotation_sent', 'confirmed', 'in_progress', 'completed', 'cancelled']);


// ============ BASE SCHEMAS ============

export const AddressSchema = z.object({

street: z.string().min(1),

city: z.string().min(1),

state: z.string().min(1),

country: z.string().min(1),

zipCode: z.string().min(1),

});


export const MoneySchema = z.object({

amount: z.number(),

currency: z.string().length(3).default('INR'),

});


// ============ EMPLOYEE MANAGEMENT ============

export const EmployeeSchema = z.object({

id: z.string().uuid(),

employeeCode: z.string().min(3),

firstName: z.string().min(1),

lastName: z.string().min(1),

email: z.string().email(),

phone: z.string().min(10),

role: UserRole,

dateOfJoining: z.date(),

salary: z.number().positive().optional(),

commissionRate: z.number().min(0).max(100).default(0), // percentage

isActive: z.boolean().default(true),

specializations: z.array(z.string()).default([]),

reportingTo: z.string().uuid().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ ATTENDANCE ============

export const AttendanceSchema = z.object({

id: z.string().uuid(),

employeeId: z.string().uuid(),

date: z.date(),

status: AttendanceStatus,

checkInTime: z.date().optional(),

checkOutTime: z.date().optional(),

workHours: z.number().min(0).optional(),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ TASK MANAGEMENT ============

export const TaskSchema = z.object({

id: z.string().uuid(),

title: z.string().min(1),

description: z.string().optional(),

assignedTo: z.array(z.string().uuid()),

assignedBy: z.string().uuid(),

status: TaskStatus,

priority: TaskPriority,

dueDate: z.date(),

completedDate: z.date().optional(),

relatedBookingId: z.string().uuid().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ LEAVE MANAGEMENT ============

export const LeaveRequestSchema = z.object({

id: z.string().uuid(),

employeeId: z.string().uuid(),

leaveType: LeaveType,

startDate: z.date(),

endDate: z.date(),

totalDays: z.number().positive(),

reason: z.string().min(10),

status: LeaveStatus,

reviewedBy: z.string().uuid().optional(),

reviewedDate: z.date().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ CUSTOMER MANAGEMENT ============

export const CustomerSchema = z.object({

id: z.string().uuid(),

customerCode: z.string().min(1),

firstName: z.string().min(1),

lastName: z.string().min(1),

email: z.string().email(),

phone: z.string().min(10),

alternatePhone: z.string().optional(),

dateOfBirth: z.date().optional(),

passportNumber: z.string().optional(),

passportExpiry: z.date().optional(),

nationality: z.string().optional(),

address: AddressSchema.optional(),

assignedTo: z.string().uuid(), // Primary consultant

totalBookings: z.number().min(0).default(0),

totalRevenue: z.number().min(0).default(0),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ UNIFIED BOOKING SYSTEM (KEY IMPROVEMENT) ============

export const BookingItemSchema = z.object({

id: z.string().uuid(),

itemType: z.enum(['flight', 'hotel', 'visa', 'tour_package', 'insurance', 'transfer']),

description: z.string(),

details: z.record(z.any()), // Flexible JSON for item-specific data

quantity: z.number().positive().default(1),

sellingPrice: MoneySchema,

costPrice: MoneySchema, // What you pay supplier

supplierName: z.string().optional(),

supplierReference: z.string().optional(),

});


export const MasterBookingSchema = z.object({

id: z.string().uuid(),

bookingNumber: z.string().min(1).describe('TRV-2025-0001'),

customerId: z.string().uuid(),

customerName: z.string().min(1),

bookedBy: z.string().uuid(),

status: BookingStatus,

// Travel Details

travelStartDate: z.date(),

travelEndDate: z.date(),

destination: z.string(),

numberOfTravelers: z.number().positive(),

// Booking Items (flights, hotels, visas, etc.)

items: z.array(BookingItemSchema).min(1),

// Financial Summary

pricing: z.object({

subtotal: MoneySchema,

discount: MoneySchema.optional(),

taxes: MoneySchema.optional(),

serviceFee: MoneySchema.optional(),

totalSellingPrice: MoneySchema, // What customer pays

totalCostPrice: MoneySchema, // What you pay suppliers

grossProfit: MoneySchema, // Selling - Cost

profitMargin: z.number(), // percentage

}),

// Payment Tracking

paymentStatus: PaymentStatus,

paidAmount: z.number().min(0).default(0),

// Dates

bookingDate: z.date().default(() => new Date()),

confirmedDate: z.date().optional(),

completedDate: z.date().optional(),

cancelledDate: z.date().optional(),

// Additional Info

specialRequests: z.string().optional(),

notes: z.string().optional(),

cancellationReason: z.string().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ PAYMENT INSTALLMENTS (CRITICAL FOR MVP) ============

export const PaymentInstallmentSchema = z.object({

id: z.string().uuid(),

bookingId: z.string().uuid(),

installmentNumber: z.number().positive(),

amount: MoneySchema,

dueDate: z.date(),

status: z.enum(['pending', 'paid', 'overdue', 'waived']),

paidDate: z.date().optional(),

paidAmount: z.number().min(0).default(0),

paymentMethod: z.enum(['cash', 'card', 'bank_transfer', 'upi', 'cheque']).optional(),

transactionId: z.string().optional(),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ VENDOR/SUPPLIER PAYMENTS (CRITICAL) ============

export const VendorPaymentSchema = z.object({

id: z.string().uuid(),

bookingId: z.string().uuid(),

bookingItemId: z.string().uuid(),

supplierName: z.string().min(1),

amount: MoneySchema,

dueDate: z.date(),

status: z.enum(['pending', 'paid', 'overdue']),

paidDate: z.date().optional(),

paymentMethod: z.enum(['bank_transfer', 'cash', 'credit', 'cheque']).optional(),

paymentReference: z.string().optional(),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ QUOTATION SYSTEM ============

export const QuotationSchema = z.object({

id: z.string().uuid(),

quotationNumber: z.string().min(1),

customerId: z.string().uuid().optional(),

customerName: z.string().min(1),

customerEmail: z.string().email(),

customerPhone: z.string(),

preparedBy: z.string().uuid(),

validUntil: z.date(),

// Quotation Items

items: z.array(BookingItemSchema).min(1),

// Pricing

pricing: z.object({

subtotal: MoneySchema,

discount: MoneySchema.optional(),

discountPercentage: z.number().min(0).max(100).default(0),

taxes: MoneySchema.optional(),

serviceFee: MoneySchema.optional(),

totalAmount: MoneySchema,

}),

// Status

status: z.enum(['draft', 'sent', 'viewed', 'accepted', 'rejected', 'expired']),

sentDate: z.date().optional(),

responseDate: z.date().optional(),

// Converted to booking

convertedToBookingId: z.string().uuid().optional(),

notes: z.string().optional(),

termsAndConditions: z.string().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ LEAD MANAGEMENT ============

export const LeadSchema = z.object({

id: z.string().uuid(),

leadSource: z.enum(['website', 'phone', 'email', 'walk_in', 'referral', 'social_media', 'other']),

customerName: z.string().min(1),

email: z.string().email().optional(),

phone: z.string().min(10),

interestedIn: z.array(z.string()),

destination: z.string().optional(),

travelDate: z.date().optional(),

budget: z.object({

min: z.number().min(0),

max: z.number().min(0),

}).optional(),

numberOfTravelers: z.number().positive().optional(),

assignedTo: z.string().uuid(),

status: z.enum(['new', 'contacted', 'qualified', 'quotation_sent', 'converted', 'lost']),

priority: z.enum(['low', 'medium', 'high']),

notes: z.string().optional(),

followUpDate: z.date().optional(),

convertedToCustomerId: z.string().uuid().optional(),

lostReason: z.string().optional(),

createdAt: z.date().default(() => new Date()),

updatedAt: z.date().default(() => new Date()),

});


// ============ COMMISSION TRACKING ============

export const CommissionSchema = z.object({

id: z.string().uuid(),

employeeId: z.string().uuid(),

bookingId: z.string().uuid(),

bookingAmount: MoneySchema,

commissionRate: z.number().min(0).max(100),

commissionAmount: MoneySchema,

status: z.enum(['pending', 'approved', 'paid']),

approvedBy: z.string().uuid().optional(),

approvedDate: z.date().optional(),

paidDate: z.date().optional(),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ SALES TARGET ============

export const SalesTargetSchema = z.object({

id: z.string().uuid(),

employeeId: z.string().uuid(),

month: z.number().min(1).max(12),

year: z.number().min(2000),

targetRevenue: MoneySchema,

achievedRevenue: MoneySchema,

targetBookings: z.number().min(0),

achievedBookings: z.number().min(0),

achievementPercentage: z.number().min(0),

bonusEligible: z.boolean().default(false),

bonusAmount: MoneySchema.optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ INVOICE GENERATION (GST COMPLIANT) ============

export const InvoiceSchema = z.object({

id: z.string().uuid(),

invoiceNumber: z.string().min(1),

invoiceDate: z.date(),

bookingId: z.string().uuid(),

customerId: z.string().uuid(),

customerName: z.string(),

customerGSTIN: z.string().optional(),

customerAddress: AddressSchema,

// Company Details

companyGSTIN: z.string().optional(),

companyPAN: z.string().optional(),

// Line Items

items: z.array(z.object({

description: z.string(),

hsnCode: z.string().optional(),

quantity: z.number().positive(),

rate: z.number().positive(),

amount: z.number().positive(),

})),

// Tax Breakdown

taxBreakdown: z.object({

subtotal: z.number(),

cgst: z.number().default(0),

sgst: z.number().default(0),

igst: z.number().default(0),

totalTax: z.number(),

grandTotal: z.number(),

}),

placeOfSupply: z.string(),

paymentStatus: PaymentStatus,

dueDate: z.date().optional(),

pdfUrl: z.string().url().optional(),

notes: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ EXPENSE CLAIMS ============

export const ExpenseClaimSchema = z.object({

id: z.string().uuid(),

employeeId: z.string().uuid(),

category: z.enum(['client_meeting', 'marketing', 'travel', 'meals', 'other']),

amount: MoneySchema,

date: z.date(),

description: z.string().min(10),

receipts: z.array(z.string().url()).min(1),

relatedBookingId: z.string().uuid().optional(),

status: z.enum(['pending', 'approved', 'rejected', 'reimbursed']),

approvedBy: z.string().uuid().optional(),

approvedDate: z.date().optional(),

reimbursedDate: z.date().optional(),

comments: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ CUSTOMER FEEDBACK ============

export const FeedbackSchema = z.object({

id: z.string().uuid(),

bookingId: z.string().uuid(),

customerId: z.string().uuid(),

employeeId: z.string().uuid(),

rating: z.number().min(1).max(5),

categories: z.object({

responsiveness: z.number().min(1).max(5),

knowledge: z.number().min(1).max(5),

professionalism: z.number().min(1).max(5),

}),

comments: z.string().optional(),

wouldRecommend: z.boolean(),

submittedDate: z.date().default(() => new Date()),

});


// ============ NOTIFICATIONS ============

export const NotificationSchema = z.object({

id: z.string().uuid(),

recipientId: z.string().uuid(),

title: z.string().min(1),

message: z.string().min(1),

type: z.enum(['task', 'booking', 'payment', 'lead', 'attendance', 'leave', 'general']),

priority: z.enum(['low', 'medium', 'high']),

isRead: z.boolean().default(false),

actionUrl: z.string().optional(),

createdAt: z.date().default(() => new Date()),

});


// ============ DOCUMENT MANAGEMENT ============

export const DocumentSchema = z.object({

id: z.string().uuid(),

entityType: z.enum(['employee', 'customer', 'booking']),

entityId: z.string().uuid(),

documentType: z.enum(['passport', 'visa', 'id_card', 'ticket', 'voucher', 'invoice', 'contract', 'other']),

documentName: z.string().min(1),

documentNumber: z.string().optional(),

fileUrl: z.string().url(),

issueDate: z.date().optional(),

expiryDate: z.date().optional(),

uploadedBy: z.string().uuid(),

createdAt: z.date().default(() => new Date()),

});


// ============ ACTIVITY LOG (FOR AUDIT) ============

export const ActivityLogSchema = z.object({

id: z.string().uuid(),

userId: z.string().uuid(),

action: z.enum(['create', 'update', 'delete', 'view']),

entityType: z.enum(['booking', 'customer', 'payment', 'quotation', 'lead']),

entityId: z.string().uuid(),

description: z.string(),

ipAddress: z.string().optional(),

timestamp: z.date().default(() => new Date()),

});


// ============ DASHBOARD ANALYTICS ============

export const DashboardMetricsSchema = z.object({

period: z.object({

startDate: z.date(),

endDate: z.date(),

}),

// Sales Metrics

totalRevenue: MoneySchema,

totalBookings: z.number(),

totalProfit: MoneySchema,

avgBookingValue: MoneySchema,

// Lead Metrics

totalLeads: z.number(),

convertedLeads: z.number(),

conversionRate: z.number(),

// Employee Metrics

totalEmployees: z.number(),

activeEmployees: z.number(),

avgAttendanceRate: z.number(),

// Customer Metrics

totalCustomers: z.number(),

newCustomers: z.number(),

repeatCustomers: z.number(),

avgCustomerRating: z.number(),

// Payment Metrics

pendingPayments: MoneySchema,

overduePayments: MoneySchema,

pendingVendorPayments: MoneySchema,

generatedAt: z.date().default(() => new Date()),

});


// ============ EXPORT ALL TYPES ============

export type Employee = z.infer<typeof EmployeeSchema>;

export type Attendance = z.infer<typeof AttendanceSchema>;

export type Task = z.infer<typeof TaskSchema>;

export type LeaveRequest = z.infer<typeof LeaveRequestSchema>;

export type Customer = z.infer<typeof CustomerSchema>;

export type MasterBooking = z.infer<typeof MasterBookingSchema>;

export type BookingItem = z.infer<typeof BookingItemSchema>;

export type PaymentInstallment = z.infer<typeof PaymentInstallmentSchema>;

export type VendorPayment = z.infer<typeof VendorPaymentSchema>;

export type Quotation = z.infer<typeof QuotationSchema>;

export type Lead = z.infer<typeof LeadSchema>;

export type Commission = z.infer<typeof CommissionSchema>;

export type SalesTarget = z.infer<typeof SalesTargetSchema>;

export type Invoice = z.infer<typeof InvoiceSchema>;

export type ExpenseClaim = z.infer<typeof ExpenseClaimSchema>;

export type Feedback = z.infer<typeof FeedbackSchema>;

export type Notification = z.infer<typeof NotificationSchema>;

export type Document = z.infer<typeof DocumentSchema>;

export type ActivityLog = z.infer<typeof ActivityLogSchema>;

export type DashboardMetrics = z.infer<typeof DashboardMetricsSchema>;

Share this article

Back to Blog
SazzadTanim

About SazzadTanim

Blogger