po Request form
This commit is contained in:
@@ -3,24 +3,9 @@
|
|||||||
import { supabase } from "$lib/supabaseClient";
|
import { supabase } from "$lib/supabaseClient";
|
||||||
import logo from "$lib/images/logo.webp";
|
import logo from "$lib/images/logo.webp";
|
||||||
|
|
||||||
type TimesheetForm = {
|
type POItem = {
|
||||||
entered_by: string;
|
id: string;
|
||||||
work_description: string;
|
item_name: string;
|
||||||
type_of_work: "Running" | "Periodic" | "Irregular";
|
|
||||||
category_of_work:
|
|
||||||
| "Cleaning"
|
|
||||||
| "Gardening/Pool"
|
|
||||||
| "Maintenance"
|
|
||||||
| "Supervision"
|
|
||||||
| "Guest Service"
|
|
||||||
| "Administration"
|
|
||||||
| "Non Billable";
|
|
||||||
villa_id: string;
|
|
||||||
datetime_in: string;
|
|
||||||
datetime_out: string;
|
|
||||||
total_work_hour: number;
|
|
||||||
remarks: string;
|
|
||||||
approval: boolean | null; // Allow null for new entries
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Villa = {
|
type Villa = {
|
||||||
@@ -33,37 +18,20 @@
|
|||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
let employees: Employee[] = [];
|
let poItemOptions: POItem[] = [];
|
||||||
let villas: Villa[] = [];
|
let villas: Villa[] = [];
|
||||||
|
let employees: Employee[] = [];
|
||||||
|
|
||||||
let form: TimesheetForm = {
|
let addPOForm = {
|
||||||
entered_by: "",
|
po_type: "",
|
||||||
work_description: "",
|
|
||||||
type_of_work: "Running",
|
|
||||||
category_of_work: "Cleaning",
|
|
||||||
villa_id: "",
|
villa_id: "",
|
||||||
datetime_in: "",
|
po_item: "",
|
||||||
datetime_out: "",
|
po_quantity: 0,
|
||||||
total_work_hour: 0,
|
po_remark: "",
|
||||||
remarks: "",
|
requested_by: "",
|
||||||
approval: null, // Default null
|
requested_date: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
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",
|
|
||||||
];
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Fetch villas
|
// Fetch villas
|
||||||
const { data: villaData, error: villaError } = await supabase
|
const { data: villaData, error: villaError } = await supabase
|
||||||
@@ -90,49 +58,58 @@
|
|||||||
} else if (empData) {
|
} else if (empData) {
|
||||||
employees = empData.map((e) => ({ id: e.id, name: e.employee_name }));
|
employees = empData.map((e) => ({ id: e.id, name: e.employee_name }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch PO Items
|
||||||
|
const { data: poItemData, error: poItemError } = await supabase
|
||||||
|
.from("vb_po_item")
|
||||||
|
.select("id, item_name")
|
||||||
|
.order("item_name", { ascending: true });
|
||||||
|
|
||||||
|
if (poItemError) {
|
||||||
|
console.error("Failed to fetch PO items:", poItemError.message);
|
||||||
|
} else if (poItemData) {
|
||||||
|
poItemOptions = poItemData;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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 submitForm() {
|
async function submitForm() {
|
||||||
calculateTotalHours();
|
if (!addPOForm.requested_by) {
|
||||||
|
|
||||||
if (!form.entered_by) {
|
|
||||||
alert("Please select an employee.");
|
alert("Please select an employee.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!form.villa_id) {
|
if (!addPOForm.villa_id) {
|
||||||
alert("Please select a villa.");
|
alert("Please select a villa.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!addPOForm.po_item) {
|
||||||
|
alert("Please select a PO item.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { error } = await supabase.from("vb_timesheet").insert([form]);
|
const { error } = await supabase.from("vb_purchase_orders").insert({
|
||||||
|
po_type: addPOForm.po_type,
|
||||||
|
villa_id: addPOForm.villa_id,
|
||||||
|
po_item: addPOForm.po_item,
|
||||||
|
po_quantity: addPOForm.po_quantity,
|
||||||
|
po_status: "requested",
|
||||||
|
po_remark: addPOForm.po_remark,
|
||||||
|
requested_by: addPOForm.requested_by,
|
||||||
|
requested_date: addPOForm.requested_date,
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
alert("Failed to submit timesheet: " + error.message);
|
alert("Failed to submit Purchase Order: " + error.message);
|
||||||
} else {
|
} else {
|
||||||
alert("Timesheet submitted successfully!");
|
alert("Purchase Order submitted successfully!");
|
||||||
form = {
|
addPOForm = {
|
||||||
entered_by: "",
|
po_type: "",
|
||||||
work_description: "",
|
|
||||||
type_of_work: "Running",
|
|
||||||
category_of_work: "Cleaning",
|
|
||||||
villa_id: "",
|
villa_id: "",
|
||||||
datetime_in: "",
|
po_item: "",
|
||||||
datetime_out: "",
|
po_quantity: 0,
|
||||||
total_work_hour: 0,
|
po_remark: "",
|
||||||
remarks: "",
|
requested_by: "",
|
||||||
approval: null,
|
requested_date: ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,127 +125,64 @@
|
|||||||
<h2 class="text-2xl font-bold text-center mb-6">Timesheet Entry</h2>
|
<h2 class="text-2xl font-bold text-center mb-6">Timesheet Entry</h2>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="t_eb" class="block text-sm font-medium mb-1">Entered By</label
|
<label>PO Type</label>
|
||||||
>
|
<select bind:value={addPOForm.po_type} class="w-full border p-2">
|
||||||
<select
|
<option value="" disabled>Select PO Type</option>
|
||||||
id="t_eb"
|
<option value="purchase">Purchase</option>
|
||||||
class="w-full border p-2 rounded"
|
<option value="project">Project</option>
|
||||||
bind:value={form.entered_by}
|
<option value="repair">Repair</option>
|
||||||
required
|
</select>
|
||||||
>
|
|
||||||
<option value="" disabled selected>Select Employee</option>
|
|
||||||
{#each employees as employee}
|
|
||||||
<option value={employee.id}>{employee.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="t_wd" class="block text-sm font-medium mb-1"
|
<label>Villa</label>
|
||||||
>Work Description</label
|
<select bind:value={addPOForm.villa_id} class="w-full border p-2">
|
||||||
>
|
<option value="" disabled>Select Villa</option>
|
||||||
<textarea
|
{#each villas as v}
|
||||||
id="t_wd"
|
<option value={v.id}>{v.name}</option>
|
||||||
class="w-full border border-gray-300 p-2 rounded"
|
{/each}
|
||||||
bind:value={form.work_description}
|
</select>
|
||||||
placeholder="Describe the work"
|
|
||||||
required
|
|
||||||
></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="t_ow" class="block text-sm font-medium mb-1"
|
<label>PO Item</label>
|
||||||
>Type of Work</label
|
<select bind:value={addPOForm.po_item} class="w-full border p-2">
|
||||||
>
|
<option value="" disabled>Select Item</option>
|
||||||
<select
|
{#each poItemOptions as item}
|
||||||
id="t_ow"
|
<option value={item.item_name}>{item.item_name}</option>
|
||||||
class="w-full border p-2 rounded"
|
{/each}
|
||||||
bind:value={form.type_of_work}
|
</select>
|
||||||
>
|
|
||||||
{#each typeOfWorkOptions as option}
|
|
||||||
<option value={option}>{option}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="t_cow" class="block text-sm font-medium mb-1"
|
<label>PO Quantity</label>
|
||||||
>Category of Work</label
|
<input type="number" bind:value={addPOForm.po_quantity} class="w-full border p-2"/>
|
||||||
>
|
|
||||||
<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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="t_vn" class="block text-sm font-medium mb-1">Villa</label>
|
<label>PO Remark</label>
|
||||||
<select
|
<textarea bind:value={addPOForm.po_remark} class="w-full border p-2"></textarea>
|
||||||
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.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="tdto" class="block text-sm font-medium mb-1"
|
<label>Requested By</label>
|
||||||
>Date/Time In</label
|
<select bind:value={addPOForm.requested_by} class="w-full border p-2">
|
||||||
>
|
<option value="" disabled>Select Employee</option>
|
||||||
<input
|
{#each employees as e}
|
||||||
id="tdto"
|
<option value={e.id}>{e.name}</option>
|
||||||
type="datetime-local"
|
{/each}
|
||||||
class="w-full border p-2 rounded"
|
</select>
|
||||||
bind:value={form.datetime_in}
|
|
||||||
on:change={calculateTotalHours}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="dto" class="block text-sm font-medium mb-1"
|
<label>Requested Date</label>
|
||||||
>Date/Time Out</label
|
<input type="date" bind:value={addPOForm.requested_date} class="w-full border p-2"/>
|
||||||
>
|
|
||||||
<input
|
|
||||||
id="dto"
|
|
||||||
type="datetime-local"
|
|
||||||
class="w-full border p-2 rounded"
|
|
||||||
bind:value={form.datetime_out}
|
|
||||||
on:change={calculateTotalHours}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
</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
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
|
class="w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
|
||||||
>
|
>
|
||||||
Submit Timesheet
|
Submit Purchase Order
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user