Kullanıcı Detayları ve Avatar
This commit is contained in:
parent
12f046f262
commit
233c9b7502
4 changed files with 111 additions and 23 deletions
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Content;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.Identity.Dto;
|
||||||
|
|
||||||
|
public class UserAvatarUpdateInput
|
||||||
|
{
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
|
public IRemoteStreamContent Avatar { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.Platform.BlobStoring;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Extensions;
|
using Sozsoft.Platform.Extensions;
|
||||||
using Sozsoft.Platform.Identity.Dto;
|
using Sozsoft.Platform.Identity.Dto;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -32,6 +34,7 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
||||||
public IRepository<Department, Guid> departmentRepository { get; }
|
public IRepository<Department, Guid> departmentRepository { get; }
|
||||||
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
||||||
|
public BlobManager BlobCdnManager { get; }
|
||||||
|
|
||||||
public PlatformIdentityAppService(
|
public PlatformIdentityAppService(
|
||||||
IIdentityUserAppService identityUserAppService,
|
IIdentityUserAppService identityUserAppService,
|
||||||
|
|
@ -45,6 +48,7 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
IRepository<WorkHour, Guid> workHourRepository,
|
IRepository<WorkHour, Guid> workHourRepository,
|
||||||
IRepository<Department, Guid> departmentRepository,
|
IRepository<Department, Guid> departmentRepository,
|
||||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||||
|
BlobManager blobCdnManager,
|
||||||
IGuidGenerator guidGenerator
|
IGuidGenerator guidGenerator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -55,6 +59,7 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
this.workHourRepository = workHourRepository;
|
this.workHourRepository = workHourRepository;
|
||||||
this.departmentRepository = departmentRepository;
|
this.departmentRepository = departmentRepository;
|
||||||
this.jobPositionRepository = jobPositionRepository;
|
this.jobPositionRepository = jobPositionRepository;
|
||||||
|
this.BlobCdnManager = blobCdnManager;
|
||||||
this.permissionRepository = permissionRepository;
|
this.permissionRepository = permissionRepository;
|
||||||
this.branchRepository = branchRepository;
|
this.branchRepository = branchRepository;
|
||||||
this.branchUsersRepository = branchUsersRepository;
|
this.branchUsersRepository = branchUsersRepository;
|
||||||
|
|
@ -273,6 +278,22 @@ public class PlatformIdentityAppService : ApplicationService
|
||||||
await UserManager.UpdateAsync(user);
|
await UserManager.UpdateAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(IdentityPermissions.Users.Update)]
|
||||||
|
public async Task UpdateAvatarAsync([FromForm] UserAvatarUpdateInput input)
|
||||||
|
{
|
||||||
|
var user = await UserManager.GetByIdAsync(input.UserId);
|
||||||
|
var fileName = $"{user.Id}.jpg";
|
||||||
|
|
||||||
|
if (input.Avatar is null || input.Avatar.ContentLength == 0)
|
||||||
|
{
|
||||||
|
await BlobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await BlobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<PermissionDefinitionRecord>> GetPermissionList()
|
public async Task<List<PermissionDefinitionRecord>> GetPermissionList()
|
||||||
{
|
{
|
||||||
var list = await permissionRepository.GetListAsync();
|
var list = await permissionRepository.GetListAsync();
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ import { ListResultDto, PagedAndSortedResultRequestDto, PagedResultDto } from '.
|
||||||
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
||||||
import apiService from './api.service'
|
import apiService from './api.service'
|
||||||
|
|
||||||
|
export interface UserAvatarUpdateInput {
|
||||||
|
userId: string
|
||||||
|
avatar?: File
|
||||||
|
}
|
||||||
|
|
||||||
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
||||||
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
@ -36,6 +41,21 @@ export const putUserDetail = (input: UserInfoViewModel) =>
|
||||||
data: input,
|
data: input,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const putUserAvatar = (input: UserAvatarUpdateInput) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('userId', input.userId)
|
||||||
|
|
||||||
|
if (input.avatar) {
|
||||||
|
formData.append('avatar', input.avatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiService.fetchData({
|
||||||
|
method: 'PUT',
|
||||||
|
url: `/api/app/platform-identity/avatar`,
|
||||||
|
data: formData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const putUserLookout = (input: UserInfoViewModel) =>
|
export const putUserLookout = (input: UserInfoViewModel) =>
|
||||||
apiService.fetchData({
|
apiService.fetchData({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ import {
|
||||||
deleteClaimUser,
|
deleteClaimUser,
|
||||||
getUserDetail,
|
getUserDetail,
|
||||||
postClaimUser,
|
postClaimUser,
|
||||||
|
putUserAvatar,
|
||||||
putUserDetail,
|
putUserDetail,
|
||||||
putUserLookout,
|
putUserLookout,
|
||||||
putUserPermission,
|
putUserPermission,
|
||||||
} from '@/services/identity.service'
|
} from '@/services/identity.service'
|
||||||
import { updateProfile } from '@/services/account.service'
|
|
||||||
import { CountryDto, getCountry } from '@/services/home.service'
|
import { CountryDto, getCountry } from '@/services/home.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
@ -97,6 +97,7 @@ function UserDetails() {
|
||||||
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
||||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||||
const [image, setImage] = useState<string | undefined>()
|
const [image, setImage] = useState<string | undefined>()
|
||||||
|
const [hasAvatarChange, setHasAvatarChange] = useState(false)
|
||||||
const cropperRef = useRef<CropperRef>(null)
|
const cropperRef = useRef<CropperRef>(null)
|
||||||
const previewRef = useRef<CropperPreviewRef>(null)
|
const previewRef = useRef<CropperPreviewRef>(null)
|
||||||
const auth = useStoreState((state) => state.auth)
|
const auth = useStoreState((state) => state.auth)
|
||||||
|
|
@ -111,15 +112,26 @@ function UserDetails() {
|
||||||
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
||||||
|
|
||||||
const getUser = async (syncAvatar = true) => {
|
const getUser = async (syncAvatar = true) => {
|
||||||
const { data } = await getUserDetail(userId || '')
|
if (!userId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await getUserDetail(userId)
|
||||||
setUserDetails(data)
|
setUserDetails(data)
|
||||||
if (syncAvatar) {
|
if (syncAvatar) {
|
||||||
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
||||||
|
setHasAvatarChange(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setUserDetails(undefined)
|
||||||
|
setImage(undefined)
|
||||||
|
setHasAvatarChange(false)
|
||||||
getUser()
|
getUser()
|
||||||
|
}, [userId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
getCountry().then(({ data }) => setCountries(data))
|
getCountry().then(({ data }) => setCountries(data))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
@ -131,8 +143,10 @@ function UserDetails() {
|
||||||
const onChooseImage = async (file: File[]) => {
|
const onChooseImage = async (file: File[]) => {
|
||||||
if (file[0]) {
|
if (file[0]) {
|
||||||
setImage(URL.createObjectURL(file[0]))
|
setImage(URL.createObjectURL(file[0]))
|
||||||
|
setHasAvatarChange(true)
|
||||||
} else {
|
} else {
|
||||||
setImage(undefined)
|
setImage(undefined)
|
||||||
|
setHasAvatarChange(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,34 +260,49 @@ function UserDetails() {
|
||||||
<TabContent value="user">
|
<TabContent value="user">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
await putUserDetail({ ...values })
|
await putUserDetail({ ...values })
|
||||||
let keepCurrentAvatar = false
|
if (values.id === auth.user.id) {
|
||||||
|
|
||||||
const avatar = await getCroppedAvatar()
|
|
||||||
const resp = await updateProfile({
|
|
||||||
name: values.name ?? '',
|
|
||||||
surname: values.surname ?? '',
|
|
||||||
avatar: avatar ? new File([avatar], 'avatar') : undefined,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (resp.status === 200) {
|
|
||||||
const avatarUrl =
|
|
||||||
AVATAR_URL(auth.user.id, auth.tenant?.tenantId) + `?${dayjs().unix()}`
|
|
||||||
|
|
||||||
setUser({
|
setUser({
|
||||||
...auth.user,
|
...auth.user,
|
||||||
name: `${resp.data.name} ${resp.data.surname}`.trim(),
|
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
||||||
avatar: avatarUrl,
|
|
||||||
})
|
})
|
||||||
setImage(avatarUrl)
|
}
|
||||||
keepCurrentAvatar = true
|
let keepCurrentAvatar = false
|
||||||
} else {
|
|
||||||
toast.push(<Notification title={resp?.error?.message} type="danger" />, {
|
if (hasAvatarChange) {
|
||||||
placement: 'top-end',
|
const avatar = await getCroppedAvatar()
|
||||||
|
const resp = await putUserAvatar({
|
||||||
|
userId: values.id!,
|
||||||
|
avatar: avatar ? new File([avatar], 'avatar') : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const avatarUrl =
|
||||||
|
AVATAR_URL(values.id, values.tenantId) + `?${dayjs().unix()}`
|
||||||
|
|
||||||
|
if (values.id === auth.user.id) {
|
||||||
|
setUser({
|
||||||
|
...auth.user,
|
||||||
|
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
||||||
|
avatar: avatarUrl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setImage(avatarUrl)
|
||||||
|
setHasAvatarChange(false)
|
||||||
|
keepCurrentAvatar = true
|
||||||
|
} else {
|
||||||
|
const errorMessage =
|
||||||
|
(resp as { error?: { message?: string } })?.error?.message || 'Hata'
|
||||||
|
|
||||||
|
toast.push(<Notification title={errorMessage} type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.push(
|
toast.push(
|
||||||
|
|
@ -542,7 +571,10 @@ function UserDetails() {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
icon={<FaTrashAlt />}
|
icon={<FaTrashAlt />}
|
||||||
onClick={() => setImage(undefined)}
|
onClick={() => {
|
||||||
|
setImage(undefined)
|
||||||
|
setHasAvatarChange(true)
|
||||||
|
}}
|
||||||
></Button>
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
{image && (
|
{image && (
|
||||||
|
|
@ -574,6 +606,7 @@ function UserDetails() {
|
||||||
<TabContent value="permission">
|
<TabContent value="permission">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { setSubmitting }) => {
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -685,6 +718,7 @@ function UserDetails() {
|
||||||
<TabContent value="work">
|
<TabContent value="work">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -866,6 +900,7 @@ function UserDetails() {
|
||||||
<TabContent value="identity">
|
<TabContent value="identity">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
@ -1222,6 +1257,7 @@ function UserDetails() {
|
||||||
<TabContent value="lockout">
|
<TabContent value="lockout">
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
initialValues={userDetails}
|
initialValues={userDetails}
|
||||||
onSubmit={async (values, { setSubmitting }) => {
|
onSubmit={async (values, { setSubmitting }) => {
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue