users dialog wip

This commit is contained in:
Gauthier Roebroeck 2025-05-22 16:35:31 +08:00
parent 646047e01c
commit 11cc783c87
6 changed files with 201 additions and 19 deletions

View file

@ -0,0 +1,20 @@
import {defineMutation, useMutation, useQueryCache} from '@pinia/colada'
import {komgaClient} from '@/api/komga-client'
import type {components} from '@/generated/openapi/komga'
export const useUpdateUser = defineMutation(() => {
const queryCache = useQueryCache()
return useMutation({
mutation: (user: components['schemas']['UserDto']) =>
komgaClient.PATCH('/api/v2/users/{id}', {
params: {path: {id: user.id}},
body: user,
}),
onSuccess: () => {
queryCache.invalidateQueries({key: ['users']})
},
onError: (error) => {
console.log('update user error', error)
},
})
})

View file

@ -21,6 +21,10 @@ declare module 'vue' {
AppFooter: typeof import('./components/AppFooter.vue')['default']
BuildCommit: typeof import('./components/BuildCommit.vue')['default']
BuildVersion: typeof import('./components/BuildVersion.vue')['default']
DialogConfirm: typeof import('./components/dialogs/DialogConfirm.vue')['default']
DialogConfirmEdit: typeof import('./components/dialogs/DialogConfirmEdit.vue')['default']
FormUserChangePassword: typeof import('./components/forms/user/FormUserChangePassword.vue')['default']
FormUserRoles: typeof import('./components/forms/user/FormUserRoles.vue')['default']
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
LoginForm: typeof import('./components/LoginForm.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']

View file

@ -0,0 +1,67 @@
<template>
<v-dialog
v-model="showDialog"
:activator="activator"
:max-width="maxWidth"
>
<v-confirm-edit
v-model="record"
hide-actions
@save="close()"
>
<template #default="{ model: proxyModel, cancel, save, isPristine }">
<v-card
:title="title"
:subtitle="subtitle"
>
<template #text>
<slot
name="text"
:proxy-model="proxyModel"
:cancel="cancel"
:save="save"
:is-pristine="isPristine"
/>
</template>
<template #actions>
<v-spacer />
<v-btn
text="Cancel"
@click="close()"
/>
<v-btn
:disabled="isPristine"
text="Save"
@click="save"
/>
</template>
</v-card>
</template>
</v-confirm-edit>
</v-dialog>
</template>
<script setup lang="ts">
const showDialog = defineModel<boolean>('dialog', {required: false})
const record = defineModel<unknown>('record', {required: true})
interface Props {
title?: string,
subtitle?: string,
maxWidth?: string | number,
activator?: Element | string,
}
const {
title = undefined,
subtitle = undefined,
maxWidth = undefined,
activator = undefined,
} = defineProps<Props>()
function close() {
showDialog.value = false
}
</script>

View file

@ -0,0 +1,10 @@
<template>
<v-text-field />
<v-text-field />
</template>
<script setup lang="ts">
import type {components} from '@/generated/openapi/komga'
const user = defineModel<components["schemas"]["UserDto"] | undefined>({required: true})
</script>

View file

@ -0,0 +1,22 @@
<template>
<v-checkbox
v-for="role in userRoles"
:key="role.value"
v-model="user.roles"
:label="role.text"
:value="role.value"
hide-details
/>
</template>
<script setup lang="ts">
import {UserRoles} from '@/types/UserRoles.ts'
import type {components} from '@/generated/openapi/komga'
const user = defineModel<components["schemas"]["UserDto"]>({required: true})
const userRoles = computed(() => Object.keys(UserRoles).map(x => ({
text: x,
value: x,
})))
</script>

View file

@ -27,34 +27,54 @@
</div>
</template>
<template #[`item.actions`]="{ item }">
<template #[`item.actions`]="{ item : user }">
<div class="d-flex ga-1 justify-end">
<v-icon-btn
v-tooltip:bottom="'Reset password'"
icon="mdi-lock-reset"
@click="changePassword(item.id)"
@click="showDialog(ACTION.PASSWORD, user)"
@mouseenter="activator = $event.currentTarget"
/>
<v-icon-btn
v-tooltip:bottom="'Edit restrictions'"
icon="mdi-book-lock"
:disabled="me?.id == item.id"
@click="editRestrictions(item.id)"
:disabled="me?.id == user.id"
@click="showDialog(ACTION.RESTRICTIONS, user)"
@mouseenter="activator = $event.currentTarget"
/>
<v-icon-btn
v-tooltip:bottom="'Edit user'"
icon="mdi-pencil"
:disabled="me?.id == item.id"
@click="editUser(item.id)"
:disabled="me?.id == user.id"
@click="showDialog(ACTION.EDIT, user)"
@mouseenter="activator = $event.currentTarget"
/>
<v-icon-btn
v-tooltip:bottom="'Delete user'"
icon="mdi-delete"
:disabled="me?.id == item.id"
@click="deleteUser(item.id)"
:disabled="me?.id == user.id"
@click="showDialog(ACTION.DELETE, user)"
@mouseenter="activator = $event.currentTarget"
/>
</div>
</template>
</v-data-table>
<DialogConfirmEdit
v-model:record="userRecord"
:activator="activator"
:title="dialogTitle"
:subtitle="userRecord?.email"
max-width="400"
@update:record="handleConfirmation()"
>
<template #text="{proxyModel}">
<component
:is="dialogComponent"
v-model="proxyModel.value"
/>
</template>
</DialogConfirmEdit>
</template>
</template>
@ -64,12 +84,18 @@ 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'
import {useUpdateUser} from '@/colada/mutations/update-user.ts'
import FormUserChangePassword from '@/components/forms/user/FormUserChangePassword.vue'
import FormUserRoles from '@/components/forms/user/FormUserRoles.vue'
import type {Component} from 'vue'
// API data
const {data: users, error, isLoading} = useUsers()
const {data: me} = useCurrentUser()
const hideFooter = computed(() => users.value && users.value.length < 11)
// Table
const hideFooter = computed(() => users.value && users.value.length < 11)
const headers = [
{title: 'Email', key: 'email'},
{title: 'Latest Activity', key: 'activity', value: (item: components["schemas"]["UserDto"]) => latestActivity[item.id]},
@ -104,20 +130,53 @@ watch(users, (users) => {
})
function editRestrictions(userId: string) {
console.log('edit restrictions: ', userId)
// Dialogs handling
const userRecord = ref<components["schemas"]["UserDto"]>()
const currentAction = ref<ACTION>()
const activator = ref<Element>()
const dialogTitle = ref<string>()
const dialogComponent = shallowRef<Component>()
const {mutate: mutateUser} = useUpdateUser()
enum ACTION {
EDIT, DELETE, RESTRICTIONS, PASSWORD
}
function deleteUser(userId: string) {
console.log('delete: ', userId)
function showDialog(action: ACTION, user: components["schemas"]["UserDto"]) {
currentAction.value = action
switch (action) {
case ACTION.EDIT:
dialogTitle.value = 'Edit Roles'
dialogComponent.value = FormUserRoles
break;
case ACTION.DELETE:
dialogTitle.value = 'Delete User'
dialogComponent.value = FormUserRoles
break;
case ACTION.RESTRICTIONS:
dialogTitle.value = 'Edit Restrictions'
dialogComponent.value = FormUserRoles
break;
case ACTION.PASSWORD:
dialogTitle.value = 'Change Password'
dialogComponent.value = FormUserChangePassword
}
userRecord.value = user
}
function editUser(userId: string) {
console.log('edit: ', userId)
}
function changePassword(userId: string) {
console.log('change password: ', userId)
function handleConfirmation() {
switch (currentAction.value) {
case ACTION.EDIT:
mutateUser(userRecord.value!)
break;
case ACTION.DELETE:
break;
case ACTION.RESTRICTIONS:
break;
case ACTION.PASSWORD:
break;
}
}
</script>