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

324 lines
11 KiB
Vue

<script setup>
import { usePageTitle } from '../composables/Core/usePageTitle';
usePageTitle('Buy View Product Market');
import { ref, onMounted, computed } from 'vue';
import axios from 'axios';
import { useNavigate } from '../composables/Core/useNavigate';
import LoadingSpinner from '../Components/LoadingSpinner.vue';
import FileImage from '../Components/Core/FileImage.vue';
import { useAuth } from '../composables/Core/useAuth';
import { useModal } from '../composables/Core/useModal';
const props = defineProps({
target: { type: String, required: false },
data: { type: Object, default: () => ({}) },
payload: { type: Object, default: null }
});
const { navigate } = useNavigate();
const { role } = useAuth();
const modal = useModal();
import { useProductStore } from '../stores/product';
const productStore = useProductStore();
const product = computed(() => productStore.currentProduct);
const loading = computed(() => productStore.loading);
const error = computed(() => productStore.error);
const fetchProductDetails = async () => {
const targetHash = props.payload?.product_hashkey || props.payload?.product_hash || props.target;
const storeHash = props.payload?.store_hashkey || props.payload?.store_hash || props.data?.store_hash;
await productStore.fetchProductById(targetHash, storeHash);
};
const goBack = () => {
const storeHash = props.payload?.store_hash || product.value?.store_hash;
if (storeHash) {
navigate({ page: 'ViewStoreMarket', props: { target: storeHash } });
} else {
navigate({ page: 'ListProductsMarket' });
}
};
const manageProduct = () => {
const storeHash = props.payload?.store_hash || product.value?.store_hash;
const productHash = props.payload?.product_hash || props.target;
if (product.value.is_from_store && storeHash) {
navigate({
page: 'ManageProductAdmin',
props: {
payload: {
product_hashkey: productHash,
store_hashkey: storeHash
}
}
});
} else {
navigate({ page: 'ManageProductAdmin', props: { target: productHash } });
}
};
const displayPrice = computed(() => {
if (!product.value) return '';
const price = product.value.store_price || product.value.price;
const prefix = product.value.is_from_store ? 'Store Price ' : '';
return `${prefix}${price} / ${product.value.unitname}`;
});
const addToCart = async () => {
try {
const productHash = props.payload?.product_hash || props.target;
const response = await axios.get(`/cart/add/one/${productHash}`);
if (response.data === true || response.data?.success) {
modal.open({
title: 'Success',
body: 'Added to cart!'
});
} else {
modal.open({
title: 'Error',
body: 'Failed to add to cart.'
});
}
} catch (e) {
console.error('Add to cart failed:', e);
modal.open({
title: 'Error',
body: 'Error adding to cart.'
});
}
};
const buyNow = () => {
// Navigate to a (yet to be created) checkout or confirmation page
modal.open({
title: 'Info',
body: 'Buy Now clicked! This would typically go to checkout.'
});
};
const printPosCode = () => {
const w = window.open('', '_blank', 'width=400,height=500');
w.document.write(`<html><body style="text-align:center;font-family:sans-serif">
<h3>${product.value.name}</h3>
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(product.value.pos_qrcode)}" />
<p style="font-size:12px">${product.value.pos_qrcode}</p>
<script>window.onload=()=>{window.print();window.close();}<\/script>
</body></html>`);
w.document.close();
};
onMounted(() => {
fetchProductDetails();
});
</script>
<template>
<div class="product-details-page pb-5">
<div v-if="loading" class="text-center py-5">
<LoadingSpinner />
<p class="mt-3 text-muted">Loading product details...</p>
</div>
<div v-else-if="error" class="tf-container mt-5 text-center">
<div class="alert alert-danger">{{ error }}</div>
<button @click="goBack" class="btn btn-outline-secondary mt-3 rounded-pill">
Go Back
</button>
</div>
<template v-else-if="product">
<!-- Hero Image Section -->
<div class="product-hero">
<button @click="goBack" class="back-btn shadow">
<i class="fas fa-chevron-left"></i>
</button>
<div class="hero-image-container">
<FileImage :src="product.photourl && product.photourl.length > 0 ? product.photourl[0] : ''"
class="hero-img" fallback="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/146710fe9ece.bin" />
</div>
</div>
<div class="tf-container product-content">
<div class="info-card shadow-sm">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h2 class="fw_7 mb-1">{{ product.name }}</h2>
<span class="badge bg-soft-success text-success rounded-pill px-3">
{{ product.category }}
</span>
</div>
<div class="text-end">
<h3 class="price-tag text-primary fw_7 mb-0">{{ displayPrice }}</h3>
<small class="text-muted" v-if="product.available !== null">
{{ product.available }} available
</small>
</div>
</div>
<div class="description-section mt-4">
<h5 class="fw_6 mb-2">Description</h5>
<p class="text-muted line-height-16">
{{ product.store_description || product.description }}
</p>
</div>
<!-- POS QR Code Section -->
<div v-if="product.pos_qrcode" class="pos-qr-section mt-4 p-3 rounded-xl text-center border">
<h6 class="fw_7 mb-2"><i class="fas fa-barcode me-2"></i> POS Scan Code</h6>
<div class="qr-container p-2 d-inline-block rounded shadow-sm mb-2 qr-container-bg">
<img :src="`https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(product.pos_qrcode)}`"
:alt="product.pos_qrcode" style="width: 150px; height: 150px;">
</div>
<div class="small text-muted fw_6 mb-2">{{ product.is_from_store ? 'Store Exclusive Code' : 'Product Identification' }}</div>
<div>
<button @click="printPosCode" class="btn btn-outline-primary btn-sm rounded-pill px-3">
<i class="fas fa-print me-2"></i> Print
</button>
</div>
<div class="stats-row d-flex justify-content-around mt-3 pt-3 border-top">
<div class="stat-item">
<div class="small text-muted">Sold Today</div>
<div class="fw_7 stat-value">
{{ product.is_from_store ? (product.store_sold_today ?? product.sold_today ?? 0) : (product.sold_today ?? 0) }}
</div>
</div>
<div class="stat-item">
<div class="small text-muted">Total Sold</div>
<div class="fw_7 stat-value">
{{ product.is_from_store ? (product.store_sold ?? product.sold ?? 0) : (product.sold ?? 0) }}
</div>
</div>
</div>
</div>
<div class="actions-grid mt-4 pt-4 border-top">
<div class="row g-2">
<div class="col-6">
<button @click="addToCart"
class="btn btn-light w-100 py-3 rounded-xl fw_6 shadow-sm border">
<img src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/d36eb6a17e27.bin" class="me-2" style="width: 20px;">
Add Cart
</button>
</div>
<div class="col-6">
<button @click="buyNow" class="btn btn-primary w-100 py-3 rounded-xl fw_6 shadow-sm">
<img src="https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/6446fb001e8b.bin" class="me-2"
style="width: 20px; filter: brightness(0) invert(1);">
Buy Now
</button>
</div>
</div>
</div>
<div v-if="['ult', 'superoperator', 'operator'].includes(role)" class="admin-actions mt-3">
<button @click="manageProduct" class="btn btn-soft-dark w-100 py-3 rounded-xl fw_6">
<i class="fas fa-cog me-2"></i> Manage Product
</button>
</div>
</div>
</div>
</template>
</div>
</template>
<style scoped>
.pos-qr-section {
background-color: var(--bg-card);
color: var(--text-primary);
}
.qr-container-bg {
background-color: var(--bg-card);
}
.stat-value {
color: var(--text-primary);
}
.product-hero {
position: relative;
width: 100%;
}
.hero-image-container {
width: 100%;
height: 350px;
background: #f0f0f0;
}
.hero-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.back-btn {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: white;
display: flex;
align-items: center;
justify-content: center;
color: #333;
}
.product-content {
margin-top: -30px;
position: relative;
z-index: 20;
}
.info-card {
background: white;
border-radius: 30px;
padding: 25px;
}
.price-tag {
color: #42b983 !important;
}
.bg-soft-success {
background: rgba(66, 185, 131, 0.1);
}
.rounded-xl {
border-radius: 12px;
}
.btn-soft-dark {
background: #f1f2f6;
color: #2c3e50;
border: none;
}
.line-height-16 {
line-height: 1.6;
}
:global(.dark-mode) .info-card {
background: #24272c;
}
:global(.dark-mode) .back-btn {
background: #24272c;
color: #fff;
}
:global(.dark-mode) .btn-soft-dark {
background: #1a1c20;
color: #e0e0e0;
}
</style>