11<script lang="ts" setup>
22import type { BreadcrumbItem , SharedData } from " @/types" ;
3-
4- import { Head , Link , useForm , usePage } from " @inertiajs/vue3 " ;
3+ import { Head , Link , router , useForm , usePage } from " @inertiajs/vue3 " ;
4+ import { computed , ref , useTemplateRef } from " vue " ;
55import DeleteUser from " @/components/DeleteUser.vue" ;
66import HeadingSmall from " @/components/HeadingSmall.vue" ;
77import InputError from " @/components/InputError.vue" ;
8+ import { Avatar , AvatarFallback , AvatarImage } from " @/components/ui/avatar" ;
89import { Button } from " @/components/ui/button" ;
910import { Input } from " @/components/ui/input" ;
1011import { Label } from " @/components/ui/label" ;
@@ -26,16 +27,63 @@ const breadcrumbs: BreadcrumbItem[] = [
2627];
2728
2829const page = usePage <SharedData >();
29- const user = page .props .auth .user as App .Data .UserData ;
30-
31- const form = useForm ({
32- first_name: user .firstName ,
33- last_name: user .lastName ,
34- email: user .email ,
30+ const user = computed (() => page .props .auth .user as App .Data .UserData );
31+ const profileImage = ref <string | null >(null );
32+ const photoInput = useTemplateRef <HTMLInputElement >(" photo-input" );
33+
34+ const form = useForm <{
35+ _method: string ;
36+ first_name: string ;
37+ last_name: string ;
38+ email: string ;
39+ profile_image? : File | null ;
40+ }>({
41+ _method: " patch" ,
42+ first_name: user .value .firstName ,
43+ last_name: user .value .lastName ,
44+ email: user .value .email ,
45+ profile_image: null ,
3546});
3647
48+ function selectNewPhoto() {
49+ photoInput .value ?.click ();
50+ }
51+
52+ function updatePhotoPreview() {
53+ const photo = photoInput .value ?.files ?.[0 ];
54+
55+ if (! photo ) {
56+ return ;
57+ }
58+
59+ form .profile_image = photo ;
60+ const reader = new FileReader ();
61+
62+ reader .onload = (e : ProgressEvent <FileReader >) => {
63+ profileImage .value = e .target ?.result as string ;
64+ };
65+
66+ reader .readAsDataURL (photo );
67+ }
68+
69+ function deletePhoto() {
70+ router .delete (route (" profile-photo.destroy" ), {
71+ preserveScroll: true ,
72+ onSuccess : () => {
73+ profileImage .value = null ;
74+ clearPhotoFileInput ();
75+ },
76+ });
77+ }
78+
79+ function clearPhotoFileInput() {
80+ if (photoInput .value ) {
81+ photoInput .value .value = " " ;
82+ }
83+ }
84+
3785function submit() {
38- form .patch (route (" profile.update" ), {
86+ form .post (route (" profile.update" ), {
3987 preserveScroll: true ,
4088 });
4189}
@@ -50,6 +98,47 @@ function submit() {
5098 <HeadingSmall description =" Update your name and email address" title =" Profile information" />
5199
52100 <form class =" space-y-6" @submit.prevent =" submit" >
101+ <div class =" grid gap-2" >
102+ <input
103+ id =" photo"
104+ ref =" photo-input"
105+ accept =" image/*"
106+ class =" hidden"
107+ type =" file"
108+ @change =" updatePhotoPreview"
109+ >
110+ <div class =" flex items-center gap-4" >
111+ <Avatar class =" h-20 w-20" >
112+ <AvatarImage
113+ :alt =" user.fullName"
114+ :src =" profileImage ?? user.profileImage ?? ''"
115+ />
116+ <AvatarFallback >
117+ {{ user.initials }}
118+ </AvatarFallback >
119+ </Avatar >
120+ <Button
121+ type =" button"
122+ variant =" outline"
123+ @click =" selectNewPhoto"
124+ >
125+ Select photo
126+ </Button >
127+ <Button
128+ v-if =" user.profileImage"
129+ type =" button"
130+ variant =" outline"
131+ @click =" deletePhoto"
132+ >
133+ Remove photo
134+ </Button >
135+ </div >
136+ <InputError
137+ :message =" form.errors.profile_image"
138+ class =" mt-2"
139+ />
140+ </div >
141+
53142 <div class =" grid grid-cols-2 gap-6" >
54143 <div class =" grid gap-2" >
55144 <Label for =" first_name" >First name</Label >
0 commit comments