feat(hr): Complete HR module with Employee Portal, Loans, Leave, Purchase Requests, Contracts
- Database: Add Loan, LoanInstallment, PurchaseRequest, LeaveEntitlement, EmployeeContract models - Database: Extend Attendance with ZK Tico fields (sourceDeviceId, externalId, rawData) - Database: Add Employee.attendancePin for device mapping - Backend: HR admin - Loans, Purchase Requests, Leave entitlements, Employee contracts CRUD - Backend: Leave reject, bulk attendance sync (ZK Tico ready) - Backend: Employee Portal API - scoped by employeeId (loans, leaves, purchase-requests, attendance, salaries) - Frontend: Employee Portal - dashboard, loans, leave, purchase-requests, attendance, salaries - Frontend: HR Admin - new tabs for Leaves, Loans, Purchase Requests, Contracts (approve/reject) - Dashboard: Add My Portal link - No destructive schema changes; additive migrations only Made-with: Cursor
This commit is contained in:
163
frontend/src/lib/api/portal.ts
Normal file
163
frontend/src/lib/api/portal.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { api } from '../api'
|
||||
|
||||
export interface PortalProfile {
|
||||
employee: {
|
||||
id: string
|
||||
uniqueEmployeeId: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
firstNameAr?: string | null
|
||||
lastNameAr?: string | null
|
||||
email: string
|
||||
department?: { name: string; nameAr?: string | null }
|
||||
position?: { title: string; titleAr?: string | null }
|
||||
}
|
||||
stats: {
|
||||
activeLoansCount: number
|
||||
pendingLeavesCount: number
|
||||
pendingPurchaseRequestsCount: number
|
||||
leaveBalance: Array<{
|
||||
leaveType: string
|
||||
totalDays: number
|
||||
carriedOver: number
|
||||
usedDays: number
|
||||
available: number
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface Loan {
|
||||
id: string
|
||||
loanNumber: string
|
||||
type: string
|
||||
amount: number
|
||||
currency: string
|
||||
installments: number
|
||||
monthlyAmount?: number
|
||||
reason?: string | null
|
||||
status: string
|
||||
approvedBy?: string | null
|
||||
approvedAt?: string | null
|
||||
rejectedReason?: string | null
|
||||
startDate?: string | null
|
||||
endDate?: string | null
|
||||
createdAt: string
|
||||
installmentsList?: Array<{
|
||||
id: string
|
||||
installmentNumber: number
|
||||
dueDate: string
|
||||
amount: number
|
||||
paidDate?: string | null
|
||||
status: string
|
||||
}>
|
||||
}
|
||||
|
||||
export interface Leave {
|
||||
id: string
|
||||
leaveType: string
|
||||
startDate: string
|
||||
endDate: string
|
||||
days: number
|
||||
reason?: string | null
|
||||
status: string
|
||||
approvedBy?: string | null
|
||||
approvedAt?: string | null
|
||||
rejectedReason?: string | null
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface PurchaseRequest {
|
||||
id: string
|
||||
requestNumber: string
|
||||
items: any[]
|
||||
totalAmount?: number | null
|
||||
reason?: string | null
|
||||
priority: string
|
||||
status: string
|
||||
approvedBy?: string | null
|
||||
approvedAt?: string | null
|
||||
rejectedReason?: string | null
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface Attendance {
|
||||
id: string
|
||||
date: string
|
||||
checkIn?: string | null
|
||||
checkOut?: string | null
|
||||
workHours?: number | null
|
||||
overtimeHours?: number | null
|
||||
status: string
|
||||
}
|
||||
|
||||
export interface Salary {
|
||||
id: string
|
||||
month: number
|
||||
year: number
|
||||
basicSalary: number
|
||||
allowances: number
|
||||
deductions: number
|
||||
commissions: number
|
||||
overtimePay: number
|
||||
netSalary: number
|
||||
status: string
|
||||
paidDate?: string | null
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export const portalAPI = {
|
||||
getMe: async (): Promise<PortalProfile> => {
|
||||
const response = await api.get('/hr/portal/me')
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
getLoans: async (): Promise<Loan[]> => {
|
||||
const response = await api.get('/hr/portal/loans')
|
||||
return response.data.data || []
|
||||
},
|
||||
|
||||
submitLoanRequest: async (data: { type: string; amount: number; installments?: number; reason?: string }): Promise<Loan> => {
|
||||
const response = await api.post('/hr/portal/loans', data)
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
getLeaveBalance: async (year?: number): Promise<PortalProfile['stats']['leaveBalance']> => {
|
||||
const params = year ? `?year=${year}` : ''
|
||||
const response = await api.get(`/hr/portal/leave-balance${params}`)
|
||||
return response.data.data || []
|
||||
},
|
||||
|
||||
getLeaves: async (): Promise<Leave[]> => {
|
||||
const response = await api.get('/hr/portal/leaves')
|
||||
return response.data.data || []
|
||||
},
|
||||
|
||||
submitLeaveRequest: async (data: { leaveType: string; startDate: string; endDate: string; reason?: string }): Promise<Leave> => {
|
||||
const response = await api.post('/hr/portal/leaves', data)
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
getPurchaseRequests: async (): Promise<PurchaseRequest[]> => {
|
||||
const response = await api.get('/hr/portal/purchase-requests')
|
||||
return response.data.data || []
|
||||
},
|
||||
|
||||
submitPurchaseRequest: async (data: { items: Array<{ description: string; quantity?: number; estimatedPrice?: number }>; reason?: string; priority?: string }): Promise<PurchaseRequest> => {
|
||||
const response = await api.post('/hr/portal/purchase-requests', data)
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
getAttendance: async (month?: number, year?: number): Promise<Attendance[]> => {
|
||||
const params = new URLSearchParams()
|
||||
if (month) params.append('month', String(month))
|
||||
if (year) params.append('year', String(year))
|
||||
const query = params.toString() ? `?${params.toString()}` : ''
|
||||
const response = await api.get(`/hr/portal/attendance${query}`)
|
||||
return response.data.data || []
|
||||
},
|
||||
|
||||
getSalaries: async (): Promise<Salary[]> => {
|
||||
const response = await api.get('/hr/portal/salaries')
|
||||
return response.data.data || []
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user