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.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Sozsoft.Platform.BlobStoring;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenIddict.Abstractions;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
|
|
@ -32,6 +34,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
public IRepository<WorkHour, Guid> workHourRepository { get; }
|
||||
public IRepository<Department, Guid> departmentRepository { get; }
|
||||
public IRepository<JobPosition, Guid> jobPositionRepository { get; }
|
||||
public BlobManager BlobCdnManager { get; }
|
||||
|
||||
public PlatformIdentityAppService(
|
||||
IIdentityUserAppService identityUserAppService,
|
||||
|
|
@ -45,6 +48,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
IRepository<WorkHour, Guid> workHourRepository,
|
||||
IRepository<Department, Guid> departmentRepository,
|
||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||
BlobManager blobCdnManager,
|
||||
IGuidGenerator guidGenerator
|
||||
)
|
||||
{
|
||||
|
|
@ -55,6 +59,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
this.workHourRepository = workHourRepository;
|
||||
this.departmentRepository = departmentRepository;
|
||||
this.jobPositionRepository = jobPositionRepository;
|
||||
this.BlobCdnManager = blobCdnManager;
|
||||
this.permissionRepository = permissionRepository;
|
||||
this.branchRepository = branchRepository;
|
||||
this.branchUsersRepository = branchUsersRepository;
|
||||
|
|
@ -273,6 +278,22 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
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()
|
||||
{
|
||||
var list = await permissionRepository.GetListAsync();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ import { ListResultDto, PagedAndSortedResultRequestDto, PagedResultDto } from '.
|
|||
import { AuditLogDto } from '../proxy/auditLog/audit-log'
|
||||
import apiService from './api.service'
|
||||
|
||||
export interface UserAvatarUpdateInput {
|
||||
userId: string
|
||||
avatar?: File
|
||||
}
|
||||
|
||||
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
||||
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
||||
method: 'GET',
|
||||
|
|
@ -36,6 +41,21 @@ export const putUserDetail = (input: UserInfoViewModel) =>
|
|||
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) =>
|
||||
apiService.fetchData({
|
||||
method: 'PUT',
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ import {
|
|||
deleteClaimUser,
|
||||
getUserDetail,
|
||||
postClaimUser,
|
||||
putUserAvatar,
|
||||
putUserDetail,
|
||||
putUserLookout,
|
||||
putUserPermission,
|
||||
} from '@/services/identity.service'
|
||||
import { updateProfile } from '@/services/account.service'
|
||||
import { CountryDto, getCountry } from '@/services/home.service'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import dayjs from 'dayjs'
|
||||
|
|
@ -97,6 +97,7 @@ function UserDetails() {
|
|||
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||
const [image, setImage] = useState<string | undefined>()
|
||||
const [hasAvatarChange, setHasAvatarChange] = useState(false)
|
||||
const cropperRef = useRef<CropperRef>(null)
|
||||
const previewRef = useRef<CropperPreviewRef>(null)
|
||||
const auth = useStoreState((state) => state.auth)
|
||||
|
|
@ -111,15 +112,26 @@ function UserDetails() {
|
|||
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
||||
|
||||
const getUser = async (syncAvatar = true) => {
|
||||
const { data } = await getUserDetail(userId || '')
|
||||
if (!userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const { data } = await getUserDetail(userId)
|
||||
setUserDetails(data)
|
||||
if (syncAvatar) {
|
||||
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
||||
setHasAvatarChange(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setUserDetails(undefined)
|
||||
setImage(undefined)
|
||||
setHasAvatarChange(false)
|
||||
getUser()
|
||||
}, [userId])
|
||||
|
||||
useEffect(() => {
|
||||
getCountry().then(({ data }) => setCountries(data))
|
||||
}, [])
|
||||
|
||||
|
|
@ -131,8 +143,10 @@ function UserDetails() {
|
|||
const onChooseImage = async (file: File[]) => {
|
||||
if (file[0]) {
|
||||
setImage(URL.createObjectURL(file[0]))
|
||||
setHasAvatarChange(true)
|
||||
} else {
|
||||
setImage(undefined)
|
||||
setHasAvatarChange(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,35 +260,50 @@ function UserDetails() {
|
|||
<TabContent value="user">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
await putUserDetail({ ...values })
|
||||
if (values.id === auth.user.id) {
|
||||
setUser({
|
||||
...auth.user,
|
||||
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
||||
})
|
||||
}
|
||||
let keepCurrentAvatar = false
|
||||
|
||||
if (hasAvatarChange) {
|
||||
const avatar = await getCroppedAvatar()
|
||||
const resp = await updateProfile({
|
||||
name: values.name ?? '',
|
||||
surname: values.surname ?? '',
|
||||
const resp = await putUserAvatar({
|
||||
userId: values.id!,
|
||||
avatar: avatar ? new File([avatar], 'avatar') : undefined,
|
||||
})
|
||||
|
||||
if (resp.status === 200) {
|
||||
const avatarUrl =
|
||||
AVATAR_URL(auth.user.id, auth.tenant?.tenantId) + `?${dayjs().unix()}`
|
||||
AVATAR_URL(values.id, values.tenantId) + `?${dayjs().unix()}`
|
||||
|
||||
if (values.id === auth.user.id) {
|
||||
setUser({
|
||||
...auth.user,
|
||||
name: `${resp.data.name} ${resp.data.surname}`.trim(),
|
||||
name: `${values.name ?? ''} ${values.surname ?? ''}`.trim(),
|
||||
avatar: avatarUrl,
|
||||
})
|
||||
}
|
||||
|
||||
setImage(avatarUrl)
|
||||
setHasAvatarChange(false)
|
||||
keepCurrentAvatar = true
|
||||
} else {
|
||||
toast.push(<Notification title={resp?.error?.message} type="danger" />, {
|
||||
const errorMessage =
|
||||
(resp as { error?: { message?: string } })?.error?.message || 'Hata'
|
||||
|
||||
toast.push(<Notification title={errorMessage} type="danger" />, {
|
||||
placement: 'top-end',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
toast.push(
|
||||
<Notification type="success" duration={2000}>
|
||||
|
|
@ -542,7 +571,10 @@ function UserDetails() {
|
|||
<Button
|
||||
type="button"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={() => setImage(undefined)}
|
||||
onClick={() => {
|
||||
setImage(undefined)
|
||||
setHasAvatarChange(true)
|
||||
}}
|
||||
></Button>
|
||||
</div>
|
||||
{image && (
|
||||
|
|
@ -574,6 +606,7 @@ function UserDetails() {
|
|||
<TabContent value="permission">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
|
|
@ -685,6 +718,7 @@ function UserDetails() {
|
|||
<TabContent value="work">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
|
|
@ -866,6 +900,7 @@ function UserDetails() {
|
|||
<TabContent value="identity">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
|
|
@ -1222,6 +1257,7 @@ function UserDetails() {
|
|||
<TabContent value="lockout">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
|
|
|
|||
Loading…
Reference in a new issue