[Update] Search bersadarkan nama
This commit is contained in:
parent
be38f618a0
commit
dac6f59018
@ -12,13 +12,13 @@ use Illuminate\Support\Facades\DB;
|
|||||||
|
|
||||||
class TransaksiController extends Controller
|
class TransaksiController extends Controller
|
||||||
{
|
{
|
||||||
// List semua transaksi
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$limit = $request->query('limit', 10);
|
$limit = $request->query('limit', 10);
|
||||||
$page = $request->query('page', 1);
|
$page = $request->query('page', 1);
|
||||||
$startDate = $request->query('start_date');
|
$startDate = $request->query('start_date');
|
||||||
$endDate = $request->query('end_date');
|
$endDate = $request->query('end_date');
|
||||||
|
$search = $request->query('search');
|
||||||
|
|
||||||
$query = Transaksi::with(['kasir', 'sales', 'itemTransaksi.produk']);
|
$query = Transaksi::with(['kasir', 'sales', 'itemTransaksi.produk']);
|
||||||
|
|
||||||
@ -35,10 +35,15 @@ class TransaksiController extends Controller
|
|||||||
$query->whereDate('created_at', $today);
|
$query->whereDate('created_at', $today);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Order by latest
|
// Search berdasarkan kode transaksi atau nama pelanggan
|
||||||
$query->latest();
|
if ($search) {
|
||||||
|
$query->where(function ($q) use ($search) {
|
||||||
|
$q->where('kode_transaksi', 'like', '%' . $search . '%')
|
||||||
|
->orWhere('nama_pembeli', 'like', '%' . $search . '%');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Pagination
|
$query->latest();
|
||||||
$transaksi = $query->paginate($limit, ['*'], 'page', $page);
|
$transaksi = $query->paginate($limit, ['*'], 'page', $page);
|
||||||
|
|
||||||
// Transform data
|
// Transform data
|
||||||
|
|||||||
@ -4,37 +4,22 @@
|
|||||||
<hr class="border-B mb-5" />
|
<hr class="border-B mb-5" />
|
||||||
|
|
||||||
<!-- Filter Section -->
|
<!-- Filter Section -->
|
||||||
<div class="flex flex-col lg:flex-row my-3 gap-3 lg:gap-5">
|
<div class="flex flex-col md:flex-row justify-between my-3 gap-3 md:gap-5">
|
||||||
<!-- Date Range Filter -->
|
<!-- Date Range Filter -->
|
||||||
<div class="w-full lg:w-1/3">
|
<div class="w-full md:w-1/3">
|
||||||
<DatePicker
|
<DatePicker v-model="dateRange" label="Filter Tanggal" placeholder="Pilih rentang tanggal" :max-days="31"
|
||||||
v-model="dateRange"
|
@change="handleDateChange" />
|
||||||
label="Filter Tanggal"
|
|
||||||
placeholder="Pilih rentang tanggal"
|
|
||||||
:max-days="31"
|
|
||||||
@change="handleDateChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Section - Improved Responsiveness -->
|
<div class="flex flex-col sm:flex-row w-full md:w-1/3">
|
||||||
<div class="flex flex-col sm:flex-row w-full gap-2 lg:gap-3">
|
|
||||||
<!-- Search Input Container -->
|
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<InputField
|
<input placeholder="Cari kode transaksi atau nama pembeli" v-model="searchQuery"
|
||||||
placeholder="Cari kode transaksi..."
|
class="mt-1 block w-full rounded-l-md shadow-sm sm:text-sm bg-A text-D border-B focus:border-C focus:ring focus:outline-none focus:ring-D focus:ring-opacity-50 p-2" />
|
||||||
v-model="searchQuery"
|
|
||||||
class="w-full"
|
|
||||||
@input="handleSearch"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<!-- Reset Button -->
|
<button @click="handleSearch"
|
||||||
<div class="flex-shrink-0">
|
class="mt-1 px-4 py-2 bg-C hover:bg-C/80 text-D rounded-r-md text-sm font-medium transition-colors">
|
||||||
<button
|
Cari
|
||||||
@click="handleResetFilter"
|
|
||||||
class="w-full sm:w-auto px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors whitespace-nowrap text-sm font-medium"
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,10 +46,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</th>
|
</th>
|
||||||
<th class="border-x border-C px-3 py-3 text-left">
|
<th class="border-x border-C px-3 py-3 text-left">
|
||||||
<button @click="handleSort('total_harga')"
|
<button @click="handleSort('nama_pembeli')"
|
||||||
class="flex items-center justify-between w-full hover:text-D/80 transition-colors">
|
class="flex items-center justify-between w-full hover:text-D/80 transition-colors">
|
||||||
<span>Nama Pembeli</span>
|
<span>Nama Pembeli</span>
|
||||||
<i :class="getSortIcon('total_harga')" class="ml-2"></i>
|
<i :class="getSortIcon('nama_pembeli')" class="ml-2"></i>
|
||||||
</button>
|
</button>
|
||||||
</th>
|
</th>
|
||||||
<th class="border-x border-C px-3 py-3 text-left">
|
<th class="border-x border-C px-3 py-3 text-left">
|
||||||
@ -125,13 +110,14 @@
|
|||||||
{{ trx.kode_transaksi }}
|
{{ trx.kode_transaksi }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<!-- Nama pembeli -->
|
||||||
<td class="text-sm border-x border-C px-3 py-3">
|
<td class="text-sm border-x border-C px-3 py-3">
|
||||||
{{ trx.nama_pembeli }}
|
{{ trx.nama_pembeli || '-' }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Total -->
|
<!-- Total -->
|
||||||
<td class="text-sm border-x border-C px-3 py-3 text-center">
|
<td class="text-sm border-x border-C px-3 py-3 text-center">
|
||||||
Rp{{ (trx.total_harga || 0).toLocaleString() }}
|
Rp{{ (trx.total_harga || 0).toLocaleString('id-ID') }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Jumlah Item -->
|
<!-- Jumlah Item -->
|
||||||
@ -141,11 +127,9 @@
|
|||||||
|
|
||||||
<!-- Aksi -->
|
<!-- Aksi -->
|
||||||
<td class="border-r border-C px-3 py-3 text-center">
|
<td class="border-r border-C px-3 py-3 text-center">
|
||||||
<button
|
<button @click="lihatDetail(trx)"
|
||||||
@click="lihatDetail(trx)"
|
|
||||||
class="inline-flex items-center px-3 py-1.5 bg-C hover:bg-C/80 text-D rounded-md text-xs font-medium transition-colors"
|
class="inline-flex items-center px-3 py-1.5 bg-C hover:bg-C/80 text-D rounded-md text-xs font-medium transition-colors"
|
||||||
:disabled="isDetailLoading"
|
:disabled="isDetailLoading">
|
||||||
>
|
|
||||||
<i v-if="isDetailLoading && selectedTransaksi.id === trx.id"
|
<i v-if="isDetailLoading && selectedTransaksi.id === trx.id"
|
||||||
class="fas fa-spinner fa-spin mr-1"></i>
|
class="fas fa-spinner fa-spin mr-1"></i>
|
||||||
<span>Lihat Detail</span>
|
<span>Lihat Detail</span>
|
||||||
@ -169,11 +153,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button @click="goToPage(pagination.current_page - 1)" :disabled="pagination.current_page === 1 || loading"
|
||||||
@click="goToPage(pagination.current_page - 1)"
|
class="px-3 py-2 text-sm font-medium border rounded-md bg-A border-C disabled:opacity-50 disabled:cursor-not-allowed hover:bg-C/50 transition-colors">
|
||||||
:disabled="pagination.current_page === 1 || loading"
|
|
||||||
class="px-3 py-2 text-sm font-medium border rounded-md bg-A border-C disabled:opacity-50 disabled:cursor-not-allowed hover:bg-C/50 transition-colors"
|
|
||||||
>
|
|
||||||
<i class="fas fa-chevron-left mr-1"></i>
|
<i class="fas fa-chevron-left mr-1"></i>
|
||||||
Sebelumnya
|
Sebelumnya
|
||||||
</button>
|
</button>
|
||||||
@ -182,11 +163,9 @@
|
|||||||
Halaman {{ pagination.current_page }} dari {{ pagination.last_page }}
|
Halaman {{ pagination.current_page }} dari {{ pagination.last_page }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<button
|
<button @click="goToPage(pagination.current_page + 1)"
|
||||||
@click="goToPage(pagination.current_page + 1)"
|
|
||||||
:disabled="pagination.current_page === pagination.last_page || loading"
|
:disabled="pagination.current_page === pagination.last_page || loading"
|
||||||
class="px-3 py-2 text-sm font-medium border rounded-md bg-A border-C disabled:opacity-50 disabled:cursor-not-allowed hover:bg-C/50 transition-colors"
|
class="px-3 py-2 text-sm font-medium border rounded-md bg-A border-C disabled:opacity-50 disabled:cursor-not-allowed hover:bg-C/50 transition-colors">
|
||||||
>
|
|
||||||
Berikutnya
|
Berikutnya
|
||||||
<i class="fas fa-chevron-right ml-1"></i>
|
<i class="fas fa-chevron-right ml-1"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -194,11 +173,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal Detail Transaksi -->
|
<!-- Modal Detail Transaksi -->
|
||||||
<StrukView
|
<StrukView :is-open="isDetailOpen" :transaksi="selectedTransaksi" @close="closeDetail" />
|
||||||
:is-open="isDetailOpen"
|
|
||||||
:transaksi="selectedTransaksi"
|
|
||||||
@close="closeDetail"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Loading Overlay for Detail -->
|
<!-- Loading Overlay for Detail -->
|
||||||
<div v-if="isDetailLoading" class="fixed inset-0 bg-black/60 flex items-center justify-center z-[9999] p-4">
|
<div v-if="isDetailLoading" class="fixed inset-0 bg-black/60 flex items-center justify-center z-[9999] p-4">
|
||||||
@ -214,7 +189,6 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import DatePicker from '@/components/DatePicker.vue'
|
import DatePicker from '@/components/DatePicker.vue'
|
||||||
import InputField from '@/components/InputField.vue'
|
|
||||||
import StrukView from '@/components/StrukView.vue'
|
import StrukView from '@/components/StrukView.vue'
|
||||||
|
|
||||||
// Props & Emits
|
// Props & Emits
|
||||||
@ -252,7 +226,6 @@ const selectedTransaksi = ref({})
|
|||||||
const filteredTransaksi = computed(() => {
|
const filteredTransaksi = computed(() => {
|
||||||
let filtered = [...transaksi.value]
|
let filtered = [...transaksi.value]
|
||||||
|
|
||||||
// Date filter
|
|
||||||
if (dateRange.value.start && dateRange.value.end) {
|
if (dateRange.value.start && dateRange.value.end) {
|
||||||
const startDate = new Date(dateRange.value.start)
|
const startDate = new Date(dateRange.value.start)
|
||||||
const endDate = new Date(dateRange.value.end)
|
const endDate = new Date(dateRange.value.end)
|
||||||
@ -274,15 +247,7 @@ const filteredTransaksi = computed(() => {
|
|||||||
filtered = filtered.filter(trx => trx.metode_pembayaran === pembayaranDipilih.value)
|
filtered = filtered.filter(trx => trx.metode_pembayaran === pembayaranDipilih.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search filter
|
// Removed searchQuery filter to prevent client-side filtering
|
||||||
if (searchQuery.value) {
|
|
||||||
const query = searchQuery.value.toLowerCase()
|
|
||||||
filtered = filtered.filter(trx =>
|
|
||||||
trx.kode_transaksi.toLowerCase().includes(query) ||
|
|
||||||
(trx.nama_pelanggan && trx.nama_pelanggan.toLowerCase().includes(query))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered
|
return filtered
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -303,7 +268,7 @@ const sortedTransaksi = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableColumns = computed(() => 7)
|
const tableColumns = computed(() => 6)
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const fetchTransaksi = async (page = 1) => {
|
const fetchTransaksi = async (page = 1) => {
|
||||||
@ -328,12 +293,9 @@ const fetchTransaksi = async (page = 1) => {
|
|||||||
transaksi.value = response.data.data || []
|
transaksi.value = response.data.data || []
|
||||||
pagination.value = response.data.pagination || null
|
pagination.value = response.data.pagination || null
|
||||||
|
|
||||||
console.log("trans:", transaksi.value);
|
console.log("data", transaksi.value)
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching transaksi:', error)
|
console.error('Error fetching transaksi:', error)
|
||||||
alert('Gagal memuat data transaksi: ' + (error.response?.data?.message || error.message))
|
|
||||||
transaksi.value = []
|
transaksi.value = []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@ -347,12 +309,8 @@ const handleDateChange = (newRange) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
// Debounce search (optional)
|
|
||||||
clearTimeout(window.searchTimeout)
|
|
||||||
window.searchTimeout = setTimeout(() => {
|
|
||||||
pagination.value = null
|
pagination.value = null
|
||||||
fetchTransaksi(1)
|
fetchTransaksi(1)
|
||||||
}, 300)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSort = (field) => {
|
const handleSort = (field) => {
|
||||||
@ -395,7 +353,6 @@ const lihatDetail = async (trx) => {
|
|||||||
total_items: response.data.itemTransaksi?.length || 0
|
total_items: response.data.itemTransaksi?.length || 0
|
||||||
}
|
}
|
||||||
isDetailOpen.value = true
|
isDetailOpen.value = true
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching detail:', error)
|
console.error('Error fetching detail:', error)
|
||||||
alert('Gagal memuat detail transaksi: ' + (error.response?.data?.message || error.message))
|
alert('Gagal memuat detail transaksi: ' + (error.response?.data?.message || error.message))
|
||||||
@ -409,15 +366,6 @@ const closeDetail = () => {
|
|||||||
selectedTransaksi.value = {}
|
selectedTransaksi.value = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResetFilter = () => {
|
|
||||||
const today = new Date().toISOString().split('T')[0]
|
|
||||||
dateRange.value = { start: today, end: today }
|
|
||||||
statusDipilih.value = ''
|
|
||||||
pembayaranDipilih.value = ''
|
|
||||||
searchQuery.value = ''
|
|
||||||
fetchTransaksi(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper Functions
|
// Helper Functions
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
return new Date(dateString).toLocaleDateString('id-ID', {
|
return new Date(dateString).toLocaleDateString('id-ID', {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user