add avatar on account-settings

This commit is contained in:
yogesh_gohil
2019-11-21 18:45:22 +05:30
parent 4ec34550ed
commit 812afc2dab
18 changed files with 262 additions and 28 deletions

View File

@ -251,6 +251,7 @@ class CompanyController extends Controller
}
return response()->json([
'user' => $user,
'success' => true
]);
}

View File

@ -99,6 +99,32 @@ class OnboardingController extends Controller
]);
}
public function uploadAdminAvatar(Request $request)
{
$setting = Setting::getSetting('profile_complete');
if ($setting == '1' || $setting == 'COMPLETED') {
return response()->json(['error' => 'Profile already created.']);
}
$data = json_decode($request->admin_avatar);
if($data) {
$user = User::find($data->id);
if($user) {
$user->clearMediaCollection('admin_avatar');
$user->addMediaFromBase64($data->data)
->usingFileName($data->name)
->toMediaCollection('admin_avatar');
}
}
return response()->json([
'user' => $user,
'success' => true
]);
}
public function adminCompany(CompanyRequest $request)
{
$setting = Setting::getSetting('profile_complete');

View File

@ -603,6 +603,7 @@ export default {
updated_message: 'Company information updated successfully'
},
account_settings: {
profile_picture: 'Profile Picture',
name: 'Name',
email: 'Email',
password: 'Password',

View File

@ -599,6 +599,7 @@ export default {
updated_message: 'Información de la empresa actualizada con éxito'
},
account_settings: {
profile_picture: 'Foto de perfil',
name: 'Nombre',
email: 'Email',
password: 'Contraseña',

View File

@ -599,6 +599,7 @@ export default {
updated_message: 'Informations sur la société mises à jour avec succès'
},
account_settings: {
profile_picture: 'Image de profil',
name: 'Nom',
email: 'Email',
password: 'Mot de passe',

View File

@ -12,11 +12,7 @@ export const loadData = ({ commit, dispatch, state }, id) => {
export const editCompany = ({ commit, dispatch, state }, data) => {
return new Promise((resolve, reject) => {
window.axios.post('/api/settings/company', data, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
window.axios.post('/api/settings/company', data).then((response) => {
// commit(types.UPDATE_ITEM, response.data)
resolve(response)
}).catch((err) => {

View File

@ -1,8 +1,9 @@
// import * as types from './mutation-types'
import * as types from './mutation-types'
export const loadData = ({ commit, dispatch, state }, id) => {
return new Promise((resolve, reject) => {
window.axios.get(`/api/settings/profile`).then((response) => {
commit(types.SET_USER, response.data)
resolve(response)
}).catch((err) => {
reject(err)
@ -13,7 +14,29 @@ export const loadData = ({ commit, dispatch, state }, id) => {
export const editUser = ({ commit, dispatch, state }, data) => {
return new Promise((resolve, reject) => {
window.axios.put('/api/settings/profile', data).then((response) => {
// commit(types.UPDATE_USER, response.data)
commit(types.UPDATE_USER, response.data)
resolve(response)
}).catch((err) => {
reject(err)
})
})
}
export const uploadOnboardAvatar = ({ commit, dispatch, state }, data) => {
return new Promise((resolve, reject) => {
window.axios.post(`/api/admin/profile/upload-avatar`, data).then((response) => {
commit(types.UPDATE_USER, response.data.user)
resolve(response)
}).catch((err) => {
reject(err)
})
})
}
export const uploadAvatar = ({ commit, dispatch, state }, data) => {
return new Promise((resolve, reject) => {
window.axios.post('/api/settings/profile/upload-avatar', data).then((response) => {
commit(types.UPDATE_USER, response.data.user)
resolve(response)
}).catch((err) => {
reject(err)

View File

@ -1,2 +1,3 @@
export const SET_USER = 'SET_USER'
export const UPDATE_USER = 'UPDATE_USER'
export const UPDATE_USER_AVATAR = 'UPDATE_USER_AVATAR'

View File

@ -2,10 +2,14 @@ import * as types from './mutation-types'
export default {
[types.SET_USER] (state, data) {
state.user = data.user
state.user = data
},
[types.UPDATE_USER] (state, data) {
state.user = data
},
[types.UPDATE_USER_AVATAR] (state, data) {
state.user.avatar = data.avatar
}
}

View File

@ -58,7 +58,7 @@
aria-expanded="false"
class="avatar"
>
<img src="/images/avatar.png" alt="Avatar">
<img :src="ProfilePicture" alt="Avatar">
</a>
<v-dropdown-item>
<router-link class="dropdown-item" to="/admin/settings">
@ -83,7 +83,25 @@
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters('userProfile', [
'user'
]),
ProfilePicture () {
if (this.user && this.user.avatar !== null) {
return this.user.avatar
} else {
return '/images/default-avatar.jpg'
}
}
},
created () {
this.loadData()
},
methods: {
...mapActions('userProfile', [
'loadData'
]),
...mapActions({
companySelect: 'changeCompany'
}),

View File

@ -12,6 +12,9 @@
<div class="col-md-6">
<label class="input-label">{{ $tc('settings.company_info.company_logo') }}</label>
<div id="pick-avatar" class="image-upload-box">
<div class="overlay">
<font-awesome-icon class="white-icon" icon="cloud-upload-alt"/>
</div>
<img v-if="previewLogo" :src="previewLogo" class="preview-logo">
<div v-else class="upload-content">
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
@ -174,7 +177,6 @@ export default {
isFetchingData: false,
formData: {
name: null,
logo: '',
email: '',
phone: '',
zip: '',
@ -301,17 +303,8 @@ export default {
return true
}
this.isLoading = true
let data = new FormData()
data.append('name', this.formData.name)
data.append('address_street_1', this.formData.address_street_1)
data.append('address_street_2', this.formData.address_street_2)
data.append('city_id', this.formData.city_id)
data.append('state_id', this.formData.state_id)
data.append('country_id', this.formData.country_id)
data.append('zip', this.formData.zip)
data.append('phone', this.formData.phone)
let response = await this.editCompany(data)
let response = await this.editCompany(this.formData)
if (response.data.success) {
this.isLoading = false
if (this.fileObject && this.previewLogo) {

View File

@ -8,6 +8,31 @@
{{ $t('settings.account_settings.section_description') }}
</p>
</div>
<div class="row mb-4">
<div class="col-md-6">
<label class="input-label">{{ $tc('settings.account_settings.profile_picture') }}</label>
<div id="pick-avatar" class="image-upload-box avatar-upload">
<div class="overlay">
<font-awesome-icon class="white-icon" icon="camera"/>
</div>
<img v-if="previewAvatar" :src="previewAvatar" class="preview-logo">
<div v-else class="upload-content">
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
<p class="upload-text"> {{ $tc('general.choose_file') }} </p>
</div>
</div>
</div>
<avatar-cropper
:labels="{ submit: 'submit', cancel: 'Cancle'}"
:cropper-options="cropperOptions"
:output-options="cropperOutputOptions"
:output-quality="0.8"
:upload-handler="cropperHandler"
trigger="#pick-avatar"
@changed="setFileObject"
@error="handleUploadError"
/>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-group">
<label class="input-label">{{ $tc('settings.account_settings.name') }}</label>
@ -81,19 +106,33 @@
<script>
import { validationMixin } from 'vuelidate'
import { mapActions } from 'vuex'
import AvatarCropper from 'vue-avatar-cropper'
const { required, requiredIf, sameAs, email, minLength } = require('vuelidate/lib/validators')
export default {
components: { AvatarCropper },
mixins: [validationMixin],
data () {
return {
isLoading: false,
cropperOutputOptions: {
width: 150,
height: 150
},
cropperOptions: {
autoCropArea: 1,
viewMode: 0,
movable: true,
zoomable: true
},
formData: {
name: null,
email: null,
password: null,
confirm_password: null
}
},
isLoading: false,
previewAvatar: null,
fileObject: null
}
},
validations: {
@ -128,12 +167,23 @@ export default {
methods: {
...mapActions('userProfile', [
'loadData',
'editUser'
'editUser',
'uploadAvatar'
]),
cropperHandler (cropper) {
this.previewAvatar = cropper.getCroppedCanvas().toDataURL(this.cropperOutputMime)
},
setFileObject (file) {
this.fileObject = file
},
handleUploadError (message, type, xhr) {
window.toastr['error']('Oops! Something went wrong...')
},
async setInitialData () {
let response = await this.loadData()
this.formData.name = response.data.name
this.formData.email = response.data.email
this.previewAvatar = response.data.avatar
},
async updateUserData () {
this.$v.formData.$touch()
@ -151,6 +201,14 @@ export default {
let response = await this.editUser(data)
if (response.data.success) {
this.isLoading = false
if (this.fileObject && this.previewAvatar) {
let avatarData = new FormData()
avatarData.append('admin_avatar', JSON.stringify({
name: this.fileObject.name,
data: this.previewAvatar
}))
this.uploadAvatar(avatarData)
}
window.toastr['success'](this.$t('settings.account_settings.updated_message'))
return true
}

View File

@ -7,6 +7,9 @@
<div class="col-md-6">
<label class="input-label">{{ $tc('settings.company_info.company_logo') }}</label>
<div id="pick-avatar" class="image-upload-box">
<div class="overlay">
<font-awesome-icon class="white-icon" icon="cloud-upload-alt"/>
</div>
<img v-if="previewLogo" :src="previewLogo" class="preview-logo">
<div v-else class="upload-content">
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>

View File

@ -3,6 +3,31 @@
<form action="" @submit.prevent="next()">
<p class="form-title">{{ $t('wizard.account_info') }}</p>
<p class="form-desc">{{ $t('wizard.account_info_desc') }}</p>
<div class="row mb-4">
<div class="col-md-6">
<label class="input-label">{{ $tc('settings.account_settings.profile_picture') }}</label>
<div id="pick-avatar" class="image-upload-box avatar-upload">
<div class="overlay">
<font-awesome-icon class="white-icon" icon="camera"/>
</div>
<img v-if="previewAvatar" :src="previewAvatar" class="preview-logo">
<div v-else class="upload-content">
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
<p class="upload-text"> {{ $tc('general.choose_file') }} </p>
</div>
</div>
</div>
<avatar-cropper
:labels="{ submit: 'submit', cancel: 'Cancle'}"
:cropper-options="cropperOptions"
:output-options="cropperOutputOptions"
:output-quality="0.8"
:upload-handler="cropperHandler"
trigger="#pick-avatar"
@changed="setFileObject"
@error="handleUploadError"
/>
</div>
<div class="row">
<div class="col-md-6">
<label class="form-label">{{ $t('wizard.name') }}</label><span class="text-danger"> *</span>
@ -75,24 +100,37 @@
</div>
</template>
<script>
import MultiSelect from 'vue-multiselect'
import AvatarCropper from 'vue-avatar-cropper'
import { validationMixin } from 'vuelidate'
import { mapActions } from 'vuex'
const { required, requiredIf, sameAs, minLength, email } = require('vuelidate/lib/validators')
export default {
components: {
MultiSelect
AvatarCropper
},
mixins: [validationMixin],
data () {
return {
cropperOutputOptions: {
width: 150,
height: 150
},
cropperOptions: {
autoCropArea: 1,
viewMode: 0,
movable: true,
zoomable: true
},
profileData: {
name: null,
email: null,
password: null,
confirm_password: null
},
loading: false
loading: false,
previewAvatar: null,
fileObject: null
}
},
validations: {
@ -124,6 +162,18 @@ export default {
}
},
methods: {
...mapActions('userProfile', [
'uploadOnboardAvatar'
]),
cropperHandler (cropper) {
this.previewAvatar = cropper.getCroppedCanvas().toDataURL(this.cropperOutputMime)
},
setFileObject (file) {
this.fileObject = file
},
handleUploadError (message, type, xhr) {
window.toastr['error']('Oops! Something went wrong...')
},
async next () {
this.$v.profileData.$touch()
if (this.$v.profileData.$invalid) {
@ -131,7 +181,20 @@ export default {
}
this.loading = true
let response = await window.axios.post('/api/admin/onboarding/profile', this.profileData)
console.log('user_id', response.data.user.id)
if (response.data) {
if (this.fileObject && this.previewAvatar) {
let avatarData = new FormData()
avatarData.append('admin_avatar', JSON.stringify({
name: this.fileObject.name,
data: this.previewAvatar,
id: response.data.user.id
}))
console.log(avatarData);
this.uploadOnboardAvatar(avatarData)
}
this.$emit('next')
this.loading = false
}

View File

@ -53,7 +53,8 @@ import {
faPaperPlane,
faEyeSlash,
faSyncAlt,
faRocket
faRocket,
faCamera
} from '@fortawesome/free-solid-svg-icons'
import { far } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
@ -117,7 +118,8 @@ library.add(
faCopy,
faPaperPlane,
faSyncAlt,
faRocket
faRocket,
faCamera
)
Vue.component('font-awesome-icon', FontAwesomeIcon)

View File

@ -35,4 +35,41 @@
margin-bottom: 10px;
}
.white-icon {
font-size: 30px;
line-height: 23px;
color: $white;
margin-bottom: 10px;
}
.overlay {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
transition: .5s ease;
background-color: rgba(0,0,0,0.5);
opacity: 0;
}
&:hover {
.overlay {
opacity: 1;
}
}
}
.avatar-upload {
height: 130px;
width: 130px;
.preview-logo {
max-height: 80% !important;
}
}

View File

@ -62,6 +62,7 @@
.avatar img {
width: 36px;
height: 36px;
border-radius: 2px;
}

View File

@ -96,6 +96,11 @@ Route::group(['middleware' => 'redirect-if-installed'], function () {
'uses' => 'OnboardingController@adminProfile'
]);
Route::post('/admin/profile/upload-avatar', [
'as' => 'admin.on_boarding.avatar',
'uses' => 'OnboardingController@uploadAdminAvatar'
]);
Route::post('/admin/onboarding/company', [
'as' => 'admin.company',
'uses' => 'OnboardingController@adminCompany'