// Ported from legacy henza_admin/src/store/users/index.js import { defineStore } from 'pinia'; import api from '../api/client'; /** * Ported VERBATIM in behaviour from legacy store/users/index.js:3-9. * * getParents(['a','b','c']) -> ['a.b', 'a'] */ function getParents(keys: string[]): string[] { const work = [...keys]; const parents: string[] = []; while (work.length > 1) { work.pop(); parents.push(work.join('.')); } return parents; } /** * Ported from legacy store/users/index.js:10-22. * * Granting "persons.list.client.omde" implicitly grants "persons.list.client", * "persons.list" and "persons", which is what keeps parent menu nodes visible. * * The legacy version assigned to a module-scoped `let rid` and relied on that * outer variable inside the .map() callback. This version is pure — identical * output, but no state leaking between calls (legacy's `rid` survived logout). * * NOTE it only walks UP; a rid never implies its children. That is why a user * granted "wallet" still cannot see the nav item guarded by "wallet.list" — * a legacy bug, preserved deliberately. See docs/ADMIN-PORT-PLAN.md. */ export function checkParent(values: string[]): string[] { let rid: string[] = [...values]; values.forEach((x) => { const keys = x.split('.'); if (keys.length > 1) { rid = [...new Set([...rid, ...getParents(keys)])]; } }); return rid; } export interface UserInfo { id?: string; name?: string; fullName?: string; /** * Legacy split names into fname/lname. The schema stores one fullName, but the * API returns both keys so ported templates binding either keep working. */ fname?: string; lname?: string; mobile?: string; role?: string; ut?: number | null; branch_id?: string | null; user_type?: { id: number; title: string } | null; } export const useUsersStore = defineStore('users', { state: () => ({ // legacy: cityFilters — id -> title map used by table filters cityFilters: {} as Record, userInfo: undefined as UserInfo | undefined, // undefined = "not fetched yet" (distinct from [] = "fetched, has none"). // The router guard relies on that distinction, as legacy's did. roles: undefined as string[] | undefined, // The super-admin-only features (see utils/superAdmin.ts) show for this // account alone, whatever rids other admins hold. Set by the server. isSuperAdmin: false, }), getters: { /** * Port of the global `Vue.prototype.$hasPermission` from legacy main.js. * Returns undefined when roles have not loaded — legacy did the same, which * renders as falsy in v-if. */ hasPermission: (state) => (permissionId: string) => { const roles = state.roles; if (roles) { return roles.includes(permissionId); } return undefined; }, }, actions: { // legacy mutation setCities: builds an id -> title lookup setCities(payload: { id: string | number; title: string }[]) { payload.forEach((x) => { this.cityFilters[x.id] = x.title; }); }, async getCities() { try { const res = await api.post('cities/list'); this.setCities(res.data.rows); } catch (e) { console.log(e); } }, setUserInfo(payload: UserInfo) { this.userInfo = payload; }, deleteUserInfo() { this.userInfo = {}; }, /** legacy action getRoles: commits checkParent(roles), never the raw list. */ getRoles(roles: string[]) { this.roles = checkParent(roles); }, setSuperAdmin(value: unknown) { this.isSuperAdmin = value === true; }, deleteRoles() { this.roles = undefined; this.isSuperAdmin = false; }, }, });