randomize file name

This commit is contained in:
aji@catalis.app
2025-06-22 17:13:09 +07:00
parent 1402513990
commit b071dc6db5
9 changed files with 212 additions and 74 deletions

View File

@@ -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);

View File

@@ -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>

View File

@@ -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")

View File

@@ -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) {

View File

@@ -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),

View File

@@ -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}

View File

@@ -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!");
@@ -218,7 +231,7 @@
isSubmitting = false;
}
}
async function generateIssueNumber(): Promise<string> {
const { data } = await supabase
.from("vb_issues")
@@ -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"
@@ -253,7 +266,7 @@
</div>
<!-- Title -->
<h2 class="text-2xl font-semibold text-center">Submit New Issue</h2>
<!-- 2 Column Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Left Column -->
@@ -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"
@@ -311,14 +341,16 @@
<span class="block mb-2">Click to upload</span>
<span class="text-xs">or drag and drop</span>
</label>
<p class="mt-2 text-xs">
Supported formats: JPG, PNG, GIF
</p>
</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"
@@ -326,18 +358,18 @@
/>
</div>
{/if}
</label>
</label>
</div>
<div>
<label class="block text-sm font-medium mb-1">
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>

View File

@@ -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>

View File

@@ -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"