initial: bootstrap from BukidBountyApp base
This commit is contained in:
148
.claude/plans/a13c7ea870dcf8f3de8e22185ead7dcf-complete.md
Normal file
148
.claude/plans/a13c7ea870dcf8f3de8e22185ead7dcf-complete.md
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user