Created edit for users

This commit is contained in:
tdv
2025-12-04 16:07:51 +02:00
parent 50850897c0
commit 1284c1c4c1
5 changed files with 178 additions and 16 deletions

View File

@@ -14,11 +14,13 @@ const props = defineProps<{ row: Users }>() // ← accept full row
const emit = defineEmits<{
(e: 'deleted', id: string): void
(e: 'error', err: unknown): void
(e: 'updated', payload: { id: string; username: string; role: 'admin' | 'user' }): void
}>()
const isEditOpen = ref(false)
const isDeleteOpen = ref(false)
const deleting = ref(false)
const updating = ref(false)
async function onDeleteConfirmed() {
try {
@@ -34,8 +36,50 @@ async function onDeleteConfirmed() {
}
}
function onEditConfirm() {
isEditOpen.value = false
async function onEditConfirm(payload: { username: string; password?: string; role: 'admin' | 'user' }) {
try {
updating.value = true
// Build UpdateUserDto payload
const body: {
username?: string
password?: string
role?: 'admin' | 'user'
} = {}
// Only include fields that really changed / are provided
if (payload.username && payload.username !== props.row.username) {
body.username = payload.username
}
if (payload.password) {
body.password = payload.password
}
if (payload.role && payload.role !== props.row.role) {
body.role = payload.role
}
// If nothing changed, skip request
if (Object.keys(body).length === 0) {
return
}
await api.put(
`/users/${encodeURIComponent(String(props.row.id))}`,
body
)
emit('updated', {
id: String(props.row.id),
username: payload.username || props.row.username,
role: payload.role || (props.row.role as 'admin' | 'user'),
})
} catch (err) {
console.error(err)
emit('error', err)
} finally {
updating.value = false
// dialog is already closed in EditUserDialog via v-model update
}
}
</script>
@@ -55,6 +99,6 @@ function onEditConfirm() {
</DropdownMenuContent>
</DropdownMenu>
<EditUserDialog v-model:modelValue="isEditOpen" @confirm="onEditConfirm" />
<EditUserDialog v-model:modelValue="isEditOpen" :user="props.row" @confirm="onEditConfirm" />
<DeleteUserDialog v-model:modelValue="isDeleteOpen" :loading="deleting" @confirm="onDeleteConfirmed" />
</template>

View File

@@ -11,8 +11,9 @@ import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import { defineProps, defineEmits } from 'vue'
import { defineProps, defineEmits, reactive, watch } from 'vue'
import type { PropType } from 'vue'
import type { Users } from '@/lib/interfaces'
// 1) runtime props so Vue + TS agree
const props = defineProps({
@@ -20,26 +21,58 @@ const props = defineProps({
type: Boolean as PropType<boolean>,
required: true,
},
user: {
type: Object as PropType<Users>,
required: true,
},
})
// 2) two emits: v-model and confirm
const emit = defineEmits<{
(
e: 'confirm',
payload: { username: string; password?: string; role: 'admin' | 'user' }
): void
(e: 'update:modelValue', v: boolean): void
(e: 'confirm'): void
}>()
const form = reactive({
username: '',
password: '',
isAdmin: false,
})
// when dialog opens or user changes sync form with props.user
watch(
() => [props.modelValue, props.user],
() => {
if (props.modelValue && props.user) {
form.username = props.user.username
form.password = ''
form.isAdmin = props.user.role === 'admin'
}
},
{ immediate: true }
)
function onSave() {
emit('confirm')
// close the dialog
const payload: { username: string; password?: string; role: 'admin' | 'user' } = {
username: form.username,
role: form.isAdmin ? 'admin' : 'user',
}
// only send password if user entered something
if (form.password.trim() !== '') {
payload.password = form.password.trim()
}
emit('confirm', payload)
emit('update:modelValue', false)
}
</script>
<template>
<Dialog
:open="props.modelValue"
@update:open="(v: boolean) => emit('update:modelValue', v)"
>
<Dialog :open="props.modelValue" @update:open="(v: boolean) => emit('update:modelValue', v)">
<DialogContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
@@ -51,15 +84,15 @@ function onSave() {
<div class="grid gap-4 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<Label for="username" class="text-right">Username</Label>
<Input id="username" class="col-span-3" />
<Input id="username" class="col-span-3" v-model="form.username"/>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="password" class="text-right">Password</Label>
<Input id="password" class="col-span-3" type="password" />
<Input id="password" class="col-span-3" type="password" v-model="form.password"/>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<div class="grid grid-cols-4 items-center gap-4">
<Label for="isAdmin" class="text-right">Make admin</Label>
<Switch id="isAdmin"/>
<Switch id="isAdmin" v-model:checked="form.isAdmin"/>
</div>
</div>