[Update] Durasi alert halaman nampan
This commit is contained in:
parent
9c00f3db7d
commit
9323eb2700
@ -82,68 +82,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pop-up pindah item -->
|
<!-- Pop-up pindah item -->
|
||||||
<div v-if="isPopupVisible" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
|
<div v-if="isPopupVisible" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
|
||||||
<div class="bg-white rounded-xl shadow-lg max-w-sm w-full p-6 relative">
|
<div class="bg-white rounded-xl shadow-lg max-w-sm w-full p-6 relative">
|
||||||
<div class="flex justify-center mb-2">
|
<div class="flex justify-center mb-2">
|
||||||
<div class="p-2 border rounded-lg">
|
<div class="p-2 border rounded-lg">
|
||||||
<img :src="qrCodeUrl" alt="QR Code" class="size-36" />
|
<img :src="qrCodeUrl" alt="QR Code" class="size-36" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-center text-D font-bold text-lg">
|
<div class="text-center text-D font-bold text-lg">
|
||||||
{{ selectedItem.kode_item }}
|
{{ selectedItem.kode_item }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center text-gray-700 font-medium mb-3">
|
<div class="text-center text-gray-700 font-medium mb-3">
|
||||||
{{ selectedItem.produk.nama }}
|
{{ selectedItem.produk.nama }}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-center mb-4">
|
|
||||||
<button @click="printQR" class="bg-D text-A px-4 py-2 rounded hover:bg-D/80 transition">
|
|
||||||
<i class="fas fa-print mr-2"></i>Cetak
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dropdown -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="tray-select" class="block text-sm font-medium mb-1">Nama Nampan</label>
|
|
||||||
<InputSelect v-if="isAdmin" v-model="selectedTrayId"
|
|
||||||
:options="trays.map(tray => ({ label: tray.nama, value: tray.id }))" placeholder="Pilih Nampan"
|
|
||||||
class="mt-2" />
|
|
||||||
<div class="bg-A px-3 py-2 rounded text-D font-medium" v-else>
|
|
||||||
{{trays.find(tray => tray.id === selectedTrayId)?.nama}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-center mb-4">
|
||||||
|
<button @click="printQR" class="bg-D text-A px-4 py-2 rounded hover:bg-D/80 transition">
|
||||||
|
<i class="fas fa-print mr-2"></i>Cetak
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button @click="closePopup" class="px-4 py-2 rounded bg-gray-400 hover:bg-gray-500 text-white transition">
|
<!-- Dropdown -->
|
||||||
{{ isAdmin ? 'Batal' : 'Tutup' }}
|
<div class="mb-4">
|
||||||
</button>
|
<label for="tray-select" class="block text-sm font-medium mb-1">Nama Nampan</label>
|
||||||
<button @click="showDeleteConfirm = true"
|
<InputSelect v-if="isAdmin" v-model="selectedTrayId"
|
||||||
class="px-4 py-2 rounded bg-red-500 text-white hover:bg-red-600 transition flex items-center">
|
:options="trays.map(tray => ({ label: tray.nama, value: tray.id }))" placeholder="Pilih Nampan"
|
||||||
<i class="fas fa-trash mr-2"></i>Hapus
|
class="mt-2" />
|
||||||
</button>
|
<div class="bg-A px-3 py-2 rounded text-D font-medium" v-else>
|
||||||
<button v-if="isAdmin" @click="saveMove" :disabled="!selectedTrayId" class="px-4 py-2 rounded transition"
|
{{trays.find(tray => tray.id === selectedTrayId)?.nama}}
|
||||||
:class="selectedTrayId ? 'bg-C hover:bg-C/80 text-D' : 'bg-gray-400 cursor-not-allowed'">
|
</div>
|
||||||
Simpan
|
</div>
|
||||||
</button>
|
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button @click="closePopup" class="px-4 py-2 rounded bg-gray-400 hover:bg-gray-500 text-white transition">
|
||||||
|
{{ isAdmin ? 'Batal' : 'Tutup' }}
|
||||||
|
</button>
|
||||||
|
<button @click="showDeleteConfirm = true"
|
||||||
|
class="px-4 py-2 rounded bg-red-500 text-white hover:bg-red-600 transition flex items-center">
|
||||||
|
<i class="fas fa-trash mr-2"></i>Hapus
|
||||||
|
</button>
|
||||||
|
<button v-if="isAdmin" @click="saveMove" :disabled="!selectedTrayId" class="px-4 py-2 rounded transition"
|
||||||
|
:class="selectedTrayId ? 'bg-C hover:bg-C/80 text-D' : 'bg-gray-400 cursor-not-allowed'">
|
||||||
|
Simpan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Modal Konfirmasi Hapus -->
|
<!-- Modal Konfirmasi Hapus -->
|
||||||
<ConfirmDeleteModal
|
<ConfirmDeleteModal :isOpen="showDeleteConfirm" title="Konfirmasi Hapus Item"
|
||||||
:isOpen="showDeleteConfirm"
|
message="Apakah kamu yakin ingin menghapus item ini?" confirmText="Ya, Hapus" cancelText="Batal"
|
||||||
title="Konfirmasi Hapus Item"
|
@confirm="confirmDelete" @cancel="cancelDelete" />
|
||||||
message="Apakah kamu yakin ingin menghapus item ini?"
|
</div>
|
||||||
confirmText="Ya, Hapus"
|
|
||||||
cancelText="Batal"
|
|
||||||
@confirm="confirmDelete"
|
|
||||||
@cancel="cancelDelete"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -159,6 +152,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["edit", "delete"]);
|
const emit = defineEmits(["edit", "delete"]);
|
||||||
|
|
||||||
const trays = ref([]);
|
const trays = ref([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
@ -167,6 +161,7 @@ const error = ref(null);
|
|||||||
const isPopupVisible = ref(false);
|
const isPopupVisible = ref(false);
|
||||||
const selectedItem = ref(null);
|
const selectedItem = ref(null);
|
||||||
const selectedTrayId = ref("");
|
const selectedTrayId = ref("");
|
||||||
|
const showDeleteConfirm = ref(false);
|
||||||
|
|
||||||
// QR Code generator
|
// QR Code generator
|
||||||
const qrCodeUrl = computed(() => {
|
const qrCodeUrl = computed(() => {
|
||||||
@ -235,44 +230,23 @@ const printQR = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const showDeleteConfirm = ref(false);
|
|
||||||
|
|
||||||
const confirmDelete = async () => {
|
const confirmDelete = async () => {
|
||||||
if (!selectedItem.value) return;
|
if (!selectedItem.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Panggil API hapus item
|
|
||||||
await axios.delete(`/api/item/${selectedItem.value.id}`, {
|
await axios.delete(`/api/item/${selectedItem.value.id}`, {
|
||||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tampilkan alert sukses
|
|
||||||
alert.value = { success: `Item ${selectedItem.value.kode_item} berhasil dihapus.` };
|
|
||||||
|
|
||||||
// Refresh data
|
|
||||||
await refreshData();
|
await refreshData();
|
||||||
|
|
||||||
// Tutup modal & popup
|
|
||||||
showDeleteConfirm.value = false;
|
showDeleteConfirm.value = false;
|
||||||
closePopup();
|
closePopup();
|
||||||
|
|
||||||
// Auto hide alert
|
|
||||||
clearTimeout(timer.value);
|
|
||||||
timer.value = setTimeout(() => { alert.value = null; }, 3000);
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal menghapus item:", err.response?.data || err);
|
console.error("Gagal menghapus item:", err.response?.data || err);
|
||||||
alert.value = { error: err.response?.data?.message || "Gagal menghapus item. Silakan coba lagi." };
|
error.value = err.response?.data?.message || "Gagal menghapus item. Silakan coba lagi.";
|
||||||
|
|
||||||
// Auto hide alert error
|
|
||||||
clearTimeout(timer.value);
|
|
||||||
timer.value = setTimeout(() => { alert.value = null; }, 5000);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const cancelDelete = () => {
|
const cancelDelete = () => {
|
||||||
showDeleteConfirm.value = false;
|
showDeleteConfirm.value = false;
|
||||||
};
|
};
|
||||||
@ -307,6 +281,7 @@ const saveMove = async () => {
|
|||||||
closePopup();
|
closePopup();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Gagal memindahkan item:", err.response?.data || err);
|
console.error("Gagal memindahkan item:", err.response?.data || err);
|
||||||
|
error.value = err.response?.data?.message || "Gagal memindahkan item. Silakan coba lagi.";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -345,7 +320,7 @@ const emptyTrays = computed(() => {
|
|||||||
return trays.value.filter(tray => parseFloat(totalWeight(tray)) === 0).length;
|
return trays.value.filter(tray => parseFloat(totalWeight(tray)) === 0).length;
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Ambil data nampan + item ---
|
// Ambil data nampan + item
|
||||||
const refreshData = async () => {
|
const refreshData = async () => {
|
||||||
try {
|
try {
|
||||||
const nampanRes = await axios.get("/api/nampan", {
|
const nampanRes = await axios.get("/api/nampan", {
|
||||||
@ -370,4 +345,7 @@ const filteredTrays = computed(() => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
refreshData();
|
refreshData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Expose refreshData to parent
|
||||||
|
defineExpose({ refreshData });
|
||||||
</script>
|
</script>
|
||||||
@ -9,7 +9,8 @@
|
|||||||
<button @click="openModal" class="px-4 py-2 sm:px-2 sm:py-1 hover:bg-B bg-C rounded-md shadow w-full">
|
<button @click="openModal" class="px-4 py-2 sm:px-2 sm:py-1 hover:bg-B bg-C rounded-md shadow w-full">
|
||||||
Tambah Nampan
|
Tambah Nampan
|
||||||
</button>
|
</button>
|
||||||
<button @click="promptEmptyAllTrays" class="px-4 py-2 sm:px-2 sm:py-1 bg-red-500 hover:bg-red-600 text-white rounded-md w-full">
|
<button @click="promptEmptyAllTrays"
|
||||||
|
class="px-4 py-2 sm:px-2 sm:py-1 bg-red-500 hover:bg-red-600 text-white rounded-md w-full">
|
||||||
Kosongkan Semua Nampan
|
Kosongkan Semua Nampan
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -17,21 +18,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-6" v-if="alert">
|
<div class="px-6" v-if="alert">
|
||||||
<div v-if="alert.error" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
|
<div v-if="alert.error" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4"
|
||||||
|
role="alert">
|
||||||
<strong class="font-bold">Error!</strong>
|
<strong class="font-bold">Error!</strong>
|
||||||
<span class="block sm:inline">{{ alert.error }}</span>
|
<span class="block sm:inline">{{ alert.error }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="alert.success" class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
|
<div v-if="alert.success"
|
||||||
|
class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
|
||||||
<strong class="font-bold">Success!</strong>
|
<strong class="font-bold">Success!</strong>
|
||||||
<span class="block sm:inline">{{ alert.success }}</span>
|
<span class="block sm:inline">{{ alert.success }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TrayList :search="searchQuery" @edit="editTray" @delete="promptDeleteTray" />
|
<TrayList ref="trayList" :search="searchQuery" @edit="editTray" @delete="promptDeleteTray" />
|
||||||
|
|
||||||
<!-- Modal untuk tambah/edit nampan -->
|
<!-- Modal untuk tambah/edit nampan -->
|
||||||
<div v-if="showModal" class="fixed inset-0 bg-black/75 flex justify-center items-center z-50 backdrop-blur-sm">
|
<div v-if="showModal" class="fixed inset-0 bg-black/75 flex justify-center items-center z-50 backdrop-blur-sm">
|
||||||
<div class="bg-white rounded-lg shadow-lg p-6 w-96 transform transition-all duration-300 scale-95 opacity-0 animate-fadeIn">
|
<div
|
||||||
|
class="bg-white rounded-lg shadow-lg p-6 w-96 transform transition-all duration-300 scale-95 opacity-0 animate-fadeIn">
|
||||||
<h2 class="text-lg font-semibold mb-4 text-D">
|
<h2 class="text-lg font-semibold mb-4 text-D">
|
||||||
{{ editingTrayId ? "Edit Nampan" : "Tambah Nampan" }}
|
{{ editingTrayId ? "Edit Nampan" : "Tambah Nampan" }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -39,22 +43,16 @@
|
|||||||
<InputField id="tray-name" v-model="trayName" type="text" placeholder="Contoh: A1" class="mb-1" />
|
<InputField id="tray-name" v-model="trayName" type="text" placeholder="Contoh: A1" class="mb-1" />
|
||||||
<p v-if="errorCreate" class="text-red-500 text-sm mb-4">{{ errorCreate }}</p>
|
<p v-if="errorCreate" class="text-red-500 text-sm mb-4">{{ errorCreate }}</p>
|
||||||
<div class="flex justify-end mt-3 gap-2">
|
<div class="flex justify-end mt-3 gap-2">
|
||||||
<button @click="closeModal" class="px-4 py-2 bg-gray-400 hover:bg-gray-500 text-white rounded-md">Batal</button>
|
<button @click="closeModal"
|
||||||
|
class="px-4 py-2 bg-gray-400 hover:bg-gray-500 text-white rounded-md">Batal</button>
|
||||||
<button @click="saveTray" class="px-4 py-2 bg-C hover:bg-C/80 rounded-md text-D">Simpan</button>
|
<button @click="saveTray" class="px-4 py-2 bg-C hover:bg-C/80 rounded-md text-D">Simpan</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Komponen ConfirmDeleteModal yang diperbaiki -->
|
<!-- Komponen ConfirmDeleteModal yang diperbaiki -->
|
||||||
<ConfirmDeleteModal
|
<ConfirmDeleteModal :isOpen="isConfirmModalVisible" :title="confirmModalTitle" :message="confirmModalMessage"
|
||||||
:isOpen="isConfirmModalVisible"
|
:confirmText="confirmText" :cancelText="cancelText" @confirm="handleConfirmAction" @cancel="closeConfirmModal" />
|
||||||
:title="confirmModalTitle"
|
|
||||||
:message="confirmModalMessage"
|
|
||||||
:confirmText="confirmText"
|
|
||||||
:cancelText="cancelText"
|
|
||||||
@confirm="handleConfirmAction"
|
|
||||||
@cancel="closeConfirmModal"
|
|
||||||
/>
|
|
||||||
</mainLayout>
|
</mainLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -75,6 +73,7 @@ const editingTrayId = ref(null);
|
|||||||
const errorCreate = ref("");
|
const errorCreate = ref("");
|
||||||
const timer = ref(null);
|
const timer = ref(null);
|
||||||
const alert = ref(null);
|
const alert = ref(null);
|
||||||
|
const trayList = ref(null); // Add ref for TrayList
|
||||||
|
|
||||||
// State untuk modal konfirmasi
|
// State untuk modal konfirmasi
|
||||||
const isConfirmModalVisible = ref(false);
|
const isConfirmModalVisible = ref(false);
|
||||||
@ -109,7 +108,9 @@ const saveTray = async () => {
|
|||||||
alert.value = { success: "Nampan berhasil ditambahkan" };
|
alert.value = { success: "Nampan berhasil ditambahkan" };
|
||||||
}
|
}
|
||||||
closeModal();
|
closeModal();
|
||||||
location.reload();
|
if (trayList.value) {
|
||||||
|
await trayList.value.refreshData(); // Call refreshData on TrayList
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
errorCreate.value = error.response?.data?.message || "Gagal menyimpan nampan.";
|
errorCreate.value = error.response?.data?.message || "Gagal menyimpan nampan.";
|
||||||
@ -153,7 +154,9 @@ const handleConfirmAction = async () => {
|
|||||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||||
});
|
});
|
||||||
alert.value = { success: "Nampan berhasil dihapus" };
|
alert.value = { success: "Nampan berhasil dihapus" };
|
||||||
location.reload();
|
if (trayList.value) {
|
||||||
|
await trayList.value.refreshData(); // Call refreshData on TrayList
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert.value = { error: "Gagal menghapus nampan. Silakan coba lagi." };
|
alert.value = { error: "Gagal menghapus nampan. Silakan coba lagi." };
|
||||||
@ -165,7 +168,9 @@ const handleConfirmAction = async () => {
|
|||||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||||
});
|
});
|
||||||
alert.value = { success: "Semua nampan berhasil dikosongkan" };
|
alert.value = { success: "Semua nampan berhasil dikosongkan" };
|
||||||
location.reload();
|
if (trayList.value) {
|
||||||
|
await trayList.value.refreshData(); // Call refreshData on TrayList
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert.value = { error: "Gagal mengosongkan nampan. Silakan coba lagi." };
|
alert.value = { error: "Gagal mengosongkan nampan. Silakan coba lagi." };
|
||||||
@ -187,9 +192,17 @@ const editTray = (tray) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from { opacity: 0; transform: scale(0.95); }
|
from {
|
||||||
to { opacity: 1; transform: scale(1); }
|
opacity: 0;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-fadeIn {
|
.animate-fadeIn {
|
||||||
animation: fadeIn 0.25s ease-out forwards;
|
animation: fadeIn 0.25s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user