perbaikan data HR dengan BENEFITS

This commit is contained in:
Aji Setiaji
2025-09-12 04:31:56 +07:00
parent 019f7499eb
commit 20facfc48e

View File

@@ -54,6 +54,17 @@
created_at?: Date;
};
type EmployeeBenefits = {
id: string;
employee_id: string;
basic_salary: number;
position_allowance: number;
meal_allowance: number;
transportation_allowance: number;
created_at: Date;
updated_at: Date;
};
const EmployeeStatus = {
Active: "Active",
Inactive: "Inactive",
@@ -110,7 +121,10 @@
{ key: "temporary_address", title: "Temporary Address" },
{ key: "job_title", title: "Job Title" },
{ key: "emergency_contact_name", title: "Emergency Contact Name" },
{ key: "emergency_contact_relation", title: "Emergency Contact Relation" },
{
key: "emergency_contact_relation",
title: "Emergency Contact Relation",
},
{ key: "emergency_contact_phone", title: "Emergency Contact Phone" },
{ key: "bank_account", title: "Bank Account" },
{ key: "bank_account_name", title: "Employee Bank Account Name" },
@@ -130,10 +144,20 @@
{ key: "child_2", title: "Child 2" },
{ key: "child_3", title: "Child 3" },
{ key: "document", title: "Document" },
{ key: "benefits", title: "Benefits" },
{ key: "created_at", title: "Created At" },
{ key: "actions", title: "Actions" },
];
const columnBenefits: columns[] = [
{ key: "basic_salary", title: "Basic Salary" },
{ key: "position_allowance", title: "Position Allowance" },
{ key: "meal_allowance", title: "Meal Allowance" },
{ key: "transportation_allowance", title: "Transportation Allowance" },
{ key: "created_at", title: "Created At" },
{ key: "updated_at", title: "Updated At" },
];
let currentPage = offset + 1;
let rowsPerPage = 10;
let totalItems = 0;
@@ -177,6 +201,21 @@
console.log("Total Items:", totalItems);
}
async function fetchEmployeeBenefits(employeeId: string) {
const { data, error } = await supabase
.from("vb_benefits")
.select("*")
.eq("employee_id", employeeId)
.single();
if (error) {
console.error("Error fetching Employee Benefits:", error);
return null;
}
return data as EmployeeBenefits;
}
$: totalPages = Math.ceil(totalItems / rowsPerPage);
function goToPage(page: number) {
@@ -185,7 +224,13 @@
currentPage = page;
offset = (currentPage - 1) * rowsPerPage;
fetchEmployee(search, "created_at", "desc", currentPage - 1, rowsPerPage);
fetchEmployee(
search,
"created_at",
"desc",
currentPage - 1,
rowsPerPage,
);
}
function pageRange(
@@ -220,7 +265,13 @@
currentPage = page;
offset = (currentPage - 1) * rowsPerPage;
fetchEmployee(search, "created_at", "desc", currentPage - 1, rowsPerPage);
fetchEmployee(
search,
"created_at",
"desc",
currentPage - 1,
rowsPerPage,
);
}
onMount(() => {
@@ -231,6 +282,7 @@
$: currentPage = 1;
let showModal = false;
let showModalBenefits = false;
let isEditing = false;
let currentEditingId: string | null = null;
let newEmployeeInsert: EmployeeItem = {
@@ -282,21 +334,68 @@
created_at: new Date(),
};
let newEmployeeBenefits: EmployeeBenefits = {
id: "",
employee_id: "",
basic_salary: 0,
position_allowance: 0,
meal_allowance: 0,
transportation_allowance: 0,
created_at: new Date(),
updated_at: new Date(),
};
let employeeBenefits: EmployeeBenefits = {
id: "",
employee_id: "",
basic_salary: 0,
position_allowance: 0,
meal_allowance: 0,
transportation_allowance: 0,
created_at: new Date(),
updated_at: new Date(),
}
const excludedKeys = ["id", "actions", "created_at", "no"];
const formColumns = columns.filter(
(col) => !excludedKeys.includes(col.key),
);
function openModal(newEmployeeItem?: EmployeeItem) {
if (newEmployeeItem) {
console.log("Editing Employee:", newEmployeeItem);
const excludedKeysBenefits = [
"id",
"employee_id",
"created_at",
"updated_at",
];
const formColumnsBenefits = columnBenefits.filter(
(col) => !excludedKeysBenefits.includes(col.key),
);
function openModal(newEmployeeItem?: EmployeeItem, emp?: number) {
if (newEmployeeItem) {
isEditing = true;
currentEditingId = newEmployeeItem.id;
console.log("Current Editing ID:", currentEditingId);
// Copy data to avoid direct mutation
newEmployeeInsert = { ...newEmployeeItem };
// Fetch and populate benefits data
fetchEmployeeBenefits(newEmployeeItem.id).then((benefits) => {
if (benefits) {
newEmployeeBenefits = benefits;
} else {
newEmployeeBenefits = {
id: "",
employee_id: newEmployeeItem.id,
basic_salary: newEmployeeItem.salary || 0,
position_allowance: 0,
meal_allowance: 0,
transportation_allowance: 0,
created_at: new Date(),
updated_at: new Date(),
};
}
});
} else {
isEditing = false;
currentEditingId = null;
@@ -349,10 +448,42 @@
place_of_birth: "",
created_at: new Date(),
};
newEmployeeBenefits = {
id: "",
employee_id: "",
basic_salary: 0,
position_allowance: 0,
meal_allowance: 0,
transportation_allowance: 0,
created_at: new Date(),
updated_at: new Date(),
};
}
showModal = true;
}
async function openModalBenefits(employee: EmployeeItem) {
const benefits = await fetchEmployeeBenefits(employee.id);
if (benefits) {
employeeBenefits = benefits;
} else {
employeeBenefits = {
id: "",
employee_id: employee.id,
basic_salary: employee.salary || 0,
position_allowance: 0,
meal_allowance: 0,
transportation_allowance: 0,
created_at: new Date(),
updated_at: new Date(),
};
}
showModalBenefits = true;
}
async function saveEmployee(event: Event) {
event.preventDefault();
@@ -372,9 +503,50 @@
alert("Error updating Employee: " + error.message);
console.error("Error updating Employee:", error);
return;
} else {
alert("Employee updated successfully!");
}
// cek apakah data vb_benefits sudah ada
const { data: existingBenefits, error: checkError } = await supabase
.from("vb_benefits")
.select("id")
.eq("employee_id", currentEditingId)
.maybeSingle();
if (checkError) {
console.error("Error checking vb_benefits:", checkError);
}
if (existingBenefits) {
// update jika ada
const { error: benefitsError } = await supabase
.from("vb_benefits")
.update(newEmployeeBenefits)
.eq("employee_id", currentEditingId);
if (benefitsError) {
console.error("Error updating vb_benefits:", benefitsError);
}
} else {
const idBenefit = uuidv4();
newEmployeeBenefits.id = idBenefit;
newEmployeeBenefits.employee_id = currentEditingId;
// insert jika belum ada
const { error: benefitsInsertError } = await supabase
.from("vb_benefits")
.insert({
...newEmployeeBenefits,
});
if (benefitsInsertError) {
console.error(
"Error inserting vb_benefits:",
benefitsInsertError,
);
}
}
alert("Employee Updated successfully!");
} else {
newEmployeeInsert.id = uuidv4(); // Generate a new UUID for the ID
@@ -386,6 +558,21 @@
alert("Error creating New Employee: " + error.message);
console.error("Error creating New Employee:", error);
return;
}
newEmployeeBenefits.id = uuidv4();
newEmployeeBenefits.employee_id = newEmployeeInsert.id;
const { error: benefitsError } = await supabase
.from("vb_employee_benefits")
.insert(newEmployeeBenefits);
if (benefitsError) {
console.error(
"Error creating Employee Benefits:",
benefitsError,
);
return;
} else {
alert("New Employee created successfully!");
}
@@ -467,9 +654,7 @@
placeholder="🔍 Search by item name..."
class="border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:outline-none px-4 py-2 rounded-xl text-sm w-64 transition"
on:input={(e) => {
search = (
e.target as HTMLInputElement
).value.toLowerCase();
search = (e.target as HTMLInputElement).value.toLowerCase();
if (search !== "" && search.length > 3) {
fetchEmployee(search, "created_at", "desc", 0, 10);
@@ -575,7 +760,9 @@
<td class="px-4 py-2">
{#if row[col.key as keyof EmployeeItem]}
<a
href={row[col.key as keyof EmployeeItem] as string}
href={row[
col.key as keyof EmployeeItem
] as string}
target="_blank"
class="text-blue-600 hover:underline"
>View</a
@@ -597,7 +784,7 @@
)
: "N/A"}
</td>
{:else if col.key === "contract_start" || col.key === "end_of_contract" || col.key === "date_of_birth"}
{:else if col.key === "contract_start" || col.key === "end_of_contract" || col.key === "date_of_birth" || col.key === "hire_date" || col.key === "leaving_date"}
<td class="px-4 py-2">
{row[col.key as keyof EmployeeItem]
? new Date(
@@ -607,6 +794,15 @@
).toLocaleDateString()
: "N/A"}
</td>
{:else if col.key === "benefits"}
<td class="px-4 py-2">
<button
class="inline-flex items-center gap-1 rounded bg-green-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-green-700"
on:click={() => openModalBenefits(row)}
>
💼 View Benefits
</button>
</td>
{:else if col.key === "created_at"}
<td class="px-4 py-2">
{row[col.key as keyof EmployeeItem]
@@ -681,6 +877,7 @@
</button>
</div>
</div>
</div>
{#if showModal}
@@ -756,7 +953,9 @@
col.key,
)}"
>
<option value="" disabled>Select marital status</option>
<option value="" disabled
>Select marital status</option
>
{#each Object.entries(MaritalStatus) as [key, value]}
<option value={key}>{value}</option>
{/each}
@@ -784,6 +983,38 @@
</div>
{/each}
<h3 class="text-lg font-semibold mb-2">Benefits</h3>
{#each formColumnsBenefits as col}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
{#if col.key === "basic_salary"}
<input
type="number"
id={col.key}
bind:value={newEmployeeBenefits[col.key]}
class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
{:else}
<input
type="number"
id={col.key}
bind:value={
newEmployeeBenefits[
col.key as keyof EmployeeBenefits
]
}
class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
{/if}
</div>
{/each}
<div class="flex justify-end space-x-2">
<button
type="button"
@@ -803,3 +1034,59 @@
</div>
</div>
{/if}
{#if showModalBenefits}
<div
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
>
<div
class="bg-white rounded-lg shadow-lg p-6 w-full max-w-md max-h-[90vh] overflow-y-auto"
>
<h2 class="text-xl font-semibold mb-4">Employee Benefits</h2>
<div class="space-y-4">
{#each formColumnsBenefits as col}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
{#if col.key === "basic_salary"}
<input
type="number"
id={col.key}
bind:value={employeeBenefits[col.key]}
class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
readonly
/>
{:else}
<input
type="number"
id={col.key}
bind:value={
employeeBenefits[
col.key as keyof EmployeeBenefits
]
}
class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
readonly
/>
{/if}
</div>
{/each}
<div class="flex justify-end">
<button
type="button"
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-xl hover:bg-gray-300"
on:click={() => (showModalBenefits = false)}
>
Close
</button>
</div>
</div>
</div>
</div>
{/if}