mirror of
https://github.com/gotson/komga.git
synced 2025-12-15 13:05:57 +01:00
first shot at users view
This commit is contained in:
parent
33415041de
commit
6ff0015e1e
5 changed files with 133 additions and 5 deletions
|
|
@ -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])
|
||||
|
|
|
|||
11
next-ui/src/colada/queries/users.ts
Normal file
11
next-ui/src/colada/queries/users.ts
Normal 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),
|
||||
})
|
||||
})
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue