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

6.3 KiB
Raw Permalink Blame History

task, cycles, context, private, started, finished
task cycles context private started finished
Fix homepage fragment persisting from previous login when switching account types 5 true false 2026-05-17T16:22:34Z 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.

// 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:

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.

// useAuth.js  add export:
export function resetRole() {
    globalRole.value = UserTypes.PUBLIC;
    isFetching.value = false;
    sessionStorage.removeItem('user_acct_type');
}
// 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.

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:12acctType: sessionStorage.getItem('user_acct_type') || null
  • user.js:78-84 — the stale guard (primary bug)
  • user.js:190resetCurrentUser() only clears user, not acctType
  • useAuth.js:7globalRole module-level ref
  • useAuth.js:51-53 — module-level fetchRole() call (won't fire if sessionStorage is non-public)
  • useAuth.js:76-86role 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.