149 lines
8.1 KiB
Markdown
149 lines
8.1 KiB
Markdown
---
|
|
task: Create HomeStoreManager.vue — a dedicated Store Manager home dashboard — and wire it into Home.vue
|
|
cycles: 5
|
|
context: true
|
|
private: false
|
|
started: 2026-05-16T00:00:00Z
|
|
finished: 2026-05-16T00:02:00Z
|
|
---
|
|
|
|
## files
|
|
- `resources/js/Pages/Home.vue` [lines 67-69] — STORE_MANAGER currently mapped to `HomeShared`; change to new `HomeStoreManager`
|
|
- `resources/js/Pages/Fragments/Home/HomeStoreManager.vue` — NEW FILE to create
|
|
- `resources/js/Pages/Fragments/Home/HomeStoreOwner.vue` — primary reference pattern to follow (BalanceBox + ServiceButtonGrid + openPos logic)
|
|
- `resources/js/Pages/Fragments/Home/HomeShared.vue` [lines 56-93] — has `openPos()` store-select logic; copy this exact pattern
|
|
- `resources/js/Components/Core/Stats/BalanceBox.vue` — props: `stats[]`, `footerItems[]`, `boxStyle`
|
|
- `resources/js/Components/Core/Services/ServiceButtonGrid.vue` — prop: `items[]` with `icon`, `title`, `pagename|action`
|
|
- `resources/js/Components/Core/Services/SideTextButtonList.vue` — quick text actions
|
|
- `resources/js/Components/Core/Skeleton/HomeSkeleton.vue` — loading skeleton
|
|
- `resources/js/composables/usePageData.js` — `fetchPageData(url, payload)`
|
|
- `routes/web.php` [lines 76-135] — `/home-data` — already returns `transactions_today_no`, `cash_flow_today_php`; also returns `my_stores_no` scoped to store_managers pivot
|
|
- `app/Http/Controllers/Support/VueRouteMap.php` — must confirm `ManageStoresAdmin`, `PosMain`, `PosHistory`, `PosAccessKeys`, `ManageProductsAdmin` allow `store manager`
|
|
|
|
## steps
|
|
1. **`routes/web.php` @ `/home-data`** — The existing `$myStoresCount` for non-Big3 only counts `owner_id` stores (line ~113). Update to also count stores where the user is in the `store_managers` pivot, so STORE_MANAGER sees the correct count:
|
|
```php
|
|
$myStoresCount = !$isBig3 && $user
|
|
? \App\Models\Market\Store::where('owner_id', $user->id)
|
|
->orWhereHas('managers', fn($q) => $q->where('user_id', $user->id))
|
|
->count()
|
|
: $storeCount;
|
|
```
|
|
This change is backward-compatible (STORE_OWNER still works, now STORE_MANAGER also gets a nonzero count).
|
|
|
|
2. **Create `resources/js/Pages/Fragments/Home/HomeStoreManager.vue`** with this structure:
|
|
|
|
**Script (setup):**
|
|
- Imports: `ref, onMounted, h`, `usePageData`, `useNavigate`, `useAuth`, `useModal`, `BalanceBox`, `ServiceButtonGrid`, `SideTextButtonList`, `HomeSkeleton`, `axios`
|
|
- `stats` ref:
|
|
```js
|
|
[
|
|
{ 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: 'Assigned', align: 'right', numberId: 'my_stores_no' },
|
|
]
|
|
```
|
|
- `balanceFooterItems` ref:
|
|
```js
|
|
[
|
|
{ title: 'Open POS', icon: '<pos-icon-url>', action: 'openPos' },
|
|
{ title: 'My Stores', icon: '<store-icon-url>', pagename: 'ManageStoresAdmin' },
|
|
]
|
|
```
|
|
- `services` array (8 tiles):
|
|
1. `{ icon:'<pos icon>', title:'Open POS', action:'openPos' }`
|
|
2. `{ icon:'<inventory icon>', title:'Inventory', pagename:'ManageProductsAdmin' }`
|
|
3. `{ icon:'<history icon>', title:'POS History', action:'openPosHistory' }`
|
|
4. `{ icon:'<store icon>', title:'Manage Stores', pagename:'ManageStoresAdmin' }`
|
|
5. `{ icon:'<product icon>', title:'Add Product', pagename:'CreateProductStoreOwner' }`
|
|
6. `{ icon:'<key icon>', title:'POS Keys', pagename:'PosAccessKeys' }`
|
|
7. `{ icon:'<customers icon>', title:'Customers', action:'viewCustomers' }`
|
|
8. `{ icon:'<report icon>', title:'Reports', pagename:'ListReports' }`
|
|
- `quickActions` array (SideTextButtonList items):
|
|
- `{ text:'Onboard New User', pagename:'CreateUser', icon:'...' }`
|
|
- `{ text:'My Personal Profile', pagename:'UserInfoEdit', icon:'...' }`
|
|
- `{ text:'Add Transaction', pagename:'AddTransaction', icon:'...' }`
|
|
- `openPos()` — identical copy of the logic in `HomeShared.vue` lines 77-92:
|
|
- POST to `/ListStores/MyStores/data`
|
|
- If 0 stores → modal warn
|
|
- If 1 store → navigate to `PosMain` with `target: stores[0].hashkey`
|
|
- If >1 → `showStoreSelectModal(stores)` with per-store buttons
|
|
- `openPosHistory()` — same store-picker logic but navigates to `PosHistory` with the store's hashkey
|
|
- `viewCustomers()` — navigate to `ManageStoresAdmin` (customers are visible per-store there)
|
|
- `applyStats()` — standard pattern from HomeStoreOwner
|
|
- `onMounted` → `fetchPageData('/home-data', {})` then `applyStats()`
|
|
|
|
**Template:**
|
|
```html
|
|
<div class="home-fragment pb-5">
|
|
<HomeSkeleton v-if="loading" />
|
|
<template v-else>
|
|
<BalanceBox :stats="stats" :footerItems="balanceFooterItems" @footer-click="handleItemClick" />
|
|
<div class="tf-container mt-4">
|
|
<h5 class="fw_7 mb-3">Store Manager Tools</h5>
|
|
<ServiceButtonGrid :items="services" @item-click="handleItemClick" />
|
|
</div>
|
|
<div class="tf-container mt-4">
|
|
<h5 class="fw_7 mb-3">Quick Actions</h5>
|
|
<SideTextButtonList :items="quickActions" @item-click="handleItemClick" />
|
|
</div>
|
|
</template>
|
|
</div>
|
|
```
|
|
|
|
**Style:** No `bg-white`/`bg-light`/`text-dark`. Theme-aware only.
|
|
|
|
3. **`resources/js/Pages/Home.vue`** — Add import line: `import HomeStoreManager from './Fragments/Home/HomeStoreManager.vue';`
|
|
— Change lines 67-69 from `<HomeShared title="Store Manager" />` to `<HomeStoreManager />`.
|
|
|
|
4. **`app/Http/Controllers/Support/VueRouteMap.php`** — Confirm all pages used by this dashboard have `'store manager'` in their `allowedUserTypes`. Required pages: `ManageStoresAdmin`, `ManageProductsAdmin`, `CreateProductStoreOwner`, `PosAccessKeys`, `PosHistory`, `ListReports`, `AddTransaction`, `CreateUser`, `UserInfoEdit`. Add `'store manager'` to any that are missing it.
|
|
|
|
5. **`app/Http/Controllers/Helpers/Permissions/UserPermissions.php`** — Confirm `STORE_MANAGER` has these actions in `roles()`:
|
|
- `ViewTransactions` (for Reports)
|
|
- `ViewAccountingReports` (for ListReports)
|
|
- `CreatePosAccessKey`, `DeletePosAccessKey`, `TogglePosAccessKey`
|
|
- `CreateProductForOwnStore`, `AddProducttoOwnStore`
|
|
Add any that are missing.
|
|
|
|
## context
|
|
```
|
|
// HomeShared.vue openPos() pattern (lines 77-92):
|
|
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.' });
|
|
}
|
|
};
|
|
|
|
// Home.vue lines 67-69 currently:
|
|
// <template v-else-if="isStoreManager">
|
|
// <HomeShared title="Store Manager" />
|
|
// </template>
|
|
|
|
// /home-data stats keys available: transactions_today_no, cash_flow_today_php, my_stores_no
|
|
// CDN icon URLs in use (from HomeStoreOwner.vue):
|
|
// POS key: https://cdn.jsdelivr.net/gh/.../a/5b5ef88c0ad1.svg
|
|
// Store: https://cdn.jsdelivr.net/gh/.../a/85605eacd4c8.bin
|
|
// Product: https://cdn.jsdelivr.net/gh/.../a/f0a0193d728e.bin
|
|
// Import: https://cdn.jsdelivr.net/gh/.../a/ef1a9a079a2d.svg
|
|
// Reports: https://cdn.jsdelivr.net/gh/.../a/f87407046b18.bin
|
|
// Profile: https://cdn.jsdelivr.net/gh/.../a/ac7a1cebe580.bin
|
|
// AddTxn: https://cdn.jsdelivr.net/gh/.../a/c9fd442fe676.bin
|
|
// Users: https://cdn.jsdelivr.net/gh/.../a/516ed2aaaa4c.bin
|
|
```
|
|
|
|
## notes
|
|
- dictionary: `ai-docs/dictionary.md`
|
|
- linters: none detected
|
|
- constraints: Do NOT give store manager access to global product editing or user-type creation beyond their hierarchy. The `openPos` logic must re-use the exact same store-picker modal pattern from HomeShared. Use CDN icon URLs already in the project — do not introduce new external URLs. No `bg-white` or hardcoded colors.
|