diff --git a/.env.example b/.env.example deleted file mode 100644 index 35db1dd..0000000 --- a/.env.example +++ /dev/null @@ -1,65 +0,0 @@ -APP_NAME=Laravel -APP_ENV=local -APP_KEY= -APP_DEBUG=true -APP_URL=http://localhost - -APP_LOCALE=en -APP_FALLBACK_LOCALE=en -APP_FAKER_LOCALE=en_US - -APP_MAINTENANCE_DRIVER=file -# APP_MAINTENANCE_STORE=database - -PHP_CLI_SERVER_WORKERS=4 - -BCRYPT_ROUNDS=12 - -LOG_CHANNEL=stack -LOG_STACK=single -LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug - -DB_CONNECTION=sqlite -# DB_HOST=127.0.0.1 -# DB_PORT=3306 -# DB_DATABASE=laravel -# DB_USERNAME=root -# DB_PASSWORD= - -SESSION_DRIVER=database -SESSION_LIFETIME=120 -SESSION_ENCRYPT=false -SESSION_PATH=/ -SESSION_DOMAIN=null - -BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=database - -CACHE_STORE=database -# CACHE_PREFIX= - -MEMCACHED_HOST=127.0.0.1 - -REDIS_CLIENT=phpredis -REDIS_HOST=127.0.0.1 -REDIS_PASSWORD=null -REDIS_PORT=6379 - -MAIL_MAILER=log -MAIL_SCHEME=null -MAIL_HOST=127.0.0.1 -MAIL_PORT=2525 -MAIL_USERNAME=null -MAIL_PASSWORD=null -MAIL_FROM_ADDRESS="hello@example.com" -MAIL_FROM_NAME="${APP_NAME}" - -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_DEFAULT_REGION=us-east-1 -AWS_BUCKET= -AWS_USE_PATH_STYLE_ENDPOINT=false - -VITE_APP_NAME="${APP_NAME}" diff --git a/app/Http/Controllers/FotoSementaraController.php b/app/Http/Controllers/FotoSementaraController.php index 3aa5ecb..339ad6d 100644 --- a/app/Http/Controllers/FotoSementaraController.php +++ b/app/Http/Controllers/FotoSementaraController.php @@ -11,7 +11,7 @@ class FotoSementaraController extends Controller public function upload(Request $request) { $request->validate([ - 'id_produk' => 'required|exists:produk,id', + 'id_user' => 'required|exists:users,id', 'foto' => 'required|image|mimes:jpg,jpeg,png|max:2048', ]); @@ -19,11 +19,11 @@ class FotoSementaraController extends Controller $url = asset('storage/' . $path); $foto = FotoSementara::create([ - 'id_produk' => $request->id_produk, + 'id_user' => $request->id_user, 'url' => $url, ]); - return response()->json(['message' => 'Foto berhasil disimpan'], 201); + return response()->json($foto, 201); } public function hapus($id) @@ -45,6 +45,9 @@ class FotoSementaraController extends Controller public function getAll($user_id) { $data = FotoSementara::where('id_user', $user_id); + if (!$data->exists()) { + return response()->json(['message' => 'Tidak ada foto ditemukan'], 404); + } return response()->json($data); } diff --git a/app/Http/Controllers/NampanController.php b/app/Http/Controllers/NampanController.php index 396f90a..7bf5049 100644 --- a/app/Http/Controllers/NampanController.php +++ b/app/Http/Controllers/NampanController.php @@ -13,7 +13,7 @@ class NampanController extends Controller public function index() { return response()->json( - Nampan::withCount('items')->get() + Nampan::with('items.produk.foto')->withCount('items')->get() ); } @@ -43,7 +43,7 @@ class NampanController extends Controller public function show(int $id) { return response()->json( - Nampan::with('items')->find($id) + Nampan::with('items.produk.foto')->find($id) ); } diff --git a/app/Http/Controllers/TransaksiController.php b/app/Http/Controllers/TransaksiController.php index 0eb206a..055a1bb 100644 --- a/app/Http/Controllers/TransaksiController.php +++ b/app/Http/Controllers/TransaksiController.php @@ -13,14 +13,20 @@ class TransaksiController extends Controller // List semua transaksi public function index() { - $transaksi = Transaksi::with(['kasir', 'sales', 'items.item.produk'])->get(); + $limit = request()->query('limit', null); + $query = Transaksi::with(['kasir', 'sales', 'items.item.produk'])->latest(); + if ($limit) { + $query->limit((int)$limit); + } + $transaksi = $query->get(); + $transaksi = Transaksi::with(['kasir', 'sales', 'items.item.produk'])->latest()->limit(100)->get(); return response()->json($transaksi); } // Detail transaksi by ID public function show($id) { - $transaksi = Transaksi::with(['kasir', 'sales', 'items.item.produk'])->findOrFail($id); + $transaksi = Transaksi::with(['kasir', 'sales', 'items.item.produk.foto'])->findOrFail($id); return response()->json($transaksi); } diff --git a/app/Models/Nampan.php b/app/Models/Nampan.php index 80c8c4f..5044fba 100644 --- a/app/Models/Nampan.php +++ b/app/Models/Nampan.php @@ -13,9 +13,18 @@ class Nampan extends Model protected $fillable = [ 'nama' ]; + protected $appends = ['berat_total']; + public function items() { return $this->hasMany(Item::class, 'id_nampan'); } + + public function getBeratTotalAttribute() + { + return $this->items() + ->join('produks', 'items.id_produk', '=', 'produks.id') + ->sum('produks.berat'); + } } diff --git a/resources/js/components/BrankasList.vue b/resources/js/components/BrankasList.vue index dcdb2fc..0135531 100644 --- a/resources/js/components/BrankasList.vue +++ b/resources/js/components/BrankasList.vue @@ -8,8 +8,8 @@
Product Image diff --git a/resources/js/components/InputField.vue b/resources/js/components/InputField.vue new file mode 100644 index 0000000..b82db89 --- /dev/null +++ b/resources/js/components/InputField.vue @@ -0,0 +1,28 @@ + + + diff --git a/resources/js/components/InputSelect.vue b/resources/js/components/InputSelect.vue new file mode 100644 index 0000000..1a3833e --- /dev/null +++ b/resources/js/components/InputSelect.vue @@ -0,0 +1,31 @@ + + + diff --git a/resources/js/components/TrayList.vue b/resources/js/components/TrayList.vue index eea9b85..86dd17f 100644 --- a/resources/js/components/TrayList.vue +++ b/resources/js/components/TrayList.vue @@ -26,34 +26,50 @@ >
-

