1761 lines
77 KiB
TypeScript
1761 lines
77 KiB
TypeScript
import {
|
||
Button,
|
||
Checkbox,
|
||
DatePicker,
|
||
FormContainer,
|
||
FormItem,
|
||
Input,
|
||
Notification,
|
||
Select,
|
||
Tabs,
|
||
toast,
|
||
Upload,
|
||
} from '@/components/ui'
|
||
import Dialog from '@/components/ui/Dialog'
|
||
import DateTimepicker from '@/components/ui/DatePicker/DateTimepicker'
|
||
import Table from '@/components/ui/Table'
|
||
import TBody from '@/components/ui/Table/TBody'
|
||
import Td from '@/components/ui/Table/Td'
|
||
import Th from '@/components/ui/Table/Th'
|
||
import THead from '@/components/ui/Table/THead'
|
||
import Tr from '@/components/ui/Table/Tr'
|
||
import TabContent from '@/components/ui/Tabs/TabContent'
|
||
import TabList from '@/components/ui/Tabs/TabList'
|
||
import TabNav from '@/components/ui/Tabs/TabNav'
|
||
import {
|
||
deleteClaimUser,
|
||
getUserDetail,
|
||
postClaimUser,
|
||
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'
|
||
import { Field, FieldArray, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
||
import { useEffect, useRef, useState } from 'react'
|
||
import { Helmet } from 'react-helmet'
|
||
import {
|
||
FaBuilding,
|
||
FaLockOpen,
|
||
FaUser,
|
||
FaFileAlt,
|
||
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 { AdaptableCard, ConfirmDialog, Container } from '@/components/shared'
|
||
import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models'
|
||
import { APP_NAME, AVATAR_URL } from '@/constants/app.constant'
|
||
import { useStoreActions, useStoreState } from '@/store'
|
||
import { useSetting } from '@/utils/hooks/useSetting'
|
||
|
||
export interface ClaimTypeDto {
|
||
claimType: string
|
||
claimValue: string
|
||
}
|
||
|
||
function UserDetails() {
|
||
const { userId } = useParams()
|
||
const { translate } = useLocalization()
|
||
const [userDetails, setUserDetails] = useState<UserInfoViewModel>()
|
||
const [loading, setLoading] = useState(true)
|
||
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 { setting } = useSetting()
|
||
|
||
const isEmailUpdateEnabled = setting('Abp.Identity.User.IsEmailUpdateEnabled')
|
||
const isRequireVerifiedAccount = setting('Abp.Identity.Profile.General.RequireVerifiedAccount')
|
||
const isRequireConfirmedEmail = setting('Abp.Identity.SignIn.RequireConfirmedEmail')
|
||
const isRequireConfirmedPhoneNumber = setting('Abp.Identity.SignIn.RequireConfirmedPhoneNumber')
|
||
const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled')
|
||
|
||
const getUser = async (syncAvatar = true) => {
|
||
const { data } = await getUserDetail(userId || '')
|
||
setUserDetails(data)
|
||
if (syncAvatar) {
|
||
setImage(`${AVATAR_URL(data.id, data.tenantId)}?${dayjs().unix()}`)
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
getUser()
|
||
getCountry().then(({ data }) => setCountries(data))
|
||
}, [])
|
||
|
||
const scheme = Yup.object().shape({
|
||
claimType: Yup.string().required(),
|
||
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>,
|
||
) => {
|
||
setLoading(true)
|
||
setSubmitting(true)
|
||
|
||
try {
|
||
await postClaimUser({ userId, claimType: values.claimType, claimValue: values.claimValue })
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Kaydet')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
setOpen(false)
|
||
getUser()
|
||
} catch (error) {
|
||
toast.push(
|
||
<Notification type="danger" duration={2000}>
|
||
{'Hata'}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
} finally {
|
||
setLoading(false)
|
||
setSubmitting(false)
|
||
}
|
||
}
|
||
|
||
return userDetails ? (
|
||
<>
|
||
<Helmet
|
||
titleTemplate={`%s | ${APP_NAME}`}
|
||
title={userDetails.email}
|
||
defaultTitle={APP_NAME}
|
||
></Helmet>
|
||
<AdaptableCard>
|
||
<Tabs defaultValue="user">
|
||
<TabList>
|
||
<TabNav value="user" icon={<FaUser className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.UserInformation')}
|
||
</TabNav>
|
||
<TabNav value="permission" icon={<FaCheckCircle className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.Permissions')}
|
||
</TabNav>
|
||
<TabNav value="work" icon={<FaBuilding className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.WorkInformation')}
|
||
</TabNav>
|
||
<TabNav value="identity" icon={<FaUserAstronaut className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.IndentityInformation')}
|
||
</TabNav>
|
||
<TabNav value="lockout" icon={<FaLockOpen className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.LockoutManagement')}
|
||
</TabNav>
|
||
<TabNav value="claimTypes" icon={<FaFileAlt className="text-sm" />}>
|
||
{translate('::Abp.Identity.User.ClaimTypes')}
|
||
</TabNav>
|
||
</TabList>
|
||
<TabContent value="user">
|
||
<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}>
|
||
{translate('::Kaydet')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
|
||
getUser(!keepCurrentAvatar)
|
||
setSubmitting(false)
|
||
}}
|
||
>
|
||
{({ isSubmitting, values }) => {
|
||
return (
|
||
<Form>
|
||
<div>
|
||
<FormContainer size="md">
|
||
<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">
|
||
{translate(
|
||
'::Abp.Identity.User.UserInformation.ContactInformation',
|
||
)}
|
||
</h6>
|
||
<FormItem label={translate('::Abp.Account.EmailAddress')}>
|
||
<Field
|
||
type="text"
|
||
disabled={isEmailUpdateEnabled?.toLowerCase() !== 'true'}
|
||
name="email"
|
||
placeholder="Email Address"
|
||
prefix={<FaEnvelope className="text-xl" />}
|
||
component={Input}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.Name')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="name"
|
||
placeholder="Name"
|
||
component={Input}
|
||
prefix={<FaUserCircle className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.Surname')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="surname"
|
||
placeholder="Surname"
|
||
component={Input}
|
||
prefix={<FaUserCircle className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="phoneNumber"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.PhoneNumber',
|
||
)}
|
||
prefix={<FaPhone className="text-xl" />}
|
||
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>
|
||
|
||
<div>
|
||
<h6 className="mb-4">
|
||
{translate(
|
||
'::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>) => {
|
||
const nationalityOptions: SelectBoxOption[] = countries.map(
|
||
(c) => ({ value: c.name, label: c.name }),
|
||
)
|
||
return (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={nationalityOptions}
|
||
isClearable={true}
|
||
value={nationalityOptions.filter(
|
||
(o) => o.value === values.nationality,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value ?? null)
|
||
}
|
||
/>
|
||
)
|
||
}}
|
||
</Field>
|
||
</FormItem>
|
||
<FormItem label={translate('::Abp.Account.EducationLevel')}>
|
||
<Field type="text" name="educationLevel">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => {
|
||
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',
|
||
},
|
||
]
|
||
return (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={educationOptions}
|
||
isClearable={true}
|
||
value={educationOptions.filter(
|
||
(o) => o.value === values.educationLevel,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value ?? null)
|
||
}
|
||
/>
|
||
)
|
||
}}
|
||
</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')}>
|
||
<Field type="text" name="bloodType">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => {
|
||
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-' },
|
||
]
|
||
return (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={bloodTypeOptions}
|
||
isClearable={true}
|
||
value={bloodTypeOptions.filter(
|
||
(o) => o.value === values.bloodType,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value ?? null)
|
||
}
|
||
/>
|
||
)
|
||
}}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<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}
|
||
/>
|
||
) : (
|
||
<img
|
||
className="cropper max-h-[300px] max-w-[300px]"
|
||
src={image || AVATAR_URL(values.id, values.tenantId)}
|
||
/>
|
||
)}
|
||
<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}
|
||
>
|
||
<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"
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</FormItem>
|
||
</div>
|
||
</div>
|
||
</FormContainer>
|
||
</div>
|
||
|
||
<div className="mt-4 ltr:text-right">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</div>
|
||
</TabContent>
|
||
|
||
<TabContent value="permission">
|
||
<div className="px-4 py-6">
|
||
<Formik
|
||
initialValues={userDetails}
|
||
onSubmit={async (values, { setSubmitting }) => {
|
||
setSubmitting(true)
|
||
|
||
await putUserPermission({ ...values })
|
||
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Abp.Identity.User.SavePermission')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
|
||
getUser()
|
||
|
||
setSubmitting(false)
|
||
}}
|
||
>
|
||
{({ isSubmitting, values }) => {
|
||
const roles = values.roles
|
||
const branches = values.branches
|
||
|
||
return (
|
||
<Form>
|
||
<div>
|
||
<FormContainer size="md">
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
|
||
{/* Şube Management */}
|
||
<div>
|
||
<h6 className="mb-4">
|
||
{translate('::Abp.Identity.User.UserInformation.BranchManagement')}
|
||
</h6>
|
||
<div className="border-2 rounded-lg p-4">
|
||
<FieldArray name="branches">
|
||
{({ form, remove, push }) => (
|
||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 text-center">
|
||
{branches && branches.length > 0
|
||
? branches.map((_, index: number) => (
|
||
<div key={index} className="p-2">
|
||
<FormItem
|
||
labelClass="block text-center"
|
||
className="mb-0 justify-center"
|
||
label={branches[index].name}
|
||
>
|
||
<Field
|
||
className="mr-0"
|
||
name={`branches[${index}].isAssigned`}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
))
|
||
: null}
|
||
</div>
|
||
)}
|
||
</FieldArray>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Role Management */}
|
||
<div>
|
||
<h6 className="mb-4">
|
||
{translate('::Abp.Identity.User.UserInformation.RoleManagement')}
|
||
</h6>
|
||
<div className="border-2 rounded-lg p-4">
|
||
<FieldArray name="roles">
|
||
{({ form, remove, push }) => (
|
||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 text-center">
|
||
{roles && roles.length > 0
|
||
? roles.map((_, index: number) => (
|
||
<div key={index} className="p-2">
|
||
<FormItem
|
||
labelClass="block text-center"
|
||
className="mb-0 justify-center"
|
||
label={roles[index].name}
|
||
>
|
||
<Field
|
||
className="mr-0"
|
||
name={`roles[${index}].isAssigned`}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
))
|
||
: null}
|
||
</div>
|
||
)}
|
||
</FieldArray>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</FormContainer>
|
||
</div>
|
||
|
||
<div className="mt-4 ltr:text-right">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</div>
|
||
</TabContent>
|
||
|
||
<TabContent value="work">
|
||
<div className="px-4 py-6">
|
||
<Formik
|
||
initialValues={userDetails}
|
||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||
setSubmitting(true)
|
||
await putUserDetail({ ...values })
|
||
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Kaydet')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
|
||
getUser()
|
||
setSubmitting(false)
|
||
}}
|
||
>
|
||
{({ isSubmitting, values }) => {
|
||
const departments = values.departments
|
||
const jobPositions = values.jobPositions
|
||
|
||
const departmentOptions: SelectBoxOption[] = departments.map((department) => ({
|
||
value: department.id,
|
||
label: department.name,
|
||
}))
|
||
|
||
const jobPositionOptions: SelectBoxOptionWithGroup[] = jobPositions.map(
|
||
(jobPosition) => ({
|
||
value: jobPosition.id,
|
||
label: jobPosition.name,
|
||
group: jobPosition.departmentId,
|
||
}),
|
||
)
|
||
|
||
return (
|
||
<Form>
|
||
<div>
|
||
<FormContainer size="md">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||
<div>
|
||
<FormItem
|
||
label={translate(
|
||
'::Abp.Identity.User.UserInformation.DepartmentId',
|
||
)}
|
||
>
|
||
<Field type="text" name="departmentId">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={departmentOptions}
|
||
isClearable={true}
|
||
value={departmentOptions.filter(
|
||
(option) => option.value === values.departmentId,
|
||
)}
|
||
onChange={(option) => {
|
||
form.setFieldValue(field.name, option?.value)
|
||
form.setFieldValue('jobPositionId', null)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate(
|
||
'::Abp.Identity.User.UserInformation.JobPositionId',
|
||
)}
|
||
>
|
||
<Field type="text" name="jobPositionId">
|
||
{({ field, form }: FieldProps<SelectBoxOptionWithGroup>) => (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={jobPositionOptions.filter(
|
||
(option) => option.group === values.departmentId,
|
||
)}
|
||
isClearable={true}
|
||
value={jobPositionOptions.filter(
|
||
(option) => option.value === values.jobPositionId,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value)
|
||
}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.SskNo')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="sskNo"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.SskNo',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaIdCard className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.HireDate')}
|
||
>
|
||
<Field name="hireDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.HireDate',
|
||
)}
|
||
inputPrefix={<FaBriefcase className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate(
|
||
'::Abp.Identity.User.UserInformation.TerminationDate',
|
||
)}
|
||
>
|
||
<Field name="terminationDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.TerminationDate',
|
||
)}
|
||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
</div>
|
||
</FormContainer>
|
||
</div>
|
||
|
||
<div className="mt-4 ltr:text-right">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</div>
|
||
</TabContent>
|
||
|
||
<TabContent value="identity">
|
||
<div className="px-4 py-6">
|
||
<Formik
|
||
initialValues={userDetails}
|
||
onSubmit={async (values, { resetForm, setSubmitting }) => {
|
||
setSubmitting(true)
|
||
await putUserDetail({ ...values })
|
||
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Kaydet')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
|
||
getUser()
|
||
setSubmitting(false)
|
||
}}
|
||
>
|
||
{({ isSubmitting, values }) => {
|
||
const maritalStatusOptions: SelectBoxOption[] = [
|
||
{
|
||
value: 'Bekar',
|
||
label: translate('::App.MaritalStatus.Single') || 'Bekar',
|
||
},
|
||
{
|
||
value: 'Evli',
|
||
label: translate('::App.MaritalStatus.Married') || 'Evli',
|
||
},
|
||
{
|
||
value: 'Boşanmış',
|
||
label: translate('::App.MaritalStatus.Divorced') || 'Boşanmış',
|
||
},
|
||
{
|
||
value: 'Dul',
|
||
label: translate('::App.MaritalStatus.Widowed') || 'Dul',
|
||
},
|
||
]
|
||
|
||
return (
|
||
<Form>
|
||
<FormContainer size="md">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-5 w-full">
|
||
<div>
|
||
<FormItem
|
||
label={translate(
|
||
'::Abp.Identity.User.UserInformation.IdentityNumber',
|
||
)}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="identityNumber"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.IdentityNumber',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaIdCard className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.SerialNo')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="serialNo"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.SerialNo',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaHashtag className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.Province')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="province"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.Province',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaMapMarkerAlt className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.District')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="district"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.District',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaCity className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.Village')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="village"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.Village',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaHome className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.VolumeNo')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="volumeNo"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.VolumeNo',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaBook className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate(
|
||
'::Abp.Identity.User.UserInformation.FamilySequenceNo',
|
||
)}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="familySequenceNo"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.FamilySequenceNo',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaUsers className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.SequenceNo')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="sequenceNo"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.SequenceNo',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaHashtag className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.IssuedPlace')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="issuedPlace"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.IssuedPlace',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaMapPin className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.IssuedDate')}
|
||
>
|
||
<Field name="issuedDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.IssuedDate',
|
||
)}
|
||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.BirthPlace')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="birthPlace"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.BirthPlace',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaMapMarkerAlt className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.BirthDate')}
|
||
>
|
||
<Field name="birthDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.BirthDate',
|
||
)}
|
||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.FatherName')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="fatherName"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.FatherName',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaUserTie className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.MotherName')}
|
||
>
|
||
<Field
|
||
type="text"
|
||
name="motherName"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.MotherName',
|
||
)}
|
||
component={Input}
|
||
prefix={<FaFemale className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.MaritalStatus')}
|
||
>
|
||
<Field type="text" name="maritalStatus">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={maritalStatusOptions}
|
||
isClearable={true}
|
||
value={maritalStatusOptions.filter(
|
||
(o) => o.value === values.maritalStatus,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value ?? null)
|
||
}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
|
||
<div>
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.UserInformation.MarriageDate')}
|
||
>
|
||
<Field name="marriageDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.UserInformation.MarriageDate',
|
||
)}
|
||
inputPrefix={<FaHeart className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
</div>
|
||
</div>
|
||
</FormContainer>
|
||
|
||
<div className="mt-4 ltr:text-right">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</div>
|
||
</TabContent>
|
||
|
||
<TabContent value="lockout">
|
||
<div className="px-4 py-6">
|
||
<Formik
|
||
initialValues={userDetails}
|
||
onSubmit={async (values, { setSubmitting }) => {
|
||
setSubmitting(true)
|
||
|
||
await putUserLookout({ ...values })
|
||
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Abp.Identity.User.SaveLockout')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
|
||
getUser()
|
||
|
||
setSubmitting(false)
|
||
}}
|
||
>
|
||
{({ isSubmitting, values }) => {
|
||
const workHours = values.workHours
|
||
|
||
const workHourOptions: SelectBoxOption[] = workHours.map((workHour) => ({
|
||
value: workHour.name,
|
||
label: workHour.name,
|
||
}))
|
||
|
||
return (
|
||
<Form>
|
||
<FormContainer size="md">
|
||
<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">
|
||
{translate('::Abp.Identity.User.LockoutManagement.AccountStatus')}
|
||
</h6>
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate('::Abp.Identity.User.LockoutManagement.Status')}
|
||
>
|
||
<Field
|
||
name="isActive"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.Status',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AdminVerification',
|
||
)}
|
||
>
|
||
<Field
|
||
name="isVerified"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AdminVerification',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
|
||
{isRequireConfirmedEmail?.toLowerCase() === 'true' && (
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.EmailConfirmed',
|
||
)}
|
||
>
|
||
<Field
|
||
name="emailConfirmed"
|
||
disabled={isRequireConfirmedEmail?.toLowerCase() !== 'true'}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.EmailConfirmed',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
)}
|
||
|
||
{isRequireConfirmedPhoneNumber?.toLowerCase() === 'true' && (
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.PhoneNumberConfirmed',
|
||
)}
|
||
>
|
||
<Field
|
||
name="phoneNumberConfirmed"
|
||
disabled={isRequireConfirmedPhoneNumber?.toLowerCase() !== 'true'}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.PhoneNumberConfirmed',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
)}
|
||
|
||
{isTwoFactorEnabled?.toLowerCase() === 'true' && (
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.TwoFactorEnabled',
|
||
)}
|
||
>
|
||
<Field
|
||
name="twoFactorEnabled"
|
||
disabled={isTwoFactorEnabled?.toLowerCase() !== 'true'}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.TwoFactorEnabled',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
)}
|
||
</div>
|
||
|
||
{/* Login & Lockout Settings */}
|
||
<div>
|
||
<h6 className="mb-4">
|
||
{translate(
|
||
'::Abp.Identity.User.LockoutManagement.LoginAndLockoutSettings',
|
||
)}
|
||
</h6>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountLockoutEnabled',
|
||
)}
|
||
>
|
||
<Field
|
||
name="lockoutEnabled"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountLockoutEnabled',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountLocked',
|
||
)}
|
||
>
|
||
<Field
|
||
name="lockUser"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountLocked',
|
||
)}
|
||
component={Checkbox}
|
||
disabled={values.lockoutEnabled === false}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountEndDate',
|
||
)}
|
||
>
|
||
<Field name="lockoutEnd">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccountEndDate',
|
||
)}
|
||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||
onChange={(date) => {
|
||
form.setFieldValue(
|
||
field.name,
|
||
date ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss') : null,
|
||
)
|
||
}}
|
||
disabled={values.lockoutEnabled === false}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.ShouldChangePwOnNextLogin',
|
||
)}
|
||
>
|
||
<Field
|
||
name="shouldChangePasswordOnNextLogin"
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.ShouldChangePwOnNextLogin',
|
||
)}
|
||
component={Checkbox}
|
||
/>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.AccessFailedCount',
|
||
)}
|
||
>
|
||
<Field
|
||
type="number"
|
||
name="accessFailedCount"
|
||
component={Input}
|
||
prefix={<FaExclamationTriangle className="text-xl" />}
|
||
/>
|
||
</FormItem>
|
||
</div>
|
||
|
||
{/* Login & Lockout Settings */}
|
||
<div>
|
||
<h6 className="mb-4">
|
||
{translate('::Abp.Identity.User.LockoutManagement.ConnectionsTimes')}
|
||
</h6>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate('::Abp.Identity.User.LockoutManagement.WorkHour')}
|
||
>
|
||
<Field type="text" name="workHour">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={workHourOptions}
|
||
isClearable={true}
|
||
value={workHourOptions.filter(
|
||
(option) => option.value === values.workHour,
|
||
)}
|
||
onChange={(option) =>
|
||
form.setFieldValue(field.name, option?.value)
|
||
}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
layout="horizontal"
|
||
labelClass="!justify-start"
|
||
labelWidth="50%"
|
||
label={translate(
|
||
'::Abp.Identity.User.LockoutManagement.LoginEndDate',
|
||
)}
|
||
>
|
||
<Field name="loginEndDate">
|
||
{({ field, form }: FieldProps) => (
|
||
<DatePicker
|
||
field={field}
|
||
form={form}
|
||
value={field.value ? dayjs(field.value).toDate() : null}
|
||
placeholder={translate(
|
||
'::Abp.Identity.User.LockoutManagement.LoginEndDate',
|
||
)}
|
||
inputPrefix={<FaCalendarAlt className="text-xl" />}
|
||
onChange={(date) => {
|
||
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.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>
|
||
|
||
<div className="mt-4 ltr:text-right">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
</div>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</div>
|
||
</TabContent>
|
||
|
||
<TabContent value="claimTypes">
|
||
<div className="px-4 py-6">
|
||
<Table compact>
|
||
<THead>
|
||
<Tr>
|
||
<Th className="text-center">
|
||
<Button
|
||
shape="circle"
|
||
variant="plain"
|
||
type="button"
|
||
size="sm"
|
||
title="Add"
|
||
icon={<FaFileAlt />}
|
||
onClick={async (e) => {
|
||
setOpen(true)
|
||
}}
|
||
/>
|
||
</Th>
|
||
<Th>{translate('::Abp.Identity.User.ClaimType')}</Th>
|
||
<Th>{translate('::Abp.Identity.User.ClaimValue')}</Th>
|
||
</Tr>
|
||
</THead>
|
||
<TBody>
|
||
{userDetails.claims.filter((a) => a.isAssigned === true) &&
|
||
userDetails.claims.filter((a) => a.isAssigned === true).length > 0 ? (
|
||
userDetails.claims.map((claim) => (
|
||
<Tr key={claim.id}>
|
||
<Td>
|
||
<Button
|
||
shape="circle"
|
||
variant="plain"
|
||
type="button"
|
||
size="sm"
|
||
title="Delete"
|
||
icon={<FaTrashAlt />}
|
||
onClick={() => setConfirmDeleteClaim(claim)}
|
||
/>
|
||
</Td>
|
||
<Td>{claim.claimType}</Td>
|
||
<Td>{claim.claimValue}</Td>
|
||
</Tr>
|
||
))
|
||
) : (
|
||
<Tr>
|
||
<Td colSpan={3} className="text-center">
|
||
{translate('::Abp.Identity.User.NoClaimsFound')}
|
||
</Td>
|
||
</Tr>
|
||
)}
|
||
</TBody>
|
||
</Table>
|
||
</div>
|
||
</TabContent>
|
||
</Tabs>
|
||
</AdaptableCard>
|
||
|
||
<Dialog isOpen={open} onClose={() => setOpen(false)} onRequestClose={() => setOpen(false)}>
|
||
<Formik
|
||
initialValues={{ claimType: '', claimValue: '' }}
|
||
validationSchema={scheme}
|
||
onSubmit={handleSubmit}
|
||
>
|
||
{({ touched, errors, values, isSubmitting }) => {
|
||
const claimOptions: SelectBoxOption[] = userDetails?.claims.map((claim) => ({
|
||
value: claim.claimType,
|
||
label: claim.claimType,
|
||
}))
|
||
|
||
return (
|
||
<Form>
|
||
<FormContainer size="sm">
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.ClaimType')}
|
||
invalid={errors.claimType && touched.claimType}
|
||
errorMessage={errors.claimType}
|
||
>
|
||
<Field type="text" name="claimType">
|
||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||
<Select
|
||
field={field}
|
||
form={form}
|
||
options={claimOptions}
|
||
isClearable={true}
|
||
value={claimOptions.filter((option) => option.value === values.claimType)}
|
||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
||
/>
|
||
)}
|
||
</Field>
|
||
</FormItem>
|
||
|
||
<FormItem
|
||
label={translate('::Abp.Identity.User.ClaimValue')}
|
||
invalid={errors.claimValue && touched.claimValue}
|
||
errorMessage={errors.claimValue}
|
||
>
|
||
<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">
|
||
<Button variant="solid" loading={isSubmitting} type="submit">
|
||
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||
</Button>
|
||
<Button type="button" variant="plain" onClick={() => setOpen(false)}>
|
||
{translate('::Cancel')}
|
||
</Button>
|
||
</div>
|
||
</FormContainer>
|
||
</Form>
|
||
)
|
||
}}
|
||
</Formik>
|
||
</Dialog>
|
||
|
||
<ConfirmDialog
|
||
isOpen={!!confirmDeleteClaim}
|
||
type="danger"
|
||
title={translate('::DeleteConfirmation')}
|
||
confirmText={translate('::Delete')}
|
||
cancelText={translate('::Cancel')}
|
||
confirmButtonColor="red-600"
|
||
onCancel={() => setConfirmDeleteClaim(null)}
|
||
onConfirm={async () => {
|
||
if (confirmDeleteClaim) {
|
||
await deleteClaimUser(confirmDeleteClaim.id, userId)
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::Abp.Identity.User.ClaimDeleted')}
|
||
</Notification>,
|
||
{ placement: 'top-end' },
|
||
)
|
||
setConfirmDeleteClaim(null)
|
||
getUser()
|
||
}
|
||
}}
|
||
>
|
||
<p>
|
||
<span className="font-semibold">{confirmDeleteClaim?.claimType}</span> claim silmek
|
||
istediğinize emin misiniz?
|
||
</p>
|
||
</ConfirmDialog>
|
||
</>
|
||
) : (
|
||
<></>
|
||
)
|
||
}
|
||
|
||
export default UserDetails
|