Files
blazor-hrm/Features/Organization/Employee/Components/_EmployeeUpdateForm.razor
T
2026-07-21 14:08:10 +07:00

285 lines
20 KiB
Plaintext

@using Indotalent.ConfigBackEnd.Extensions
@using Indotalent.Features.Organization.Employee
@using Indotalent.Features.Organization.Employee.Cqrs
@using Indotalent.Shared.Utils
@using MudBlazor
@implements IDisposable
@inject EmployeeService EmployeeService
@inject ISnackbar Snackbar
<style>
.field-label { font-size: 13px; font-weight: 700; color: #424242; margin-bottom: 4px; display: block; }
.section-header { font-weight: 800; color: #0D47A1; letter-spacing: 0.5px; margin-bottom: 8px; }
</style>
<MudPaper Elevation="0" Square="true" Class="pa-6 mb-3 d-flex align-center justify-space-between" Style="border: 1px solid #DCEBFA;">
<div class="d-flex align-center gap-4">
<MudIconButton Icon="@Icons.Material.Filled.ArrowBack" OnClick="() => OnCancel.InvokeAsync()" />
<div>
<MudText Typo="Typo.h5" Style="font-weight: 900; color: #1a1a1a;">@(ReadOnly ? "Employee Profile" : "Edit Employee")</MudText>
<MudText Typo="Typo.body2" Style="color: #64748b;">Managing records for @_model.FirstName @_model.LastName</MudText>
</div>
</div>
</MudPaper>
<MudPaper Elevation="0" Outlined="true" Class="pa-8" Style="border-radius: 0px; border: 1px solid #DCEBFA;">
@if (_isLoadingData)
{
<div class="d-flex flex-column align-center pa-10">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" />
<MudText Class="mt-4">Synchronizing Records...</MudText>
</div>
}
else
{
<MudForm @ref="_form" Model="_model">
<MudStack Spacing="6">
<MudGrid Spacing="3">
<MudItem xs="12"><div class="section-header">BASIC IDENTITY</div><MudDivider /></MudItem>
<MudItem xs="6">
<label class="field-label">Employee Code</label>
<MudTextField @bind-Value="_model.Code" For="@(() => _model.Code)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Salary Grade</label>
<MudSelect @bind-Value="_model.GradeId"
For="@(() => _model.GradeId)"
Validation="@(_validator.ValidateValue())"
ReadOnly="ReadOnly"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Dense="true">
@foreach (var item in _lookupData.Grades)
{
<MudSelectItem Value="@item.Id">
<div class="d-flex justify-space-between w-100 gap-4">
<MudText Typo="Typo.body2"><b>@item.Name</b> (@item.Code)</MudText>
<MudText Typo="Typo.caption" Style="color: #64748b;">
Range: @item.SalaryFrom.ToString("N0") - @item.SalaryTo.ToString("N0")
</MudText>
</div>
</MudSelectItem>
}
</MudSelect>
</MudItem>
<MudItem xs="4">
<label class="field-label">First Name</label>
<MudTextField @bind-Value="_model.FirstName" For="@(() => _model.FirstName)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Middle Name</label>
<MudTextField @bind-Value="_model.MiddleName" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Last Name</label>
<MudTextField @bind-Value="_model.LastName" For="@(() => _model.LastName)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">PAYROLL & BANKING</div><MudDivider /></MudItem>
<MudItem xs="6">
<label class="field-label">Basic Salary (Monthly)</label>
<MudNumericField @bind-Value="_model.BasicSalary"
For="@(() => _model.BasicSalary)"
Validation="@(_validator.ValidateValue())"
ReadOnly="ReadOnly"
HideSpinButtons="false"
Step="1"
Min="0"
Format="N0"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Payments"
AdornmentColor="Color.Success"
Variant="Variant.Outlined"
Margin="Margin.Dense" />
@if (!string.IsNullOrEmpty(_model.GradeId))
{
var selectedGrade = _lookupData.Grades.FirstOrDefault(x => x.Id == _model.GradeId);
if (selectedGrade != null)
{
<MudText Typo="Typo.caption" Color="Color.Info" Class="mt-1">
Allowed range: <b>@selectedGrade.SalaryFrom.ToString("N0")</b> - <b>@selectedGrade.SalaryTo.ToString("N0")</b>
</MudText>
}
}
</MudItem>
<MudItem xs="6">
<label class="field-label">Salary Bank Name</label>
<MudTextField @bind-Value="_model.SalaryBankName" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Bank Account Name</label>
<MudTextField @bind-Value="_model.SalaryBankAccountName" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Bank Account Number</label>
<MudTextField @bind-Value="_model.SalaryBankAccountNumber" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">PERSONAL DETAILS</div><MudDivider /></MudItem>
<MudItem xs="6"><label class="field-label">Place of Birth</label><MudTextField @bind-Value="_model.PlaceOfBirth" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">Date of Birth</label><MudDatePicker @bind-Date="_model.DateOfBirth" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4">
<label class="field-label">Gender</label>
<MudSelect @bind-Value="_model.Gender" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" Dense="true">
<MudSelectItem Value="@("Male")">Male</MudSelectItem>
<MudSelectItem Value="@("Female")">Female</MudSelectItem>
</MudSelect>
</MudItem>
<MudItem xs="4"><label class="field-label">Marital Status</label><MudTextField @bind-Value="_model.MaritalStatus" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Religion</label><MudTextField @bind-Value="_model.Religion" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Identity Number (KTP)</label><MudTextField @bind-Value="_model.IdentityNumber" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Tax Number (NPWP)</label><MudTextField @bind-Value="_model.TaxNumber" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Blood Type</label><MudTextField @bind-Value="_model.BloodType" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="12"><label class="field-label">Last Education</label><MudTextField @bind-Value="_model.LastEducation" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">ORGANIZATIONAL</div><MudDivider /></MudItem>
<MudItem xs="4">
<label class="field-label">Branch</label>
<MudSelect @bind-Value="_model.BranchId" For="@(() => _model.BranchId)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" Dense="true">
@foreach (var item in _lookupData.Branches) { <MudSelectItem Value="@item.Id">@item.Name</MudSelectItem> }
</MudSelect>
</MudItem>
<MudItem xs="4">
<label class="field-label">Department</label>
<MudSelect @bind-Value="_model.DepartmentId" For="@(() => _model.DepartmentId)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" Dense="true">
@foreach (var item in _lookupData.Departments) { <MudSelectItem Value="@item.Id">@item.Name</MudSelectItem> }
</MudSelect>
</MudItem>
<MudItem xs="4">
<label class="field-label">Designation</label>
<MudSelect @bind-Value="_model.DesignationId" For="@(() => _model.DesignationId)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" Dense="true">
@foreach (var item in _lookupData.Designations) { <MudSelectItem Value="@item.Id">@item.Name</MudSelectItem> }
</MudSelect>
</MudItem>
<MudItem xs="4"><label class="field-label">Joined Date</label><MudDatePicker @bind-Date="_model.JoinedDate" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Resigned Date</label><MudDatePicker @bind-Date="_model.ResignedDate" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Employee Status</label><MudTextField @bind-Value="_model.EmployeeStatus" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">Employment Type</label><MudTextField @bind-Value="_model.EmploymentType" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="12"><label class="field-label">Job Description</label><MudTextField @bind-Value="_model.JobDescription" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" Lines="2" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">ADDRESS & CONTACT</div><MudDivider /></MudItem>
<MudItem xs="6">
<label class="field-label">Email</label>
<MudTextField @bind-Value="_model.Email" For="@(() => _model.Email)" Validation="@(_validator.ValidateValue())" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6"><label class="field-label">Phone</label><MudTextField @bind-Value="_model.Phone" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="12"><label class="field-label">Street Address</label><MudTextField @bind-Value="_model.StreetAddress" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">City</label><MudTextField @bind-Value="_model.City" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Province</label><MudTextField @bind-Value="_model.StateProvince" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Zip Code</label><MudTextField @bind-Value="_model.ZipCode" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">SOCIAL MEDIA</div><MudDivider /></MudItem>
<MudItem xs="4"><label class="field-label">LinkedIn</label><MudTextField @bind-Value="_model.SocialMediaLinkedIn" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Instagram</label><MudTextField @bind-Value="_model.SocialMediaInstagram" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">TikTok</label><MudTextField @bind-Value="_model.SocialMediaTikTok" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">Facebook</label><MudTextField @bind-Value="_model.SocialMediaFacebook" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">X (Twitter)</label><MudTextField @bind-Value="_model.SocialMediaX" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">ADDITIONAL</div><MudDivider /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 1</label><MudTextField @bind-Value="_model.OtherInformation1" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 2</label><MudTextField @bind-Value="_model.OtherInformation2" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 3</label><MudTextField @bind-Value="_model.OtherInformation3" ReadOnly="ReadOnly" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-4">
<MudText Typo="Typo.button" Color="Color.Primary" Style="font-weight: 800;">Audit History</MudText>
<MudDivider Class="mt-2 mb-4" />
</MudItem>
<MudItem xs="12" sm="6">
<MudText Typo="Typo.subtitle2">Created At</MudText>
<MudText Typo="Typo.body2" Style="color: #1a1a1a; font-weight: 600;">@DateTimeExtensions.ToString(_model.CreatedAt)</MudText>
</MudItem>
<MudItem xs="12" sm="6">
<MudText Typo="Typo.subtitle2">Created By</MudText>
<MudText Typo="Typo.body2" Style="color: #1a1a1a; font-weight: 600;">@(!string.IsNullOrEmpty(_model.CreatedBy) ? _model.CreatedBy : "System")</MudText>
</MudItem>
<MudItem xs="12" sm="6">
<MudText Typo="Typo.subtitle2">Last Updated At</MudText>
<MudText Typo="Typo.body2" Style="color: #1a1a1a; font-weight: 600;">@DateTimeExtensions.ToString(_model.UpdatedAt)</MudText>
</MudItem>
<MudItem xs="12" sm="6">
<MudText Typo="Typo.subtitle2">Last Updated By</MudText>
<MudText Typo="Typo.body2" Style="color: #1a1a1a; font-weight: 600;">@(!string.IsNullOrEmpty(_model.UpdatedBy) ? _model.UpdatedBy : "System")</MudText>
</MudItem>
</MudGrid>
<div class="d-flex justify-end gap-2 mt-10">
<MudButton OnClick="() => OnCancel.InvokeAsync()" Variant="Variant.Outlined" Style="border: 1px solid #e0e0e0; border-radius: 4px; text-transform: none; font-weight: 700;">@(ReadOnly ? "Back to List" : "Cancel")</MudButton>
@if (!ReadOnly)
{
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="Submit" Disabled="_processing" Style="border-radius: 4px; text-transform: none; font-weight: 700;">
@if (_processing) { <MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" /> <MudText Class="ms-2">Updating...</MudText> }
else { <MudText>Save Changes</MudText> }
</MudButton>
}
</div>
</MudStack>
</MudForm>
}
</MudPaper>
@code {
[Parameter] public UpdateEmployeeRequest Data { get; set; } = new();
[Parameter] public bool ReadOnly { get; set; } = false;
[Parameter] public EventCallback OnCancel { get; set; }
[Parameter] public EventCallback OnSuccess { get; set; }
private MudForm? _form;
private UpdateEmployeeRequest _model = new();
private UpdateEmployeeValidator _validator = new();
private LookupResponse _lookupData = new();
private bool _processing = false;
private bool _isLoadingData = true;
private bool _isDisposed = false;
public void Dispose() => _isDisposed = true;
protected override void OnInitialized() { _model = Data; }
protected override async Task OnInitializedAsync()
{
try {
_isLoadingData = true;
var response = await EmployeeService.GetLookupAsync();
if (response?.IsSuccess == true)
{
_lookupData = response.Value ?? new();
}
} finally {
if (!_isDisposed) { _isLoadingData = false; StateHasChanged(); }
}
}
private async Task Submit()
{
await _form!.Validate();
if (!_form.IsValid) return;
_processing = true;
try {
var res = await EmployeeService.UpdateEmployeeAsync(_model);
await Task.Delay(500);
if (res?.IsSuccess == true)
{
Snackbar.Add("Updated successfully", Severity.Success);
await OnSuccess.InvokeAsync();
}
} finally { _processing = false; }
}
}