Files
BarangaySystem/resources/js/Components/Market/PosHistoryCard.vue
2026-06-06 18:43:00 +08:00

260 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, computed } from 'vue';
const props = defineProps({
session: {
type: Object,
required: true
}
});
const isExpanded = ref(false);
const toggleExpand = () => {
isExpanded.value = !isExpanded.value;
};
const transactions = computed(() => {
return props.session.transactions || [];
});
const formattedDate = computed(() => {
if (!props.session.created_at) return 'N/A';
const date = new Date(props.session.created_at);
return date.toLocaleString('en-PH', {
month: 'short',
day: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
}).replace(',', ' •');
});
const statusClass = computed(() => {
switch (props.session.status) {
case 'completed': return 'badge-soft-success';
case 'active': return 'badge-soft-primary';
case 'voided': return 'badge-soft-danger';
default: return 'badge-soft-secondary';
}
});
const paymentIcon = computed(() => {
switch (props.session.payment_method?.toLowerCase()) {
case 'cash': return 'fas fa-money-bill-wave';
case 'credit': return 'fas fa-credit-card';
case 'online': return 'fas fa-mobile-alt';
default: return 'fas fa-receipt';
}
});
const formatCurrency = (amount) => {
return new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP'
}).format(amount);
};
const getProductInfo = (transaction) => {
return {
name: transaction.product?.name || 'Unknown Product',
quantity: transaction.quantity || 0,
unitPrice: transaction.price_at_sale || 0,
totalPrice: transaction.total_price || 0
};
};
</script>
<template>
<div class="card mb-3 border-0 shadow-sm rounded-4 overflow-hidden pos-history-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-start mb-2">
<div>
<span :class="['badge rounded-pill px-3 py-2 text-uppercase fw-bold', statusClass]">
{{ session.status }}
</span>
<h6 class="mb-0 mt-2 text-primary fw-bold">
{{ session.customer_name || 'Walk-in Customer' }}
</h6>
<small class="text-muted d-block mt-1">
<i class="far fa-clock me-1"></i> {{ formattedDate }}
</small>
</div>
<div class="text-end">
<h5 class="mb-0 fw-black text-dark">
{{ formatCurrency(session.total_amount) }}
</h5>
<small class="text-muted">
{{ session.items_count }} {{ session.items_count === 1 ? 'item' : 'items' }}
</small>
</div>
</div>
<div class="d-flex align-items-center justify-content-between mt-3 pt-3 border-top border-light">
<div class="d-flex align-items-center">
<div class="payment-icon-wrapper bg-light rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
<i :class="[paymentIcon, 'text-muted sm']"></i>
</div>
<span class="text-muted small text-capitalize">{{ session.payment_method || 'N/A' }}</span>
</div>
<div v-if="session.hashkey" class="text-muted small">
<span class="badge bg-light text-muted fw-normal rounded-pill">#{{ session.hashkey.substring(0, 8) }}</span>
</div>
</div>
<!-- Expandable Items Section -->
<div v-if="isExpanded && transactions.length > 0" class="items-section mt-3 pt-3 border-top border-light">
<div class="items-header d-flex align-items-center mb-2">
<i class="fas fa-box-open text-muted me-2"></i>
<span class="text-muted small fw-bold">Transaction Items</span>
</div>
<div class="items-list">
<div v-for="item in transactions" :key="item.id" class="item-row d-flex align-items-center justify-content-between py-2 border-bottom border-light">
<div class="item-info flex-grow-1">
<div class="item-name text-dark fw-semibold small">
{{ getProductInfo(item).name }}
</div>
<div class="item-qty text-muted small">
{{ getProductInfo(item).quantity }} × {{ formatCurrency(getProductInfo(item).unitPrice) }}
</div>
</div>
<div class="item-total text-end">
<span class="fw-bold text-dark small">
{{ formatCurrency(getProductInfo(item).totalPrice) }}
</span>
</div>
</div>
</div>
</div>
<!-- Footer with Toggle Button -->
<div
v-if="transactions.length > 0"
class="card-footer-toggle d-flex align-items-center justify-content-center mt-3 pt-3 border-top border-light cursor-pointer"
@click="toggleExpand"
>
<span class="text-muted small fw-bold me-2">
{{ isExpanded ? 'Hide Items' : 'View Items' }}
</span>
<i :class="['fas text-muted small', isExpanded ? 'fa-chevron-up' : 'fa-chevron-down']"></i>
</div>
</div>
</div>
</template>
<style scoped>
.pos-history-card {
transition: transform 0.2s, box-shadow 0.2s;
background: var(--bg-card);
}
.pos-history-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0,0,0,0.05) !important;
}
:global(.dark-mode) .pos-history-card {
background: rgba(var(--bg-card-rgb), 0.7);
backdrop-filter: blur(15px);
}
.badge-soft-success {
background-color: rgba(40, 167, 69, 0.1);
color: #28a745;
}
.badge-soft-primary {
background-color: rgba(0, 123, 255, 0.1);
color: #007bff;
}
.badge-soft-danger {
background-color: rgba(220, 53, 69, 0.1);
color: #dc3545;
}
.badge-soft-secondary {
background-color: rgba(108, 117, 125, 0.1);
color: #6c757d;
}
:global(.dark-mode) .badge-soft-success { background-color: rgba(40, 167, 69, 0.2); }
:global(.dark-mode) .badge-soft-primary { background-color: rgba(0, 123, 255, 0.2); }
:global(.dark-mode) .badge-soft-danger { background-color: rgba(220, 53, 69, 0.2); }
:global(.dark-mode) .border-light { border-color: rgba(255,255,255,0.05) !important; }
/* Font Awesome standard sizes for visual hierarchy as per dictionary */
.sm { font-size: 0.875rem; }
/* Items section styling */
.items-section {
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
max-height: 0;
}
to {
opacity: 1;
max-height: 500px;
}
}
.item-row:last-child {
border-bottom: none !important;
}
.item-row {
transition: background-color 0.2s;
}
.item-row:hover {
background-color: rgba(0, 0, 0, 0.02);
}
:global(.dark-mode) .item-row:hover {
background-color: rgba(255, 255, 255, 0.02);
}
:global(.dark-mode) .item-name,
:global(.dark-mode) .item-total span {
color: var(--text-primary) !important;
}
/* Toggle button styling */
.card-footer-toggle {
transition: background-color 0.2s;
}
.cursor-pointer {
cursor: pointer;
}
.card-footer-toggle:hover {
background-color: rgba(0, 0, 0, 0.02);
}
:global(.dark-mode) .card-footer-toggle:hover {
background-color: rgba(255, 255, 255, 0.02);
}
/* Mobile responsiveness */
@media (max-width: 576px) {
.item-name {
font-size: 0.75rem;
}
.item-qty {
font-size: 0.7rem;
}
.item-total span {
font-size: 0.75rem;
}
}
</style>