employee and task management
SazzadTanim
Blogger
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