diff --git a/frontend/src/modelTypes/IUser.ts b/frontend/src/modelTypes/IUser.ts index d57d66acf..470ceb7ae 100644 --- a/frontend/src/modelTypes/IUser.ts +++ b/frontend/src/modelTypes/IUser.ts @@ -23,4 +23,5 @@ export interface IUser extends IAbstract { isLocalUser: boolean deletionScheduledAt: string | Date | null + isAdmin?: boolean } diff --git a/frontend/src/models/user.ts b/frontend/src/models/user.ts index 24d64665d..d3cba06dd 100644 --- a/frontend/src/models/user.ts +++ b/frontend/src/models/user.ts @@ -81,6 +81,7 @@ export default class UserModel extends AbstractModel implements IUser { isLocalUser: boolean deletionScheduledAt: null + isAdmin?: boolean constructor(data: Partial = {}) { super() diff --git a/frontend/src/stores/config.test.ts b/frontend/src/stores/config.test.ts new file mode 100644 index 000000000..1d16c8a9d --- /dev/null +++ b/frontend/src/stores/config.test.ts @@ -0,0 +1,40 @@ +import {describe, it, expect, beforeEach} from 'vitest' +import {setActivePinia, createPinia} from 'pinia' +import {computed} from 'vue' + +import {useConfigStore} from './config' + +describe('config store', () => { + beforeEach(() => { + setActivePinia(createPinia()) + }) + + describe('isProFeatureEnabled', () => { + it('returns true when the feature is in the enabledProFeatures list', () => { + const store = useConfigStore() + store.enabledProFeatures = ['admin_panel'] + expect(store.isProFeatureEnabled('admin_panel')).toBe(true) + }) + + it('returns false for features not present in the list', () => { + const store = useConfigStore() + store.enabledProFeatures = ['admin_panel'] + expect(store.isProFeatureEnabled('time_tracking')).toBe(false) + }) + + it('returns false when the list is empty (free mode)', () => { + const store = useConfigStore() + store.enabledProFeatures = [] + expect(store.isProFeatureEnabled('admin_panel')).toBe(false) + }) + + it('reacts to store updates when wrapped in computed', () => { + const store = useConfigStore() + store.enabledProFeatures = [] + const enabled = computed(() => store.isProFeatureEnabled('admin_panel')) + expect(enabled.value).toBe(false) + store.enabledProFeatures = ['admin_panel'] + expect(enabled.value).toBe(true) + }) + }) +}) diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index 40f47a4c0..6ca087ff4 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -44,6 +44,7 @@ export interface ConfigState { }, }, publicTeamsEnabled: boolean, + enabledProFeatures: string[], } export const useConfigStore = defineStore('config', () => { @@ -83,6 +84,7 @@ export const useConfigStore = defineStore('config', () => { }, }, publicTeamsEnabled: false, + enabledProFeatures: [], }) const migratorsEnabled = computed(() => state.availableMigrators?.length > 0) @@ -100,6 +102,10 @@ export const useConfigStore = defineStore('config', () => { Object.assign(state, config) } + function isProFeatureEnabled(name: string): boolean { + return state.enabledProFeatures?.includes(name) ?? false + } + async function update(): Promise { const HTTP = HTTPFactory() const {data: config} = await HTTP.get('info') @@ -108,7 +114,7 @@ export const useConfigStore = defineStore('config', () => { throw new InvalidApiUrlProvidedError() } - setConfig(objectToCamelCase(config)) + setConfig(objectToCamelCase(config) as ConfigState) return !!config } @@ -118,6 +124,7 @@ export const useConfigStore = defineStore('config', () => { migratorsEnabled, apiBase, setConfig, + isProFeatureEnabled, update, } diff --git a/pkg/routes/api/v1/user_show.go b/pkg/routes/api/v1/user_show.go index 9e850f1d6..d5a391267 100644 --- a/pkg/routes/api/v1/user_show.go +++ b/pkg/routes/api/v1/user_show.go @@ -38,6 +38,7 @@ type UserWithSettings struct { DeletionScheduledAt time.Time `json:"deletion_scheduled_at"` IsLocalUser bool `json:"is_local_user"` AuthProvider string `json:"auth_provider"` + IsAdmin bool `json:"is_admin"` } // UserShow gets all information about the current user @@ -83,6 +84,7 @@ func UserShow(c *echo.Context) error { }, DeletionScheduledAt: u.DeletionScheduledAt, IsLocalUser: u.Issuer == user.IssuerLocal, + IsAdmin: u.IsAdmin, } us.AuthProvider, err = getAuthProviderName(u)