Files
blazor-saas-hrm/Features/Organization/Employee/Components/_EmployeeCreateForm.razor
T
2026-07-21 14:22:06 +07:00

303 lines
18 KiB
Plaintext

@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;">Add New Employee</MudText>
<MudText Typo="Typo.body2" Style="color: #64748b;">Create a comprehensive employee profile and organizational placement.</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())" 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())"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Dense="true"
Placeholder="Select Grade">
@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())" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Middle Name</label>
<MudTextField @bind-Value="_model.MiddleName" 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())" 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)"
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" Variant="Variant.Outlined" Margin="Margin.Dense" Placeholder="e.g. BCA" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Bank Account Name</label>
<MudTextField @bind-Value="_model.SalaryBankAccountName" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Bank Account Number</label>
<MudTextField @bind-Value="_model.SalaryBankAccountNumber" 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" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Date of Birth</label>
<MudDatePicker @bind-Date="_model.DateOfBirth" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Gender</label>
<MudSelect @bind-Value="_model.Gender" For="@(() => _model.Gender)" Validation="@(_validator.ValidateValue())" 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" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Religion</label>
<MudTextField @bind-Value="_model.Religion" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Identity Number (KTP)</label>
<MudTextField @bind-Value="_model.IdentityNumber" For="@(() => _model.IdentityNumber)" Validation="@(_validator.ValidateValue())" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Tax Number (NPWP)</label>
<MudTextField @bind-Value="_model.TaxNumber" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Blood Type</label>
<MudTextField @bind-Value="_model.BloodType" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="12">
<label class="field-label">Last Education</label>
<MudTextField @bind-Value="_model.LastEducation" 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())" 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())" 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())" 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" For="@(() => _model.JoinedDate)" Validation="@(_validator.ValidateValue())" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Resigned Date</label>
<MudDatePicker @bind-Date="_model.ResignedDate" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4">
<label class="field-label">Employee Status</label>
<MudTextField @bind-Value="_model.EmployeeStatus" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Employment Type</label>
<MudTextField @bind-Value="_model.EmploymentType" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="12">
<label class="field-label">Job Description</label>
<MudTextField @bind-Value="_model.JobDescription" 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())" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<label class="field-label">Phone</label>
<MudTextField @bind-Value="_model.Phone" For="@(() => _model.Phone)" Validation="@(_validator.ValidateValue())" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="12">
<label class="field-label">Street Address</label>
<MudTextField @bind-Value="_model.StreetAddress" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="4"><label class="field-label">City</label><MudTextField @bind-Value="_model.City" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Province</label><MudTextField @bind-Value="_model.StateProvince" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Zip Code</label><MudTextField @bind-Value="_model.ZipCode" 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" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Instagram</label><MudTextField @bind-Value="_model.SocialMediaInstagram" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">TikTok</label><MudTextField @bind-Value="_model.SocialMediaTikTok" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">Facebook</label><MudTextField @bind-Value="_model.SocialMediaFacebook" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="6"><label class="field-label">X (Twitter)</label><MudTextField @bind-Value="_model.SocialMediaX" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
</MudGrid>
<MudGrid Spacing="3">
<MudItem xs="12" Class="mt-2"><div class="section-header">OTHERS</div><MudDivider /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 1</label><MudTextField @bind-Value="_model.OtherInformation1" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 2</label><MudTextField @bind-Value="_model.OtherInformation2" Variant="Variant.Outlined" Margin="Margin.Dense" /></MudItem>
<MudItem xs="4"><label class="field-label">Other Info 3</label><MudTextField @bind-Value="_model.OtherInformation3" Variant="Variant.Outlined" Margin="Margin.Dense" /></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;">Cancel</MudButton>
<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">Creating...</MudText>
}
else
{
<MudText>Create Employee</MudText>
}
</MudButton>
</div>
</MudStack>
</MudForm>
}
</MudPaper>
@code {
[Parameter] public EventCallback OnCancel { get; set; }
[Parameter] public EventCallback OnSuccess { get; set; }
private MudForm? _form;
private CreateEmployeeRequest _model = new();
private CreateEmployeeValidator _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 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.CreateEmployeeAsync(_model);
await Task.Delay(500);
if (res?.IsSuccess == true)
{
Snackbar.Add("Employee Created Successfully", Severity.Success);
await OnSuccess.InvokeAsync();
}
} finally { _processing = false; }
}
}