Add profile picture removal
linter / quality (pull_request) Successful in 1m36s
tests / ci (8.4) (pull_request) Successful in 1m54s

This commit is contained in:
2026-06-24 15:30:11 +02:00
parent 62e4a8487c
commit 0d26113ba7
4 changed files with 81 additions and 15 deletions
+42 -15
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { Form, Head, Link, usePage } from '@inertiajs/vue3';
import { Form, Head, Link, useForm, usePage } from '@inertiajs/vue3';
import { Trash2 } from 'lucide-vue-next';
import { computed, onBeforeUnmount, ref } from 'vue';
import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileController';
import DeleteUser from '@/components/DeleteUser.vue';
@@ -28,28 +29,41 @@ const breadcrumbItems: BreadcrumbItem[] = [
const page = usePage();
const user = computed(() => page.props.auth.user);
const avatarPreview = ref<string | null>(null);
const avatarDeleteForm = useForm({});
const displayedAvatar = computed(
() => avatarPreview.value ?? user.value.avatar,
);
const canRemoveAvatar = computed(
() => user.value.avatar !== null && !user.value.avatar_is_generated,
);
const clearAvatarPreview = (): void => {
if (avatarPreview.value) {
URL.revokeObjectURL(avatarPreview.value);
avatarPreview.value = null;
}
};
const handleAvatarChange = (event: Event) => {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (avatarPreview.value) {
URL.revokeObjectURL(avatarPreview.value);
avatarPreview.value = null;
}
clearAvatarPreview();
if (file) {
avatarPreview.value = URL.createObjectURL(file);
}
};
const removeAvatar = (): void => {
avatarDeleteForm.delete(ProfileController.destroyAvatar.url(), {
preserveScroll: true,
onSuccess: clearAvatarPreview,
});
};
onBeforeUnmount(() => {
if (avatarPreview.value) {
URL.revokeObjectURL(avatarPreview.value);
}
clearAvatarPreview();
});
</script>
@@ -93,13 +107,26 @@ onBeforeUnmount(() => {
</AvatarFallback>
</Avatar>
<div class="grid flex-1 gap-2">
<Input
id="avatar"
name="avatar"
type="file"
accept="image/png,image/jpeg,image/webp"
@change="handleAvatarChange"
/>
<div class="flex flex-col gap-2 sm:flex-row">
<Input
id="avatar"
name="avatar"
type="file"
accept="image/png,image/jpeg,image/webp"
@change="handleAvatarChange"
/>
<Button
v-if="canRemoveAvatar"
type="button"
variant="outline"
:disabled="avatarDeleteForm.processing"
data-test="delete-profile-picture-button"
@click="removeAvatar"
>
<Trash2 class="size-4" />
Remove
</Button>
</div>
<p class="text-muted-foreground text-sm">
PNG, JPG, or WebP up to 2 MB.
</p>