Profile ve UserDetail komponentleri düzenlendi
This commit is contained in:
parent
231860e85a
commit
a3e66081e9
9 changed files with 627 additions and 260 deletions
|
|
@ -133,6 +133,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
return new UserInfoViewModel()
|
||||
{
|
||||
Id = user.Id,
|
||||
TenantId = user.TenantId,
|
||||
UserName = user.UserName,
|
||||
Name = user.Name,
|
||||
Surname = user.Surname,
|
||||
|
|
@ -219,6 +220,7 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
user.SetLoginEndDate(UserInfo.LoginEndDate);
|
||||
user.SetWorkHour(UserInfo.WorkHour);
|
||||
user.SetIsActive(UserInfo.IsActive);
|
||||
user.SetLastPasswordChangeTime(UserInfo.LastPasswordChangeTime);
|
||||
|
||||
user.SetEmailConfirmed(UserInfo.EmailConfirmed);
|
||||
user.SetPhoneNumberConfirmed(UserInfo.PhoneNumberConfirmed);
|
||||
|
|
@ -239,7 +241,6 @@ public class PlatformIdentityAppService : ApplicationService
|
|||
user.Name = UserInfo.Name;
|
||||
user.Surname = UserInfo.Surname;
|
||||
user.SetPhoneNumber(UserInfo.PhoneNumber, user.PhoneNumberConfirmed);
|
||||
user.SetLastPasswordChangeTime(UserInfo.LastPasswordChangeTime);
|
||||
user.SetRocketUsername(UserInfo.RocketUsername);
|
||||
user.SetWorkHour(UserInfo.WorkHour);
|
||||
user.SetDepartmentId(UserInfo.DepartmentId);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ const _UserDropdown = ({ className }: CommonProps) => {
|
|||
const tenant = useStoreState((state) => state.abpConfig.config?.currentTenant)
|
||||
const { translate } = useLocalization()
|
||||
const { signOut } = useAuth()
|
||||
const displayName = name || userName
|
||||
const tenantName = tenant?.name
|
||||
|
||||
const dropdownItemList: DropdownList[] = [
|
||||
{
|
||||
|
|
@ -42,12 +44,20 @@ const _UserDropdown = ({ className }: CommonProps) => {
|
|||
]
|
||||
|
||||
const UserAvatar = (
|
||||
<div className={classNames(className, 'flex items-center gap-2')}>
|
||||
<Avatar size={32} shape="circle" src={avatar} alt="avatar" />
|
||||
<div className="hidden md:block">
|
||||
<div className="text-xs">{userName}</div>
|
||||
<div className="font-bold">{name}</div>
|
||||
<div className="font-bold italic">{tenant?.name}</div>
|
||||
<div className={classNames(className, 'flex items-center gap-2 px-1.5 py-0')}>
|
||||
<Avatar size={30} shape="circle" src={avatar} alt="avatar" />
|
||||
<div className="hidden max-w-[160px] flex-col items-start leading-[1.05] md:flex">
|
||||
<span className="max-w-full truncate text-[12px] text-gray-800 dark:text-gray-400">
|
||||
{userName}
|
||||
</span>
|
||||
<span className="mt-1 max-w-full truncate text-[12px] font-bold text-gray-900 dark:text-gray-100">
|
||||
{displayName}
|
||||
</span>
|
||||
{tenantName && (
|
||||
<span className="mt-1 max-w-full truncate text-[12px] font-semibold text-indigo-600 dark:text-indigo-300">
|
||||
{tenantName}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -55,11 +65,18 @@ const _UserDropdown = ({ className }: CommonProps) => {
|
|||
return (
|
||||
<Dropdown menuStyle={{ minWidth: 240 }} renderTitle={UserAvatar} placement="bottom-end">
|
||||
<Dropdown.Item variant="header">
|
||||
<div className="py-2 px-3 flex items-center gap-2">
|
||||
<Avatar shape="circle" src={avatar} alt="avatar" />
|
||||
<div>
|
||||
<div className="font-bold text-gray-900 dark:text-gray-100">{name}</div>
|
||||
<div className="text-xs">{email}</div>
|
||||
<div className="flex items-center gap-3 px-3 py-2.5">
|
||||
<Avatar size={40} shape="circle" src={avatar} alt="avatar" />
|
||||
<div className="min-w-0">
|
||||
<div className="truncate font-bold text-gray-900 dark:text-gray-100">
|
||||
{displayName}
|
||||
</div>
|
||||
<div className="truncate text-xs text-gray-800 dark:text-gray-300">{email}</div>
|
||||
{tenantName && (
|
||||
<div className="mt-1 truncate text-xs font-semibold text-indigo-600 dark:text-indigo-300">
|
||||
{tenantName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ platformApiService.interceptors.response.use(
|
|||
authority: [tokenDetails?.role],
|
||||
name: `${tokenDetails?.given_name} ${tokenDetails?.family_name}`.trim(),
|
||||
role: 'teacher',
|
||||
tenantId: tokenDetails?.tenantid,
|
||||
},
|
||||
})
|
||||
setIsRefreshing(false)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export interface AuthStoreModel {
|
|||
name: string
|
||||
avatar?: string
|
||||
role: string
|
||||
tenantId?: string
|
||||
}
|
||||
tenant?: {
|
||||
tenantId?: string
|
||||
|
|
@ -59,6 +60,7 @@ export const initialState: AuthStoreModel = {
|
|||
name: '',
|
||||
avatar: '',
|
||||
role: 'teacher',
|
||||
tenantId: '',
|
||||
},
|
||||
tenant: {
|
||||
tenantId: '',
|
||||
|
|
@ -82,7 +84,8 @@ export const authModel: AuthModel = {
|
|||
state.user.userName = payload.user.userName
|
||||
state.user.authority = payload.user.authority
|
||||
state.user.email = payload.user.email
|
||||
state.user.avatar = AVATAR_URL(payload.user.id, state.tenant?.tenantId) + `?${dayjs().unix()}`
|
||||
state.user.tenantId = payload.user.tenantId
|
||||
state.user.avatar = AVATAR_URL(payload.user.id, state.user?.tenantId) + `?${dayjs().unix()}`
|
||||
}),
|
||||
signOut: action(() => ({ ...initialState })),
|
||||
// signOut: action((state) => ({ ...initialState, tenantId: state.tenant?.tenantId })),
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ function useAuth() {
|
|||
expiresIn: number
|
||||
}) => {
|
||||
const tokenDetails: any = jwtDecode(token)
|
||||
|
||||
signInStore({
|
||||
session: { token, refreshToken, expiresIn, expire: tokenDetails?.exp, signedIn: true },
|
||||
user: {
|
||||
|
|
@ -44,6 +45,7 @@ function useAuth() {
|
|||
authority: [tokenDetails?.role],
|
||||
name: `${tokenDetails?.given_name} ${tokenDetails?.family_name}`.trim(),
|
||||
role: 'teacher',
|
||||
tenantId: tokenDetails?.tenantid,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { APP_NAME } from '@/constants/app.constant'
|
|||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Suspense, lazy, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { FaCheckCircle, FaCompressAlt, FaNode, FaUser } from 'react-icons/fa'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
type AccountSetting = {
|
||||
|
|
@ -35,19 +36,23 @@ const Profile = () => {
|
|||
{
|
||||
label: string
|
||||
path: string
|
||||
icon?: JSX.Element
|
||||
}
|
||||
> = {
|
||||
general: {
|
||||
label: translate('::Abp.Identity.Profile.General'),
|
||||
path: 'general',
|
||||
icon: <FaUser />,
|
||||
},
|
||||
password: {
|
||||
label: translate('::Abp.Identity.Password'),
|
||||
path: 'password',
|
||||
icon: <FaCheckCircle />,
|
||||
},
|
||||
notificationSettings: {
|
||||
label: translate('::Abp.Identity.NotificationSettings'),
|
||||
path: 'notification-settings',
|
||||
icon: <FaCompressAlt />,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +83,9 @@ const Profile = () => {
|
|||
<TabList>
|
||||
{Object.keys(settingsMenu).map((key) => (
|
||||
<TabNav key={key} value={key}>
|
||||
{settingsMenu[key].icon && (
|
||||
<span className="mr-2">{settingsMenu[key].icon}</span>
|
||||
)}
|
||||
{settingsMenu[key].label}
|
||||
</TabNav>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { Input, Upload } from '@/components/ui'
|
||||
import { Input, Select, Upload } from '@/components/ui'
|
||||
import Button from '@/components/ui/Button'
|
||||
import { FormContainer } from '@/components/ui/Form'
|
||||
import { FormContainer, FormItem } from '@/components/ui/Form'
|
||||
import Notification from '@/components/ui/Notification'
|
||||
import toast from '@/components/ui/toast'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import dayjs from 'dayjs'
|
||||
import type { FieldProps } from 'formik'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
|
@ -26,13 +25,15 @@ import {
|
|||
FaUserCircle,
|
||||
FaPhone,
|
||||
FaPlus,
|
||||
FaHome,
|
||||
FaUniversity,
|
||||
} from 'react-icons/fa'
|
||||
import * as Yup from 'yup'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import FormRow from '@/views/shared/FormRow'
|
||||
import FormDesription from '@/views/shared/FormDesription'
|
||||
import { ProfileDto, UpdateProfileDto } from '@/proxy/account/models'
|
||||
import { getProfile, updateProfile } from '@/services/account.service'
|
||||
import { CountryDto, getCountry } from '@/services/home.service'
|
||||
import { SelectBoxOption } from '@/types/shared'
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().min(3).max(50).required(),
|
||||
|
|
@ -44,6 +45,7 @@ const General = () => {
|
|||
const [profileData, setProfileData] = useState<ProfileDto>()
|
||||
const [formData, setFormData] = useState<UpdateProfileDto>()
|
||||
const [image, setImage] = useState<string | undefined>()
|
||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||
|
||||
const auth = useStoreState((state) => state.auth)
|
||||
const { setUser } = useStoreActions((actions) => actions.auth.user)
|
||||
|
|
@ -54,8 +56,9 @@ const General = () => {
|
|||
|
||||
const fetchData = async () => {
|
||||
setLoading(true)
|
||||
const response = await getProfile()
|
||||
const [response, countryResponse] = await Promise.all([getProfile(), getCountry()])
|
||||
setProfileData(response.data)
|
||||
setCountries(countryResponse.data)
|
||||
setImage(auth.user.avatar)
|
||||
setFormData({
|
||||
name: response.data.name,
|
||||
|
|
@ -150,6 +153,69 @@ const General = () => {
|
|||
}
|
||||
})
|
||||
|
||||
const getProfileExtraValue = (key: string) => {
|
||||
const extraProperties = profileData?.extraProperties
|
||||
const value =
|
||||
extraProperties?.[key] ?? extraProperties?.[`${key.charAt(0).toLowerCase()}${key.slice(1)}`]
|
||||
|
||||
return typeof value === 'string' || typeof value === 'number' ? String(value) : undefined
|
||||
}
|
||||
|
||||
const getSelectValue = (options: SelectBoxOption[], value?: string) => {
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return options.find((option) => option.value === value) ?? { value, label: value }
|
||||
}
|
||||
|
||||
const nationalityOptions: SelectBoxOption[] = countries.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))
|
||||
|
||||
const educationOptions: SelectBoxOption[] = [
|
||||
{
|
||||
value: 'İlkokul',
|
||||
label: translate('::App.EducationLevel.Primary') || 'İlkokul',
|
||||
},
|
||||
{
|
||||
value: 'Ortaokul',
|
||||
label: translate('::App.EducationLevel.MiddleSchool') || 'Ortaokul',
|
||||
},
|
||||
{
|
||||
value: 'Lise',
|
||||
label: translate('::App.EducationLevel.HighSchool') || 'Lise',
|
||||
},
|
||||
{
|
||||
value: 'Ön Lisans',
|
||||
label: translate('::App.EducationLevel.Associate') || 'Ön Lisans',
|
||||
},
|
||||
{
|
||||
value: 'Lisans',
|
||||
label: translate('::App.EducationLevel.Bachelor') || 'Lisans',
|
||||
},
|
||||
{
|
||||
value: 'Yüksek Lisans',
|
||||
label: translate('::App.EducationLevel.Master') || 'Yüksek Lisans',
|
||||
},
|
||||
{
|
||||
value: 'Doktora',
|
||||
label: translate('::App.EducationLevel.PhD') || 'Doktora',
|
||||
},
|
||||
]
|
||||
|
||||
const bloodTypeOptions: SelectBoxOption[] = [
|
||||
{ value: 'A Rh+', label: 'A Rh+' },
|
||||
{ value: 'A Rh-', label: 'A Rh-' },
|
||||
{ value: 'B Rh+', label: 'B Rh+' },
|
||||
{ value: 'B Rh-', label: 'B Rh-' },
|
||||
{ value: 'AB Rh+', label: 'AB Rh+' },
|
||||
{ value: 'AB Rh-', label: 'AB Rh-' },
|
||||
{ value: '0 Rh+', label: '0 Rh+' },
|
||||
{ value: '0 Rh-', label: '0 Rh-' },
|
||||
]
|
||||
|
||||
if (loading) {
|
||||
return <></>
|
||||
}
|
||||
|
|
@ -165,50 +231,27 @@ const General = () => {
|
|||
}}
|
||||
>
|
||||
{({ touched, errors, isSubmitting, resetForm }) => {
|
||||
const validatorProps = { touched, errors }
|
||||
return (
|
||||
<Form>
|
||||
<FormContainer>
|
||||
<FormDesription
|
||||
title={translate('::Abp.Identity.Profile.General')}
|
||||
desc={translate('::Abp.Identity.Profile.General.Description')}
|
||||
/>
|
||||
<FormRow
|
||||
name="email"
|
||||
label={translate('::Abp.Account.EmailAddress')}
|
||||
{...validatorProps}
|
||||
>
|
||||
<FormContainer size="md">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||||
<div>
|
||||
<h6 className="mb-4">
|
||||
{translate('::Abp.Identity.User.UserInformation.ContactInformation')}
|
||||
</h6>
|
||||
|
||||
<FormItem label={translate('::Abp.Account.EmailAddress')}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaEnvelope className="text-xl" />}
|
||||
value={profileData?.email}
|
||||
></Input>
|
||||
</FormRow>
|
||||
<FormRow
|
||||
name="phoneNumber"
|
||||
label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
||||
{...validatorProps}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaPhone className="text-xl" />}
|
||||
value={profileData?.phoneNumber}
|
||||
></Input>
|
||||
</FormRow>
|
||||
<FormRow name="phoneNumber" label={translate('::RocketUsername')} {...validatorProps}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaFacebookMessenger className="text-xl" />}
|
||||
value={profileData?.extraProperties?.['RocketUsername'] as string | undefined}
|
||||
></Input>
|
||||
</FormRow>
|
||||
<FormRow
|
||||
name="name"
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.Name')}
|
||||
{...validatorProps}
|
||||
invalid={(errors.name && touched.name) as boolean}
|
||||
errorMessage={errors.name}
|
||||
>
|
||||
<Field
|
||||
type="text"
|
||||
|
|
@ -218,11 +261,11 @@ const General = () => {
|
|||
component={Input}
|
||||
prefix={<FaUserCircle className="text-xl" />}
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow
|
||||
name="surname"
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.Surname')}
|
||||
{...validatorProps}
|
||||
invalid={(errors.surname && touched.surname) as boolean}
|
||||
errorMessage={errors.surname}
|
||||
>
|
||||
<Field
|
||||
type="text"
|
||||
|
|
@ -232,14 +275,80 @@ const General = () => {
|
|||
component={Input}
|
||||
prefix={<FaUserCircle className="text-xl" />}
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow name="avatar" label="Avatar" alignCenter={false} {...validatorProps}>
|
||||
<Field name="avatar">
|
||||
{({ field, form }: FieldProps) => {
|
||||
return (
|
||||
<>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaPhone className="text-xl" />}
|
||||
value={profileData?.phoneNumber}
|
||||
></Input>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::RocketUsername')}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaFacebookMessenger className="text-xl" />}
|
||||
value={profileData?.extraProperties?.['RocketUsername'] as string | undefined}
|
||||
></Input>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex flex-col lg:flex-row items-start gap-5">
|
||||
<h6 className="mb-4">
|
||||
{translate('::Abp.Identity.User.UserInformation.AdditionalInformation')}
|
||||
</h6>
|
||||
|
||||
<FormItem label={translate('::Abp.Account.HomeAddress')}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaHome className="text-xl" />}
|
||||
value={getProfileExtraValue('HomeAddress')}
|
||||
></Input>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Account.Nationality')}>
|
||||
<Select<SelectBoxOption>
|
||||
isDisabled
|
||||
options={nationalityOptions}
|
||||
value={getSelectValue(
|
||||
nationalityOptions,
|
||||
getProfileExtraValue('Nationality'),
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Account.EducationLevel')}>
|
||||
<Select<SelectBoxOption>
|
||||
isDisabled
|
||||
options={educationOptions}
|
||||
value={getSelectValue(
|
||||
educationOptions,
|
||||
getProfileExtraValue('EducationLevel'),
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Account.GraduationSchool')}>
|
||||
<Input
|
||||
type="text"
|
||||
disabled
|
||||
prefix={<FaUniversity className="text-xl" />}
|
||||
value={getProfileExtraValue('GraduationSchool')}
|
||||
></Input>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Account.BloodType')}>
|
||||
<Select<SelectBoxOption>
|
||||
isDisabled
|
||||
options={bloodTypeOptions}
|
||||
value={getSelectValue(bloodTypeOptions, getProfileExtraValue('BloodType'))}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="mb-4">Avatar</h6>
|
||||
|
||||
<FormItem>
|
||||
<div className="flex flex-col gap-4">
|
||||
{image ? (
|
||||
<Cropper
|
||||
ref={cropperRef}
|
||||
|
|
@ -260,8 +369,8 @@ const General = () => {
|
|||
src={auth.user.avatar}
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-row gap-4">
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Upload
|
||||
className="cursor-pointer"
|
||||
showList={false}
|
||||
|
|
@ -274,7 +383,6 @@ const General = () => {
|
|||
</Upload>
|
||||
<Button
|
||||
type="button"
|
||||
className="my-2"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={() => setImage(undefined)}
|
||||
></Button>
|
||||
|
|
@ -282,17 +390,14 @@ const General = () => {
|
|||
{image && (
|
||||
<CropperPreview
|
||||
ref={previewRef}
|
||||
className="preview max-w-[100px] avatar-img avatar-circle border border-gray-400"
|
||||
className="preview max-w-[100px] avatar-img avatar-circle"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Field>
|
||||
</FormRow>
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button className="ltr:mr-2 rtl:ml-2" type="button" onClick={() => resetForm()}>
|
||||
{translate('::Cancel')}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
Select,
|
||||
Tabs,
|
||||
toast,
|
||||
Upload,
|
||||
} from '@/components/ui'
|
||||
import Dialog from '@/components/ui/Dialog'
|
||||
import DateTimepicker from '@/components/ui/DatePicker/DateTimepicker'
|
||||
|
|
@ -29,11 +30,12 @@ import {
|
|||
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'
|
||||
import { Field, FieldArray, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import {
|
||||
FaBuilding,
|
||||
|
|
@ -43,13 +45,42 @@ import {
|
|||
FaTrashAlt,
|
||||
FaCheckCircle,
|
||||
FaUserAstronaut,
|
||||
FaEnvelope,
|
||||
FaPhone,
|
||||
FaUserCircle,
|
||||
FaFacebookMessenger,
|
||||
FaHome,
|
||||
FaUniversity,
|
||||
FaCalendarAlt,
|
||||
FaBriefcase,
|
||||
FaIdCard,
|
||||
FaMapMarkerAlt,
|
||||
FaCity,
|
||||
FaBook,
|
||||
FaHashtag,
|
||||
FaMapPin,
|
||||
FaUserTie,
|
||||
FaFemale,
|
||||
FaHeart,
|
||||
FaExclamationTriangle,
|
||||
FaUsers,
|
||||
FaPlus,
|
||||
} from 'react-icons/fa'
|
||||
import {
|
||||
CircleStencil,
|
||||
Cropper,
|
||||
CropperPreview,
|
||||
CropperPreviewRef,
|
||||
CropperRef,
|
||||
} from 'react-advanced-cropper'
|
||||
import 'react-advanced-cropper/dist/style.css'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import * as Yup from 'yup'
|
||||
import { SelectBoxOption, SelectBoxOptionWithGroup } from '@/types/shared'
|
||||
import { ConfirmDialog, Container } from '@/components/shared'
|
||||
import { AdaptableCard, ConfirmDialog, Container } from '@/components/shared'
|
||||
import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import { APP_NAME, AVATAR_URL } from '@/constants/app.constant'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
|
||||
export interface ClaimTypeDto {
|
||||
claimType: string
|
||||
|
|
@ -64,10 +95,18 @@ function UserDetails() {
|
|||
const [open, setOpen] = useState(false)
|
||||
const [confirmDeleteClaim, setConfirmDeleteClaim] = useState<AssignedClaimViewModel | null>(null)
|
||||
const [countries, setCountries] = useState<CountryDto[]>([])
|
||||
const [image, setImage] = useState<string | undefined>()
|
||||
const cropperRef = useRef<CropperRef>(null)
|
||||
const previewRef = useRef<CropperPreviewRef>(null)
|
||||
const auth = useStoreState((state) => state.auth)
|
||||
const { setUser } = useStoreActions((actions) => actions.auth.user)
|
||||
|
||||
const getUser = async () => {
|
||||
const getUser = async (syncAvatar = true) => {
|
||||
const { data } = await getUserDetail(userId || '')
|
||||
setUserDetails(data)
|
||||
if (syncAvatar) {
|
||||
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -80,6 +119,58 @@ function UserDetails() {
|
|||
claimValue: Yup.string().required(),
|
||||
})
|
||||
|
||||
const onChooseImage = async (file: File[]) => {
|
||||
if (file[0]) {
|
||||
setImage(URL.createObjectURL(file[0]))
|
||||
} else {
|
||||
setImage(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const beforeUpload = (files: FileList | null, fileList: File[]) => {
|
||||
let valid: string | boolean = true
|
||||
|
||||
const allowedFileType = ['image/jpeg', 'image/png', 'image/gif']
|
||||
const maxFileSize = 2000000
|
||||
|
||||
if (fileList.length >= 1) {
|
||||
return `Sadece bir dosya seçebilirsiniz`
|
||||
}
|
||||
|
||||
if (files) {
|
||||
for (const f of files) {
|
||||
if (!allowedFileType.includes(f.type)) {
|
||||
valid = '.jpg, .jpeg, .gif veya .png yükleyebilirsiniz'
|
||||
} else if (f.size >= maxFileSize) {
|
||||
valid = 'En fazla 2mb dosya yükleyebilirsiniz'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
const getCroppedAvatar = () =>
|
||||
new Promise<Blob | null>((resolve, reject) => {
|
||||
const canvas = cropperRef.current?.getCanvas({
|
||||
minHeight: 100,
|
||||
minWidth: 100,
|
||||
maxHeight: 300,
|
||||
maxWidth: 300,
|
||||
})
|
||||
if (canvas) {
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
resolve(blob)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
}, 'image/jpeg')
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
|
||||
const handleSubmit = async (
|
||||
values: ClaimTypeDto,
|
||||
{ setSubmitting }: FormikHelpers<ClaimTypeDto>,
|
||||
|
|
@ -121,35 +212,60 @@ function UserDetails() {
|
|||
title={userDetails.email}
|
||||
defaultTitle={APP_NAME}
|
||||
></Helmet>
|
||||
<Container>
|
||||
<AdaptableCard>
|
||||
<Tabs defaultValue="user">
|
||||
<TabList>
|
||||
<TabNav value="user" icon={<FaUser />}>
|
||||
<TabNav value="user" icon={<FaUser className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.UserInformation')}
|
||||
</TabNav>
|
||||
<TabNav value="permission" icon={<FaCheckCircle />}>
|
||||
<TabNav value="permission" icon={<FaCheckCircle className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.Permissions')}
|
||||
</TabNav>
|
||||
<TabNav value="work" icon={<FaBuilding />}>
|
||||
<TabNav value="work" icon={<FaBuilding className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.WorkInformation')}
|
||||
</TabNav>
|
||||
<TabNav value="identity" icon={<FaUserAstronaut />}>
|
||||
<TabNav value="identity" icon={<FaUserAstronaut className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.IndentityInformation')}
|
||||
</TabNav>
|
||||
<TabNav value="lockout" icon={<FaLockOpen />}>
|
||||
<TabNav value="lockout" icon={<FaLockOpen className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.LockoutManagement')}
|
||||
</TabNav>
|
||||
<TabNav value="claimTypes" icon={<FaFileAlt />}>
|
||||
<TabNav value="claimTypes" icon={<FaFileAlt className="text-sm" />}>
|
||||
{translate('::Abp.Identity.User.ClaimTypes')}
|
||||
</TabNav>
|
||||
</TabList>
|
||||
<TabContent value="user">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
await putUserDetail({ ...values })
|
||||
let keepCurrentAvatar = false
|
||||
|
||||
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({
|
||||
...auth.user,
|
||||
name: `${resp.data.name} ${resp.data.surname}`.trim(),
|
||||
avatar: avatarUrl,
|
||||
})
|
||||
setImage(avatarUrl)
|
||||
keepCurrentAvatar = true
|
||||
} else {
|
||||
toast.push(<Notification title={resp?.error?.message} type="danger" />, {
|
||||
placement: 'top-end',
|
||||
})
|
||||
}
|
||||
|
||||
toast.push(
|
||||
<Notification type="success" duration={2000}>
|
||||
|
|
@ -160,7 +276,7 @@ function UserDetails() {
|
|||
},
|
||||
)
|
||||
|
||||
getUser()
|
||||
getUser(!keepCurrentAvatar)
|
||||
setSubmitting(false)
|
||||
}}
|
||||
>
|
||||
|
|
@ -169,7 +285,7 @@ function UserDetails() {
|
|||
<Form>
|
||||
<div>
|
||||
<FormContainer size="md">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||||
{/* Personal Information */}
|
||||
<div>
|
||||
<h6 className="mb-4">
|
||||
|
|
@ -177,6 +293,17 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.ContactInformation',
|
||||
)}
|
||||
</h6>
|
||||
<FormItem label={translate('::Abp.Account.EmailAddress')}>
|
||||
<Field
|
||||
type="text"
|
||||
disabled
|
||||
name="email"
|
||||
placeholder="Email Address"
|
||||
prefix={<FaEnvelope className="text-xl" />}
|
||||
component={Input}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.Name')}
|
||||
>
|
||||
|
|
@ -185,6 +312,7 @@ function UserDetails() {
|
|||
name="name"
|
||||
placeholder="Name"
|
||||
component={Input}
|
||||
prefix={<FaUserCircle className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
|
|
@ -196,18 +324,10 @@ function UserDetails() {
|
|||
name="surname"
|
||||
placeholder="Surname"
|
||||
component={Input}
|
||||
prefix={<FaUserCircle className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label={translate('::Abp.Account.EmailAddress')}>
|
||||
<Field
|
||||
type="text"
|
||||
disabled
|
||||
name="email"
|
||||
placeholder="Email Address"
|
||||
component={Input}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
||||
>
|
||||
|
|
@ -217,27 +337,18 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.PhoneNumber',
|
||||
)}
|
||||
prefix={<FaPhone className="text-xl" />}
|
||||
component={Input}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="mb-4"> </h6>
|
||||
<FormItem label={translate('::Abp.Account.HomeAddress')}>
|
||||
<Field
|
||||
type="text"
|
||||
name="homeAddress"
|
||||
placeholder={translate('::Abp.Account.HomeAddress')}
|
||||
component={Input}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem size="sm" label={translate('::RocketUsername')}>
|
||||
<Field
|
||||
type="text"
|
||||
name="rocketUsername"
|
||||
placeholder={translate('::RocketUsername')}
|
||||
component={Input}
|
||||
prefix={<FaFacebookMessenger className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -248,6 +359,16 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.AdditionalInformation',
|
||||
)}
|
||||
</h6>
|
||||
<FormItem label={translate('::Abp.Account.HomeAddress')}>
|
||||
<Field
|
||||
type="text"
|
||||
name="homeAddress"
|
||||
placeholder={translate('::Abp.Account.HomeAddress')}
|
||||
component={Input}
|
||||
prefix={<FaHome className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label={translate('::Abp.Account.Nationality')}>
|
||||
<Field type="text" name="nationality">
|
||||
{({ field, form }: FieldProps<SelectBoxOption>) => {
|
||||
|
|
@ -330,12 +451,14 @@ function UserDetails() {
|
|||
}}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label={translate('::Abp.Account.GraduationSchool')}>
|
||||
<Field
|
||||
type="text"
|
||||
name="graduationSchool"
|
||||
placeholder={translate('::GraduationSchool')}
|
||||
component={Input}
|
||||
prefix={<FaUniversity className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label={translate('::Abp.Account.BloodType')}>
|
||||
|
|
@ -371,76 +494,73 @@ function UserDetails() {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<h6 className="mb-4">
|
||||
{translate('::Abp.Identity.User.UserInformation.AccountTimestamps')}
|
||||
</h6>
|
||||
<FormItem
|
||||
label={translate(
|
||||
'::Abp.Identity.User.UserInformation.PasswordChangeTime',
|
||||
)}
|
||||
>
|
||||
<Field name="lastPasswordChangeTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<DateTimepicker
|
||||
inputFormat="DD/MM/YYYY HH:mm"
|
||||
field={field}
|
||||
form={form}
|
||||
value={field.value ? dayjs(field.value).toDate() : null}
|
||||
placeholder="Select Date"
|
||||
onChange={(date: any) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||||
)
|
||||
<h6 className="mb-4">Avatar</h6>
|
||||
|
||||
<FormItem>
|
||||
<div className="flex flex-col gap-4">
|
||||
{image ? (
|
||||
<Cropper
|
||||
ref={cropperRef}
|
||||
src={image}
|
||||
onUpdate={(cropper) => {
|
||||
setTimeout(() => {
|
||||
previewRef.current?.update(cropper)
|
||||
}, 100)
|
||||
}}
|
||||
className="cropper max-h-[300px] max-w-[300px]"
|
||||
stencilComponent={CircleStencil}
|
||||
minHeight={100}
|
||||
minWidth={100}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.CreateTime')}
|
||||
>
|
||||
<Field name="creationTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Input
|
||||
field={field}
|
||||
form={form}
|
||||
value={
|
||||
field.value
|
||||
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
|
||||
: undefined
|
||||
) : (
|
||||
<img
|
||||
className="cropper max-h-[300px] max-w-[300px]"
|
||||
src={
|
||||
image ||
|
||||
AVATAR_URL(
|
||||
values.id,
|
||||
values.tenantId,
|
||||
)
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={translate('::Abp.Identity.User.UserInformation.UpdateTime')}
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Upload
|
||||
className="cursor-pointer"
|
||||
showList={false}
|
||||
multiple={false}
|
||||
uploadLimit={1}
|
||||
beforeUpload={beforeUpload}
|
||||
onChange={onChooseImage}
|
||||
>
|
||||
<Field name="lastModificationTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Input
|
||||
field={field}
|
||||
form={form}
|
||||
value={
|
||||
field.value
|
||||
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
|
||||
: undefined
|
||||
}
|
||||
disabled
|
||||
<Button icon={<FaPlus />} type="button"></Button>
|
||||
</Upload>
|
||||
<Button
|
||||
type="button"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={() => setImage(undefined)}
|
||||
></Button>
|
||||
</div>
|
||||
{image && (
|
||||
<CropperPreview
|
||||
ref={previewRef}
|
||||
className="preview max-w-[100px] avatar-img avatar-circle"
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
</FormContainer>
|
||||
</div>
|
||||
|
||||
<Button variant="solid" block loading={isSubmitting} type="submit">
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
|
|
@ -449,7 +569,7 @@ function UserDetails() {
|
|||
</TabContent>
|
||||
|
||||
<TabContent value="permission">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
|
|
@ -547,8 +667,8 @@ function UserDetails() {
|
|||
</FormContainer>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<Button variant="solid" block loading={isSubmitting} type="submit">
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -560,7 +680,7 @@ function UserDetails() {
|
|||
</TabContent>
|
||||
|
||||
<TabContent value="work">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
|
|
@ -601,7 +721,7 @@ function UserDetails() {
|
|||
<Form>
|
||||
<div>
|
||||
<FormContainer size="md">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||||
<div>
|
||||
<FormItem
|
||||
label={translate(
|
||||
|
|
@ -666,6 +786,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.SskNo',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaIdCard className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -683,6 +804,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.HireDate',
|
||||
)}
|
||||
inputPrefix={<FaBriefcase className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -710,6 +832,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.TerminationDate',
|
||||
)}
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -725,9 +848,11 @@ function UserDetails() {
|
|||
</FormContainer>
|
||||
</div>
|
||||
|
||||
<Button variant="solid" block loading={isSubmitting} type="submit">
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
|
|
@ -736,7 +861,7 @@ function UserDetails() {
|
|||
</TabContent>
|
||||
|
||||
<TabContent value="identity">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||||
|
|
@ -779,7 +904,7 @@ function UserDetails() {
|
|||
return (
|
||||
<Form>
|
||||
<FormContainer size="md">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-5 w-full">
|
||||
<div>
|
||||
<FormItem
|
||||
label={translate(
|
||||
|
|
@ -793,6 +918,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.IdentityNumber',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaIdCard className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -808,6 +934,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.SerialNo',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaHashtag className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -823,6 +950,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.Province',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaMapMarkerAlt className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -838,6 +966,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.District',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaCity className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -853,6 +982,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.Village',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaHome className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -868,6 +998,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.VolumeNo',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaBook className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -885,6 +1016,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.FamilySequenceNo',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaUsers className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -900,6 +1032,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.SequenceNo',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaHashtag className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -915,6 +1048,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.IssuedPlace',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaMapPin className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -932,6 +1066,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.IssuedDate',
|
||||
)}
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -955,6 +1090,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.BirthPlace',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaMapMarkerAlt className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -972,6 +1108,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.BirthDate',
|
||||
)}
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -995,6 +1132,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.FatherName',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaUserTie className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -1010,6 +1148,7 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.UserInformation.MotherName',
|
||||
)}
|
||||
component={Input}
|
||||
prefix={<FaFemale className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
|
@ -1050,6 +1189,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.UserInformation.MarriageDate',
|
||||
)}
|
||||
inputPrefix={<FaHeart className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -1064,9 +1204,11 @@ function UserDetails() {
|
|||
</div>
|
||||
</FormContainer>
|
||||
|
||||
<Button variant="solid" block loading={isSubmitting} type="submit">
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
|
|
@ -1075,7 +1217,7 @@ function UserDetails() {
|
|||
</TabContent>
|
||||
|
||||
<TabContent value="lockout">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Formik
|
||||
initialValues={userDetails}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
|
|
@ -1108,7 +1250,7 @@ function UserDetails() {
|
|||
return (
|
||||
<Form>
|
||||
<FormContainer size="md">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 w-full">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||||
{/* Account Status */}
|
||||
<div>
|
||||
<h6 className="mb-4">
|
||||
|
|
@ -1258,6 +1400,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.LockoutManagement.AccountEndDate',
|
||||
)}
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -1295,7 +1438,12 @@ function UserDetails() {
|
|||
'::Abp.Identity.User.LockoutManagement.AccessFailedCount',
|
||||
)}
|
||||
>
|
||||
<Field type="number" name="accessFailedCount" component={Input} />
|
||||
<Field
|
||||
type="number"
|
||||
name="accessFailedCount"
|
||||
component={Input}
|
||||
prefix={<FaExclamationTriangle className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
|
|
@ -1346,6 +1494,7 @@ function UserDetails() {
|
|||
placeholder={translate(
|
||||
'::Abp.Identity.User.LockoutManagement.LoginEndDate',
|
||||
)}
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
|
|
@ -1356,13 +1505,88 @@ function UserDetails() {
|
|||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
layout="horizontal"
|
||||
labelClass="!justify-start"
|
||||
labelWidth="50%"
|
||||
label={translate(
|
||||
'::Abp.Identity.User.UserInformation.PasswordChangeTime',
|
||||
)}
|
||||
>
|
||||
<Field name="lastPasswordChangeTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<DateTimepicker
|
||||
inputFormat="DD/MM/YYYY HH:mm"
|
||||
field={field}
|
||||
form={form}
|
||||
value={field.value ? dayjs(field.value).toDate() : null}
|
||||
placeholder="Select Date"
|
||||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||||
onChange={(date: any) => {
|
||||
form.setFieldValue(
|
||||
field.name,
|
||||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
layout="horizontal"
|
||||
labelClass="!justify-start"
|
||||
labelWidth="50%"
|
||||
label={translate('::Abp.Identity.User.UserInformation.CreateTime')}
|
||||
>
|
||||
<Field name="creationTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Input
|
||||
field={field}
|
||||
form={form}
|
||||
value={
|
||||
field.value
|
||||
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
|
||||
: undefined
|
||||
}
|
||||
disabled
|
||||
prefix={<FaCalendarAlt className="text-xl" />}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
layout="horizontal"
|
||||
labelClass="!justify-start"
|
||||
labelWidth="50%"
|
||||
label={translate('::Abp.Identity.User.UserInformation.UpdateTime')}
|
||||
>
|
||||
<Field name="lastModificationTime">
|
||||
{({ field, form }: FieldProps) => (
|
||||
<Input
|
||||
field={field}
|
||||
form={form}
|
||||
value={
|
||||
field.value
|
||||
? dayjs(field.value).format('DD/MM/YYYY HH:mm')
|
||||
: undefined
|
||||
}
|
||||
disabled
|
||||
prefix={<FaCalendarAlt className="text-xl" />}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormItem>
|
||||
</div>
|
||||
</div>
|
||||
</FormContainer>
|
||||
|
||||
<Button variant="solid" block loading={isSubmitting} type="submit">
|
||||
<div className="mt-4 ltr:text-right">
|
||||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
|
|
@ -1371,7 +1595,7 @@ function UserDetails() {
|
|||
</TabContent>
|
||||
|
||||
<TabContent value="claimTypes">
|
||||
<div className="mt-5">
|
||||
<div className="px-4 py-6">
|
||||
<Table compact>
|
||||
<THead>
|
||||
<Tr>
|
||||
|
|
@ -1424,7 +1648,7 @@ function UserDetails() {
|
|||
</div>
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</Container>
|
||||
</AdaptableCard>
|
||||
|
||||
<Dialog isOpen={open} onClose={() => setOpen(false)} onRequestClose={() => setOpen(false)}>
|
||||
<Formik
|
||||
|
|
@ -1465,7 +1689,13 @@ function UserDetails() {
|
|||
invalid={errors.claimValue && touched.claimValue}
|
||||
errorMessage={errors.claimValue}
|
||||
>
|
||||
<Field type="text" autoComplete="off" name="claimValue" component={Input} />
|
||||
<Field
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
name="claimValue"
|
||||
component={Input}
|
||||
prefix={<FaFileAlt className="text-xl" />}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<div className="mt-6 flex flex-row justify-end gap-3">
|
||||
|
|
|
|||
Loading…
Reference in a new issue