@drax/identity-vue 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/combobox/RoleCombobox.vue +10 -0
- package/src/combobox/TenantCombobox.vue +37 -0
- package/src/components/PermissionSelector/PermissionSelector.vue +5 -2
- package/src/composables/useTenant.ts +86 -0
- package/src/cruds/role-crud/RoleCrud.vue +6 -3
- package/src/cruds/role-crud/RoleList.vue +12 -2
- package/src/cruds/tenant-crud/TenantCrud.vue +166 -0
- package/src/cruds/tenant-crud/TenantList.vue +92 -0
- package/src/cruds/user-crud/UserCrud.vue +9 -4
- package/src/cruds/user-crud/UserList.vue +1 -0
- package/src/forms/RoleForm.vue +14 -1
- package/src/forms/TenantForm.vue +48 -0
- package/src/forms/UserCreateForm.vue +16 -3
- package/src/forms/UserEditForm.vue +17 -4
- package/src/forms/UserPasswordForm.vue +7 -1
- package/src/index.ts +30 -13
- package/src/pages/TenantCrudPage.vue +12 -0
- package/src/views/TenantView.vue +24 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.16",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"format": "prettier --write src/"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@drax/common-front": "^0.0.
|
|
27
|
+
"@drax/common-front": "^0.0.16",
|
|
28
28
|
"vue-i18n": "^9.13.1"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"vue-tsc": "^2.0.11",
|
|
62
62
|
"vuetify": "^3.6.4"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "edddb7a152dab85eb7bca07bd79c497bfd12c51f"
|
|
65
65
|
}
|
|
@@ -5,6 +5,14 @@ import type { PropType } from 'vue'
|
|
|
5
5
|
const props = defineProps({
|
|
6
6
|
errorMessages: {
|
|
7
7
|
type: String as PropType<string | string[] | undefined>,
|
|
8
|
+
},
|
|
9
|
+
multiple:{
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false,
|
|
12
|
+
},
|
|
13
|
+
clearable:{
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: false,
|
|
8
16
|
}
|
|
9
17
|
})
|
|
10
18
|
|
|
@@ -29,6 +37,8 @@ onMounted(async () => {
|
|
|
29
37
|
item-value="id"
|
|
30
38
|
variant="outlined"
|
|
31
39
|
:error-messages="errorMessages"
|
|
40
|
+
:multiple="multiple"
|
|
41
|
+
:clearable="clearable"
|
|
32
42
|
></v-select>
|
|
33
43
|
</template>
|
|
34
44
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import {ref, onMounted, defineModel} from 'vue'
|
|
4
|
+
import type { PropType } from 'vue'
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
errorMessages: {
|
|
7
|
+
type: String as PropType<string | string[] | undefined>,
|
|
8
|
+
}
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const model = defineModel()
|
|
12
|
+
import {useTenant} from "../composables/useTenant";
|
|
13
|
+
const {fetchTenant} = useTenant()
|
|
14
|
+
let items = ref([])
|
|
15
|
+
|
|
16
|
+
onMounted(async () => {
|
|
17
|
+
items.value = await fetchTenant()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<v-select
|
|
25
|
+
v-model="model"
|
|
26
|
+
label="Tenant"
|
|
27
|
+
:items="items"
|
|
28
|
+
item-title="name"
|
|
29
|
+
item-value="id"
|
|
30
|
+
variant="outlined"
|
|
31
|
+
:error-messages="errorMessages"
|
|
32
|
+
></v-select>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<style scoped>
|
|
36
|
+
|
|
37
|
+
</style>
|
|
@@ -69,12 +69,15 @@ const permissionGroups = computed(() => {
|
|
|
69
69
|
v-slot="{ isSelected, toggle }"
|
|
70
70
|
:value="permission"
|
|
71
71
|
>
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
<v-chip
|
|
73
74
|
:color="isSelected? 'green' : 'grey-darken-3'" class=""
|
|
74
75
|
@click="toggle" variant="flat" :rounded="false" border
|
|
75
76
|
>
|
|
76
77
|
{{ $t ? $t('permission.'+permission) : permission }}
|
|
77
|
-
</v-
|
|
78
|
+
</v-chip>
|
|
79
|
+
|
|
80
|
+
|
|
78
81
|
</v-item>
|
|
79
82
|
</v-card-text>
|
|
80
83
|
</v-card>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {inject, ref} from "vue";
|
|
2
|
+
import type {ITenant, TenantSystem} from "@drax/identity-front";
|
|
3
|
+
import {ClientError} from "@drax/common-front";
|
|
4
|
+
import type { IClientInputError} from "@drax/common-front";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function useTenant() {
|
|
8
|
+
|
|
9
|
+
const tenantSystem = inject('TenantSystem') as TenantSystem
|
|
10
|
+
|
|
11
|
+
let tenantError = ref<string>('')
|
|
12
|
+
let inputErrors = ref<IClientInputError>()
|
|
13
|
+
let loading = ref(false);
|
|
14
|
+
|
|
15
|
+
async function fetchTenant(page = 1, perPage = 5) {
|
|
16
|
+
loading.value = true
|
|
17
|
+
let tenants = tenantSystem.fetchTenant()
|
|
18
|
+
loading.value = false
|
|
19
|
+
return tenants
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function paginateTenant(page = 1, perPage = 5, string = "") {
|
|
23
|
+
loading.value = true
|
|
24
|
+
let paginatedtenant = tenantSystem.paginateTenant(page, perPage,string)
|
|
25
|
+
loading.value = false
|
|
26
|
+
return paginatedtenant
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function createTenant(tenantData: ITenant) {
|
|
30
|
+
try {
|
|
31
|
+
loading.value = true
|
|
32
|
+
let tenant: ITenant = await tenantSystem.createTenant(tenantData)
|
|
33
|
+
return tenant
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err instanceof ClientError) {
|
|
36
|
+
inputErrors.value = err.inputErrors
|
|
37
|
+
}if(err instanceof Error) {
|
|
38
|
+
tenantError.value = err.message
|
|
39
|
+
}
|
|
40
|
+
throw err
|
|
41
|
+
}finally {
|
|
42
|
+
loading.value = false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function editTenant(id: string, tenantData: ITenant) {
|
|
48
|
+
try {
|
|
49
|
+
loading.value = true
|
|
50
|
+
let tenant: ITenant = await tenantSystem.editTenant(id, tenantData)
|
|
51
|
+
return tenant
|
|
52
|
+
} catch (err) {
|
|
53
|
+
|
|
54
|
+
if (err instanceof ClientError) {
|
|
55
|
+
inputErrors.value = err.inputErrors
|
|
56
|
+
}
|
|
57
|
+
if(err instanceof Error) {
|
|
58
|
+
tenantError.value = err.message
|
|
59
|
+
}
|
|
60
|
+
throw err
|
|
61
|
+
}finally {
|
|
62
|
+
loading.value = false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function deleteTenant(id: string) {
|
|
67
|
+
try {
|
|
68
|
+
loading.value = true
|
|
69
|
+
await tenantSystem.deleteTenant(id)
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.log("composable deleteTenant error: ", err, )
|
|
72
|
+
if (err instanceof ClientError) {
|
|
73
|
+
inputErrors.value = err.inputErrors
|
|
74
|
+
}
|
|
75
|
+
if(err instanceof Error) {
|
|
76
|
+
tenantError.value = err.message
|
|
77
|
+
}
|
|
78
|
+
throw err
|
|
79
|
+
}finally {
|
|
80
|
+
loading.value = false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {fetchTenant, paginateTenant, createTenant, editTenant, deleteTenant, loading, tenantError, inputErrors}
|
|
85
|
+
|
|
86
|
+
}
|
|
@@ -5,6 +5,7 @@ import {useRole} from "../../composables/useRole";
|
|
|
5
5
|
import type {IRole} from "@drax/identity-front";
|
|
6
6
|
import RoleForm from "../../forms/RoleForm.vue";
|
|
7
7
|
import RoleView from "../../views/RoleView.vue";
|
|
8
|
+
import TenantForm from "@/forms/TenantForm.vue";
|
|
8
9
|
|
|
9
10
|
const {createRole, editRole, deleteRole, loading, roleError, inputErrors} = useRole()
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ let dialog = ref(false);
|
|
|
20
21
|
let dialogMode = ref<DialogMode>(null);
|
|
21
22
|
let dialogTitle = ref('');
|
|
22
23
|
const roleList = ref<RoleList | null>(null);
|
|
23
|
-
let form = ref<IRole>({name: "", permissions: [], readonly: false})
|
|
24
|
+
let form = ref<IRole>({name: "", permissions: [], childRoles:[], readonly: false})
|
|
24
25
|
let target = ref<IRole>();
|
|
25
26
|
let targetId = ref<string>('');
|
|
26
27
|
let filterEnable = ref(false);
|
|
@@ -75,7 +76,7 @@ let buttonText = computed(() => {
|
|
|
75
76
|
function toCreate() {
|
|
76
77
|
dialogMode.value = 'create';
|
|
77
78
|
dialogTitle.value = 'role.creating';
|
|
78
|
-
form.value = {name: "", permissions: [], readonly: false}
|
|
79
|
+
form.value = {name: "", permissions: [], childRoles:[], readonly: false}
|
|
79
80
|
dialog.value = true;
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -84,6 +85,7 @@ function toEdit(item: IRole) {
|
|
|
84
85
|
dialogMode.value = 'edit';
|
|
85
86
|
dialogTitle.value = 'role.updating';
|
|
86
87
|
const {id, ...rest} = item;
|
|
88
|
+
rest.childRoles = rest.childRoles ? rest.childRoles.map(c => c.id) : []
|
|
87
89
|
targetId.value = id;
|
|
88
90
|
form.value = {...rest}
|
|
89
91
|
dialog.value = true;
|
|
@@ -102,7 +104,7 @@ function toDelete(item: IRole) {
|
|
|
102
104
|
</script>
|
|
103
105
|
|
|
104
106
|
<template>
|
|
105
|
-
<v-container>
|
|
107
|
+
<v-container fluid>
|
|
106
108
|
|
|
107
109
|
<v-sheet border rounded>
|
|
108
110
|
<v-toolbar>
|
|
@@ -138,6 +140,7 @@ function toDelete(item: IRole) {
|
|
|
138
140
|
<RoleForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
|
|
139
141
|
v-model="form"
|
|
140
142
|
:inputErrors="inputErrors"
|
|
143
|
+
@formSubmit="save"
|
|
141
144
|
/>
|
|
142
145
|
<RoleView v-if="dialogMode === 'delete' && target" :role="target"></RoleView>
|
|
143
146
|
</v-card-text>
|
|
@@ -21,6 +21,7 @@ const page = ref(1)
|
|
|
21
21
|
const headers = ref([
|
|
22
22
|
//{title: 'ID', align: 'start', sortable: false, key: 'id'},
|
|
23
23
|
{ title: t('role.name'), key: 'name', align: 'start' },
|
|
24
|
+
{ title: t('role.childRoles'), key: 'childRoles', align: 'start' },
|
|
24
25
|
{ title: t('role.permissions'), key: 'permissions', align: 'start' },
|
|
25
26
|
{ title: t('role.readonly'), key: 'readonly', align: 'start' },
|
|
26
27
|
{ title: '', key: 'actions', align: 'start', minWidth: '150px' },
|
|
@@ -88,6 +89,15 @@ defineExpose({
|
|
|
88
89
|
</v-chip>
|
|
89
90
|
</template>
|
|
90
91
|
|
|
92
|
+
<template v-slot:item.childRoles="{ value }" >
|
|
93
|
+
<v-chip v-for="role in value"
|
|
94
|
+
:key="role" color="blue"
|
|
95
|
+
class="ma-1"
|
|
96
|
+
>
|
|
97
|
+
{{role.name}}
|
|
98
|
+
</v-chip>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
91
101
|
<template v-slot:item.readonly="{ value }" >
|
|
92
102
|
<v-chip v-if="value" color="red" >
|
|
93
103
|
<v-icon class="mdi mdi-pencil-off-outline"></v-icon>
|
|
@@ -98,8 +108,8 @@ defineExpose({
|
|
|
98
108
|
</template>
|
|
99
109
|
|
|
100
110
|
<template v-slot:item.actions="{item}" >
|
|
101
|
-
<v-btn v-if="hasPermission('
|
|
102
|
-
<v-btn v-if="hasPermission('
|
|
111
|
+
<v-btn v-if="hasPermission('role:update')" :disabled="item.readonly" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
|
|
112
|
+
<v-btn v-if="hasPermission('role:delete')" :disabled="item.readonly" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
|
|
103
113
|
</template>
|
|
104
114
|
|
|
105
115
|
</v-data-table-server>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, ref} from 'vue'
|
|
3
|
+
import TenantList from "./TenantList.vue";
|
|
4
|
+
import {useTenant} from "../../composables/useTenant";
|
|
5
|
+
import type {ITenant} from "@drax/identity-front";
|
|
6
|
+
import TenantForm from "../../forms/TenantForm.vue";
|
|
7
|
+
import TenantView from "../../views/TenantView.vue";
|
|
8
|
+
|
|
9
|
+
const {createTenant, editTenant, deleteTenant, loading, tenantError, inputErrors} = useTenant()
|
|
10
|
+
|
|
11
|
+
interface TenantList {
|
|
12
|
+
loadItems: () => void;
|
|
13
|
+
items: ITenant[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type DialogMode = 'create' | 'edit' | 'delete' | null;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
let dialog = ref(false);
|
|
20
|
+
let dialogMode = ref<DialogMode>(null);
|
|
21
|
+
let dialogTitle = ref('');
|
|
22
|
+
const tenantList = ref<TenantList | null>(null);
|
|
23
|
+
let form = ref<ITenant>({name: ""})
|
|
24
|
+
let target = ref<ITenant>();
|
|
25
|
+
let targetId = ref<string>('');
|
|
26
|
+
let filterEnable = ref(false);
|
|
27
|
+
|
|
28
|
+
function cancel() {
|
|
29
|
+
dialog.value = false
|
|
30
|
+
inputErrors.value = {}
|
|
31
|
+
tenantError.value = '';
|
|
32
|
+
dialogMode.value = null
|
|
33
|
+
dialogTitle.value = ''
|
|
34
|
+
targetId.value = ''
|
|
35
|
+
target.value = undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function save() {
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
if (dialogMode.value === 'create') {
|
|
42
|
+
await createTenant(form.value)
|
|
43
|
+
} else if (dialogMode.value === 'edit') {
|
|
44
|
+
await editTenant(targetId.value, form.value)
|
|
45
|
+
} else if (dialogMode.value === 'delete') {
|
|
46
|
+
await deleteTenant(targetId.value)
|
|
47
|
+
}
|
|
48
|
+
dialog.value = false
|
|
49
|
+
inputErrors.value = {}
|
|
50
|
+
tenantError.value = '';
|
|
51
|
+
if (tenantList.value !== null) {
|
|
52
|
+
tenantList.value.loadItems()
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error(e)
|
|
56
|
+
if (e instanceof Error) {
|
|
57
|
+
tenantError.value = e.message
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let buttonText = computed(() => {
|
|
63
|
+
switch (dialogMode.value) {
|
|
64
|
+
case 'create':
|
|
65
|
+
return 'action.create'
|
|
66
|
+
case 'edit':
|
|
67
|
+
return 'action.update'
|
|
68
|
+
case 'delete':
|
|
69
|
+
return 'action.delete'
|
|
70
|
+
default:
|
|
71
|
+
return 'action.sent'
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
function toCreate() {
|
|
76
|
+
dialogMode.value = 'create';
|
|
77
|
+
dialogTitle.value = 'tenant.creating';
|
|
78
|
+
form.value = {name: ""}
|
|
79
|
+
dialog.value = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function toEdit(item: ITenant) {
|
|
83
|
+
console.log('toEdit', item)
|
|
84
|
+
dialogMode.value = 'edit';
|
|
85
|
+
dialogTitle.value = 'tenant.updating';
|
|
86
|
+
const {id, ...rest} = item;
|
|
87
|
+
targetId.value = id;
|
|
88
|
+
form.value = {...rest}
|
|
89
|
+
dialog.value = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toDelete(item: ITenant) {
|
|
93
|
+
console.log('toDelete', item)
|
|
94
|
+
dialogMode.value = 'delete';
|
|
95
|
+
dialogTitle.value = 'tenant.deleting';
|
|
96
|
+
target.value = item
|
|
97
|
+
const {id} = item;
|
|
98
|
+
targetId.value = id;
|
|
99
|
+
dialog.value = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<v-container fluid>
|
|
106
|
+
|
|
107
|
+
<v-sheet border rounded>
|
|
108
|
+
<v-toolbar>
|
|
109
|
+
<v-toolbar-title>{{ $t('tenant.managing') }}</v-toolbar-title>
|
|
110
|
+
<v-spacer></v-spacer>
|
|
111
|
+
<v-btn icon @click="filterEnable = !filterEnable">
|
|
112
|
+
<v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
|
113
|
+
</v-btn>
|
|
114
|
+
<v-btn color="primary" @click="toCreate">
|
|
115
|
+
{{ $t('action.create') }}
|
|
116
|
+
</v-btn>
|
|
117
|
+
</v-toolbar>
|
|
118
|
+
<v-theme-provider with-background class="pa-2 rounded-b">
|
|
119
|
+
<TenantList
|
|
120
|
+
ref="tenantList"
|
|
121
|
+
@toEdit="toEdit"
|
|
122
|
+
@toDelete="toDelete"
|
|
123
|
+
:filterEnable="filterEnable"
|
|
124
|
+
/>
|
|
125
|
+
</v-theme-provider>
|
|
126
|
+
</v-sheet>
|
|
127
|
+
|
|
128
|
+
<v-dialog v-model="dialog" max-width="800">
|
|
129
|
+
<v-sheet border>
|
|
130
|
+
<v-toolbar>
|
|
131
|
+
<v-toolbar-title>{{ $t(dialogTitle) }}</v-toolbar-title>
|
|
132
|
+
</v-toolbar>
|
|
133
|
+
<v-card class="pa-10">
|
|
134
|
+
<v-card-text v-if="tenantError">
|
|
135
|
+
<v-alert type="error">{{ tenantError }}</v-alert>
|
|
136
|
+
</v-card-text>
|
|
137
|
+
<v-card-text>
|
|
138
|
+
<TenantForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
|
|
139
|
+
v-model="form"
|
|
140
|
+
:inputErrors="inputErrors"
|
|
141
|
+
@formSubmit="save"
|
|
142
|
+
/>
|
|
143
|
+
<TenantView v-if="dialogMode === 'delete' && target" :tenant="target"></TenantView>
|
|
144
|
+
</v-card-text>
|
|
145
|
+
<v-card-actions>
|
|
146
|
+
<v-spacer></v-spacer>
|
|
147
|
+
<v-btn variant="text" @click="cancel" :loading="loading">Cancelar</v-btn>
|
|
148
|
+
<v-btn variant="flat"
|
|
149
|
+
:color="dialogMode==='delete' ? 'red' : 'primary'"
|
|
150
|
+
@click="save"
|
|
151
|
+
:loading="loading"
|
|
152
|
+
>
|
|
153
|
+
{{ $t(buttonText) }}
|
|
154
|
+
</v-btn>
|
|
155
|
+
</v-card-actions>
|
|
156
|
+
|
|
157
|
+
</v-card>
|
|
158
|
+
</v-sheet>
|
|
159
|
+
</v-dialog>
|
|
160
|
+
|
|
161
|
+
</v-container>
|
|
162
|
+
</template>
|
|
163
|
+
|
|
164
|
+
<style scoped>
|
|
165
|
+
|
|
166
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import {defineProps, ref} from "vue";
|
|
4
|
+
import {useTenant} from "../../composables/useTenant";
|
|
5
|
+
import {useAuth} from "../../composables/useAuth";
|
|
6
|
+
import {useI18n} from "vue-i18n";
|
|
7
|
+
|
|
8
|
+
const {hasPermission} = useAuth()
|
|
9
|
+
const {paginateTenant} = useTenant()
|
|
10
|
+
const {t} = useI18n()
|
|
11
|
+
|
|
12
|
+
const props = defineProps({
|
|
13
|
+
filterEnable: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: false,
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const itemsPerPage = ref(5)
|
|
20
|
+
const page = ref(1)
|
|
21
|
+
const headers = ref([
|
|
22
|
+
//{title: 'ID', align: 'start', sortable: false, key: 'id'},
|
|
23
|
+
{ title: t('tenant.name'), key: 'name', align: 'start' },
|
|
24
|
+
{ title: '', key: 'actions', align: 'start', minWidth: '150px' },
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
const serverItems = ref([])
|
|
28
|
+
const totalItems = ref(0)
|
|
29
|
+
const loading = ref(false)
|
|
30
|
+
const search = ref('')
|
|
31
|
+
|
|
32
|
+
async function loadItems(){
|
|
33
|
+
try{
|
|
34
|
+
loading.value = true
|
|
35
|
+
const r = await paginateTenant(page.value,itemsPerPage.value, search.value)
|
|
36
|
+
serverItems.value = r.items
|
|
37
|
+
totalItems.value = r.total
|
|
38
|
+
}catch (e){
|
|
39
|
+
console.error(e)
|
|
40
|
+
}finally {
|
|
41
|
+
loading.value = false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
defineExpose({
|
|
48
|
+
loadItems
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<v-data-table-server
|
|
55
|
+
v-if="hasPermission('user:manage')"
|
|
56
|
+
v-model:items-per-page="itemsPerPage"
|
|
57
|
+
v-model:page="page"
|
|
58
|
+
:headers="headers"
|
|
59
|
+
:items="serverItems"
|
|
60
|
+
:items-length="totalItems"
|
|
61
|
+
:loading="loading"
|
|
62
|
+
:search="search"
|
|
63
|
+
item-value="name"
|
|
64
|
+
@update:options="loadItems"
|
|
65
|
+
>
|
|
66
|
+
<template v-slot:top>
|
|
67
|
+
<v-toolbar border density="compact" v-if="filterEnable" class="grey-lighten-1">
|
|
68
|
+
<v-toolbar-title>{{ $t('action.filter') }}</v-toolbar-title>
|
|
69
|
+
<v-spacer></v-spacer>
|
|
70
|
+
<v-text-field v-model="search" hide-details
|
|
71
|
+
density="compact" class="mr-2"
|
|
72
|
+
variant="outlined"
|
|
73
|
+
append-inner-icon="mdi-magnify"
|
|
74
|
+
:label="$t('action.search')"
|
|
75
|
+
single-line clearable @click:clear="() => search = ''"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
</v-toolbar>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
<template v-slot:item.actions="{item}" >
|
|
83
|
+
<v-btn v-if="hasPermission('tenant:update')" :disabled="item.readonly" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
|
|
84
|
+
<v-btn v-if="hasPermission('tenant:delete')" :disabled="item.readonly" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
</v-data-table-server>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<style scoped>
|
|
91
|
+
|
|
92
|
+
</style>
|
|
@@ -8,6 +8,7 @@ import UserCreateForm from "../../forms/UserCreateForm.vue";
|
|
|
8
8
|
import UserEditForm from "../../forms/UserEditForm.vue";
|
|
9
9
|
import UserPasswordForm from "../../forms/UserPasswordForm.vue";
|
|
10
10
|
import UserView from "../../views/UserView.vue";
|
|
11
|
+
import TenantForm from "@/forms/TenantForm.vue";
|
|
11
12
|
|
|
12
13
|
const {createUser, editUser, changeUserPassword, deleteUser, loading, userError, inputErrors} = useUser()
|
|
13
14
|
|
|
@@ -23,8 +24,8 @@ let dialog = ref(false);
|
|
|
23
24
|
let dialogMode = ref<DialogMode>(null);
|
|
24
25
|
let dialogTitle = ref('');
|
|
25
26
|
const userList = ref<UserList | null>(null);
|
|
26
|
-
let createForm = ref<IUserCreate>({name: "", username: "", password: "", email: "", phone: "", role: "", active: true})
|
|
27
|
-
let editForm = ref<IUserUpdate>({name: "", username: "", email: "", phone: "", role: "", active: true})
|
|
27
|
+
let createForm = ref<IUserCreate>({name: "", username: "", password: "", email: "", phone: "", role: "", tenant: "", active: true})
|
|
28
|
+
let editForm = ref<IUserUpdate>({name: "", username: "", email: "", phone: "", role: "", tenant: "", active: true})
|
|
28
29
|
let passwordForm = ref<IUserPassword>({newPassword: "", confirmPassword: ""})
|
|
29
30
|
let passwordChanged = ref(false);
|
|
30
31
|
let target = ref<IUser>();
|
|
@@ -92,7 +93,7 @@ function toCreate() {
|
|
|
92
93
|
actionButtonEnable.value = true
|
|
93
94
|
dialogMode.value = 'create';
|
|
94
95
|
dialogTitle.value = 'user.creating';
|
|
95
|
-
createForm.value = {name: "", username: "", password: "", email: "", phone: "", role: "", active: true}
|
|
96
|
+
createForm.value = {name: "", username: "", password: "", email: "", phone: "", role: "", tenant: "", active: true}
|
|
96
97
|
dialog.value = true;
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -103,6 +104,7 @@ function toEdit(item: IUser) {
|
|
|
103
104
|
const {id, ...rest} = item;
|
|
104
105
|
targetId.value = id;
|
|
105
106
|
rest.role = rest.role ? rest.role.id : null
|
|
107
|
+
rest.tenant = rest.tenant ? rest.tenant.id : null
|
|
106
108
|
editForm.value = {...rest}
|
|
107
109
|
dialog.value = true;
|
|
108
110
|
}
|
|
@@ -131,7 +133,7 @@ function toChangePassword(item: IUser) {
|
|
|
131
133
|
</script>
|
|
132
134
|
|
|
133
135
|
<template>
|
|
134
|
-
<v-container>
|
|
136
|
+
<v-container fluid>
|
|
135
137
|
|
|
136
138
|
<v-sheet border rounded>
|
|
137
139
|
<v-toolbar>
|
|
@@ -169,12 +171,14 @@ function toChangePassword(item: IUser) {
|
|
|
169
171
|
v-if="dialogMode === 'create'"
|
|
170
172
|
v-model="createForm"
|
|
171
173
|
:inputErrors="inputErrors"
|
|
174
|
+
@formSubmit="save"
|
|
172
175
|
/>
|
|
173
176
|
|
|
174
177
|
<UserEditForm
|
|
175
178
|
v-if="dialogMode === 'edit'"
|
|
176
179
|
v-model="editForm"
|
|
177
180
|
:inputErrors="inputErrors"
|
|
181
|
+
@formSubmit="save"
|
|
178
182
|
/>
|
|
179
183
|
|
|
180
184
|
<UserView v-if="dialogMode === 'delete'
|
|
@@ -187,6 +191,7 @@ function toChangePassword(item: IUser) {
|
|
|
187
191
|
v-model="passwordForm"
|
|
188
192
|
:inputErrors="inputErrors"
|
|
189
193
|
:passwordChanged="passwordChanged"
|
|
194
|
+
@formSubmit="save"
|
|
190
195
|
/>
|
|
191
196
|
|
|
192
197
|
|
|
@@ -24,6 +24,7 @@ const headers = ref([
|
|
|
24
24
|
{ title: t('user.username'), key: 'username', align: 'start' },
|
|
25
25
|
{ title: t('user.email'), key: 'email', align: 'start' },
|
|
26
26
|
{ title: t('user.role'), key: 'role.name', align: 'start' },
|
|
27
|
+
{ title: t('user.tenant'), key: 'tenant.name', align: 'start' },
|
|
27
28
|
{ title: t('user.active'), key: 'active', align: 'start' },
|
|
28
29
|
{ title: '', key: 'actions', align: 'start', fixed:true },
|
|
29
30
|
])
|
package/src/forms/RoleForm.vue
CHANGED
|
@@ -4,6 +4,7 @@ import type {IRole} from "@drax/identity-front";
|
|
|
4
4
|
import {useI18nValidation} from "../composables/useI18nValidation";
|
|
5
5
|
import type {IClientInputError} from "@drax/common-front";
|
|
6
6
|
import PermissionSelector from "../components/PermissionSelector/PermissionSelector.vue";
|
|
7
|
+
import RoleCombobox from "../combobox/RoleCombobox.vue";
|
|
7
8
|
|
|
8
9
|
const {$ta} = useI18nValidation()
|
|
9
10
|
|
|
@@ -19,10 +20,17 @@ const form = defineModel<IRole>({
|
|
|
19
20
|
default: () => ({name: "", permissions: [], readonly: false})
|
|
20
21
|
})
|
|
21
22
|
|
|
23
|
+
const emits = defineEmits(['formSubmit'])
|
|
24
|
+
|
|
25
|
+
// Function to call when form is attempted to be submitted
|
|
26
|
+
const onSubmit = () => {
|
|
27
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
</script>
|
|
23
31
|
|
|
24
32
|
<template>
|
|
25
|
-
<form>
|
|
33
|
+
<form @submit.prevent="onSubmit">
|
|
26
34
|
<v-text-field
|
|
27
35
|
variant="outlined"
|
|
28
36
|
id="name-input"
|
|
@@ -33,6 +41,11 @@ const form = defineModel<IRole>({
|
|
|
33
41
|
:error-messages="$ta(inputErrors.name)"
|
|
34
42
|
></v-text-field>
|
|
35
43
|
|
|
44
|
+
<RoleCombobox
|
|
45
|
+
v-model="form.childRoles"
|
|
46
|
+
:error-messages="$ta(inputErrors.role)"
|
|
47
|
+
multiple
|
|
48
|
+
></RoleCombobox>
|
|
36
49
|
|
|
37
50
|
<PermissionSelector v-model="form.permissions"></PermissionSelector>
|
|
38
51
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {defineModel, type PropType} from "vue";
|
|
3
|
+
import type {ITenant} from "@drax/identity-front";
|
|
4
|
+
import {useI18nValidation} from "../composables/useI18nValidation";
|
|
5
|
+
import type {IClientInputError} from "@drax/common-front";
|
|
6
|
+
|
|
7
|
+
const {$ta} = useI18nValidation()
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
inputErrors: {
|
|
11
|
+
type: Object as PropType<IClientInputError>,
|
|
12
|
+
default: () => ({name: ""})
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const form = defineModel<ITenant>({
|
|
17
|
+
type: Object,
|
|
18
|
+
default: () => ({name: ""})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Define emits
|
|
22
|
+
const emits = defineEmits(['formSubmit'])
|
|
23
|
+
|
|
24
|
+
// Function to call when form is attempted to be submitted
|
|
25
|
+
const onSubmit = () => {
|
|
26
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<form @submit.prevent="onSubmit">
|
|
33
|
+
<v-text-field
|
|
34
|
+
variant="outlined"
|
|
35
|
+
id="name-input"
|
|
36
|
+
label="Name"
|
|
37
|
+
v-model="form.name"
|
|
38
|
+
prepend-inner-icon="mdi-card-account-details"
|
|
39
|
+
required
|
|
40
|
+
:error-messages="$ta(inputErrors.name)"
|
|
41
|
+
></v-text-field>
|
|
42
|
+
|
|
43
|
+
</form>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<style scoped>
|
|
47
|
+
|
|
48
|
+
</style>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import {ref, defineModel, type PropType} from "vue";
|
|
3
3
|
import RoleCombobox from "../combobox/RoleCombobox.vue";
|
|
4
|
+
import TenantCombobox from "../combobox/TenantCombobox.vue";
|
|
4
5
|
import type {IClientInputError} from "@drax/common-front";
|
|
5
6
|
import type {IUserCreate} from "@drax/identity-front/src/interfaces/IUser";
|
|
6
7
|
import {useI18nValidation} from "../composables/useI18nValidation";
|
|
@@ -10,21 +11,28 @@ const {$ta} = useI18nValidation()
|
|
|
10
11
|
const props = defineProps({
|
|
11
12
|
inputErrors: {
|
|
12
13
|
type: Object as PropType<IClientInputError>,
|
|
13
|
-
default: () => ({name: "", username: "", password: "", email: "", phone: "", role: "", active: ""})
|
|
14
|
+
default: () => ({name: "", username: "", password: "", email: "", phone: "", role: "", tenant: "", active: ""})
|
|
14
15
|
}
|
|
15
16
|
})
|
|
16
17
|
|
|
17
18
|
const form = defineModel<IUserCreate>({
|
|
18
19
|
type: Object,
|
|
19
|
-
default: () => ({name: "", username: "", password: "", email: "", phone: "", role: "", active: true})
|
|
20
|
+
default: () => ({name: "", username: "", password: "", email: "", phone: "", role: "", tenant: "", active: true})
|
|
20
21
|
})
|
|
21
22
|
|
|
22
23
|
let passwordVisibility = ref(false)
|
|
23
24
|
|
|
25
|
+
// Define emits
|
|
26
|
+
const emits = defineEmits(['formSubmit'])
|
|
27
|
+
|
|
28
|
+
// Function to call when form is attempted to be submitted
|
|
29
|
+
const onSubmit = () => {
|
|
30
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
31
|
+
}
|
|
24
32
|
</script>
|
|
25
33
|
|
|
26
34
|
<template>
|
|
27
|
-
<form>
|
|
35
|
+
<form @submit.prevent="onSubmit">
|
|
28
36
|
|
|
29
37
|
<v-text-field
|
|
30
38
|
variant="outlined"
|
|
@@ -65,6 +73,11 @@ let passwordVisibility = ref(false)
|
|
|
65
73
|
:error-messages="$ta(inputErrors.role)"
|
|
66
74
|
></RoleCombobox>
|
|
67
75
|
|
|
76
|
+
<TenantCombobox
|
|
77
|
+
v-model="form.tenant"
|
|
78
|
+
:error-messages="$ta(inputErrors.tenant)"
|
|
79
|
+
></TenantCombobox>
|
|
80
|
+
|
|
68
81
|
<v-text-field
|
|
69
82
|
v-model="form.email"
|
|
70
83
|
variant="outlined"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import {defineModel, type PropType} from "vue";
|
|
3
3
|
import RoleCombobox from "../combobox/RoleCombobox.vue";
|
|
4
|
+
import TenantCombobox from "../combobox/TenantCombobox.vue";
|
|
4
5
|
import type {IClientInputError} from "@drax/common-front";
|
|
5
6
|
import type {IUserUpdate} from "@drax/identity-front/src/interfaces/IUser";
|
|
6
7
|
import {useI18nValidation} from "../composables/useI18nValidation";
|
|
@@ -9,20 +10,27 @@ const {$ta} = useI18nValidation()
|
|
|
9
10
|
const props = defineProps({
|
|
10
11
|
inputErrors: {
|
|
11
12
|
type: Object as PropType<IClientInputError>,
|
|
12
|
-
default: () => ({name: "", username: "", email: "", phone: "", role: "", active: ""})
|
|
13
|
+
default: () => ({name: "", username: "", email: "", phone: "", role: "", tenant: "", active: ""})
|
|
13
14
|
}
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
const form = defineModel<IUserUpdate>({
|
|
17
18
|
type: Object,
|
|
18
|
-
default: () => ({name: "", username: "", email: "", phone: "", role: "", active: true})
|
|
19
|
+
default: () => ({name: "", username: "", email: "", phone: "", role: "", tenant: "", active: true})
|
|
19
20
|
})
|
|
20
21
|
|
|
22
|
+
// Define emits
|
|
23
|
+
const emits = defineEmits(['formSubmit'])
|
|
24
|
+
|
|
25
|
+
// Function to call when form is attempted to be submitted
|
|
26
|
+
const onSubmit = () => {
|
|
27
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
28
|
+
}
|
|
21
29
|
|
|
22
30
|
</script>
|
|
23
31
|
|
|
24
32
|
<template>
|
|
25
|
-
<form>
|
|
33
|
+
<form @submit.prevent="onSubmit">
|
|
26
34
|
<v-sheet>
|
|
27
35
|
</v-sheet>
|
|
28
36
|
|
|
@@ -51,6 +59,11 @@ const form = defineModel<IUserUpdate>({
|
|
|
51
59
|
:error-messages="$ta(inputErrors.role)"
|
|
52
60
|
></RoleCombobox>
|
|
53
61
|
|
|
62
|
+
<TenantCombobox
|
|
63
|
+
v-model="form.tenant"
|
|
64
|
+
:error-messages="$ta(inputErrors.tenant)"
|
|
65
|
+
></TenantCombobox>
|
|
66
|
+
|
|
54
67
|
<v-text-field
|
|
55
68
|
v-model="form.email"
|
|
56
69
|
variant="outlined"
|
|
@@ -31,7 +31,13 @@ function confirmPasswordRule(value: string) {
|
|
|
31
31
|
return form.value.newPassword === form.value.confirmPassword || t('validation.password.confirmed')
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Define emits
|
|
35
|
+
const emits = defineEmits(['formSubmit'])
|
|
34
36
|
|
|
37
|
+
// Function to call when form is attempted to be submitted
|
|
38
|
+
const onSubmit = () => {
|
|
39
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
40
|
+
}
|
|
35
41
|
</script>
|
|
36
42
|
|
|
37
43
|
<template>
|
|
@@ -42,7 +48,7 @@ function confirmPasswordRule(value: string) {
|
|
|
42
48
|
</v-alert>
|
|
43
49
|
</v-sheet>
|
|
44
50
|
<v-sheet v-else>
|
|
45
|
-
<form ref="changeUserPassword">
|
|
51
|
+
<form ref="changeUserPassword" @submit.prevent="onSubmit">
|
|
46
52
|
|
|
47
53
|
<div class="text-subtitle-1 text-medium-emphasis">{{ $t('user.newPassword') }}</div>
|
|
48
54
|
<v-text-field
|
package/src/index.ts
CHANGED
|
@@ -2,20 +2,31 @@ import IdentityLogin from "./components/IdentityLogin/IdentityLogin.vue";
|
|
|
2
2
|
import IdentityProfileAvatar from "./components/IdentityProfileAvatar/IdentityProfileAvatar.vue";
|
|
3
3
|
import IdentityProfileDrawer from "./components/IdentityProfileDrawer/IdentityProfileDrawer.vue";
|
|
4
4
|
import IdentityProfileView from "./components/IdentityProfileView/IdentityProfileView.vue";
|
|
5
|
+
|
|
5
6
|
import UserCrud from "./cruds/user-crud/UserCrud.vue";
|
|
6
7
|
import UserList from "./cruds/user-crud/UserList.vue";
|
|
7
8
|
import UserCrudPage from "./pages/UserCrudPage.vue";
|
|
8
9
|
import UserCreateForm from "./forms/UserCreateForm.vue";
|
|
9
10
|
import UserEditForm from "./forms/UserEditForm.vue";
|
|
10
|
-
import
|
|
11
|
+
import UserView from "./views/UserView.vue";
|
|
11
12
|
|
|
13
|
+
import RoleForm from "./forms/RoleForm.vue";
|
|
14
|
+
import RoleView from "./views/RoleView.vue";
|
|
12
15
|
import RoleCrud from "./cruds/role-crud/RoleCrud.vue";
|
|
13
16
|
import RoleList from "./cruds/role-crud/RoleList.vue";
|
|
14
17
|
import RoleCrudPage from "./pages/RoleCrudPage.vue";
|
|
15
18
|
|
|
19
|
+
|
|
20
|
+
import TenantForm from "./forms/TenantForm.vue";
|
|
21
|
+
import TenantView from "./views/TenantView.vue";
|
|
22
|
+
import TenantCrud from "./cruds/tenant-crud/TenantCrud.vue";
|
|
23
|
+
import TenantList from "./cruds/tenant-crud/TenantList.vue";
|
|
24
|
+
import TenantCrudPage from "./pages/TenantCrudPage.vue";
|
|
25
|
+
|
|
16
26
|
import {useAuth} from "./composables/useAuth.js";
|
|
17
27
|
import {useUser} from "./composables/useUser.js";
|
|
18
28
|
import {useRole} from "./composables/useRole.js";
|
|
29
|
+
import {useTenant} from "./composables/useTenant.js";
|
|
19
30
|
import {useAuthStore} from "./stores/auth/AuthStore.js";
|
|
20
31
|
|
|
21
32
|
export {
|
|
@@ -25,26 +36,32 @@ export {
|
|
|
25
36
|
IdentityProfileView,
|
|
26
37
|
IdentityProfileDrawer,
|
|
27
38
|
|
|
28
|
-
//
|
|
39
|
+
//User
|
|
40
|
+
UserView,
|
|
29
41
|
UserCreateForm,
|
|
30
42
|
UserEditForm,
|
|
31
|
-
RoleForm,
|
|
32
|
-
|
|
33
|
-
//Vue Composables
|
|
34
|
-
useAuth,
|
|
35
|
-
useUser,
|
|
36
|
-
useRole,
|
|
37
|
-
|
|
38
|
-
//Cruds
|
|
39
43
|
UserCrud,
|
|
40
44
|
UserList,
|
|
45
|
+
UserCrudPage,
|
|
46
|
+
useAuth,
|
|
47
|
+
useUser,
|
|
41
48
|
|
|
49
|
+
//Role
|
|
50
|
+
RoleView,
|
|
51
|
+
RoleForm,
|
|
42
52
|
RoleCrud,
|
|
43
53
|
RoleList,
|
|
44
|
-
|
|
45
|
-
//Pages
|
|
46
|
-
UserCrudPage,
|
|
47
54
|
RoleCrudPage,
|
|
55
|
+
useRole,
|
|
56
|
+
|
|
57
|
+
//Tenant
|
|
58
|
+
TenantView,
|
|
59
|
+
TenantForm,
|
|
60
|
+
TenantCrud,
|
|
61
|
+
TenantList,
|
|
62
|
+
TenantCrudPage,
|
|
63
|
+
useTenant,
|
|
64
|
+
|
|
48
65
|
|
|
49
66
|
//Stores
|
|
50
67
|
useAuthStore
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type {PropType} from "vue";
|
|
3
|
+
import type {ITenant} from "@drax/identity-front";
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
tenant:{
|
|
7
|
+
type: Object as PropType<ITenant>,
|
|
8
|
+
required: true,
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<v-sheet border class="position-relative d-flex justify-center align-center" height="150">
|
|
15
|
+
<v-sheet class="text-center">
|
|
16
|
+
<h6 class="text-h6">{{$t ? $t('tenant.name') : 'Name'}} : {{ tenant.name }}</h6>
|
|
17
|
+
</v-sheet>
|
|
18
|
+
</v-sheet>
|
|
19
|
+
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<style scoped>
|
|
23
|
+
|
|
24
|
+
</style>
|