timesheet backend
This commit is contained in:
@@ -73,8 +73,10 @@
|
|||||||
{ label: "Cleaning", value: "Cleaning" },
|
{ label: "Cleaning", value: "Cleaning" },
|
||||||
{ label: "Gardening/Pool", value: "Gardening/Pool" },
|
{ label: "Gardening/Pool", value: "Gardening/Pool" },
|
||||||
{ label: "Maintenance", value: "Maintenance" },
|
{ label: "Maintenance", value: "Maintenance" },
|
||||||
{ label: "Security", value: "Security" },
|
{ label: "Supervision", value: "Supervision" },
|
||||||
{ label: "Other", value: "Other" },
|
{ label: "Guest Service", value: "Guest Service" },
|
||||||
|
{ label: "Administration", value: "Administration" },
|
||||||
|
{ label: "Non Billable", value: "Non Billable" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const typeOfWork = [
|
const typeOfWork = [
|
||||||
@@ -83,13 +85,6 @@
|
|||||||
{ label: "Irregular", value: "Irregular" },
|
{ label: "Irregular", value: "Irregular" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const reportedBy = [
|
|
||||||
{ label: "Admin", value: "Admin" },
|
|
||||||
{ label: "Staff", value: "Staff" },
|
|
||||||
{ label: "Manager", value: "Manager" },
|
|
||||||
{ label: "Guest", value: "Guest" },
|
|
||||||
];
|
|
||||||
|
|
||||||
let currentUserId: string | null = null;
|
let currentUserId: string | null = null;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@@ -110,7 +105,8 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from("vb_villas")
|
.from("vb_villas")
|
||||||
.select("id, villa_name");
|
.select("id, villa_name")
|
||||||
|
.eq("villa_status", "Active");
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error fetching villas:", error);
|
console.error("Error fetching villas:", error);
|
||||||
@@ -127,8 +123,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: columns[] = [
|
const columns: columns[] = [
|
||||||
{ key: "name", title: "Name" },
|
{ key: "name", title: "Work Description" },
|
||||||
{ key: "staff_id", title: "Staff Report" },
|
{ key: "staff_id", title: "Staff Name" },
|
||||||
{ key: "date_in", title: "Date In" },
|
{ key: "date_in", title: "Date In" },
|
||||||
{ key: "date_out", title: "Date Out" },
|
{ key: "date_out", title: "Date Out" },
|
||||||
{ key: "type_of_work", title: "Type of Work" },
|
{ key: "type_of_work", title: "Type of Work" },
|
||||||
@@ -136,13 +132,23 @@
|
|||||||
{ key: "approval", title: "Approval" },
|
{ key: "approval", title: "Approval" },
|
||||||
{ key: "villa_name", title: "Villa Name" },
|
{ key: "villa_name", title: "Villa Name" },
|
||||||
{ key: "approved_by", title: "Approved By" },
|
{ key: "approved_by", title: "Approved By" },
|
||||||
{ key: "approved_date", title: "Approved Date" },
|
{ key: "approved_date", title: "Approved/Rejected Date" },
|
||||||
{ key: "total_hours_work", title: "Total Hours Work" },
|
{ key: "total_hours_work", title: "Total Hours Work" },
|
||||||
{ key: "remarks", title: "Remarks" },
|
{ key: "remarks", title: "Remarks" },
|
||||||
{ key: "vacant", title: "Vacant" },
|
|
||||||
{ key: "created_at", title: "Created At" },
|
{ key: "created_at", title: "Created At" },
|
||||||
{ key: "actions", title: "Actions" },
|
{ key: "actions", title: "Actions" },
|
||||||
];
|
];
|
||||||
|
function calculateTotalHours() {
|
||||||
|
if (form.datetime_in && form.datetime_out) {
|
||||||
|
const start = new Date(form.datetime_in);
|
||||||
|
const end = new Date(form.datetime_out);
|
||||||
|
const diffInMs = end.getTime() - start.getTime();
|
||||||
|
const hours = diffInMs / (1000 * 60 * 60);
|
||||||
|
form.total_work_hour = Math.max(Number(hours.toFixed(2)), 0);
|
||||||
|
} else {
|
||||||
|
form.total_work_hour = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchTimeSheets(
|
async function fetchTimeSheets(
|
||||||
filter: string | null = null,
|
filter: string | null = null,
|
||||||
@@ -202,7 +208,8 @@
|
|||||||
let reportedBy: { label: string; value: string }[] = [];
|
let reportedBy: { label: string; value: string }[] = [];
|
||||||
const { data: staffData, error: staffError } = await supabase
|
const { data: staffData, error: staffError } = await supabase
|
||||||
.from("vb_employee")
|
.from("vb_employee")
|
||||||
.select("id, employee_name");
|
.select("id, employee_name")
|
||||||
|
.eq("employee_status", "Active");
|
||||||
if (staffError) {
|
if (staffError) {
|
||||||
console.error("Error fetching staff:", staffError);
|
console.error("Error fetching staff:", staffError);
|
||||||
} else if (staffData) {
|
} else if (staffData) {
|
||||||
@@ -262,6 +269,43 @@
|
|||||||
if (page >= 1 && page <= totalPages) currentPage = page;
|
if (page >= 1 && page <= totalPages) currentPage = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
// get current user
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
currentUserId = user?.id ?? null;
|
||||||
|
|
||||||
|
// fetch employees
|
||||||
|
const { data: empData, error: empErr } = await supabase
|
||||||
|
.from("vb_employee")
|
||||||
|
.select("id, employee_name")
|
||||||
|
.eq("employee_status", "Active");
|
||||||
|
|
||||||
|
if (!empErr && empData) {
|
||||||
|
employees = empData.map((e) => ({
|
||||||
|
id: e.id,
|
||||||
|
name: e.employee_name,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load employees", empErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch villas
|
||||||
|
const { data: villaData, error: villaErr } = await supabase
|
||||||
|
.from("vb_villas")
|
||||||
|
.select("id, villa_name")
|
||||||
|
.eq("villa_status", "Active");
|
||||||
|
|
||||||
|
if (!villaErr && villaData) {
|
||||||
|
villas = villaData;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load villas", villaErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchTimeSheets(); // this still calls your table loader
|
||||||
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
fetchTimeSheets();
|
fetchTimeSheets();
|
||||||
});
|
});
|
||||||
@@ -280,29 +324,79 @@
|
|||||||
|
|
||||||
function openModal(issue?: Record<string, any>) {
|
function openModal(issue?: Record<string, any>) {
|
||||||
if (issue) {
|
if (issue) {
|
||||||
|
// Edit mode
|
||||||
isEditing = true;
|
isEditing = true;
|
||||||
currentEditingId = issue.id;
|
currentEditingId = issue.id;
|
||||||
|
|
||||||
newIssue = {
|
form = {
|
||||||
work_description: issue.name,
|
entered_by: employees.find(e => e.name === issue.staff_id)?.id || "",
|
||||||
entered_by: issue.entered_by || "", // you may need to store the UUID if not already
|
work_description: issue.name,
|
||||||
type_of_work: issue.type_of_work,
|
type_of_work: issue.type_of_work,
|
||||||
category_of_work: issue.category_of_work,
|
category_of_work: issue.category_of_work,
|
||||||
villa_id: dataVilla.find(v => v.villa_name === issue.villa_name)?.id || "",
|
villa_id: villas.find(v => v.villa_name === issue.villa_name)?.id || "",
|
||||||
date_in: issue.date_in?.toISOString().slice(0, 16), // for datetime-local
|
datetime_in: issue.date_in?.toISOString().slice(0, 16),
|
||||||
date_out: issue.date_out?.toISOString().slice(0, 16),
|
datetime_out: issue.date_out?.toISOString().slice(0, 16),
|
||||||
remarks: issue.remarks,
|
total_work_hour: 0,
|
||||||
total_work_hour: issue.total_hours_work,
|
remarks: issue.remarks,
|
||||||
|
approval: null // leave null or bring in if editing allowed
|
||||||
};
|
};
|
||||||
|
calculateTotalHours();
|
||||||
} else {
|
} else {
|
||||||
|
// Add mode
|
||||||
isEditing = false;
|
isEditing = false;
|
||||||
currentEditingId = null;
|
currentEditingId = null;
|
||||||
newIssue = {};
|
form = {
|
||||||
|
entered_by: "",
|
||||||
|
work_description: "",
|
||||||
|
type_of_work: "Running",
|
||||||
|
category_of_work: "Cleaning",
|
||||||
|
villa_id: "",
|
||||||
|
datetime_in: "",
|
||||||
|
datetime_out: "",
|
||||||
|
total_work_hour: 0,
|
||||||
|
remarks: "",
|
||||||
|
approval: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
showModal = true;
|
showModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Employee = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
let employees: Employee[] = [];
|
||||||
|
let villas: Villa[] = [];
|
||||||
|
const typeOfWorkOptions: TimesheetForm["type_of_work"][] = [
|
||||||
|
"Running",
|
||||||
|
"Periodic",
|
||||||
|
"Irregular",
|
||||||
|
];
|
||||||
|
const categoryOptions: TimesheetForm["category_of_work"][] = [
|
||||||
|
"Cleaning",
|
||||||
|
"Gardening/Pool",
|
||||||
|
"Maintenance",
|
||||||
|
"Supervision",
|
||||||
|
"Guest Service",
|
||||||
|
"Administration",
|
||||||
|
"Non Billable",
|
||||||
|
];
|
||||||
|
let form: TimesheetForm = {
|
||||||
|
entered_by: "",
|
||||||
|
work_description: "",
|
||||||
|
type_of_work: "Running",
|
||||||
|
category_of_work: "Cleaning",
|
||||||
|
villa_id: "",
|
||||||
|
datetime_in: "",
|
||||||
|
datetime_out: "",
|
||||||
|
total_work_hour: 0,
|
||||||
|
remarks: "",
|
||||||
|
approval: null, // Default null
|
||||||
|
};
|
||||||
|
|
||||||
async function saveIssue(event: Event) {
|
async function saveIssue(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -344,14 +438,12 @@
|
|||||||
villa_id: formData.get("villa_id") as string,
|
villa_id: formData.get("villa_id") as string,
|
||||||
datetime_in: formData.get("date_in") as string,
|
datetime_in: formData.get("date_in") as string,
|
||||||
datetime_out: formData.get("date_out") as string,
|
datetime_out: formData.get("date_out") as string,
|
||||||
total_work_hour: Math.abs(
|
total_work_hour: newIssue.total_work_hour ?? 0,
|
||||||
new Date(formData.get("date_out") as string).getTime() -
|
|
||||||
new Date(formData.get("date_in") as string).getTime(),
|
|
||||||
),
|
|
||||||
remarks: formData.get("remarks") as string,
|
remarks: formData.get("remarks") as string,
|
||||||
approval: null, // Default null
|
approval: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const { error } = await supabase
|
const { error } = await supabase
|
||||||
.from("vb_timesheet")
|
.from("vb_timesheet")
|
||||||
.insert([TimesheetsInsert]);
|
.insert([TimesheetsInsert]);
|
||||||
@@ -428,6 +520,63 @@
|
|||||||
await fetchTimeSheets();
|
await fetchTimeSheets();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function submitForm() {
|
||||||
|
calculateTotalHours();
|
||||||
|
|
||||||
|
if (!form.entered_by || !form.villa_id) {
|
||||||
|
alert("Please select an employee and villa.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
if (isEditing && currentEditingId) {
|
||||||
|
const { error: updateError } = await supabase
|
||||||
|
.from("vb_timesheet")
|
||||||
|
.update({
|
||||||
|
entered_by: form.entered_by,
|
||||||
|
work_description: form.work_description,
|
||||||
|
type_of_work: form.type_of_work,
|
||||||
|
category_of_work: form.category_of_work,
|
||||||
|
villa_id: form.villa_id,
|
||||||
|
datetime_in: form.datetime_in,
|
||||||
|
datetime_out: form.datetime_out,
|
||||||
|
total_work_hour: form.total_work_hour,
|
||||||
|
remarks: form.remarks,
|
||||||
|
approval: form.approval,
|
||||||
|
})
|
||||||
|
.eq("id", currentEditingId);
|
||||||
|
|
||||||
|
error = updateError;
|
||||||
|
} else {
|
||||||
|
const { error: insertError } = await supabase
|
||||||
|
.from("vb_timesheet")
|
||||||
|
.insert([form]);
|
||||||
|
|
||||||
|
error = insertError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
alert("Failed to save timesheet: " + error.message);
|
||||||
|
} else {
|
||||||
|
alert("Timesheet saved successfully!");
|
||||||
|
form = {
|
||||||
|
entered_by: "",
|
||||||
|
work_description: "",
|
||||||
|
type_of_work: "Running",
|
||||||
|
category_of_work: "Cleaning",
|
||||||
|
villa_id: "",
|
||||||
|
datetime_in: "",
|
||||||
|
datetime_out: "",
|
||||||
|
total_work_hour: 0,
|
||||||
|
remarks: "",
|
||||||
|
approval: null,
|
||||||
|
};
|
||||||
|
await fetchTimeSheets();
|
||||||
|
showModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -550,14 +699,12 @@
|
|||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key] || "Not Approved"}
|
{row[col.key] || "Not Approved"}
|
||||||
</td>
|
</td>
|
||||||
{:else if col.key === "approved_date"}
|
{:else if col.key === "approved_date"}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key] !== undefined
|
{(row[col.key] && !isNaN(Date.parse(row[col.key])))
|
||||||
? new Date(
|
? new Date(row[col.key]).toLocaleDateString()
|
||||||
row[col.key]!,
|
: "N/A"}
|
||||||
).toLocaleDateString()
|
</td>
|
||||||
: "N/A"}
|
|
||||||
</td>
|
|
||||||
{:else if col.key === "total_hours_work"}
|
{:else if col.key === "total_hours_work"}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key].toFixed(2)} hours
|
{row[col.key].toFixed(2)} hours
|
||||||
@@ -598,6 +745,20 @@
|
|||||||
🗑️ Delete
|
🗑️ Delete
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
{:else if col.key === "date_in" || col.key === "date_out"}
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
{row[col.key]
|
||||||
|
? new Date(row[col.key]).toLocaleString("en-GB", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
: "N/A"}
|
||||||
|
</td>
|
||||||
|
|
||||||
{:else}
|
{:else}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key as keyof TimesheetDisplay]}
|
{row[col.key as keyof TimesheetDisplay]}
|
||||||
@@ -650,190 +811,138 @@
|
|||||||
|
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
{#if showModal}
|
{#if showModal}
|
||||||
<div
|
<div class="fixed inset-0 bg-black bg-opacity-50 z-50 overflow-y-auto py-10 px-4 flex justify-center items-start">
|
||||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
|
|
||||||
|
|
||||||
|
<form
|
||||||
|
on:submit|preventDefault={submitForm}
|
||||||
|
class="w-full max-w-lg bg-white p-6 rounded-2xl shadow-xl space-y-4"
|
||||||
|
>
|
||||||
|
<h2 class="text-2xl font-bold text-center mb-6">Timesheet Entry</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="t_eb" class="block text-sm font-medium mb-1">Entered By</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="t_eb"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.entered_by}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="" disabled selected>Select Employee</option>
|
||||||
|
{#each employees as employee}
|
||||||
|
<option value={employee.id}>{employee.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="t_wd" class="block text-sm font-medium mb-1"
|
||||||
|
>Work Description</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
id="t_wd"
|
||||||
|
class="w-full border border-gray-300 p-2 rounded"
|
||||||
|
bind:value={form.work_description}
|
||||||
|
placeholder="Describe the work"
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="t_ow" class="block text-sm font-medium mb-1"
|
||||||
|
>Type of Work</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="t_ow"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.type_of_work}
|
||||||
|
>
|
||||||
|
{#each typeOfWorkOptions as option}
|
||||||
|
<option value={option}>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="t_cow" class="block text-sm font-medium mb-1"
|
||||||
|
>Category of Work</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="t_cow"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.category_of_work}
|
||||||
|
>
|
||||||
|
{#each categoryOptions as option}
|
||||||
|
<option value={option}>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="t_vn" class="block text-sm font-medium mb-1">Villa</label>
|
||||||
|
<select
|
||||||
|
id="t_vn"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.villa_id}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="" disabled selected>Select Villa</option>
|
||||||
|
{#each villas as villa}
|
||||||
|
<option value={villa.id}>{villa.villa_name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="tdto" class="block text-sm font-medium mb-1"
|
||||||
|
>Date/Time In</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="tdto"
|
||||||
|
type="datetime-local"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.datetime_in}
|
||||||
|
on:change={calculateTotalHours}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="dto" class="block text-sm font-medium mb-1"
|
||||||
|
>Date/Time Out</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="dto"
|
||||||
|
type="datetime-local"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
bind:value={form.datetime_out}
|
||||||
|
on:change={calculateTotalHours}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-sm">
|
||||||
|
<label for="ttwo" class="block font-medium mb-1">Total Work Hours</label>
|
||||||
|
<div id="ttwo" class="px-3 py-2">{form.total_work_hour}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="trmk" class="block text-sm font-medium mb-1">Remarks</label>
|
||||||
|
<textarea
|
||||||
|
id="trmk"
|
||||||
|
class="w-full border border-gray-300 p-2 rounded"
|
||||||
|
bind:value={form.remarks}
|
||||||
|
placeholder="Optional remarks"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
|
||||||
>
|
>
|
||||||
<form
|
{isEditing ? 'Update Timesheet' : 'New Entry'}
|
||||||
on:submit|preventDefault={saveIssue}
|
</button>
|
||||||
class="bg-white p-6 rounded shadow-lg w-[600px] max-h-[90vh] overflow-y-auto space-y-4"
|
</form>
|
||||||
>
|
|
||||||
<h3 class="text-lg font-semibold">
|
|
||||||
{isEditing ? "Edit Issue" : "Add New Issue"}
|
|
||||||
</h3>
|
|
||||||
{#each formColumns as col}
|
|
||||||
{#if col.key === "name"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
col.key,
|
|
||||||
)}"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if col.key === "remarks"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
col.key,
|
|
||||||
)}"
|
|
||||||
required
|
|
||||||
></textarea>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if col.key === "type_of_work" || col.key === "category_of_work"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
col.key,
|
|
||||||
)}"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
{#each col.key === "type_of_work" ? typeOfWork : categoryOfWork as option}
|
|
||||||
<option value={option.value}>
|
|
||||||
{option.label}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if col.key === "villa_id"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-
|
|
||||||
2 rounded {errorClass(col.key)}"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="" disabled selected
|
|
||||||
>Select Villa</option
|
|
||||||
>
|
|
||||||
{#each dataVilla as villa}
|
|
||||||
<option value={villa.id}
|
|
||||||
>{villa.villa_name}</option
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if col.key === "date_in" || col.key === "date_out"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="datetime-local"
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
col.key,
|
|
||||||
)}"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if col.key === "entered_by"}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
{col.title}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
name={col.key}
|
|
||||||
bind:value={newIssue[col.key]}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
col.key,
|
|
||||||
)}"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
{#each reportedBy as option}
|
|
||||||
<option value={option.value}>
|
|
||||||
{option.label}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
{#if $formErrors[col.key]}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors[col.key]}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<!-- Handle other fields if necessary -->
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm font-medium mb-1">
|
|
||||||
Total Work Hour
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="total_work_hour"
|
|
||||||
bind:value={newIssue.total_work_hour}
|
|
||||||
class="w-full border p-2 rounded {errorClass(
|
|
||||||
'total_work_hour',
|
|
||||||
)}"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
{#if $formErrors.total_work_hour}
|
|
||||||
<p class="text-red-500 text-xs mt-1">
|
|
||||||
{$formErrors.total_work_hour}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end gap-2 mt-4">
|
|
||||||
<button
|
|
||||||
class="px-4 py-2 text-sm rounded bg-gray-200 hover:bg-gray-300"
|
|
||||||
on:click={() => (showModal = false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="px-4 py-2 text-sm rounded bg-blue-600 text-white hover:bg-blue-700"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user