Files
BarangaySystem/.claude/plans/926a10dd4cfc0c544f3bd303986a17a6-complete.md
2026-06-06 18:43:00 +08:00

118 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
task: Fix homepage fragment persisting from previous login when switching account types
cycles: 5
context: true
private: false
started: 2026-05-17T16:22:34Z
finished: 2026-05-17T16:24:00Z
---
## files
- resources/js/stores/user.js [lines 63-95] — fetchCurrentUser() has a guard that skips role re-fetch when acctType is already set; this allows stale sessionStorage role to persist
- resources/js/Pages/Home.vue [lines 1-21] — renders role fragments before userStore loading is complete, causing flash of wrong fragment
- resources/js/composables/Core/useAuth.js [lines 7-53] — module-level globalRole ref; module-level fetchRole() guard prevents re-fetch when sessionStorage already has a non-public role
- resources/js/Pages/Auth/Login.vue [lines 13-38] — sessionStorage.clear() in onMounted runs synchronously, but app.js fetchCurrentUser() is async and can set sessionStorage AFTER the clear
## steps
### Step 1 — Fix stale-role guard in `fetchCurrentUser()` (user.js:78-84)
The guard `if (!this.acctType || this.acctType === 'public')` prevents re-fetching the real server-side role when `acctType` is already populated from sessionStorage. This is the primary bug: after login, if sessionStorage has the old user's role (e.g. 'ultimate'), `acctType` initialises to 'ultimate' from the store state factory, the guard is false, and the role is never corrected against the server — even though the session now belongs to a completely different user.
Remove the guard entirely so that `fetchCurrentUser()` **always** re-fetches `acct_type` from `/get/user/acct-type` after successfully loading the user object.
```js
// user.js fetchCurrentUser(), after setting this.user:
// REMOVE the `if (!this.acctType || this.acctType === 'public')` wrapper.
// Always fetch and overwrite acctType from the server.
const resRole = await axios.get('/get/user/acct-type')
if (resRole.data && resRole.data.acct_type) {
this.acctType = resRole.data.acct_type
sessionStorage.setItem('user_acct_type', this.acctType)
}
```
Keep the 401/419 catch path unchanged — it already sets `acctType = 'public'`.
### Step 2 — Add loading guard to Home.vue before rendering role fragments
While `userStore.loading` is true (fetchCurrentUser in flight), `userStore.acctType` may still hold the stale sessionStorage value. Guard all role-specific `<template>` blocks with a top-level loading state so no fragment renders until auth is confirmed.
In Home.vue add a `userStore` import and wrap the inner content:
```js
import { useUserStore } from '../stores/user.js';
const userStore = useUserStore();
```
In the template, wrap the role switch inside a `v-if="!userStore.loading || userStore.user"` block. While loading and no user yet, show a minimal centered spinner (reuse the existing spinner pattern in the codebase — `<div class="spinner-border text-primary">`). Once `userStore.user` is resolved (non-null), the correct fragment renders reactively.
### Step 3 — Reset globalRole in useAuth.js when Login.vue mounts
Export a `resetRole()` function from `useAuth.js` that sets `globalRole.value = UserTypes.PUBLIC` and removes `sessionStorage.user_acct_type`. Call it from Login.vue's `onMounted` AFTER `sessionStorage.clear()`.
This ensures the module-level `globalRole` is synchronously reset before any async work sets it again, eliminating any window where the stale in-memory value could be read.
```js
// useAuth.js add export:
export function resetRole() {
globalRole.value = UserTypes.PUBLIC;
isFetching.value = false;
sessionStorage.removeItem('user_acct_type');
}
```
```js
// Login.vue onMounted, after sessionStorage.clear():
import { resetRole } from '../../composables/Core/useAuth.js';
import { useUserStore } from '../../stores/user.js';
onMounted(() => {
sessionStorage.clear();
localStorage.clear();
resetRole();
useUserStore().resetCurrentUser(); // clears user + acctType
// existing SW unregistration code...
});
```
### Step 4 — Extend `resetCurrentUser()` in user.js to also clear acctType
Currently `resetCurrentUser()` only sets `this.user = null`. Extend it to also reset `acctType` and remove the sessionStorage key so the role computed falls back to PUBLIC on next render.
```js
resetCurrentUser() {
this.user = null;
this.acctType = null;
sessionStorage.removeItem('user_acct_type');
},
```
## context
### Root cause trace
On a fresh page load at `/` after logging in as a different user:
1. `user.js` Pinia store state factory runs: `acctType = sessionStorage.getItem('user_acct_type') || null`
2. If sessionStorage still has the PREVIOUS user's type (e.g. 'ultimate') — possible because `fetchCurrentUser()` on the login page ran AFTER `Login.vue` cleared sessionStorage and re-set it — `acctType = 'ultimate'`
3. `app.js` calls `userStore.fetchCurrentUser()` (async)
4. Guard at user.js:78 fires: `!this.acctType` = false, `this.acctType !== 'public'` = true → **guard is false → role NOT re-fetched from server**
5. `userStore.user` is updated to the NEW user's data but `acctType` stays 'ultimate'
6. `role` computed in `useAuth.js` returns `userStore.acctType = 'ultimate'`
7. Home.vue renders `<HomeUltimate />` for the new STORE_OWNER user
### Key reactive chain
```
role (computed) = userStore.acctType || currentUser.value?.acct_type || globalRole.value
↑ populated from sessionStorage on store init → stale
```
### Affected files key refs
- `user.js:12``acctType: sessionStorage.getItem('user_acct_type') || null`
- `user.js:78-84` — the stale guard (primary bug)
- `user.js:190``resetCurrentUser()` only clears `user`, not `acctType`
- `useAuth.js:7``globalRole` module-level ref
- `useAuth.js:51-53` — module-level fetchRole() call (won't fire if sessionStorage is non-public)
- `useAuth.js:76-86``role` computed priority: userStore.acctType first
- `Home.vue:15-20` — destructures from `useAuth()` with no loading guard
## notes
- dictionary: ai-docs/dictionary.md
- linters: eslint, tsc (via vite)
- constraints: Hypervel SPA — full page reloads happen after login/logout; sessionStorage persists within a tab across reloads. The guard removal in step 1 adds one extra request per page load but is required for correctness. The loading guard in step 2 may briefly show a spinner before the dashboard; this is acceptable and far better than showing the wrong dashboard.