-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathadmin.layout.vue
More file actions
147 lines (143 loc) · 5.05 KB
/
admin.layout.vue
File metadata and controls
147 lines (143 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<template>
<v-container fluid class="pb-0">
<v-alert
v-if="error"
type="error"
variant="tonal"
density="compact"
closable
class="mx-2 mt-2"
:class="config.vuetify.theme.rounded"
icon="fa-solid fa-circle-exclamation"
@click:close="clearError"
>
<span class="text-body-medium">{{ error }}</span>
</v-alert>
<v-alert
v-if="showMailerWarning"
type="warning"
variant="tonal"
density="compact"
class="mx-2 mt-2"
:class="config.vuetify.theme.rounded"
icon="fa-solid fa-triangle-exclamation"
>
<span class="text-body-medium">
No mailer configured. Users can register with any email without verification. Set up SMTP to enable email verification.
</span>
</v-alert>
<CorePageHeaderTabs
icon="fa-solid fa-user-tie"
:title="currentBreadcrumb ? '' : 'Admin'"
:tabs="allTabs"
:can="adminCan"
:base-path="basePath"
:hide-tabs="!!currentBreadcrumb"
>
<template v-if="currentBreadcrumb" #breadcrumb>
<router-link to="/admin" class="text-medium-emphasis text-decoration-none">Admin</router-link>
<v-icon icon="fa-solid fa-chevron-right" size="x-small" class="mx-2 text-medium-emphasis"></v-icon>
<span :class="currentBreadcrumb.titleClass || ''">{{ currentBreadcrumb.title }}</span>
</template>
</CorePageHeaderTabs>
</v-container>
<router-view />
</template>
<script>
/**
* Module dependencies.
*/
import CorePageHeaderTabs from '../../core/components/core.pageHeaderTabs.component.vue';
import { ability } from '../../../lib/helpers/ability';
import { useAdminStore } from '../stores/admin.store';
import { useAuthStore } from '../../auth/stores/auth.store';
/**
* Built-in admin tabs (canonical). Downstream apps may add more via
* `config.admin.tabs` — both are merged and passed to CoreSurfaceTabBar,
* which validates + CASL-filters internally.
*/
const BUILT_IN_TABS = Object.freeze([
{ value: 'users', label: 'Users', icon: 'fa-solid fa-users', route: 'users' },
{ value: 'organizations', label: 'Organizations', icon: 'fa-solid fa-building', route: 'organizations' },
{ value: 'readiness', label: 'Readiness', icon: 'fa-solid fa-clipboard-check', route: 'readiness' },
{ value: 'activity', label: 'Activity', icon: 'fa-solid fa-clock-rotate-left', route: 'activity' },
]);
/**
* Component definition.
*
* `admin.layout.vue` is the parent layout for the admin section. It renders
* banners (error + mailer-not-configured warning), a page header that either
* shows the admin tab bar (list-page mode) or a breadcrumb pushed by a sub-
* view (detail-page mode), and a `<router-view>` for nested children.
*
* Tab rendering is delegated to `CoreSurfaceTabBar` (the same primitive used
* by `user.view.vue` and `organization.detail.component.vue`) — full chrome
* convergence across Account / Organization / Admin surfaces. The tab bar
* receives the canonical built-in tabs merged with any downstream extras from
* `config.admin.tabs`; validation + CASL gating happen inside the bar.
*/
export default {
name: 'AdminLayout',
components: { CorePageHeaderTabs },
computed: {
/**
* @desc Base path for admin tabs (where CoreSurfaceTabBar resolves relative routes).
* @returns {string}
*/
basePath() {
return '/admin';
},
/**
* @desc Global error from the admin store (surfaced as a banner above the header).
* @returns {string|null}
*/
error() {
return useAdminStore().error;
},
/**
* @desc Whether to show the mailer-not-configured warning across admin tabs.
* @returns {boolean}
*/
showMailerWarning() {
return useAuthStore().serverConfig?.mail?.configured === false;
},
/**
* @desc Current breadcrumb published by an admin sub-view via
* `useAdminStore().setBreadcrumb(...)`. When set, the layout renders
* a breadcrumb in the header instead of the tab bar.
* @returns {{ title: string, titleClass?: string } | null}
*/
currentBreadcrumb() {
return useAdminStore().currentBreadcrumb;
},
/**
* @desc Merged tab list (built-in + config-driven extras), passed to
* CoreSurfaceTabBar. Validation and CASL filtering happen inside
* the bar via `resolveSurfaceTabs`.
* @returns {Array<object>}
*/
allTabs() {
const extras = Array.isArray(this.config?.admin?.tabs) ? this.config.admin.tabs : [];
return [...BUILT_IN_TABS, ...extras];
},
/**
* @desc Reactive CASL predicate passed to CoreSurfaceTabBar. Falls back
* to allow-all when ability is not yet loaded (consistent with the
* user view's `userCan`).
* @returns {(action: string, subject: string) => boolean}
*/
adminCan() {
return (action, subject) => (ability ? ability.can(action, subject) : true);
},
},
methods: {
/**
* @desc Clear the global admin error banner.
* @returns {void}
*/
clearError() {
useAdminStore().error = null;
},
},
};
</script>