Skip to content

Commit 50bf2da

Browse files
authored
Merge pull request #658 from kubero-dev/feature/add-account-administration
Feature/add account administration
2 parents e33863d + 74bdc4f commit 50bf2da

38 files changed

Lines changed: 2677 additions & 50 deletions
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<template>
2+
<v-container>
3+
<v-tabs v-model="tab" background-color="primary" dark>
4+
<v-tab>Users</v-tab>
5+
<v-tab>Teams</v-tab>
6+
<v-tab>Roles</v-tab>
7+
<v-tab>Tokens</v-tab>
8+
</v-tabs>
9+
<v-tabs-window v-model="tab">
10+
<v-tabs-window-item :value="0">
11+
<users />
12+
</v-tabs-window-item>
13+
<v-tabs-window-item :value="1">
14+
<teams />
15+
</v-tabs-window-item>
16+
<v-tabs-window-item :value="2">
17+
<roles />
18+
</v-tabs-window-item>
19+
<v-tabs-window-item :value="3">
20+
<tokens />
21+
</v-tabs-window-item>
22+
</v-tabs-window>
23+
</v-container>
24+
</template>
25+
26+
<script lang="ts">
27+
import { defineComponent, ref } from 'vue'
28+
import users from './users.vue'
29+
import teams from './teams.vue'
30+
import roles from './roles.vue'
31+
import tokens from './tokens.vue'
32+
33+
export default defineComponent({
34+
name: 'AccountTabs',
35+
components: {
36+
users,
37+
teams,
38+
roles,
39+
tokens,
40+
},
41+
setup() {
42+
const tab = ref(0)
43+
return { tab }
44+
},
45+
})
46+
</script>
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
<template>
2+
<v-container>
3+
<v-data-table
4+
:headers="headers"
5+
:items="roles"
6+
:loading="loading"
7+
class="elevation-0 border-0"
8+
item-key="id"
9+
:search="search"
10+
>
11+
<template #top>
12+
<v-text-field
13+
v-model="search"
14+
label="Search Roles"
15+
prepend-inner-icon="mdi-magnify"
16+
single-line
17+
hide-details
18+
outlined
19+
class="mx-0 mt-2"
20+
clearable
21+
density="compact"
22+
></v-text-field>
23+
</template>
24+
<template v-slot:[`item.name`]="{ item }">
25+
<span>
26+
<v-chip
27+
class="ma-2"
28+
color="primary"
29+
label
30+
>
31+
<v-icon icon="mdi-account-circle-outline" start></v-icon>
32+
{{ item.name}}
33+
</v-chip>
34+
</span>
35+
</template>
36+
<template v-slot:[`item.permissions`]="{ item }">
37+
<span v-for="permission in item.permissions" :key="permission.id">
38+
{{ permission.resource }}: {{ permission.action }}
39+
</span>
40+
</template>
41+
<template v-slot:[`item.permissionsApp`]="{ item }">
42+
<span>
43+
<v-icon
44+
color="primary"
45+
>
46+
{{getResourcePermissions(item.permissions, 'app') }}
47+
</v-icon>
48+
</span>
49+
</template>
50+
<template v-slot:[`item.permissionsPipeline`]="{ item }">
51+
<span>
52+
<v-icon
53+
color="primary"
54+
>
55+
{{getResourcePermissions(item.permissions, 'pipeline') }}
56+
</v-icon>
57+
</span>
58+
</template>
59+
<template v-slot:[`item.permissionsAccount`]="{ item }">
60+
<span>
61+
<v-icon
62+
color="primary"
63+
>
64+
{{getResourcePermissions(item.permissions, 'account') }}
65+
</v-icon>
66+
</span>
67+
</template>
68+
<template v-slot:[`item.permissionsConfig`]="{ item }">
69+
<span>
70+
<v-icon
71+
color="primary"
72+
>
73+
{{getResourcePermissions(item.permissions, 'config') }}
74+
</v-icon>
75+
</span>
76+
</template>
77+
<template v-slot:[`item.actions`]="{ item }">
78+
<v-btn
79+
elevation="0"
80+
variant="tonal"
81+
size="small"
82+
class="ma-2"
83+
color="secondary"
84+
@click="openEditRoleDialog(item)"
85+
>
86+
<v-icon color="primary">
87+
mdi-pencil
88+
</v-icon>
89+
</v-btn>
90+
<v-btn
91+
elevation="0"
92+
variant="tonal"
93+
size="small"
94+
class="ma-2"
95+
color="secondary"
96+
@click="deleteRole(item)"
97+
>
98+
<v-icon color="primary">
99+
mdi-delete
100+
</v-icon>
101+
</v-btn>
102+
</template>
103+
</v-data-table>
104+
105+
<!-- Button to add a role
106+
<div style="display: flex; justify-content: flex-end; margin-top: 16px;">
107+
<v-btn
108+
fab
109+
color="primary"
110+
style="margin-right: 6px;"
111+
@click="openCreateDialog"
112+
>
113+
<v-icon>mdi-plus</v-icon>
114+
<span class="sr-only">Create Role</span>
115+
</v-btn>
116+
</div>
117+
-->
118+
119+
<!-- Dialog to edit a role -->
120+
<v-dialog v-model="editDialog" max-width="500px">
121+
<v-card>
122+
<v-card-title>Edit Role</v-card-title>
123+
<v-card-text>
124+
<v-text-field v-model="editedRole.name" label="Role Name"></v-text-field>
125+
</v-card-text>
126+
<v-card-actions>
127+
<v-spacer />
128+
<v-btn text @click="editDialog = false">Abort</v-btn>
129+
<v-btn color="primary" @click="saveEdit">Save</v-btn>
130+
</v-card-actions>
131+
</v-card>
132+
</v-dialog>
133+
134+
<!-- Dialog for a new Role -->
135+
<v-dialog v-model="createDialog" max-width="500px">
136+
<v-card>
137+
<v-card-title>Create Role</v-card-title>
138+
<v-card-text>
139+
<v-text-field v-model="newRole.name" label="Role Name"></v-text-field>
140+
</v-card-text>
141+
<v-card-actions>
142+
<v-spacer />
143+
<v-btn text @click="createDialog = false">Abort</v-btn>
144+
<v-btn color="primary" @click="saveCreate">Create</v-btn>
145+
</v-card-actions>
146+
</v-card>
147+
</v-dialog>
148+
</v-container>
149+
</template>
150+
151+
<script lang="ts">
152+
import { defineComponent, ref, onMounted } from 'vue'
153+
import axios from 'axios'
154+
155+
export default defineComponent({
156+
name: 'RolesTable',
157+
setup() {
158+
interface Role {
159+
id: string | number;
160+
name: string;
161+
permissions?: Permission[];
162+
}
163+
interface Permission {
164+
id: string | number;
165+
resource: string;
166+
action: string;
167+
}
168+
const roles = ref<Role[]>([])
169+
const loading = ref(false)
170+
const search = ref('')
171+
const editDialog = ref(false)
172+
const createDialog = ref(false)
173+
const editedRole = ref<Role | any>({})
174+
const newRole = ref({
175+
name: '',
176+
})
177+
178+
const headers = [
179+
{ title: 'Role', value: 'name' },
180+
//{ title: 'Permissions', value: 'permissions' },
181+
{ title: 'App', value: 'permissionsApp', align: 'center' as const },
182+
{ title: 'Pipeline', value: 'permissionsPipeline', align: 'center' as const },
183+
{ title: 'Accounts', value: 'permissionsAccount', align: 'center' as const},
184+
{ title: 'Configuration', value: 'permissionsConfig', align: 'center' as const},
185+
//{ title: 'Actions', value: 'actions', sortable: false, align: 'end' },
186+
]
187+
188+
const loadRoles = async () => {
189+
loading.value = true
190+
try {
191+
const res = await axios.get('/api/roles')
192+
roles.value = res.data
193+
} catch (e) {
194+
roles.value = []
195+
}
196+
loading.value = false
197+
}
198+
199+
const openEditRoleDialog = (role: Role) => {
200+
editedRole.value = { ...role }
201+
editDialog.value = true
202+
}
203+
204+
const saveEdit = async () => {
205+
try {
206+
await axios.put(`/api/roles/${editedRole.value.id}`, editedRole.value)
207+
await loadRoles()
208+
editDialog.value = false
209+
} catch (e) {
210+
console.error('Error saving role:', e)
211+
}
212+
}
213+
214+
const deleteRole = async (role: Role) => {
215+
try {
216+
await axios.delete(`/api/roles/${role.id}`)
217+
await loadRoles()
218+
} catch (e) {
219+
console.error('Error deleting role:', e)
220+
}
221+
}
222+
223+
const openCreateDialog = () => {
224+
newRole.value = { name: '' }
225+
createDialog.value = true
226+
}
227+
228+
const saveCreate = async () => {
229+
try {
230+
await axios.post('/api/roles', newRole.value)
231+
await loadRoles()
232+
createDialog.value = false
233+
} catch (e) {
234+
console.error('Error creating role:', e)
235+
}
236+
}
237+
/*
238+
const getResources = async () => {
239+
try {
240+
const res = await axios.get('/api/roles/resources')
241+
return res.data
242+
} catch (e) {
243+
console.error('Error fetching resources:', e)
244+
return []
245+
}
246+
}
247+
*/
248+
const getResourcePermissions = (permissions: any, resource: string) => {
249+
for (const permission of permissions) {
250+
if (permission.resource === resource) {
251+
switch (permission.action) {
252+
case 'write':
253+
return 'mdi-pencil';
254+
break;
255+
case 'read':
256+
return 'mdi-eye';
257+
break;
258+
}
259+
return
260+
}
261+
}
262+
return 'mdi-cancel'
263+
}
264+
265+
onMounted(() => {
266+
loadRoles()
267+
})
268+
269+
return {
270+
roles,
271+
headers,
272+
loading,
273+
search,
274+
editDialog,
275+
createDialog,
276+
editedRole,
277+
newRole,
278+
openEditRoleDialog,
279+
saveEdit,
280+
deleteRole,
281+
openCreateDialog,
282+
saveCreate,
283+
getResourcePermissions,
284+
}
285+
},
286+
})
287+
</script>

0 commit comments

Comments
 (0)