randomize file name
This commit is contained in:
@@ -211,9 +211,22 @@
|
||||
|
||||
if (selectedFile) {
|
||||
// Upload file to Supabase Storage
|
||||
const oldFilePath = newUser.profile_picture
|
||||
? newUser.profile_picture.split("/").pop()
|
||||
: null;
|
||||
if (oldFilePath) {
|
||||
await supabase.storage
|
||||
.from("villabugis")
|
||||
.remove([`profile_pictures/${oldFilePath}`]);
|
||||
}
|
||||
|
||||
const fileName = selectedFile.name;
|
||||
const fileExtension = fileName.split(".").pop();
|
||||
const randomFileName = `${crypto.randomUUID()}.${fileExtension}`;
|
||||
|
||||
const { data, error } = await supabase.storage
|
||||
.from("villabugis")
|
||||
.upload(`profile_pictures/${selectedFile.name}`, selectedFile);
|
||||
.upload(`profile_pictures/${randomFileName}`, selectedFile);
|
||||
|
||||
if (error) {
|
||||
console.error("Error uploading file:", error);
|
||||
|
||||
@@ -340,12 +340,26 @@
|
||||
|
||||
//upload image if selected
|
||||
if (selectedFile) {
|
||||
//delete previous image
|
||||
const oldFilePath = newIssue.issue_related_image
|
||||
? newIssue.issue_related_image.split("/").pop()
|
||||
: null;
|
||||
if (oldFilePath) {
|
||||
const { error: deleteError } = await supabase.storage
|
||||
.from("villabugis")
|
||||
.remove([`issues/${oldFilePath}`]);
|
||||
if (deleteError) {
|
||||
console.error("Error deleting old image:", deleteError);
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = selectedFile.name;
|
||||
const fileExtension = fileName.split(".").pop();
|
||||
const randomFileName = `${crypto.randomUUID()}.${fileExtension}`;
|
||||
|
||||
const { data, error } = await supabase.storage
|
||||
.from("villabugis")
|
||||
.upload(
|
||||
`issues/${Date.now()}_${selectedFile.name}`,
|
||||
selectedFile,
|
||||
);
|
||||
.upload(`issues/${randomFileName}`, selectedFile);
|
||||
|
||||
console.log("Image upload data:", data);
|
||||
|
||||
@@ -389,7 +403,9 @@
|
||||
"description_of_the_issue",
|
||||
) as string,
|
||||
reported_date: formData.get("reported_date") as string,
|
||||
issue_related_image: imagePreviewUrl || "",
|
||||
issue_related_image: newIssue.issue_related_image
|
||||
? newIssue.issue_related_image
|
||||
: (formData.get("issue_related_image") as string),
|
||||
issue_source: formData.get("issue_source") as string,
|
||||
reported_by: formData.get("reported_by") as string,
|
||||
input_by: formData.get("input_by") as string,
|
||||
@@ -419,6 +435,11 @@
|
||||
|
||||
await fetchIssues();
|
||||
showModal = false;
|
||||
|
||||
//clear form data
|
||||
newIssue = {};
|
||||
|
||||
selectedFile = null;
|
||||
}
|
||||
|
||||
// function get public URL for image supabase
|
||||
@@ -1165,7 +1186,12 @@
|
||||
<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)}
|
||||
on:click={() => (
|
||||
(showModal = false),
|
||||
(newIssue = {}),
|
||||
(selectedFile = null),
|
||||
(imagePreviewUrl = null)
|
||||
)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import { getSessionAuthId } from "$lib/utils/authUtil";
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
@@ -83,14 +84,28 @@
|
||||
async function uploadProfilePicture() {
|
||||
if (!file) return;
|
||||
|
||||
const filePath = `profile_pictures/${user.id}-${file.name}`;
|
||||
// delete file lama if exists
|
||||
if (user.profile_picture) {
|
||||
const oldFilePath = user.profile_picture.split("/").pop();
|
||||
if (oldFilePath) {
|
||||
await supabase.storage
|
||||
.from("villabugis")
|
||||
.remove([`profile_pictures/${oldFilePath}`]);
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = file.name;
|
||||
const fileExtension = fileName.split(".").pop();
|
||||
const randomFileName = `${crypto.randomUUID()}.${fileExtension}`;
|
||||
|
||||
const filePath = `profile_pictures/${user.id}-${randomFileName}`;
|
||||
const { error } = await supabase.storage
|
||||
.from("profile")
|
||||
.from("villabugis")
|
||||
.upload(filePath, file, { upsert: true });
|
||||
|
||||
if (!error) {
|
||||
const { data: publicUrlData } = supabase.storage
|
||||
.from("profile")
|
||||
.from("villabugis")
|
||||
.getPublicUrl(filePath);
|
||||
user.profile_picture = publicUrlData.publicUrl;
|
||||
} else {
|
||||
@@ -100,11 +115,19 @@
|
||||
|
||||
async function updateProfile() {
|
||||
loading = true;
|
||||
|
||||
const sessionId = await getSessionAuthId();
|
||||
if (!sessionId) {
|
||||
message = "Anda harus login terlebih dahulu";
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (file) await uploadProfilePicture();
|
||||
|
||||
user.last_updated = new Date().toISOString();
|
||||
user.last_updated_by = user.email;
|
||||
user.last_updated_by = user.id;
|
||||
|
||||
const { error } = await supabase
|
||||
.from("vb_users")
|
||||
|
||||
@@ -349,9 +349,26 @@
|
||||
|
||||
// Upload image if selected
|
||||
if (selectedFile) {
|
||||
const oldFilePath = formData.get("picture_link") as string;
|
||||
if (oldFilePath) {
|
||||
// Delete old file if it exists
|
||||
const { error: deleteError } = await supabase.storage
|
||||
.from("villabugis")
|
||||
.remove([`project/${oldFilePath}`]);
|
||||
|
||||
if (deleteError) {
|
||||
console.error("Error deleting old image:", deleteError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//randomize file name uuid
|
||||
const fileName = selectedFile.name;
|
||||
const fileExtension = fileName.split(".").pop();
|
||||
const randomFileName = `${crypto.randomUUID()}.${fileExtension}`;
|
||||
|
||||
const { data, error } = await supabase.storage
|
||||
.from("villabugis")
|
||||
.upload(`project/${selectedFile.name}`, selectedFile);
|
||||
.upload(`project/${randomFileName}`, selectedFile);
|
||||
|
||||
if (error) {
|
||||
console.error("Error uploading image:", error);
|
||||
@@ -403,6 +420,12 @@
|
||||
|
||||
await fetchProjects();
|
||||
showModal = false;
|
||||
|
||||
isEditing = false;
|
||||
currentEditingId = null;
|
||||
newProjects = {};
|
||||
imagePreviewUrl = null;
|
||||
selectedFile = null;
|
||||
}
|
||||
|
||||
async function deleteProject(id: string) {
|
||||
|
||||
@@ -284,6 +284,9 @@
|
||||
"updated_by",
|
||||
"updated_name",
|
||||
"inputed_name",
|
||||
"approved_name",
|
||||
"acknowledged_name",
|
||||
"received_name",
|
||||
];
|
||||
const formColumns = columns.filter(
|
||||
(col) => !excludedKeys.includes(col.key),
|
||||
|
||||
@@ -5,18 +5,19 @@
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import { writable } from "svelte/store";
|
||||
import { on } from "svelte/events";
|
||||
import logo from "$lib/images/logo.webp";
|
||||
|
||||
type villa = {
|
||||
id: string;
|
||||
name: string;
|
||||
villa_name: string;
|
||||
};
|
||||
|
||||
const villas = writable<villa[]>([]);
|
||||
|
||||
onMount(async () => {
|
||||
const { data, error } = await supabase
|
||||
.from("villas")
|
||||
.select("id, name");
|
||||
.from("vb_villas")
|
||||
.select("id, villa_name");
|
||||
|
||||
if (error) {
|
||||
console.error("Error fetching villas:", error);
|
||||
@@ -406,10 +407,10 @@
|
||||
.insert([dinningData]);
|
||||
|
||||
if (error) {
|
||||
console.error("Error submitting transport:", error);
|
||||
console.error("Error submitting order:", error);
|
||||
} else {
|
||||
console.log("Transport submitted successfully:", data);
|
||||
alert("Transport submitted successfully!");
|
||||
console.log("You order submitted successfully:", data);
|
||||
alert("You order submitted successfully!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +449,7 @@
|
||||
<!-- logo -->
|
||||
<div class="text-center mb-6">
|
||||
<img
|
||||
src="/src/lib/images/logo.webp"
|
||||
src={logo}
|
||||
alt="Logo"
|
||||
class="mx-auto"
|
||||
loading="lazy"
|
||||
@@ -469,7 +470,7 @@
|
||||
>
|
||||
<option value="" disabled selected>Select Villa</option>
|
||||
{#each $villas as villa}
|
||||
<option value={villa.id}>{villa.name}</option>
|
||||
<option value={villa.id}>{villa.villa_name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if $formErrors.villa_id}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// For example, you could handle form submission here
|
||||
import { onMount } from "svelte";
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import logo from "$lib/images/logo.webp";
|
||||
|
||||
const priority = [
|
||||
{ label: "Low", value: "Low" },
|
||||
@@ -33,10 +34,16 @@
|
||||
{ label: "Cleanliness - Floor", value: "Cleanliness - Floor" },
|
||||
{ label: "Cleanliness - Kitchen", value: "Cleanliness - Kitchen" },
|
||||
{ label: "Cleanliness - Bathroom", value: "Cleanliness - Bathroom" },
|
||||
{ label: "Maintenance - Electrical", value: "Maintenance - Electrical",},
|
||||
{
|
||||
label: "Maintenance - Electrical",
|
||||
value: "Maintenance - Electrical",
|
||||
},
|
||||
{ label: "Maintenance - Plumbing", value: "Maintenance - Plumbing" },
|
||||
{ label: "Maintenance - HVAC", value: "Maintenance - HVAC" },
|
||||
{ label: "Maintenance - Structural", value: "Maintenance - Structural" },
|
||||
{
|
||||
label: "Maintenance - Structural",
|
||||
value: "Maintenance - Structural",
|
||||
},
|
||||
{ label: "Safety Issue", value: "Safety Issue" },
|
||||
{ label: "Security Concern", value: "Security Concern" },
|
||||
{ label: "Other", value: "Other" },
|
||||
@@ -86,7 +93,7 @@
|
||||
{ label: "Window", value: "Window" },
|
||||
{ label: "Others", value: "Others" },
|
||||
];
|
||||
//InputBy & reportedBy should dropdown data from active employee, from table vb_employee (field employee_name and employee_status = 'Active')
|
||||
//InputBy & reportedBy should dropdown data from active employee, from table vb_employee (field employee_name and employee_status = 'Active')
|
||||
|
||||
const followUp = [
|
||||
{ label: "Communication Needed by Reservation", value: "true" },
|
||||
@@ -103,7 +110,9 @@
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (target.files && target.files.length > 0) {
|
||||
const file = target.files[0];
|
||||
if (!["image/jpeg", "image/png", "image/webp"].includes(file.type)) {
|
||||
if (
|
||||
!["image/jpeg", "image/png", "image/webp"].includes(file.type)
|
||||
) {
|
||||
alert("Only JPG, PNG, or WEBP images are allowed.");
|
||||
return;
|
||||
}
|
||||
@@ -183,12 +192,16 @@
|
||||
reported_by: formData.get("reported_by") as string,
|
||||
input_by: formData.get("input_by") as string,
|
||||
resolution: formData.get("resloution") as string,
|
||||
guest_has_aggreed_issue_has_been_resolved: formData.get("guest_has_aggreed_issue_has_been_resolved") as string,
|
||||
guest_has_aggreed_issue_has_been_resolved: formData.get(
|
||||
"guest_has_aggreed_issue_has_been_resolved",
|
||||
) as string,
|
||||
follow_up: formData.get("follow_up") as string,
|
||||
guest_communication: formData.get("guest_communication") as string,
|
||||
guest_communication: formData.get(
|
||||
"guest_communication",
|
||||
) as string,
|
||||
issue_related_image: issueImageUrl,
|
||||
url_drive: formData.get("url_drive") as string,
|
||||
created_at: new Date().toISOString()
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Insert into Supabase
|
||||
@@ -204,7 +217,7 @@
|
||||
await fetch("https://flow.catalis.app/webhook/vb-issuecreate", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(issue)
|
||||
body: JSON.stringify(issue),
|
||||
});
|
||||
|
||||
alert("Issue submitted successfully!");
|
||||
@@ -244,7 +257,7 @@
|
||||
<!-- logo -->
|
||||
<div class="text-center mb-6">
|
||||
<img
|
||||
src="/src/lib/images/logo.webp"
|
||||
src={logo}
|
||||
alt="Logo"
|
||||
class="mx-auto"
|
||||
loading="lazy"
|
||||
@@ -262,21 +275,30 @@
|
||||
<label class="block text-sm font-medium mb-1">
|
||||
Description of Issues<span class="text-red-500">*</span>
|
||||
<input
|
||||
name="description_of_the_issue"
|
||||
type="text"
|
||||
placeholder="Tell detail of the issue"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
required
|
||||
/>
|
||||
name="description_of_the_issue"
|
||||
type="text"
|
||||
placeholder="Tell detail of the issue"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">Issue Source
|
||||
<label class="block text-sm font-medium mb-1"
|
||||
>Issue Source
|
||||
<span class="text-red-500">*</span>
|
||||
<select name="issue_source" class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600" required>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
<select
|
||||
name="issue_source"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each issueSource as source}
|
||||
<option value={source.value}>{source.label}</option>
|
||||
<option value={source.value}
|
||||
>{source.label}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
@@ -286,19 +308,27 @@
|
||||
Villa Name
|
||||
<span class="text-red-500">*</span>
|
||||
<select
|
||||
name="villa_name"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600" required>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
name="villa_name"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each dataVilla as villa}
|
||||
<option value={villa.id}>{villa.villa_name}</option>
|
||||
<option value={villa.id}
|
||||
>{villa.villa_name}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">
|
||||
Issue related image
|
||||
<div class="w-full border-2 border-dashed rounded-xl px-4 py-10 text-center text-gray-400 cursor-pointer hover:bg-gray-50 transition relative">
|
||||
<div
|
||||
class="w-full border-2 border-dashed rounded-xl px-4 py-10 text-center text-gray-400 cursor-pointer hover:bg-gray-50 transition relative"
|
||||
>
|
||||
<input
|
||||
name="issue_related_image"
|
||||
type="file"
|
||||
@@ -318,7 +348,9 @@
|
||||
</div>
|
||||
{#if issueImageUrl}
|
||||
<div class="mt-4">
|
||||
<p class="text-sm text-gray-600 mb-2">Preview:</p>
|
||||
<p class="text-sm text-gray-600 mb-2">
|
||||
Preview:
|
||||
</p>
|
||||
<img
|
||||
src={issueImageUrl}
|
||||
alt="Issue preview"
|
||||
@@ -333,11 +365,11 @@
|
||||
Date Reported
|
||||
<span class="text-red-500">*</span>
|
||||
<input
|
||||
name="reported_date"
|
||||
type="date"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
required
|
||||
/>
|
||||
name="reported_date"
|
||||
type="date"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
@@ -349,7 +381,9 @@
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each dataUser as reporter}
|
||||
<option value={reporter.id}>
|
||||
{reporter.employee_name}
|
||||
@@ -359,12 +393,13 @@
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">URLDrive
|
||||
<label class="block text-sm font-medium mb-1"
|
||||
>URLDrive
|
||||
<input
|
||||
name="url_drive"
|
||||
type="url"
|
||||
placeholder="Enter URL"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
name="url_drive"
|
||||
type="url"
|
||||
placeholder="Enter URL"
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -380,7 +415,9 @@
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each priority as p}
|
||||
<option value={p.value}>{p.label}</option>
|
||||
{/each}
|
||||
@@ -395,7 +432,9 @@
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each issueTypes as type}
|
||||
<option value={type.value}>{type.label}</option>
|
||||
{/each}
|
||||
@@ -410,7 +449,9 @@
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400 text-gray-600"
|
||||
required
|
||||
>
|
||||
<option value="" disabled selected>Select option...</option>
|
||||
<option value="" disabled selected
|
||||
>Select option...</option
|
||||
>
|
||||
{#each areaOfVilla as area}
|
||||
<option value={area.value}>{area.label}</option>
|
||||
{/each}
|
||||
@@ -427,7 +468,6 @@
|
||||
class="w-full border rounded-xl px-4 py-2 focus:outline-none focus:ring-2 focus:ring-purple-400"
|
||||
/>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-1">
|
||||
@@ -451,7 +491,9 @@
|
||||
>Select option...</option
|
||||
>
|
||||
{#each dataUser as input}
|
||||
<option value={input.id}>{input.employee_name}</option>
|
||||
<option value={input.id}
|
||||
>{input.employee_name}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
@@ -467,7 +509,9 @@
|
||||
>Select option...</option
|
||||
>
|
||||
{#each followUp as follow}
|
||||
<option value={follow.value}>{follow.label}</option>
|
||||
<option value={follow.value}
|
||||
>{follow.label}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
@@ -500,13 +544,14 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm">Guest has agreed issue has been resolved
|
||||
<label class="text-sm"
|
||||
>Guest has agreed issue has been resolved
|
||||
<input
|
||||
name="guest_has_aggreed_issue_has_been_resolved"
|
||||
value="false"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 rounded border-gray-300"
|
||||
/>
|
||||
name="guest_has_aggreed_issue_has_been_resolved"
|
||||
value="false"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 rounded border-gray-300"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import logo from "$lib/images/logo.webp";
|
||||
|
||||
type TimesheetForm = {
|
||||
entered_by: string;
|
||||
@@ -140,6 +141,8 @@
|
||||
on:submit|preventDefault={submitForm}
|
||||
class="w-full max-w-lg bg-white p-6 rounded-2xl shadow-xl space-y-4"
|
||||
>
|
||||
<img src={logo} alt="Villa Logo" class="mx-auto mb-6" width="250" />
|
||||
|
||||
<h2 class="text-2xl font-bold text-center mb-6">Timesheet Entry</h2>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { onMount } from "svelte";
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import { writable } from "svelte/store";
|
||||
import logo from "$lib/images/logo.webp";
|
||||
|
||||
const area = [
|
||||
{ label: "Laksamana", value: "Laksamana" },
|
||||
@@ -115,7 +116,7 @@
|
||||
<!-- logo -->
|
||||
<div class="text-center mb-6">
|
||||
<img
|
||||
src="/src/lib/images/logo.webp"
|
||||
src={logo}
|
||||
alt="Logo"
|
||||
class="mx-auto"
|
||||
loading="lazy"
|
||||
|
||||
Reference in New Issue
Block a user