add relation timesheet -> employee

This commit is contained in:
2025-06-06 18:41:44 +08:00
parent 59fa90c352
commit c1d87b9797

View File

@@ -1,188 +1,241 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { supabase } from '$lib/supabaseClient'; import { supabase } from '$lib/supabaseClient';
type TimesheetForm = { type TimesheetForm = {
entered_by: string; entered_by: string;
work_description: string; work_description: string;
type_of_work: 'Running' | 'Periodic' | 'Irregular'; type_of_work: 'Running' | 'Periodic' | 'Irregular';
category_of_work: category_of_work:
| 'Cleaning' | 'Cleaning'
| 'Gardening/Pool' | 'Gardening/Pool'
| 'Maintenance' | 'Maintenance'
| 'Supervision' | 'Supervision'
| 'Guest Service' | 'Guest Service'
| 'Administration' | 'Administration'
| 'Non Billable'; | 'Non Billable';
villa_id: string; villa_id: string;
datetime_in: string; datetime_in: string;
datetime_out: string; datetime_out: string;
total_work_hour: number; total_work_hour: number;
remarks: string; remarks: string;
approval: boolean; approval: boolean;
}; };
type Villa = { type Villa = {
id: string; id: string;
name: string; name: string;
}; };
let villas: Villa[] = []; type Employee = {
id: string;
let form: TimesheetForm = { name: string;
entered_by: '', };
work_description: '',
type_of_work: 'Running', let employees: Employee[] = [];
category_of_work: 'Cleaning', let villas: Villa[] = [];
villa_id: '',
datetime_in: '', let form: TimesheetForm = {
datetime_out: '', entered_by: '',
total_work_hour: 0, work_description: '',
remarks: '', type_of_work: 'Running',
approval: false category_of_work: 'Cleaning',
}; villa_id: '',
datetime_in: '',
const typeOfWorkOptions: TimesheetForm['type_of_work'][] = ['Running', 'Periodic', 'Irregular']; datetime_out: '',
const categoryOptions: TimesheetForm['category_of_work'][] = [ total_work_hour: 0,
'Cleaning', remarks: '',
'Gardening/Pool', approval: false,
'Maintenance', };
'Supervision',
'Guest Service', const typeOfWorkOptions: TimesheetForm['type_of_work'][] = ['Running', 'Periodic', 'Irregular'];
'Administration', const categoryOptions: TimesheetForm['category_of_work'][] = [
'Non Billable' 'Cleaning',
]; 'Gardening/Pool',
'Maintenance',
onMount(async () => { 'Supervision',
const { data, error } = await supabase 'Guest Service',
.from('vb_villas') 'Administration',
.select('id, villa_name, villa_status') 'Non Billable',
.eq('villa_status', 'Active'); ];
if (error) { onMount(async () => {
console.error('Failed to fetch villas:', error.message); // Fetch villas
} else { const { data: villaData, error: villaError } = await supabase
villas = data.map(v => ({ .from('vb_villas')
id: v.id, .select('id, villa_name, villa_status')
name: v.villa_name .eq('villa_status', 'Active');
}));
} if (villaError) {
}); console.error('Failed to fetch villas:', villaError.message);
} else if (villaData) {
function calculateTotalHours() { villas = villaData.map((v) => ({ id: v.id, name: v.villa_name }));
}
// Fetch employees
const { data: empData, error: empError } = await supabase
.from('vb_employee')
.select('id, employee_name')
.eq('employee_status', 'Active');
if (empError) {
console.error('Failed to fetch employees:', empError.message);
} else if (empData) {
employees = empData.map((e) => ({ id: e.id, name: e.employee_name }));
}
});
function calculateTotalHours() {
if (form.datetime_in && form.datetime_out) { if (form.datetime_in && form.datetime_out) {
const start = new Date(form.datetime_in); const start = new Date(form.datetime_in);
const end = new Date(form.datetime_out); const end = new Date(form.datetime_out);
const diffInMs = end.getTime() - start.getTime(); const diffInMs = end.getTime() - start.getTime();
const hours = diffInMs / (1000 * 60 * 60);
// Convert milliseconds to hours (with decimal), round to 2 decimal places form.total_work_hour = Math.max(Number(hours.toFixed(2)), 0);
const hours = diffInMs / (1000 * 60 * 60);
form.total_work_hour = Math.max(Number(hours.toFixed(2)), 0);
} else { } else {
form.total_work_hour = 0; form.total_work_hour = 0;
} }
}
async function submitForm() {
calculateTotalHours();
if (!form.entered_by) {
alert('Please select an employee.');
return;
}
if (!form.villa_id) {
alert('Please select a villa.');
return;
} }
const { error } = await supabase.from('vb_timesheet').insert([form]);
async function submitForm() {
calculateTotalHours(); if (error) {
alert('Failed to submit timesheet: ' + error.message);
const { error } = await supabase.from('vb_timesheet').insert([form]); } else {
alert('Timesheet submitted successfully!');
if (error) { form = {
alert('Failed to submit timesheet: ' + error.message); entered_by: '',
} else { work_description: '',
alert('Timesheet submitted successfully!'); type_of_work: 'Running',
form = { category_of_work: 'Cleaning',
entered_by: '', villa_id: '',
work_description: '', datetime_in: '',
type_of_work: 'Running', datetime_out: '',
category_of_work: 'Cleaning', total_work_hour: 0,
villa_id: '', remarks: '',
datetime_in: '', approval: false,
datetime_out: '', };
total_work_hour: 0,
remarks: '',
approval: false
};
}
} }
}
</script> </script>
<form on:submit|preventDefault={submitForm} class="space-y-4 max-w-md mx-auto p-4"> <div class="min-h-screen bg-gray-100 flex items-center justify-center">
<h2 class="text-xl font-bold mb-4">Timesheet Entry</h2> <form
on:submit|preventDefault={submitForm}
<input class="w-full max-w-lg bg-white p-6 rounded-2xl shadow-xl space-y-4"
class="w-full border p-2 rounded" >
bind:value={form.entered_by} <h2 class="text-2xl font-bold text-center mb-6">Timesheet Entry</h2>
placeholder="Entered by"
required <div>
/> <label for="t_eb" class="block text-sm font-medium mb-1">Entered By</label>
<select
<textarea id="t_eb"
class="w-full border p-2 rounded" class="w-full border p-2 rounded"
bind:value={form.work_description} bind:value={form.entered_by}
placeholder="Work Description" required
required >
></textarea> <option value="" disabled selected>Select Employee</option>
{#each employees as employee}
<select class="w-full border p-2 rounded" bind:value={form.type_of_work}> <option value={employee.id}>{employee.name}</option>
{#each typeOfWorkOptions as option} {/each}
<option value={option}>{option}</option> </select>
{/each} </div>
</select>
<div>
<select class="w-full border p-2 rounded" bind:value={form.category_of_work}> <label for="t_wd" class="block text-sm font-medium mb-1">Work Description</label>
{#each categoryOptions as option} <textarea
<option value={option}>{option}</option> id="t_wd"
{/each} class="w-full border border-gray-300 p-2 rounded"
</select> bind:value={form.work_description}
placeholder="Describe the work"
<select class="w-full border p-2 rounded" bind:value={form.villa_id} required> 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} {#each villas as villa}
<option value={villa.id}>{villa.name}</option> <option value={villa.id}>{villa.name}</option>
{/each} {/each}
{/each} </select>
</select> </div>
<div>
<label class="block"> <label for="tdto" class="block text-sm font-medium mb-1">Date/Time In</label>
<input <input
id="tdto"
type="datetime-local" type="datetime-local"
class="w-full border p-2 rounded" class="w-full border p-2 rounded"
bind:value={form.datetime_in} bind:value={form.datetime_in}
on:change={calculateTotalHours} on:change={calculateTotalHours}
required required
/> />
/> </div>
</label>
<div>
<label class="block"> <label for="dto" class="block text-sm font-medium mb-1">Date/Time Out</label>
<input <input
id="dto"
type="datetime-local" type="datetime-local"
class="w-full border p-2 rounded" class="w-full border p-2 rounded"
bind:value={form.datetime_out} bind:value={form.datetime_out}
on:change={calculateTotalHours} on:change={calculateTotalHours}
required required
/> />
/>
</label>
<div class="text-sm">
</div> </div>
</div>
<div class="text-sm">
<textarea <label for="ttwo" class="block font-medium mb-1">Total Work Hours</label>
class="w-full border p-2 rounded" <div id="ttwo" class="px-3 py-2">{form.total_work_hour}</div>
bind:value={form.remarks} </div>
placeholder="Remarks"
></textarea> <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"
type="submit" class="w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
> >
Submit Timesheet Submit Timesheet
</button> </button>
</form> </form>
</form> </div>