first commit
This commit is contained in:
parent
8c5285b988
commit
527ad45ab8
|
|
@ -1,4 +1,5 @@
|
|||
// src/components/masterData/TambahBarang.tsx
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { PlusCircle, Image } from "lucide-react";
|
||||
|
|
@ -12,14 +13,42 @@ export default function TambahBarang({
|
|||
onClose,
|
||||
onRefresh,
|
||||
}: TambahBarangProps) {
|
||||
|
||||
// STATE INPUT FORM
|
||||
const [kodeBarang, setKodeBarang] = useState("");
|
||||
const [namaBarang, setNamaBarang] = useState("");
|
||||
|
||||
// BISA PILIH / KETIK SENDIRI
|
||||
const [kategoriSelected, setKategoriSelected] = useState("");
|
||||
const [lokasiSelected, setLokasiSelected] = useState("");
|
||||
|
||||
const [kondisiFisik, setKondisiFisik] = useState("");
|
||||
const [stokAwal, setStokAwal] = useState("");
|
||||
|
||||
// LIST DINAMIS
|
||||
const [kategoriList, setKategoriList] = useState<string[]>([
|
||||
"Elektronik",
|
||||
"Alat Tulis",
|
||||
"Kebersihan",
|
||||
"Mebel dan Furnitur",
|
||||
"Perangkat Jaringan",
|
||||
"Peralatan Operasional",
|
||||
"Peralatan Bengkel",
|
||||
"Buku dan Modul",
|
||||
]);
|
||||
|
||||
const [lokasiList, setLokasiList] = useState<string[]>([
|
||||
"Gudang Pusat",
|
||||
"Lab PPLG",
|
||||
"Lab TKJ",
|
||||
"X PPLG",
|
||||
"XI PPLG",
|
||||
"XII PPLG",
|
||||
"X TKJ",
|
||||
"XI TKJ",
|
||||
"XII TKJ",
|
||||
]);
|
||||
|
||||
// FILE FOTO
|
||||
const [fotoBarang, setFotoBarang] = useState<File | null>(null);
|
||||
|
||||
|
|
@ -34,37 +63,34 @@ export default function TambahBarang({
|
|||
// LOGIKA KODE OTOMATIS
|
||||
useEffect(() => {
|
||||
if (kategoriSelected && lokasiSelected) {
|
||||
const inisialKategori: { [key: string]: string } = {
|
||||
Elektronik: "ELK",
|
||||
"Alat Tulis": "ATK",
|
||||
Kebersihan: "KBR",
|
||||
"Mebel dan Furnitur": "MUB",
|
||||
"Perangkat Jaringan": "JRG",
|
||||
"Peralatan Operasional": "OPR",
|
||||
"Peralatan Bengkel": "PBG",
|
||||
"Buku dan Modul": "BKM",
|
||||
|
||||
// AMBIL INISIAL OTOMATIS
|
||||
const ambilInisial = (text: string) => {
|
||||
const kata = text
|
||||
.replace(/[^a-zA-Z0-9 ]/g, "")
|
||||
.toUpperCase()
|
||||
.split(" ")
|
||||
.filter(Boolean);
|
||||
|
||||
if (kata.length >= 2) {
|
||||
return (
|
||||
kata[0].substring(0, 2) +
|
||||
kata[1].substring(0, 1)
|
||||
);
|
||||
}
|
||||
|
||||
return kata[0]?.substring(0, 3) || "BRG";
|
||||
};
|
||||
|
||||
const inisialLokasi: { [key: string]: string } = {
|
||||
"Gudang Pusat": "GUD",
|
||||
"Lab PPLG": "LAB-PPLG",
|
||||
"Lab TKJ": "LAB-TKJ",
|
||||
"X PPLG": "KLS-X-PPLG",
|
||||
"XI PPLG": "KLS-XI-PPLG",
|
||||
"XII PPLG": "KLS-XII-PPLG",
|
||||
"X TKJ": "KLS-X-TKJ",
|
||||
"XI TKJ": "KLS-XI-TKJ",
|
||||
"XII TKJ": "KLS-XII-TKJ",
|
||||
};
|
||||
|
||||
const kat = inisialKategori[kategoriSelected] || "BRG";
|
||||
const lok = inisialLokasi[lokasiSelected] || "GUD";
|
||||
const kat = ambilInisial(kategoriSelected);
|
||||
const lok = ambilInisial(lokasiSelected);
|
||||
|
||||
const nomorUrut = String(
|
||||
Math.floor(100 + Math.random() * 900)
|
||||
);
|
||||
|
||||
setKodeBarang(`${kat}/${lok}/${nomorUrut}`);
|
||||
|
||||
} else {
|
||||
setKodeBarang("");
|
||||
}
|
||||
|
|
@ -74,7 +100,9 @@ export default function TambahBarang({
|
|||
const handleFileChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
|
||||
const file = e.target.files[0];
|
||||
|
||||
setFotoBarang(file);
|
||||
|
|
@ -83,7 +111,7 @@ export default function TambahBarang({
|
|||
|
||||
setPreviewUrl(localPreview);
|
||||
|
||||
// RESET LINK KALAU PAKAI FILE
|
||||
// RESET LINK
|
||||
setFotoLink("");
|
||||
}
|
||||
};
|
||||
|
|
@ -92,25 +120,27 @@ export default function TambahBarang({
|
|||
const handleLinkChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
|
||||
const value = e.target.value;
|
||||
|
||||
setFotoLink(value);
|
||||
|
||||
// PREVIEW DARI LINK
|
||||
// PREVIEW LINK
|
||||
setPreviewUrl(value);
|
||||
|
||||
// RESET FILE KALAU PAKAI LINK
|
||||
// RESET FILE
|
||||
setFotoBarang(null);
|
||||
};
|
||||
|
||||
const handleSimpanKeDB = async (
|
||||
e: React.FormEvent
|
||||
) => {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (!kodeBarang) {
|
||||
alert(
|
||||
"Pilih Kategori dan Lokasi dulu agar kode barang tercipta otomatis!"
|
||||
"Pilih / ketik kategori dan lokasi dulu!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -121,23 +151,33 @@ export default function TambahBarang({
|
|||
}
|
||||
|
||||
if (!kondisiFisik) {
|
||||
alert("Silakan pilih Kondisi Fisik dulu!");
|
||||
alert("Silakan pilih kondisi fisik!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stokAwal) {
|
||||
alert("Stok awal jangan dikosongkan!");
|
||||
alert("Stok awal jangan kosong!");
|
||||
return;
|
||||
}
|
||||
|
||||
// VALIDASI FOTO
|
||||
if (!fotoBarang && !fotoLink) {
|
||||
alert(
|
||||
"Foto barang wajib dipilih atau menggunakan link online!"
|
||||
"Foto barang wajib dipilih atau pakai link!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("kode_barang", kodeBarang);
|
||||
|
||||
formData.append(
|
||||
"nama_barang",
|
||||
namaBarang.trim()
|
||||
);
|
||||
|
||||
// WAJIB BACKEND LAMA
|
||||
const kategoriMapping: { [key: string]: number } = {
|
||||
Elektronik: 1,
|
||||
"Alat Tulis": 7,
|
||||
|
|
@ -161,19 +201,33 @@ export default function TambahBarang({
|
|||
"XII TKJ": 338,
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
// KALAU INPUT BARU BELUM ADA
|
||||
const kategoriId =
|
||||
kategoriMapping[kategoriSelected] || 999;
|
||||
|
||||
formData.append("kode_barang", kodeBarang);
|
||||
formData.append("nama_barang", namaBarang.trim());
|
||||
const lokasiId =
|
||||
lokasiMapping[lokasiSelected] || 999;
|
||||
|
||||
// BACKEND LAMA
|
||||
formData.append(
|
||||
"kategori_id",
|
||||
String(kategoriMapping[kategoriSelected] || 1)
|
||||
String(kategoriId)
|
||||
);
|
||||
|
||||
formData.append(
|
||||
"lokasi_id",
|
||||
String(lokasiMapping[lokasiSelected] || 1)
|
||||
String(lokasiId)
|
||||
);
|
||||
|
||||
// TAMBAHAN BARU
|
||||
formData.append(
|
||||
"nama_kategori",
|
||||
kategoriSelected
|
||||
);
|
||||
|
||||
formData.append(
|
||||
"nama_lokasi",
|
||||
lokasiSelected
|
||||
);
|
||||
|
||||
formData.append(
|
||||
|
|
@ -183,19 +237,29 @@ export default function TambahBarang({
|
|||
: "Rusak"
|
||||
);
|
||||
|
||||
formData.append("stok", stokAwal);
|
||||
formData.append(
|
||||
"stok",
|
||||
stokAwal
|
||||
);
|
||||
|
||||
// FILE FOTO
|
||||
if (fotoBarang) {
|
||||
formData.append("foto_barang", fotoBarang);
|
||||
formData.append(
|
||||
"foto_barang",
|
||||
fotoBarang
|
||||
);
|
||||
}
|
||||
|
||||
// LINK FOTO
|
||||
if (fotoLink) {
|
||||
formData.append("foto_link", fotoLink);
|
||||
formData.append(
|
||||
"foto_link",
|
||||
fotoLink
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
setLoading(true);
|
||||
|
||||
const res = await axios.post(
|
||||
|
|
@ -214,6 +278,29 @@ export default function TambahBarang({
|
|||
res.status === 200 ||
|
||||
res.status === 201
|
||||
) {
|
||||
|
||||
// AUTO TAMBAH KE LIST KATEGORI
|
||||
if (
|
||||
kategoriSelected &&
|
||||
!kategoriList.includes(kategoriSelected)
|
||||
) {
|
||||
setKategoriList((prev) => [
|
||||
...prev,
|
||||
kategoriSelected,
|
||||
]);
|
||||
}
|
||||
|
||||
// AUTO TAMBAH KE LIST LOKASI
|
||||
if (
|
||||
lokasiSelected &&
|
||||
!lokasiList.includes(lokasiSelected)
|
||||
) {
|
||||
setLokasiList((prev) => [
|
||||
...prev,
|
||||
lokasiSelected,
|
||||
]);
|
||||
}
|
||||
|
||||
alert(
|
||||
`Sukses! Barang terdaftar dengan kode ${kodeBarang}`
|
||||
);
|
||||
|
|
@ -231,23 +318,30 @@ export default function TambahBarang({
|
|||
if (onRefresh) onRefresh();
|
||||
if (onClose) onClose();
|
||||
}
|
||||
|
||||
} catch (err: any) {
|
||||
|
||||
console.error(err);
|
||||
|
||||
alert(
|
||||
err.response?.data?.message ||
|
||||
"Gagal menyimpan barang!"
|
||||
"Gagal menyimpan barang!"
|
||||
);
|
||||
|
||||
} finally {
|
||||
|
||||
setLoading(false);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 p-4">
|
||||
|
||||
<div className="p-6 bg-white dark:bg-gray-800 rounded-3xl shadow-2xl max-w-2xl w-full mx-auto text-black dark:text-white max-h-[90vh] overflow-y-auto">
|
||||
|
||||
<div className="flex items-center justify-between mb-6 border-b pb-3 border-gray-100 dark:border-gray-700">
|
||||
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Input Inventaris Baru
|
||||
</h2>
|
||||
|
|
@ -260,20 +354,25 @@ export default function TambahBarang({
|
|||
✕
|
||||
</button>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={handleSimpanKeDB}
|
||||
className="space-y-4"
|
||||
>
|
||||
|
||||
{/* FOTO */}
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Foto Barang
|
||||
</label>
|
||||
|
||||
<div className="flex items-center gap-4 p-3 border border-dashed border-gray-300 dark:border-gray-600 rounded-xl bg-gray-50 dark:bg-gray-700">
|
||||
|
||||
<label className="flex items-center gap-2 bg-blue-50 hover:bg-blue-100 text-blue-600 px-4 py-2 rounded-xl text-sm font-semibold cursor-pointer border border-blue-200 transition">
|
||||
|
||||
<Image size={16} />
|
||||
|
||||
Pilih File
|
||||
|
|
@ -284,6 +383,7 @@ export default function TambahBarang({
|
|||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
</label>
|
||||
|
||||
<div className="text-xs text-gray-400">
|
||||
|
|
@ -291,10 +391,12 @@ export default function TambahBarang({
|
|||
? fotoBarang.name
|
||||
: "Belum ada file terpilih"}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* INPUT LINK FOTO */}
|
||||
<div className="mt-3">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Atau masukkan link foto online..."
|
||||
|
|
@ -302,134 +404,92 @@ export default function TambahBarang({
|
|||
onChange={handleLinkChange}
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* PREVIEW */}
|
||||
{previewUrl && (
|
||||
<div className="mt-3 w-32 h-32 rounded-xl overflow-hidden border border-gray-200 shadow-sm">
|
||||
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt="Preview"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
{/* KATEGORI & LOKASI */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
|
||||
{/* KATEGORI */}
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Kategori
|
||||
</label>
|
||||
|
||||
<select
|
||||
<input
|
||||
list="kategori-list"
|
||||
value={kategoriSelected}
|
||||
onChange={(e) =>
|
||||
setKategoriSelected(
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
placeholder="Pilih atau ketik kategori..."
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500 bg-white text-black"
|
||||
>
|
||||
<option value="">
|
||||
-- Pilih Kategori --
|
||||
</option>
|
||||
/>
|
||||
|
||||
<option value="Elektronik">
|
||||
Elektronik
|
||||
</option>
|
||||
<datalist id="kategori-list">
|
||||
{kategoriList.map((kategori, index) => (
|
||||
<option
|
||||
key={index}
|
||||
value={kategori}
|
||||
/>
|
||||
))}
|
||||
</datalist>
|
||||
|
||||
<option value="Alat Tulis">
|
||||
Alat Tulis
|
||||
</option>
|
||||
|
||||
<option value="Kebersihan">
|
||||
Kebersihan
|
||||
</option>
|
||||
|
||||
<option value="Mebel dan Furnitur">
|
||||
Mebel dan Furnitur
|
||||
</option>
|
||||
|
||||
<option value="Perangkat Jaringan">
|
||||
Perangkat Jaringan
|
||||
</option>
|
||||
|
||||
<option value="Peralatan Operasional">
|
||||
Peralatan Operasional
|
||||
</option>
|
||||
|
||||
<option value="Peralatan Bengkel">
|
||||
Peralatan Bengkel
|
||||
</option>
|
||||
|
||||
<option value="Buku dan Modul">
|
||||
Buku dan Modul
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* LOKASI */}
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Lokasi Gudang / Ruang
|
||||
</label>
|
||||
|
||||
<select
|
||||
<input
|
||||
list="lokasi-list"
|
||||
value={lokasiSelected}
|
||||
onChange={(e) =>
|
||||
setLokasiSelected(
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
placeholder="Pilih atau ketik lokasi..."
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500 bg-white text-black"
|
||||
>
|
||||
<option value="">
|
||||
-- Pilih Lokasi --
|
||||
</option>
|
||||
/>
|
||||
|
||||
<option value="Gudang Pusat">
|
||||
Gudang Pusat
|
||||
</option>
|
||||
<datalist id="lokasi-list">
|
||||
{lokasiList.map((lokasi, index) => (
|
||||
<option
|
||||
key={index}
|
||||
value={lokasi}
|
||||
/>
|
||||
))}
|
||||
</datalist>
|
||||
|
||||
<option value="Lab PPLG">
|
||||
Lab PPLG
|
||||
</option>
|
||||
|
||||
<option value="Lab TKJ">
|
||||
Lab TKJ
|
||||
</option>
|
||||
|
||||
<option value="X PPLG">
|
||||
X PPLG
|
||||
</option>
|
||||
|
||||
<option value="XI PPLG">
|
||||
XI PPLG
|
||||
</option>
|
||||
|
||||
<option value="XII PPLG">
|
||||
XII PPLG
|
||||
</option>
|
||||
|
||||
<option value="X TKJ">
|
||||
X TKJ
|
||||
</option>
|
||||
|
||||
<option value="XI TKJ">
|
||||
XI TKJ
|
||||
</option>
|
||||
|
||||
<option value="XII TKJ">
|
||||
XII TKJ
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* KODE */}
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Kode Barang
|
||||
</label>
|
||||
|
|
@ -442,10 +502,12 @@ export default function TambahBarang({
|
|||
value={kodeBarang}
|
||||
className="w-full p-3 rounded-xl border border-gray-200 bg-gray-100 dark:bg-gray-900 font-mono font-bold text-blue-600 outline-none cursor-not-allowed"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* NAMA */}
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Nama Barang
|
||||
</label>
|
||||
|
|
@ -461,12 +523,14 @@ export default function TambahBarang({
|
|||
}
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* KONDISI & STOK */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Kondisi Fisik
|
||||
</label>
|
||||
|
|
@ -480,6 +544,7 @@ export default function TambahBarang({
|
|||
}
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500 bg-white text-black"
|
||||
>
|
||||
|
||||
<option value="">
|
||||
-- Pilih Kondisi --
|
||||
</option>
|
||||
|
|
@ -491,10 +556,13 @@ export default function TambahBarang({
|
|||
<option value="Rusak">
|
||||
Rusak
|
||||
</option>
|
||||
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<label className="block text-sm font-semibold mb-1 text-gray-700 dark:text-gray-200">
|
||||
Stok Awal
|
||||
</label>
|
||||
|
|
@ -510,7 +578,9 @@ export default function TambahBarang({
|
|||
}
|
||||
className="w-full p-3 rounded-xl border border-gray-300 dark:border-gray-600 dark:bg-gray-700 outline-none focus:border-blue-500"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* BUTTON */}
|
||||
|
|
@ -531,15 +601,21 @@ export default function TambahBarang({
|
|||
disabled={loading}
|
||||
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-xl font-semibold shadow transition disabled:opacity-50"
|
||||
>
|
||||
|
||||
<PlusCircle size={18} />
|
||||
|
||||
{loading
|
||||
? "Menyimpan..."
|
||||
: "Simpan ke DB"}
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue