perbaikan data HR dengan BENEFITS
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user