Files
BarangaySystem/resources/js/Pages/ShipmentList.vue
2026-06-06 18:43:00 +08:00

101 lines
3.9 KiB
Vue

<script setup>
import { ref, onMounted, computed } from 'vue';
import axios from 'axios';
import { usePageTitle } from '../composables/Core/usePageTitle';
import { useNavigate } from '../composables/Core/useNavigate';
import LoadingSpinner from '../Components/LoadingSpinner.vue';
import TransactionListSkeleton from '../Components/Core/Skeleton/TransactionListSkeleton.vue';
import SearchBar from '../Components/Core/Search/SearchBar.vue';
usePageTitle('Shipment Tracking');
const { navigate } = useNavigate();
const shipments = ref([]);
const loading = ref(true);
const searchQuery = ref('');
const fetchShipments = async () => {
loading.value = true;
try {
const response = await axios.post('/Shipments/List');
if (response.data.success) {
shipments.value = response.data.data;
}
} catch (error) {
console.error('Failed to fetch shipments:', error);
} finally {
loading.value = false;
}
};
const getStatusBadgeClass = (status) => {
switch (status) {
case 'DELIVERED': return 'badge bg-success';
case 'IN_TRANSIT': return 'badge bg-primary';
case 'PENDING': return 'badge bg-warning text-dark';
case 'FAILED': return 'badge bg-danger';
default: return 'badge bg-secondary';
}
};
const filteredShipments = computed(() => {
if (!searchQuery.value) return shipments.value;
const q = searchQuery.value.toLowerCase();
return shipments.value.filter(s =>
s.tracking_number?.toLowerCase().includes(q) ||
s.customer?.name?.toLowerCase().includes(q) ||
s.status?.toLowerCase().includes(q)
);
});
onMounted(fetchShipments);
</script>
<template>
<div class="shipment-list-page pb-5">
<div class="tf-container mt-4">
<div class="d-flex align-items-center justify-content-between mb-4">
<h3 class="fw_6 mb-0">Shipments</h3>
<div class="badge bg-soft-primary px-3 py-2 rounded-pill text-primary">
{{ filteredShipments.length }} active
</div>
</div>
<SearchBar v-model="searchQuery" placeholder="Search by tracking or customer..." class="mb-4" />
<div v-if="loading" class="mt-2 text-center">
<TransactionListSkeleton :count="8" />
</div>
<div v-else-if="filteredShipments.length === 0" class="text-center py-5 border rounded-20 bg-light">
<i class="fas fa-box-open fa-3x text-muted mb-3 opacity-20"></i>
<h5>No shipments found</h5>
<p class="text-muted">Track your deliveries here</p>
</div>
<div v-else class="list-group list-group-flush rounded-20 overflow-hidden border shadow-sm">
<div v-for="shipment in filteredShipments" :key="shipment.hashkey"
class="list-group-item list-group-item-action p-4 border-bottom"
@click="navigate({ page: 'ShipmentDetail', props: { target: shipment.hashkey } })">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="small text-muted mb-1">#{{ shipment.tracking_number || 'No Tracking' }}</div>
<h6 class="mb-1">{{ shipment.customer?.name || 'Unknown Customer' }}</h6>
<div class="small text-muted">
<i class="fas fa-store me-1"></i> {{ shipment.store?.name || 'Direct Sale' }}
</div>
</div>
<span :class="getStatusBadgeClass(shipment.status)">{{ shipment.status }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.rounded-20 { border-radius: 20px; }
.bg-soft-primary { background-color: rgba(66, 185, 131, 0.1); }
.opacity-20 { opacity: 0.2; }
</style>