{{ tray.nama }}

+

{{ tray.nama }}

- - + +
-
-
+
+
+
Product Image
-

{{ item.produk.nama }}

-

{{ item.produk.id }}

+

{{ item.produk.nama }}

+

{{ item.produk.kategori }}

+

{{ item.produk.harga_jual.toLocaleString() }}

- {{ item.berat }}g + {{ item.produk.berat }}g
@@ -85,6 +101,8 @@ const props = defineProps({ }, }); +const emit = defineEmits(["edit", "delete"]) + const trays = ref([]); const loading = ref(true); const error = ref(null); @@ -92,15 +110,29 @@ const error = ref(null); // hitung total berat const totalWeight = (tray) => { if (!tray.items) return 0; - return tray.items.reduce((sum, item) => sum + (item.berat || 0), 0); + return tray.items.reduce((sum, item) => sum + (item.produk.berat || 0), 0); }; // ambil data dari backend onMounted(async () => { try { - const res = await axios.get("/api/nampan"); - trays.value = res.data; // harus array tray dengan items - console.log("Data nampan:", res.data); + const [nampanRes, itemRes] = await Promise.all([ + axios.get("/api/nampan"), + axios.get("/api/item") + ]); + + const nampans = nampanRes.data; + const items = itemRes.data; + + // mapping items ke nampan + trays.value = nampans.map(tray => { + return { + ...tray, + items: items.filter(item => item.id_nampan === tray.id) + }; + }); + + console.log("Nampan dengan items:", trays.value); } catch (err) { error.value = err.message || "Gagal mengambil data"; } finally { diff --git a/resources/js/pages/InputProduk.vue b/resources/js/pages/InputProduk.vue new file mode 100644 index 0000000..8ecfacd --- /dev/null +++ b/resources/js/pages/InputProduk.vue @@ -0,0 +1,384 @@ + + + diff --git a/resources/js/pages/Tray.vue b/resources/js/pages/Tray.vue index fde2d7e..a867775 100644 --- a/resources/js/pages/Tray.vue +++ b/resources/js/pages/Tray.vue @@ -1,16 +1,141 @@ \ No newline at end of file +import searchbar from '../components/searchbar.vue' +import TrayList from '../components/TrayList.vue' + +const searchQuery = ref("") // buat search +const showModal = ref(false) // <-- ini penting, biar tidak undefined +const trayName = ref("") // nama nampan baru + +// buka modal +const openModal = () => { + showModal.value = true +} + +// tutup modal +const closeModal = () => { + trayName.value = "" + editingTrayId.value = null + showModal.value = false +} +// simpan nampan baru +const saveTray = async () => { + if (!trayName.value.trim()) { + alert("Nama Nampan tidak boleh kosong") + return + } + + try { + if (editingTrayId.value) { + // mode edit + await axios.put(`/api/nampan/${editingTrayId.value}`, { nama: trayName.value }) + alert("Nampan berhasil diupdate") + } else { + // mode tambah + await axios.post("/api/nampan", { nama: trayName.value }) + alert("Nampan berhasil ditambahkan") + } + closeModal() + location.reload() + } catch (error) { + console.error(error) + alert("Gagal menyimpan nampan") + } +} + + +// kosongkan semua nampan +const emptyTray = async () => { + if (!confirm("Yakin ingin memindahkan semua item ke Brankas?")) return + + try { + await axios.post("/api/brankas", { action: "move_all_from_tray" }) + alert("Semua item berhasil dipindahkan ke Brankas") + location.reload() + } catch (error) { + console.error(error) + alert("Gagal mengosongkan nampan") + } +} + +const editTray = (tray) => { + // buka modal edit, bisa pake sama seperti modal tambah + trayName.value = tray.nama + editingTrayId.value = tray.id + showModal.value = true +} + +const deleteTray = async (id) => { + if (!confirm("Yakin ingin menghapus nampan ini?")) return + try { + await axios.delete(`/api/nampan/${id}`) + alert("Nampan berhasil dihapus") + location.reload() + } catch (error) { + console.error(error) + alert("Gagal menghapus nampan") + } +} + +const editingTrayId = ref(null) + + diff --git a/resources/js/router/index.js b/resources/js/router/index.js index f7e4dec..a08aa7a 100644 --- a/resources/js/router/index.js +++ b/resources/js/router/index.js @@ -3,6 +3,7 @@ import Home from '../pages/Home.vue' import Produk from '../pages/Produk.vue' import Brankas from '../pages/Brankas.vue' import Tray from '../pages/Tray.vue' +import InputProduk from '../pages/InputProduk.vue' const routes = [ @@ -16,6 +17,11 @@ const routes = [ name: 'Produk', component: Produk }, + { + path: '/produk/baru', + name: 'Produk', + component: InputProduk + }, { path: '/brankas', name: 'Brankas', diff --git a/routes/web.php b/routes/web.php index d6cbe28..fcdea34 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,10 +19,12 @@ Route::prefix('api')->group(function () { Route::apiResource('transaksi', TransaksiController::class); Route::get('brankas', [ItemController::class, 'brankasItem']); + + // Foto Sementara Route::post('foto/upload', [FotoSementaraController::class, 'upload']); - Route::delete('foto/hapus/', [FotoSementaraController::class, 'hapus']); - Route::get('foto/', [FotoSementaraController::class, 'getAll']); - Route::delete('foto/reset/', [FotoSementaraController::class, 'reset']); + Route::delete('foto/hapus/{id}', [FotoSementaraController::class, 'hapus']); + Route::get('foto/{user_id}', [FotoSementaraController::class, 'getAll']); + Route::delete('foto/reset/{user_id}', [FotoSementaraController::class, 'reset']); }); // Frontend SPA