diff --git a/client/web/admin/src/components/CUploader.vue b/client/web/admin/src/components/CUploader.vue index 281888285..1285620d0 100644 --- a/client/web/admin/src/components/CUploader.vue +++ b/client/web/admin/src/components/CUploader.vue @@ -11,7 +11,7 @@ @vdropzone-error="onError" @vdropzone-upload-progress="onUploadProgress" > -
+
+ + diff --git a/client/web/admin/src/views/System/User/Editor.vue b/client/web/admin/src/views/System/User/Editor.vue index 4a9b1617d..17134bb02 100644 --- a/client/web/admin/src/views/System/User/Editor.vue +++ b/client/web/admin/src/views/System/User/Editor.vue @@ -52,6 +52,18 @@ @sessionsRevoke="onSessionsRevoke" /> + + { + this.fetchUser() + this.$auth.handle() + this.animateSuccess('info') + this.toastSuccess(this.$t('notification:user.avatarUpload.success')) + }) + .catch(this.toastErrorHandler(this.$t('notification:user.avatarUpload.error'))) + .finally(() => { + this.info.processing = false + }) + }, + /** * Handles user delete event, calls user delete API endpoint * and handles response & errors @@ -451,6 +487,22 @@ export default { this.decLoader() }) }, + + onAvatarUpload () { + this.fetchUser() + }, + + onResetAvatar () { + const userID = this.userID + + this.$SystemAPI.userDeleteAvatar({ userID }) + .then(() => { + this.fetchUser() + + this.toastSuccess(this.$t('notification:user.avatarDelete.success')) + }) + .catch(this.toastErrorHandler(this.$t('notification:user.avatarDelete.error'))) + }, }, } diff --git a/lib/js/src/api-clients/system.ts b/lib/js/src/api-clients/system.ts index 5e24a6001..2f5012109 100644 --- a/lib/js/src/api-clients/system.ts +++ b/lib/js/src/api-clients/system.ts @@ -1478,6 +1478,96 @@ export default class System { return `/users/${userID}/credentials/${credentialsID}` } + // User's profile avatar + async userProfileAvatar (a: KV, extra: AxiosRequestConfig = {}): Promise { + const { + userID, + upload, + width, + height, + } = (a as KV) || {} + if (!userID) { + throw Error('field userID is empty') + } + const cfg: AxiosRequestConfig = { + ...extra, + method: 'post', + url: this.userProfileAvatarEndpoint({ + userID, + }), + } + cfg.data = { + upload, + width, + height, + } + return this.api().request(cfg).then(result => stdResolve(result)) + } + + userProfileAvatarEndpoint (a: KV): string { + const { + userID, + } = a || {} + return `/users/${userID}/avatar` + } + + // User profile avatar initial + async userProfileAvatarInitial (a: KV, extra: AxiosRequestConfig = {}): Promise { + const { + userID, + avatarColor, + avatarBgColor, + } = (a as KV) || {} + if (!userID) { + throw Error('field userID is empty') + } + const cfg: AxiosRequestConfig = { + ...extra, + method: 'post', + url: this.userProfileAvatarInitialEndpoint({ + userID, + }), + } + cfg.data = { + avatarColor, + avatarBgColor, + } + return this.api().request(cfg).then(result => stdResolve(result)) + } + + userProfileAvatarInitialEndpoint (a: KV): string { + const { + userID, + } = a || {} + return `/users/${userID}/avatar-initial` + } + + // delete user's profile avatar + async userDeleteAvatar (a: KV, extra: AxiosRequestConfig = {}): Promise { + const { + userID, + } = (a as KV) || {} + if (!userID) { + throw Error('field userID is empty') + } + const cfg: AxiosRequestConfig = { + ...extra, + method: 'delete', + url: this.userDeleteAvatarEndpoint({ + userID, + }), + } + + return this.api().request(cfg).then(result => stdResolve(result)) + } + + userDeleteAvatarEndpoint (a: KV): string { + const { + userID, + } = a || {} + return `/users/${userID}/avatar` + } + // Export users async userExport (a: KV, extra: AxiosRequestConfig = {}): Promise { const { diff --git a/lib/js/src/system/types/user.ts b/lib/js/src/system/types/user.ts index e9654bc49..0de36438b 100644 --- a/lib/js/src/system/types/user.ts +++ b/lib/js/src/system/types/user.ts @@ -11,6 +11,10 @@ interface PartialUser extends Partial