Merge branch 'production' of https://git.abbauf.com/Magang-2025/Kasir into production

This commit is contained in:
timotiabbauftech 2025-09-12 11:45:24 +07:00
commit 380ede5ca8
10 changed files with 371 additions and 357 deletions

7
.dockerignore Normal file
View File

@ -0,0 +1,7 @@
node_modules
vendor
.env
Dockerfile
docker-compose.yml
.git
.gitignore

29
Dockerfile Normal file
View File

@ -0,0 +1,29 @@
# Stage 1: Build Vue
FROM node:20 as node_builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Laravel
FROM php:8.3-fpm
RUN apt-get update && apt-get install -y \
git unzip libzip-dev libpng-dev libonig-dev libxml2-dev curl \
&& docker-php-ext-install pdo_mysql zip gd mbstring exif pcntl bcmath
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# Copy source code KECUALI public (biar ga ketiban build Vue)
COPY . .
# Copy hasil build Vue dari stage 1
COPY --from=node_builder /app/dist /var/www/html/public
RUN composer install --no-dev --optimize-autoloader
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
EXPOSE 9000
CMD ["php-fpm"]

View File

@ -2,80 +2,73 @@
namespace App\Exports; namespace App\Exports;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithStyles; use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class RingkasanExport implements FromCollection, WithHeadings, WithTitle, WithStyles class RingkasanExport implements FromCollection, WithHeadings, WithStyles
{ {
private $data; private $data;
private $page;
public function __construct(iterable $data, $page = 1) public function __construct(iterable $data)
{ {
$this->data = $data; $this->data = $data;
$this->page = $page;
} }
public function collection(): Collection
public function collection()
{ {
$collection = collect(); $rows = collect();
$items = method_exists($this->data, 'items') ? $this->data->items() : $this->data;
foreach ($items as $item) { foreach ($this->data as $item) {
$collection->push([ $tanggal = $item['tanggal'] ?? '-';
'Tanggal' => $item['tanggal'] ?? '-', $totalItem = $item['total_item'] ?? 0;
'Total Item Terjual' => $item['total_item_terjual'] ?? 0, $totalBerat = $item['total_berat'] ?? '0 g';
'Total Berat' => $item['total_berat'] ?? 0, $totalPendapatan = $item['total_pendapatan'] ?? 'Rp 0';
'Total Pendapatan' => $item['total_pendapatan'] ?? 0,
'Detail Sales' => $this->formatSalesData($item['sales'] ?? []), // Tambahkan detail sales per baris
foreach ($item['sales'] ?? [] as $sale) {
$rows->push([
'Tanggal' => $tanggal,
'Nama Sales' => $sale['nama'] ?? 'Sales Tidak Dikenal',
'Item Terjual' => $sale['item_terjual'] ?? 0,
'Berat' => $sale['berat'] ?? '-',
'Pendapatan' => $sale['pendapatan'] ?? '-',
]); ]);
} }
return $collection; // Tambahkan baris total
$rows->push([
'Tanggal' => $tanggal,
'Nama Sales' => 'TOTAL',
'Item Terjual' => $totalItem,
'Berat' => $totalBerat,
'Pendapatan' => $totalPendapatan,
]);
// Tambahkan baris kosong biar rapi
$rows->push(['Tanggal' => '', 'Nama Sales' => '', 'Item Terjual' => '', 'Berat' => '', 'Pendapatan' => '']);
}
return $rows;
} }
public function headings(): array public function headings(): array
{ {
return [ return [
'Tanggal', 'Tanggal',
'Total Item Terjual', 'Nama Sales',
'Total Berat', 'Item Terjual',
'Total Pendapatan', 'Berat',
'Detail Sales' 'Pendapatan',
]; ];
} }
public function title(): string
{
return "Ringkasan Halaman {$this->page}";
}
public function styles(Worksheet $sheet) public function styles(Worksheet $sheet)
{ {
return [ return [
1 => ['font' => ['bold' => true]], 1 => ['font' => ['bold' => true]], // Header bold
]; ];
} }
private function formatSalesData($sales): string
{
if (empty($sales)) {
return '-';
}
$formatted = [];
foreach ($sales as $sale) {
$nama = $sale['nama'] ?? 'Sales Tidak Dikenal';
$itemTerjual = $sale['item_terjual'] ?? 0;
$pendapatan = $sale['pendapatan'] ?? '-';
$formatted[] = "{$nama}: {$itemTerjual} item, {$pendapatan}";
}
return implode('; ', $formatted);
}
} }

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Models\Transaksi; use App\Models\Transaksi;
use App\Models\ItemTransaksi; use App\Models\ItemTransaksi;
use App\Models\Item; use App\Models\Item;
use App\Models\Sales;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -20,17 +21,12 @@ class TransaksiController extends Controller
} }
$transaksi = $query->get(); $transaksi = $query->get();
// Mapping agar sesuai dengan kebutuhan frontend $transaksi->each(function ($transaksi) {
$mapped = $transaksi->map(function ($trx) { $transaksi->total_items = $transaksi->itemTransaksi->count();
return [ $transaksi->tanggal = $transaksi->created_at->format('d/m/Y');
'id' => $trx->id,
'tanggal' => $trx->created_at->format('d/m/Y'),
'kode' => 'TRX-' . str_pad($trx->id, 6, '0', STR_PAD_LEFT),
'pendapatan'=> $trx->total_harga,
];
}); });
return response()->json($transaksi);
return response()->json($mapped);
} }
@ -44,31 +40,34 @@ class TransaksiController extends Controller
// Membuat transaksi baru // Membuat transaksi baru
public function store(Request $request) public function store(Request $request)
{ {
// Ambil user yang login via Sanctum $kasir = $request->user();
$kasir = $request->user(); // user authenticated
if (!$kasir) { if (!$kasir) {
return response()->json(['error' => 'Unauthorized'], 401); return response()->json(['error' => 'Unauthorized'], 401);
} }
// Validasi request (id_kasir dihapus karena otomatis dari token)
$request->validate([ $request->validate([
'id_sales' => 'nullable|exists:sales,id', 'id_sales' => 'required|exists:sales,id',
'nama_sales' => 'required|string', 'nama_pembeli' => 'required|string',
'no_hp' => 'required|string', 'no_hp' => 'required|string',
'alamat' => 'required|string', 'alamat' => 'required|string',
'ongkos_bikin' => 'nullable|numeric|min:0', 'ongkos_bikin' => 'nullable|numeric|min:0',
'total_harga' => 'required|numeric', 'total_harga' => 'required|numeric',
'items' => 'required|array', 'items' => 'required|array',
'items.*.id_item' => 'required|exists:items,id', 'items.*.kode_item' => 'required|exists:items,id',
'items.*.harga_deal' => 'required|numeric', 'items.*.harga_deal' => 'required|numeric',
]); ]);
DB::beginTransaction(); DB::beginTransaction();
try { try {
$sales = Sales::find($request->id_sales);
$transaksi = Transaksi::create([ $transaksi = Transaksi::create([
'id_kasir' => $kasir->id, // ambil dari token 'kode_transaksi' => 'belum pak',
'id_kasir' => $kasir->id,
'id_sales' => $request->id_sales, 'id_sales' => $request->id_sales,
'nama_sales' => $request->nama_sales, 'nama_sales' => $sales->nama ?? 'N/A',
'nama_pembeli' => $request->nama_pembeli,
'no_hp' => $request->no_hp, 'no_hp' => $request->no_hp,
'alamat' => $request->alamat, 'alamat' => $request->alamat,
'ongkos_bikin' => $request->ongkos_bikin ?? 0, 'ongkos_bikin' => $request->ongkos_bikin ?? 0,
@ -76,13 +75,24 @@ class TransaksiController extends Controller
]); ]);
foreach ($request->items as $it) { foreach ($request->items as $it) {
// TODO: ubah saat transaksi pake kode_item
// $item = Item::where('kode_item', $it['kode_item'])->first();
// if (!$item) {
// throw new \Exception("Item dengan kode_item {$it['kode_item']} tidak ditemukan.");
// }
$item = Item::find($it['kode_item']);
ItemTransaksi::create([ ItemTransaksi::create([
'id_transaksi' => $transaksi->id, 'id_transaksi' => $transaksi->id,
'id_item' => $it['id_item'], 'id_item' => $item->id,
'harga_deal' => $it['harga_deal'], 'harga_deal' => $it['harga_deal'],
'posisi_asal' => $item->nampan ? 'Nampan ' . $item->nampan->nama : 'Brankas',
]); ]);
Item::where('id', $it['id_item'])->update(['is_sold' => true]); $item->update([
'is_sold' => true,
'id_nampan' => null,
]);
} }
DB::commit(); DB::commit();
@ -90,7 +100,6 @@ class TransaksiController extends Controller
$transaksi->load(['itemTransaksi.item.produk.foto', 'kasir', 'sales']), $transaksi->load(['itemTransaksi.item.produk.foto', 'kasir', 'sales']),
201 201
); );
} catch (\Exception $e) { } catch (\Exception $e) {
DB::rollBack(); DB::rollBack();
return response()->json([ return response()->json([
@ -107,7 +116,12 @@ class TransaksiController extends Controller
$transaksi = Transaksi::findOrFail($id); $transaksi = Transaksi::findOrFail($id);
$transaksi->update($request->only([ $transaksi->update($request->only([
'id_sales', 'nama_sales', 'no_hp', 'alamat', 'ongkos_bikin', 'total_harga' 'id_sales',
'nama_sales',
'no_hp',
'alamat',
'ongkos_bikin',
'total_harga'
])); ]));
return response()->json($transaksi); return response()->json($transaksi);

View File

@ -11,7 +11,7 @@ class Transaksi extends Model
use HasFactory; use HasFactory;
protected $fillable = [ protected $fillable = [
'kode_transaksi', // ✅ Tambahin kolom kode transaksi 'kode_transaksi',
'id_kasir', 'id_kasir',
'id_sales', 'id_sales',
'nama_sales', 'nama_sales',
@ -25,14 +25,12 @@ class Transaksi extends Model
protected $hidden = ['updated_at', 'deleted_at']; protected $hidden = ['updated_at', 'deleted_at'];
// ✅ Auto-generate kode_transaksi saat create
protected static function boot() protected static function boot()
{ {
parent::boot(); parent::boot();
// Setelah transaksi berhasil dibuat (sudah punya ID)
static::created(function ($transaksi) { static::created(function ($transaksi) {
if (!$transaksi->kode_transaksi) { if (!$transaksi->kode_transaksi || $transaksi->kode_transaksi === 'belum pak') {
$prefix = "TRS"; $prefix = "TRS";
$date = $transaksi->created_at->format('Ymd'); $date = $transaksi->created_at->format('Ymd');
$number = str_pad($transaksi->id, 4, '0', STR_PAD_LEFT); $number = str_pad($transaksi->id, 4, '0', STR_PAD_LEFT);

39
docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
services:
laravel:
build:
context: .
dockerfile: Dockerfile
container_name: laravel_app
volumes:
- .:/var/www/html
command: php artisan serve --host=0.0.0.0 --port=8000
ports:
- "8000:8000"
depends_on:
- mysql
environment:
APP_ENV: local
APP_KEY: ${APP_KEY}
DB_CONNECTION: mysql
DB_HOST: mysql
DB_PORT: 3306
DB_DATABASE: ${DB_DATABASE}
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
mysql:
image: mysql:8
container_name: mysql_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: laravel
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:

View File

@ -1,22 +1,9 @@
<template> <template>
<ConfirmDeleteModal <ConfirmDeleteModal v-if="showDeleteModal" :isOpen="showDeleteModal" title="Konfirmasi"
v-if="showDeleteModal" message="Yakin ingin menghapus item ini?" @confirm="hapusPesanan" @cancel="closeDeleteModal" />
:isOpen="showDeleteModal"
title="Konfirmasi"
message="Yakin ingin menghapus item ini?"
@confirm="hapusPesanan"
@cancel="closeDeleteModal"
/>
<!-- ==== TAMBAHAN: Struk Overlay ==== --> <!-- ==== TAMBAHAN: Struk Overlay ==== -->
<StrukOverlay <StrukOverlay v-if="showStruk" :isOpen="showStruk" :pesanan="pesanan" :total="total" @close="closeStruk"/>
v-if="showStruk"
:isOpen="showStruk"
:pesanan="pesanan"
:total="total"
@close="closeStruk"
@confirm="simpanTransaksi"
/>
<!-- ==== END TAMBAHAN ==== --> <!-- ==== END TAMBAHAN ==== -->
<div class="p-2 sm:p-4"> <div class="p-2 sm:p-4">
@ -27,27 +14,14 @@
<!-- Input Kode Item --> <!-- Input Kode Item -->
<div> <div>
<label class="block text-sm font-medium text-D">Kode Item *</label> <label class="block text-sm font-medium text-D">Kode Item *</label>
<div <div class="flex flex-row justify-between mt-1 w-full rounded-md bg-A shadow-sm sm:text-sm border-B">
class="flex flex-row justify-between mt-1 w-full rounded-md bg-A shadow-sm sm:text-sm border-B" <input type="text" v-model="kodeItem" @keyup.enter="inputItem" placeholder="Scan atau masukkan kode item"
> class="bg-A focus:outline-none focus:border-C focus:ring focus:ring-D focus:ring-opacity-50 p-2 w-full rounded-l-md" />
<input <button v-if="!loadingItem" @click="inputItem" class="px-3 bg-D hover:bg-D/80 text-A rounded-r-md">
type="text"
v-model="kodeItem"
@keyup.enter="inputItem"
placeholder="Scan atau masukkan kode item"
class="bg-A focus:outline-none focus:border-C focus:ring focus:ring-D focus:ring-opacity-50 p-2 w-full rounded-l-md"
/>
<button
v-if="!loadingItem"
@click="inputItem"
class="px-3 bg-D hover:bg-D/80 text-A rounded-r-md"
>
<i class="fas fa-arrow-right"></i> <i class="fas fa-arrow-right"></i>
</button> </button>
<div v-else class="flex items-center justify-center px-3"> <div v-else class="flex items-center justify-center px-3">
<div <div class="rounded-full h-5 w-5 border-b-2 border-A flex items-center justify-center">
class="rounded-full h-5 w-5 border-b-2 border-A flex items-center justify-center"
>
<i class="fas fa-spinner"></i> <i class="fas fa-spinner"></i>
</div> </div>
</div> </div>
@ -57,25 +31,17 @@
<!-- Input Harga Jual --> <!-- Input Harga Jual -->
<div> <div>
<label class="block text-sm font-medium text-D">Harga Jual</label> <label class="block text-sm font-medium text-D">Harga Jual</label>
<InputField <InputField v-model="hargaJual" type="number" placeholder="Masukkan Harga Jual" />
v-model="hargaJual"
type="number"
placeholder="Masukkan Harga Jual"
/>
</div> </div>
<!-- Tombol Aksi --> <!-- Tombol Aksi -->
<div class="flex flex-col sm:flex-row justify-between gap-2"> <div class="flex flex-col sm:flex-row justify-between gap-2">
<button <button @click="tambahItem"
@click="tambahItem" class="w-full sm:w-auto px-4 py-2 rounded-md bg-C text-D font-medium hover:bg-C/80 transition">
class="w-full sm:w-auto px-4 py-2 rounded-md bg-C text-D font-medium hover:bg-C/80 transition"
>
Tambah Item Tambah Item
</button> </button>
<button <button @click="konfirmasiPenjualan"
@click="konfirmasiPenjualan" class="w-full sm:w-auto px-6 py-2 rounded-md bg-D text-A font-semibold hover:bg-D/80 transition">
class="w-full sm:w-auto px-6 py-2 rounded-md bg-D text-A font-semibold hover:bg-D/80 transition"
>
Lanjut Lanjut
</button> </button>
</div> </div>
@ -94,11 +60,7 @@
<!-- Error & Info --> <!-- Error & Info -->
<div class="mb-4"> <div class="mb-4">
<p <p v-if="error" :class="{ 'animate-shake': error }" class="text-sm text-red-600 mt-1">
v-if="error"
:class="{ 'animate-shake': error }"
class="text-sm text-red-600 mt-1"
>
{{ error }} {{ error }}
</p> </p>
<p v-if="info" class="text-sm text-C mt-1">{{ info }}</p> <p v-if="info" class="text-sm text-C mt-1">{{ info }}</p>
@ -106,9 +68,7 @@
<!-- Table Responsive --> <!-- Table Responsive -->
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table <table class="w-full border border-B rounded-lg overflow-hidden text-xs sm:text-sm">
class="w-full border border-B rounded-lg overflow-hidden text-xs sm:text-sm"
>
<thead class="bg-A text-D"> <thead class="bg-A text-D">
<tr> <tr>
<th class="border border-B p-2 w-8">No</th> <th class="border border-B p-2 w-8">No</th>
@ -124,12 +84,7 @@
Belum ada item dipesan Belum ada item dipesan
</td> </td>
</tr> </tr>
<tr <tr v-else v-for="(item, index) in pesanan" :key="index" class="hover:bg-gray-50 text-center">
v-else
v-for="(item, index) in pesanan"
:key="index"
class="hover:bg-gray-50 text-center"
>
<td class="border border-B p-2">{{ index + 1 }}</td> <td class="border border-B p-2">{{ index + 1 }}</td>
<td class="border border-B p-2 text-left truncate max-w-[120px] sm:max-w-none"> <td class="border border-B p-2 text-left truncate max-w-[120px] sm:max-w-none">
{{ item.produk.nama }} {{ item.produk.nama }}
@ -141,10 +96,7 @@
Rp{{ item.harga_deal.toLocaleString() }} Rp{{ item.harga_deal.toLocaleString() }}
</td> </td>
<td class="border border-B p-2 text-center"> <td class="border border-B p-2 text-center">
<button <button @click="openDeleteModal(index)" class="text-red-500 hover:text-red-700">
@click="openDeleteModal(index)"
class="text-red-500 hover:text-red-700"
>
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</td> </td>
@ -162,9 +114,7 @@ import { ref, computed } from "vue";
import InputField from "./InputField.vue"; import InputField from "./InputField.vue";
import axios from "axios"; import axios from "axios";
import ConfirmDeleteModal from "./ConfirmDeleteModal.vue"; import ConfirmDeleteModal from "./ConfirmDeleteModal.vue";
// ==== TAMBAHAN: Import StrukOverlay ====
import StrukOverlay from "./StrukOverlay.vue"; import StrukOverlay from "./StrukOverlay.vue";
// ==== END TAMBAHAN ====
const kodeItem = ref(""); const kodeItem = ref("");
const info = ref(""); const info = ref("");
@ -176,13 +126,7 @@ const pesanan = ref([]);
const showDeleteModal = ref(false) const showDeleteModal = ref(false)
const deleteIndex = ref(null) const deleteIndex = ref(null)
// ==== TAMBAHAN: State untuk struk ====
const showStruk = ref(false); const showStruk = ref(false);
// ==== END TAMBAHAN ====
// ==== TAMBAHAN: Emit untuk parent component ====
const emit = defineEmits(['transaksi-saved']);
// ==== END TAMBAHAN ====
let errorTimeout = null; let errorTimeout = null;
let infoTimeout = null; let infoTimeout = null;
@ -212,8 +156,7 @@ const inputItem = async () => {
if (pesanan.value.some((p) => p.id === item.value.id)) { if (pesanan.value.some((p) => p.id === item.value.id)) {
throw new Error("Item sedang dipesan"); throw new Error("Item sedang dipesan");
} }
info.value = `Item dipilih: ${item.value.produk.nama} dari ${ info.value = `Item dipilih: ${item.value.produk.nama} dari ${item.value.posisi ? item.value.posisi : "Brankas"
item.value.posisi ? item.value.posisi : "Brankas"
}`; }`;
infoTimeout = setTimeout(() => { infoTimeout = setTimeout(() => {
@ -252,6 +195,7 @@ const tambahItem = () => {
} }
// harga deal // harga deal
item.value.kode_item = kodeItem.value;
item.value.harga_deal = Number(hargaJual.value); item.value.harga_deal = Number(hargaJual.value);
pesanan.value.push(item.value); pesanan.value.push(item.value);
@ -303,46 +247,6 @@ const closeStruk = () => {
}; };
// ==== END TAMBAHAN ==== // ==== END TAMBAHAN ====
// ==== TAMBAHAN: Fungsi untuk menyimpan transaksi ====
const simpanTransaksi = async (dataTransaksi) => {
try {
// Siapkan data untuk API
const transaksiData = {
id_kasir: localStorage.getItem('user_id'), // Asumsi user_id disimpan di localStorage
id_sales: dataTransaksi.selectedSales?.id || null,
nama_sales: dataTransaksi.namaPembeli,
no_hp: dataTransaksi.nomorTelepon,
alamat: dataTransaksi.alamat,
ongkos_bikin: dataTransaksi.ongkosBikin || 0,
total_harga: total.value + (dataTransaksi.ongkosBikin || 0),
items: pesanan.value.map(item => ({
id_item: item.id,
harga_deal: item.harga_deal
}))
};
const response = await axios.post('/api/transaksi', transaksiData, {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
// Reset form setelah berhasil
pesanan.value = [];
showStruk.value = false;
// Emit ke parent untuk refresh data transaksi
emit('transaksi-saved', response.data);
alert('Transaksi berhasil disimpan!');
} catch (error) {
console.error('Error saving transaksi:', error);
alert('Error menyimpan transaksi: ' + (error.response?.data?.message || error.message));
}
};
// ==== END TAMBAHAN ====
const total = computed(() => { const total = computed(() => {
let sum = 0; let sum = 0;
pesanan.value.forEach((item) => { pesanan.value.forEach((item) => {

View File

@ -13,8 +13,8 @@
<tbody> <tbody>
<tr v-for="trx in props.transaksi" :key="trx.id" class="hover:bg-A"> <tr v-for="trx in props.transaksi" :key="trx.id" class="hover:bg-A">
<td class="border border-B p-2">{{ trx.tanggal }}</td> <td class="border border-B p-2">{{ trx.tanggal }}</td>
<td class="border border-B p-2">{{ trx.kode }}</td> <td class="border border-B p-2">{{ trx.kode_transaksi }}</td>
<td class="border border-B p-2">Rp{{ (trx.pendapatan || 0).toLocaleString() }}</td> <td class="border border-B p-2">Rp{{ (trx.total_harga || 0).toLocaleString() }}</td>
<td class="border border-B p-2 text-center"> <td class="border border-B p-2 text-center">
<button <button
@click="$emit('detail', trx)" @click="$emit('detail', trx)"

View File

@ -260,7 +260,7 @@ const fetchSales = async () => {
}) })
salesOptions.value = response.data.map(sales => ({ salesOptions.value = response.data.map(sales => ({
value: sales, value: sales.id,
label: sales.nama label: sales.nama
})) }))
@ -298,14 +298,41 @@ const handleSimpan = () => {
} }
// Emit data ke parent // Emit data ke parent
emit('confirm', { simpanTransaksi({
namaPembeli: namaPembeli.value, id_sales: selectedSales.value,
nomorTelepon: nomorTelepon.value, nama_pembeli: namaPembeli.value,
no_hp: nomorTelepon.value,
alamat: alamat.value, alamat: alamat.value,
ongkosBikin: ongkosBikin.value || 0, ongkosBikin: ongkosBikin.value || 0,
selectedSales: selectedSales.value total_harga: grandTotal.value,
items: props.pesanan
}) })
} }
// ==== TAMBAHAN: Fungsi untuk menyimpan transaksi ====
const simpanTransaksi = async (dataTransaksi) => {
console.log('Data transaksi yang akan disimpan:', dataTransaksi);
try {
const response = await axios.post('/api/transaksi', dataTransaksi, {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
// Reset form setelah berhasil
props.pesanan.value = [];
props.isOpen = false;
alert('Transaksi berhasil disimpan!');
window.location.reload();
} catch (error) {
console.error('Error saving transaksi:', error);
alert('Error menyimpan transaksi: ' + (error.response?.data?.message || error.message));
}
};
// ==== END TAMBAHAN ====
// ==== END TAMBAHAN ==== // ==== END TAMBAHAN ====
// ==== TAMBAHAN: Fetch sales saat component mounted ==== // ==== TAMBAHAN: Fetch sales saat component mounted ====

View File

@ -95,6 +95,9 @@ const fetchTransaksi = async () => {
}); });
transaksi.value = res.data; transaksi.value = res.data;
console.log("Fetched transaksi:", transaksi.value);
} catch (err) { } catch (err) {
console.error("Gagal fetch transaksi:", err); console.error("Gagal fetch transaksi:", err);
} finally { } finally {