@using Indotalent.ConfigBackEnd.Extensions @using Indotalent.Features.Payroll.Payrolls @using Indotalent.Features.Payroll.Payrolls.Cqrs @using Microsoft.AspNetCore.Components.Web @using Microsoft.JSInterop @using MudBlazor @using Features.Root.Shared @using ClosedXML.Excel @using System.IO @inject PayrollsService PayrollsService @inject IDialogService DialogService @inject ISnackbar Snackbar @inject IJSRuntime JSRuntime
Payroll Processing & History Execute monthly salary calculations and review historical payment distributions.
/ Payroll / Process
Search
@if (_isExporting) { Processing... } else { Excel } @if (_isRefreshing) { Refreshing... } else { Refresh } @if (_selectedItem != null) { View Edit Remove } else { Calculate Payrolls }
Period Total Employees Total Gross Total Deduction Total Net Pay (THP) Status @context.PeriodName @context.FromDate.ToShortDateString() - @context.ToDate.ToShortDateString() @context.TotalEmployees Employees @context.TotalGross.ToString("N0") (@context.TotalDeduction.ToString("N0")) @context.TotalTakeHomePay.ToString("N0") @context.Status
Rows per page: Showing @(GetFilteredData().Count() == 0 ? 0 : _skip + 1)-@Math.Min(_skip + _top, GetFilteredData().Count()) of @GetFilteredData().Count()
Prev @{ var totalPages = _totalPage == 0 ? 1 : _totalPage; var maxVisible = 5; var startPage = Math.Max(1, _currentPage - maxVisible / 2); var endPage = Math.Min(totalPages, startPage + maxVisible - 1); if (endPage - startPage < maxVisible - 1) { startPage = Math.Max(1, endPage - maxVisible + 1); } } @for (int i = startPage; i <= endPage; i++) { var pageNum = i; var isActive = pageNum == _currentPage; @pageNum } Next
@code { [Parameter] public EventCallback OnAdd { get; set; } [Parameter] public EventCallback OnEdit { get; set; } [Parameter] public EventCallback OnView { get; set; } private List _payrolls = new(); private GetPayrollProcessListResponse? _selectedItem; private string _searchString = ""; private int _skip = 0; private int _top = 5; private int _totalPage => GetFilteredData().Count() == 0 ? 0 : (int)Math.Ceiling((double)GetFilteredData().Count() / (_top == 0 ? 1 : _top)); private int _currentPage => (_top >= GetFilteredData().Count() || _top == 0) ? 1 : (_skip / _top) + 1; private bool _isRefreshing = false; private bool _isExporting = false; protected override async Task OnInitializedAsync() => await LoadData(); private async Task LoadData() { _isRefreshing = true; _selectedItem = null; StateHasChanged(); try { var response = await PayrollsService.GetPayrollProcessListAsync(); await Task.Delay(500); if (response != null && response.IsSuccess) { _payrolls = response.Value ?? new(); } } finally { _isRefreshing = false; StateHasChanged(); } } private IEnumerable GetFilteredData() { if (string.IsNullOrWhiteSpace(_searchString)) return _payrolls; return _payrolls.Where(x => (x.PeriodName?.Contains(_searchString, StringComparison.OrdinalIgnoreCase) ?? false) || (x.Status?.Contains(_searchString, StringComparison.OrdinalIgnoreCase) ?? false) || (x.AutoNumber?.Contains(_searchString, StringComparison.OrdinalIgnoreCase) ?? false) ); } private IEnumerable GetPagedData() => GetFilteredData().Skip(_skip).Take(_top); private Color GetStatusColor(string status) => status switch { "Paid" => Color.Success, "Draft" => Color.Warning, "Processed" => Color.Info, _ => Color.Default }; private void OnSearchClick() { _skip = 0; _selectedItem = null; StateHasChanged(); } private void HandleSearchKeyDown(KeyboardEventArgs e) { if (e.Key == "Enter") OnSearchClick(); } private void OnPageChanged(int page) { _skip = (page - 1) * _top; _selectedItem = null; StateHasChanged(); } private void OnPageSizeChanged(int size) { _top = size; _skip = 0; _selectedItem = null; StateHasChanged(); } private async Task InvokeView() { if (_selectedItem != null) await OnView.InvokeAsync(_selectedItem); } private async Task InvokeEdit() { if (_selectedItem == null) return; var res = await PayrollsService.GetPayrollProcessByIdAsync(_selectedItem.Id!); if (res?.Value != null) { var updateRequest = new UpdatePayrollProcessRequest { Id = res.Value.Id, Status = res.Value.Status, Description = res.Value.Description, PeriodName = res.Value.PeriodName }; await OnEdit.InvokeAsync(updateRequest); } } private async Task OnDelete() { if (_selectedItem == null) return; var dialog = await DialogService.ShowAsync<_DeleteConfirmation>("", new DialogParameters<_DeleteConfirmation> { { x => x.ContentText, _selectedItem.PeriodName } }); if (!(await dialog.Result).Canceled) { if (await PayrollsService.DeletePayrollProcessByIdAsync(_selectedItem.Id!)) { await LoadData(); Snackbar.Add("Payroll process deleted", Severity.Success); } } } private async Task ExportToExcel() { _isExporting = true; StateHasChanged(); try { await Task.Delay(1000); using (var workbook = new XLWorkbook()) { var worksheet = workbook.Worksheets.Add("PayrollHistory"); worksheet.Cell(1, 1).Value = "Period"; worksheet.Cell(1, 2).Value = "Total Employees"; worksheet.Cell(1, 3).Value = "Total Gross"; worksheet.Cell(1, 4).Value = "Total Deduction"; worksheet.Cell(1, 5).Value = "Net Pay (THP)"; worksheet.Cell(1, 6).Value = "Status"; var headerRange = worksheet.Range(1, 1, 1, 6); headerRange.Style.Font.Bold = true; headerRange.Style.Fill.BackgroundColor = XLColor.FromHtml("#0D47A1"); headerRange.Style.Font.FontColor = XLColor.White; int row = 2; foreach (var item in GetFilteredData()) { worksheet.Cell(row, 1).Value = item.PeriodName; worksheet.Cell(row, 2).Value = item.TotalEmployees; worksheet.Cell(row, 3).Value = item.TotalGross; worksheet.Cell(row, 4).Value = item.TotalDeduction; worksheet.Cell(row, 5).Value = item.TotalTakeHomePay; worksheet.Cell(row, 6).Value = item.Status; row++; } worksheet.Columns().AdjustToContents(); using (var stream = new MemoryStream()) { workbook.SaveAs(stream); var content = Convert.ToBase64String(stream.ToArray()); await JSRuntime.InvokeVoidAsync("downloadFile", "Payroll_History.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", content); Snackbar.Add("Excel exported successfully", Severity.Success); } } } catch (Exception ex) { Snackbar.Add($"Export failed: {ex.Message}", Severity.Error); } finally { _isExporting = false; StateHasChanged(); } } }