initial: bootstrap from BukidBountyApp base
This commit is contained in:
224
resources/js/Pages/Fragments/Home/HomeStoreOwner.vue
Normal file
224
resources/js/Pages/Fragments/Home/HomeStoreOwner.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed, h } from 'vue';
|
||||
import axios from 'axios';
|
||||
import usePageData from '../../../composables/usePageData.js';
|
||||
import { useNavigate } from '../../../composables/Core/useNavigate.js';
|
||||
import { useAuth } from '../../../composables/Core/useAuth.js';
|
||||
import { useModal } from '../../../composables/Core/useModal.js';
|
||||
|
||||
import BalanceBox from '../../../Components/Core/Stats/BalanceBox.vue';
|
||||
import ServiceButtonGrid from '../../../Components/Core/Services/ServiceButtonGrid.vue';
|
||||
import HomeSkeleton from '../../../Components/Core/Skeleton/HomeSkeleton.vue';
|
||||
|
||||
const props = defineProps({
|
||||
title: { type: String, default: 'Store Owner' },
|
||||
});
|
||||
|
||||
const { user } = useAuth();
|
||||
const { navigate } = useNavigate();
|
||||
const modal = useModal();
|
||||
|
||||
const { data, loading, error, fetchPageData } = usePageData();
|
||||
|
||||
const stats = ref([
|
||||
{ title: 'Transactions', number: 0, unit: 'Today', align: 'left', numberId: 'transactions_today_no' },
|
||||
{ title: 'Cash Flow', number: '0.00', unit: 'PHP Today', align: 'left', numberId: 'cash_flow_today_php' },
|
||||
{ title: 'My Stores', number: 0, unit: 'Active', align: 'right', numberId: 'my_stores_no' },
|
||||
]);
|
||||
|
||||
const balanceFooterItems = ref([
|
||||
{ title: 'Open POS', icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/5b5ef88c0ad1.svg', action: 'openPos' },
|
||||
{ title: 'My Stores', icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/85605eacd4c8.bin', pagename: 'ManageStoresAdmin' },
|
||||
]);
|
||||
|
||||
const services = computed(() => [
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/85605eacd4c8.bin',
|
||||
title: 'Create Store',
|
||||
action: 'chooseCreateStoreMode',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/ef1a9a079a2d.svg',
|
||||
title: 'Import Products',
|
||||
pagename: 'BatchAddProducts',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/f0a0193d728e.bin',
|
||||
title: 'New Product',
|
||||
pagename: 'CreateProductStoreOwner',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/ef1a9a079a2d.svg',
|
||||
title: 'My Products',
|
||||
pagename: 'ManageProductsAdmin',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/5b5ef88c0ad1.svg',
|
||||
title: 'POS Keys',
|
||||
pagename: 'PosAccessKeys',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/f87407046b18.bin',
|
||||
title: 'Reports',
|
||||
pagename: 'ListReports',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/fa711c34b4ef.svg',
|
||||
title: 'Accounting',
|
||||
pagename: 'AccountingDashboard',
|
||||
},
|
||||
{
|
||||
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/85605eacd4c8.bin',
|
||||
title: 'Manage Stores',
|
||||
pagename: 'ManageStoresAdmin',
|
||||
},
|
||||
]);
|
||||
|
||||
const applyStats = () => {
|
||||
if (!data.value?.stats) return;
|
||||
stats.value = stats.value.map((s) => {
|
||||
const v = data.value.stats[s.numberId];
|
||||
return v !== undefined ? { ...s, number: v } : s;
|
||||
});
|
||||
};
|
||||
|
||||
const creatingQuickStore = ref(false);
|
||||
|
||||
const quickCreateStore = async () => {
|
||||
if (creatingQuickStore.value) return;
|
||||
creatingQuickStore.value = true;
|
||||
try {
|
||||
const { data: res } = await axios.post('/Store/AutoCreate', {});
|
||||
if (res?.success) {
|
||||
await fetchPageData('/home-data', {});
|
||||
applyStats();
|
||||
modal.quickDismiss({
|
||||
title: 'Store Created',
|
||||
body: `"${res.name}" was created and assigned to your account.`,
|
||||
});
|
||||
if (res.hashkey) {
|
||||
navigate({ page: 'AddProductsToStore', props: { target: res.hashkey } });
|
||||
} else {
|
||||
navigate({ page: 'ManageStoresAdmin' });
|
||||
}
|
||||
} else {
|
||||
modal.quickDismiss({ title: 'Could not create store', body: 'Please try again.' });
|
||||
}
|
||||
} catch (e) {
|
||||
modal.quickDismiss({
|
||||
title: 'Could not create store',
|
||||
body: e?.response?.data?.message || e.message || 'Unexpected error',
|
||||
});
|
||||
} finally {
|
||||
creatingQuickStore.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openCreateStoreChooser = () => {
|
||||
modal.yesNoModal({
|
||||
title: 'Create Store',
|
||||
body: 'Choose how you want to create your store. Quick Create makes a store instantly with default details. Custom Create lets you set the name, address, photos, and more.',
|
||||
yesText: 'Quick Create',
|
||||
yesClass: 'btn btn-primary w-50 py-2 rounded-3 shadow-sm fw-bold',
|
||||
onYes: quickCreateStore,
|
||||
noText: 'Custom Create',
|
||||
noClass: 'btn btn-outline-primary w-50 py-2 rounded-3 fw-bold',
|
||||
onNo: () => navigate({ page: 'CreateStore' }),
|
||||
});
|
||||
};
|
||||
|
||||
const showStoreSelectModal = (stores) => {
|
||||
modal.open({
|
||||
title: 'Select a Store',
|
||||
body: h('div', { class: 'd-flex flex-column gap-2' },
|
||||
stores.map(store =>
|
||||
h('button', {
|
||||
class: 'btn btn-outline-primary rounded-pill text-start',
|
||||
onClick: () => {
|
||||
modal.hideModal();
|
||||
navigate({ page: 'PosMain', props: { target: store.hashkey } });
|
||||
},
|
||||
}, [
|
||||
h('i', { class: 'fas fa-store me-2' }),
|
||||
store.name,
|
||||
store.category ? h('small', { class: 'text-muted ms-2' }, `(${store.category})`) : null,
|
||||
])
|
||||
)
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
const openPos = async () => {
|
||||
try {
|
||||
const { data: stores } = await axios.post('/ListStores/MyStores/data', {});
|
||||
if (!stores || stores.length === 0) {
|
||||
modal.quickDismiss({ title: 'No Store Found', body: 'You have no active stores assigned to your account.' });
|
||||
return;
|
||||
}
|
||||
if (stores.length === 1) {
|
||||
navigate({ page: 'PosMain', props: { target: stores[0].hashkey } });
|
||||
return;
|
||||
}
|
||||
showStoreSelectModal(stores);
|
||||
} catch (e) {
|
||||
modal.quickDismiss({ title: 'Error', body: 'Could not load your stores. Please try again.' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemClick = async (item) => {
|
||||
if (item?.action === 'chooseCreateStoreMode') {
|
||||
openCreateStoreChooser();
|
||||
return;
|
||||
}
|
||||
if (item?.action === 'openPos') {
|
||||
await openPos();
|
||||
return;
|
||||
}
|
||||
if (item?.pagename) {
|
||||
navigate({ page: item.pagename, props: { data: item.pagestring || '' } });
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchPageData('/home-data', {});
|
||||
applyStats();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home-store-owner-fragment pb-5">
|
||||
<HomeSkeleton v-if="loading && !data" />
|
||||
|
||||
<div v-if="data">
|
||||
<BalanceBox
|
||||
:stats="stats"
|
||||
:footer-items="balanceFooterItems"
|
||||
@footer-click="handleItemClick"
|
||||
/>
|
||||
|
||||
<div class="tf-container mt-3">
|
||||
<h5 class="fw_7 mb-2" style="color: var(--text-primary) !important;">
|
||||
{{ title }} Tools
|
||||
</h5>
|
||||
<ServiceButtonGrid :items="services" @item-click="handleItemClick" />
|
||||
</div>
|
||||
|
||||
<div v-if="creatingQuickStore" class="tf-container mt-3 text-center">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-spinner fa-spin me-1"></i>
|
||||
Creating your store...
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error && !data" class="tf-container mt-5 text-center">
|
||||
<p style="color: var(--text-muted);">Dashboard disconnected. Please retry.</p>
|
||||
<button
|
||||
class="btn btn-primary mt-2 rounded-pill px-4"
|
||||
@click="fetchPageData('/home-data', {})"
|
||||
>
|
||||
Reconnect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user