164 lines
7.4 KiB
Markdown
164 lines
7.4 KiB
Markdown
---
|
|
task: Fix "Open POS" on HomeStoreOwner and HomeShared (StoreManager) to pass the store hashkey to PosMain. If user has one store, navigate directly with its hashkey. If multiple stores, show a store selection modal first.
|
|
cycles: 5
|
|
context: true
|
|
private: false
|
|
started: 2026-05-16T00:00:00Z
|
|
finished: 2026-05-16T00:01:00Z
|
|
---
|
|
|
|
## files
|
|
- resources/js/Pages/Fragments/Home/HomeStoreOwner.vue [lines 30-32, 115-123] — defines balanceFooterItems with pagename:'PosMain' (no target prop), handleItemClick navigates without store hashkey
|
|
- resources/js/Pages/Fragments/Home/HomeShared.vue — StoreManager home; no POS button at all; needs Open POS added with same multi-store logic
|
|
- resources/js/Pages/Home.vue [lines 62-69] — routes isStoreOwner→HomeStoreOwner, isStoreManager→HomeShared
|
|
- resources/js/Pages/PosMain.vue [lines 18-21] — expects `target` prop (store or session hashkey) and `access_key` prop; without `target` usePosSession cannot initialize the store
|
|
- resources/js/composables/Market/usePosSession.js [lines 25-47] — initialize() reads `props.target` as hashkey; if null, storeHash stays null and store products never load
|
|
- app/Http/Controllers/Market/StoreController.php [lines 1143-1206] — `listStoresForCurrentUser()` returns [{hashkey, name, category, role}] for owner+manager
|
|
- routes/web.php [line 484] — POST /ListStores/MyStores/data → StoreController@listStoresForCurrentUser (auth+module:stores middleware)
|
|
|
|
## steps
|
|
|
|
### Step 1 — HomeStoreOwner.vue: fetch stores and smart-navigate on "Open POS"
|
|
|
|
1. Add `axios` is already imported. Add a `loadingStores` ref.
|
|
2. Replace the `balanceFooterItems` "Open POS" item — keep `pagename: 'PosMain'` but add `action: 'openPos'` to differentiate from direct navigation.
|
|
3. Write an `openPos()` async function:
|
|
```
|
|
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;
|
|
}
|
|
// Multiple stores: show selection modal
|
|
showStoreSelectModal(stores);
|
|
} catch (e) {
|
|
modal.quickDismiss({ title: 'Error', body: 'Could not load your stores. Please try again.' });
|
|
}
|
|
};
|
|
```
|
|
4. Write `showStoreSelectModal(stores)` using `modal.open()` with a rendered list of store buttons (use `h()` from vue). On store click: `modal.hideModal()` then `navigate({ page: 'PosMain', props: { target: store.hashkey } })`.
|
|
5. In `handleItemClick`, intercept `item.action === 'openPos'` before the pagename check and call `openPos()`.
|
|
6. Update the `balanceFooterItems` entry to use `action: 'openPos'` instead of/alongside `pagename: 'PosMain'`.
|
|
|
|
**Exact change to balanceFooterItems (line 30):**
|
|
```js
|
|
// Before:
|
|
{ title: 'Open POS', icon: '...', pagename: 'PosMain' },
|
|
// After:
|
|
{ title: 'Open POS', icon: '...', action: 'openPos' },
|
|
```
|
|
|
|
**Exact change to handleItemClick (lines 115-123):**
|
|
```js
|
|
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 || '' } });
|
|
}
|
|
};
|
|
```
|
|
|
|
### Step 2 — HomeShared.vue: add Open POS button for StoreManager role
|
|
|
|
1. Import `computed` (already imported), `axios`, `useModal`, and `h` from vue.
|
|
2. Check if current role is `STORE_MANAGER` (use `role` from `useAuth()`).
|
|
3. Add an `openPos` function identical in logic to Step 1.
|
|
4. Add `showStoreSelectModal(stores)` function identical to Step 1.
|
|
5. Add a computed `posServices` that returns an "Open POS" button only when `role.value === UserTypes.STORE_MANAGER` (or always show for manager; the page is already role-gated).
|
|
6. In the template, add `<ServiceButtonGrid :items="posItems" @item-click="handlePosClick" />` or extend `services` to include the POS button — prefer extending `services` with `action: 'openPos'` for managers.
|
|
7. Alternatively, add "Open POS" as a `quickActionsItems` entry with roles `[UserTypes.STORE_MANAGER]` and handle it in `handleItemClick` with the same `openPos` logic.
|
|
|
|
**Simplest approach:** extend `quickActionsItems` with:
|
|
```js
|
|
{
|
|
text: 'Open POS',
|
|
action: 'openPos',
|
|
icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/5b5ef88c0ad1.svg',
|
|
roles: [UserTypes.STORE_MANAGER],
|
|
},
|
|
```
|
|
And update `handleItemClick` to check `item.action === 'openPos'`.
|
|
|
|
### Step 3 — Verify no backend changes needed
|
|
|
|
The endpoint `POST /ListStores/MyStores/data` already:
|
|
- Returns `[{hashkey, name, category, role}]` for the current user's stores (owner or manager)
|
|
- Has `auth` middleware (user must be logged in)
|
|
- Covers both STORE_OWNER and STORE_MANAGER via the query in `listStoresForCurrentUser()`
|
|
|
|
No backend changes needed.
|
|
|
|
## context
|
|
|
|
**HomeStoreOwner.vue — balanceFooterItems (line 30):**
|
|
```js
|
|
const balanceFooterItems = ref([
|
|
{ title: 'Open POS', icon: '...svg', pagename: 'PosMain' }, // BUG: no target/hashkey
|
|
{ title: 'My Stores', icon: '...bin', pagename: 'ManageStoresAdmin' },
|
|
]);
|
|
```
|
|
|
|
**HomeStoreOwner.vue — handleItemClick (lines 115-123):**
|
|
```js
|
|
const handleItemClick = (item) => {
|
|
if (item?.action === 'chooseCreateStoreMode') {
|
|
openCreateStoreChooser();
|
|
return;
|
|
}
|
|
if (item?.pagename) {
|
|
navigate({ page: item.pagename, props: { data: item.pagestring || '' } });
|
|
}
|
|
};
|
|
```
|
|
→ Navigates to `PosMain` without `target` prop. `usePosSession.initialize()` then has `props.target = null`, `storeHash` stays null, products never load, session can't start.
|
|
|
|
**PosMain.vue props (lines 18-21):**
|
|
```js
|
|
const props = defineProps({
|
|
target: { type: String, default: null }, // Session hashkey
|
|
access_key: { type: String, default: null },
|
|
});
|
|
```
|
|
|
|
**usePosSession.js initialize() (lines 25-47):**
|
|
```js
|
|
const hashkey = props.target; // null when coming from HomeStoreOwner
|
|
if (hashkey) {
|
|
await posStore.loadSession(hashkey, accessKey);
|
|
// ...
|
|
}
|
|
await posStore.fetchProducts(accessKey, storeHash.value); // storeHash is null
|
|
```
|
|
|
|
**StoreController@listStoresForCurrentUser returns (lines 1197-1202):**
|
|
```php
|
|
return [
|
|
'hashkey' => $store->hashkey,
|
|
'name' => $store->name,
|
|
'category' => $store->category,
|
|
'role' => $role, // 'owner' or 'manager'
|
|
];
|
|
```
|
|
|
|
**HomeShared.vue — StoreManager currently has NO POS button.** It only shows Market, My Wallet, Shipments in `services` and generic quickActions. The store manager use-case is identical to store owner: fetch their stores, navigate with hashkey.
|
|
|
|
**useModal available functions:** `open({ title, body, footer })`, `quickDismiss({ title, body })`, `yesNoModal(...)`, `hideModal()`. Use `open()` with `h()` to render store list.
|
|
|
|
## notes
|
|
- dictionary: none
|
|
- linters: eslint:no, phpcs:no, tsc:no
|
|
- constraints: Use existing `/ListStores/MyStores/data` POST endpoint (already exists, no new backend route needed). Modal store list should show store name and category. Use `h()` (already importable from vue) to build modal body for store selection. The `useModal` composable's `open()` accepts a Vue render function or component as `body`.
|