first shot at users view

This commit is contained in:
Gauthier Roebroeck 2025-05-21 15:00:50 +08:00
parent 33415041de
commit 6ff0015e1e
5 changed files with 133 additions and 5 deletions

View file

@ -11,7 +11,7 @@ const DATE = ts.factory.createTypeReferenceNode(ts.factory.createIdentifier("Dat
const NULL = ts.factory.createLiteralTypeNode(ts.factory.createNull()); // `null`
const ast = await openapiTS(mySchema, {
transform(schemaObject, metadata) {
transform(schemaObject) {
if (schemaObject.format === "date-time") {
return schemaObject.nullable
? ts.factory.createUnionTypeNode([DATE, NULL])

View file

@ -0,0 +1,11 @@
import {defineQuery, useQuery} from '@pinia/colada'
import {komgaClient} from '@/api/komga-client'
export const useUsers = defineQuery(() => {
return useQuery({
key: () => ['users'],
query: () => komgaClient.GET('/api/v2/users')
// unwrap the openapi-fetch structure on success
.then((res) => res.data),
})
})

View file

@ -50,7 +50,7 @@
v-if="release.version == currentVersion"
class="mx-2 mt-n3"
size="small"
label
rounded
color="info"
>
Currently installed
@ -59,7 +59,7 @@
v-if="release.version == latest?.version"
class="mx-2 mt-n3"
size="small"
label
rounded
>
Latest
</v-chip>

View file

@ -1,9 +1,122 @@
<template>
<h1>Users</h1>
<v-alert
v-if="error"
type="error"
variant="tonal"
>
Error loading data
</v-alert>
<template v-else>
<v-data-table
:loading="isLoading"
:items="users"
:headers="headers"
:hide-default-footer="users?.length < 11"
>
<template #[`item.roles`]="{ value }">
<div class="d-flex ga-1">
<v-chip
v-for="role in value"
:key="role"
:color="getRoleColor(role)"
:text="role"
size="x-small"
rounded
/>
</div>
</template>
<template #[`item.actions`]="{ item }">
<div class="d-flex ga-1 justify-end">
<v-icon-btn
v-tooltip:bottom="'Reset password'"
icon="mdi-lock-reset"
@click="changePassword(item.id)"
/>
<v-icon-btn
v-tooltip:bottom="'Edit restrictions'"
icon="mdi-book-lock"
:disabled="me?.id == item.id"
@click="editRestrictions(item.id)"
/>
<v-icon-btn
v-tooltip:bottom="'Edit user'"
icon="mdi-pencil"
:disabled="me?.id == item.id"
@click="editUser(item.id)"
/>
<v-icon-btn
v-tooltip:bottom="'Delete user'"
icon="mdi-delete"
:disabled="me?.id == item.id"
@click="deleteUser(item.id)"
/>
</div>
</template>
</v-data-table>
</template>
</template>
<script lang="ts" setup>
//
import {useUsers} from '@/colada/queries/users.ts'
import {komgaClient} from '@/api/komga-client.ts'
import type {components} from '@/generated/openapi/komga'
import {useCurrentUser} from '@/colada/queries/current-user.ts'
import {UserRoles} from '@/types/UserRoles.ts'
const {data: users, error, isLoading} = useUsers()
const {data: me} = useCurrentUser()
const headers = [
{title: 'Email', key: 'email'},
{title: 'Latest Activity', key: 'activity', value: (item: components["schemas"]["UserDto"]) => latestActivity[item.id]},
{title: 'Roles', value: 'roles', sortable: false},
{title: 'Actions', key: 'actions', align: 'end', sortable: false},
]
function getRoleColor(role: UserRoles) {
if(role === UserRoles.ADMIN) return 'error'
}
// store each user's latest activity in a map
// when the 'users' change, we call the API for each user
const latestActivity: Record<string, Date | undefined> = reactive({})
function getLatestActivity(userId: string) {
komgaClient.GET('/api/v2/users/{id}/authentication-activity/latest', {
params: {
path: { id: userId }
}
})
// unwrap the openapi-fetch structure on success
.then((res) => latestActivity[userId] = res.data?.dateTime)
.catch(() => {})
}
watch(users, (users) => {
if(users) for (const user of users) {
getLatestActivity(user.id)
}
})
function editRestrictions(userId: string) {
console.log('edit restrictions: ', userId)
}
function deleteUser(userId: string) {
console.log('delete: ', userId)
}
function editUser(userId: string) {
console.log('edit: ', userId)
}
function changePassword(userId: string) {
console.log('change password: ', userId)
}
</script>
<route lang="yaml">

View file

@ -11,6 +11,7 @@ import 'vuetify/styles'
// Composables
import {createVuetify} from 'vuetify'
import {md3} from 'vuetify/blueprints'
import {VIconBtn} from 'vuetify/labs/components'
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
@ -37,4 +38,7 @@ export default createVuetify({
},
},
blueprint: md3,
components: {
VIconBtn,
},
})