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; 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 = { const EmployeeStatus = {
Active: "Active", Active: "Active",
Inactive: "Inactive", Inactive: "Inactive",
@@ -110,7 +121,10 @@
{ key: "temporary_address", title: "Temporary Address" }, { key: "temporary_address", title: "Temporary Address" },
{ key: "job_title", title: "Job Title" }, { key: "job_title", title: "Job Title" },
{ key: "emergency_contact_name", title: "Emergency Contact Name" }, { 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: "emergency_contact_phone", title: "Emergency Contact Phone" },
{ key: "bank_account", title: "Bank Account" }, { key: "bank_account", title: "Bank Account" },
{ key: "bank_account_name", title: "Employee Bank Account Name" }, { key: "bank_account_name", title: "Employee Bank Account Name" },
@@ -130,10 +144,20 @@
{ key: "child_2", title: "Child 2" }, { key: "child_2", title: "Child 2" },
{ key: "child_3", title: "Child 3" }, { key: "child_3", title: "Child 3" },
{ key: "document", title: "Document" }, { key: "document", title: "Document" },
{ key: "benefits", title: "Benefits" },
{ key: "created_at", title: "Created At" }, { key: "created_at", title: "Created At" },
{ key: "actions", title: "Actions" }, { 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 currentPage = offset + 1;
let rowsPerPage = 10; let rowsPerPage = 10;
let totalItems = 0; let totalItems = 0;
@@ -177,6 +201,21 @@
console.log("Total Items:", totalItems); 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); $: totalPages = Math.ceil(totalItems / rowsPerPage);
function goToPage(page: number) { function goToPage(page: number) {
@@ -185,7 +224,13 @@
currentPage = page; currentPage = page;
offset = (currentPage - 1) * rowsPerPage; offset = (currentPage - 1) * rowsPerPage;
fetchEmployee(search, "created_at", "desc", currentPage - 1, rowsPerPage); fetchEmployee(
search,
"created_at",
"desc",
currentPage - 1,
rowsPerPage,
);
} }
function pageRange( function pageRange(
@@ -220,7 +265,13 @@
currentPage = page; currentPage = page;
offset = (currentPage - 1) * rowsPerPage; offset = (currentPage - 1) * rowsPerPage;
fetchEmployee(search, "created_at", "desc", currentPage - 1, rowsPerPage); fetchEmployee(
search,
"created_at",
"desc",
currentPage - 1,
rowsPerPage,
);
} }
onMount(() => { onMount(() => {
@@ -231,6 +282,7 @@
$: currentPage = 1; $: currentPage = 1;
let showModal = false; let showModal = false;
let showModalBenefits = false;
let isEditing = false; let isEditing = false;
let currentEditingId: string | null = null; let currentEditingId: string | null = null;
let newEmployeeInsert: EmployeeItem = { let newEmployeeInsert: EmployeeItem = {
@@ -282,21 +334,68 @@
created_at: new Date(), 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 excludedKeys = ["id", "actions", "created_at", "no"];
const formColumns = columns.filter( const formColumns = columns.filter(
(col) => !excludedKeys.includes(col.key), (col) => !excludedKeys.includes(col.key),
); );
function openModal(newEmployeeItem?: EmployeeItem) { const excludedKeysBenefits = [
if (newEmployeeItem) { "id",
console.log("Editing Employee:", newEmployeeItem); "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; isEditing = true;
currentEditingId = newEmployeeItem.id; currentEditingId = newEmployeeItem.id;
console.log("Current Editing ID:", currentEditingId);
// Copy data to avoid direct mutation // Copy data to avoid direct mutation
newEmployeeInsert = { ...newEmployeeItem }; 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 { } else {
isEditing = false; isEditing = false;
currentEditingId = null; currentEditingId = null;
@@ -349,10 +448,42 @@
place_of_birth: "", place_of_birth: "",
created_at: new Date(), 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; 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) { async function saveEmployee(event: Event) {
event.preventDefault(); event.preventDefault();
@@ -372,9 +503,50 @@
alert("Error updating Employee: " + error.message); alert("Error updating Employee: " + error.message);
console.error("Error updating Employee:", error); console.error("Error updating Employee:", error);
return; 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 { } else {
newEmployeeInsert.id = uuidv4(); // Generate a new UUID for the ID newEmployeeInsert.id = uuidv4(); // Generate a new UUID for the ID
@@ -386,6 +558,21 @@
alert("Error creating New Employee: " + error.message); alert("Error creating New Employee: " + error.message);
console.error("Error creating New Employee:", error); console.error("Error creating New Employee:", error);
return; 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 { } else {
alert("New Employee created successfully!"); alert("New Employee created successfully!");
} }
@@ -467,9 +654,7 @@
placeholder="🔍 Search by item name..." 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" 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) => { on:input={(e) => {
search = ( search = (e.target as HTMLInputElement).value.toLowerCase();
e.target as HTMLInputElement
).value.toLowerCase();
if (search !== "" && search.length > 3) { if (search !== "" && search.length > 3) {
fetchEmployee(search, "created_at", "desc", 0, 10); fetchEmployee(search, "created_at", "desc", 0, 10);
@@ -575,7 +760,9 @@
<td class="px-4 py-2"> <td class="px-4 py-2">
{#if row[col.key as keyof EmployeeItem]} {#if row[col.key as keyof EmployeeItem]}
<a <a
href={row[col.key as keyof EmployeeItem] as string} href={row[
col.key as keyof EmployeeItem
] as string}
target="_blank" target="_blank"
class="text-blue-600 hover:underline" class="text-blue-600 hover:underline"
>View</a >View</a
@@ -597,7 +784,7 @@
) )
: "N/A"} : "N/A"}
</td> </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"> <td class="px-4 py-2">
{row[col.key as keyof EmployeeItem] {row[col.key as keyof EmployeeItem]
? new Date( ? new Date(
@@ -607,6 +794,15 @@
).toLocaleDateString() ).toLocaleDateString()
: "N/A"} : "N/A"}
</td> </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"} {:else if col.key === "created_at"}
<td class="px-4 py-2"> <td class="px-4 py-2">
{row[col.key as keyof EmployeeItem] {row[col.key as keyof EmployeeItem]
@@ -681,6 +877,7 @@
</button> </button>
</div> </div>
</div> </div>
</div> </div>
{#if showModal} {#if showModal}
@@ -756,7 +953,9 @@
col.key, col.key,
)}" )}"
> >
<option value="" disabled>Select marital status</option> <option value="" disabled
>Select marital status</option
>
{#each Object.entries(MaritalStatus) as [key, value]} {#each Object.entries(MaritalStatus) as [key, value]}
<option value={key}>{value}</option> <option value={key}>{value}</option>
{/each} {/each}
@@ -784,6 +983,38 @@
</div> </div>
{/each} {/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"> <div class="flex justify-end space-x-2">
<button <button
type="button" type="button"
@@ -803,3 +1034,59 @@
</div> </div>
</div> </div>
{/if} {/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}