Kullanıcılara UserType sütunu eklendi

This commit is contained in:
Sedat Öztürk 2026-07-31 20:55:45 +03:00
parent 3d2b38407b
commit 3a03c8e1e8
20 changed files with 321 additions and 98 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Volo.Abp.ObjectExtending;
using static Sozsoft.Platform.PlatformConsts;
namespace Sozsoft.Platform.Identity.Dto;
@ -20,6 +21,7 @@ public class UserInfoViewModel : ExtensibleObject
public bool LockoutEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public DateTime? LoginEndDate { get; set; }
public UserType UserType { get; set; }
public bool IsVerified { get; set; }
public List<string> userRoleNames { get; set; }
public AssignedRoleViewModel[] Roles { get; set; }

View file

@ -1,4 +1,5 @@
using System;
using static Sozsoft.Platform.PlatformConsts;
namespace Sozsoft.Platform.ListForms.DynamicApi;
@ -17,5 +18,6 @@ public class CreateUpdateUserInput
public bool? IsActive { get; set; }
public bool? LockoutEnabled { get; set; }
public string[] RoleNames { get; set; }
public UserType UserType { get; set; }
}

View file

@ -8,6 +8,7 @@ using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Extensions;
using Sozsoft.Platform.Identity.Dto;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using OpenIddict.Abstractions;
@ -287,8 +288,9 @@ public class PlatformIdentityAppService : ApplicationService
user.SetGraduationSchool(UserInfo.GraduationSchool);
user.SetBloodType(UserInfo.BloodType);
user.SetAvatar(UserInfo.Avatar);
user.SetUserType(UserInfo.UserType);
await UserManager.UpdateAsync(user);
(await UserManager.UpdateAsync(user)).CheckErrors();
}
[Authorize(IdentityPermissions.Users.Update)]

View file

@ -11,6 +11,7 @@ public static class UserInfoViewModelMappingExtensions
{
public static UserInfoViewModel MapIdentityExtraProperties(this UserInfoViewModel userInfoViewModel, IdentityUser user)
{
userInfoViewModel.UserType = user.GetUserType();
userInfoViewModel.IsVerified = user.GetIsVerified();
userInfoViewModel.LoginEndDate = user.GetLoginEndDate();
userInfoViewModel.Avatar = user.GetAvatar();

View file

@ -127,6 +127,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
user.SetDepartmentId(ParseGuid(input.Data.DepartmentId));
user.SetJobPositionId(ParseGuid(input.Data.JobPositionId));
user.SetIsVerified(verify);
user.SetUserType(input.Data.UserType);
await SaveAvatarAsync(user, input.Data.Avatar);
@ -199,6 +200,8 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
user.SetJobPositionId(ParseGuid(input.Data.JobPositionId));
}
user.SetUserType(input.Data.UserType);
await SaveAvatarAsync(user, input.Data.Avatar);
(await userManager.UpdateAsync(user)).CheckErrors();

View file

@ -2502,6 +2502,24 @@
"en": "Education Level",
"tr": "Eğitim Seviyesi"
},
{
"resourceName": "Platform",
"key": "Abp.Account.UserType",
"en": "User Type",
"tr": "Kullanıcı Tipi"
},
{
"resourceName": "Platform",
"key": "Abp.Account.UserType.Internal",
"en": "Internal",
"tr": "İç Kullanıcı"
},
{
"resourceName": "Platform",
"key": "Abp.Account.UserType.External",
"en": "External",
"tr": "Dış Kullanıcı"
},
{
"resourceName": "Platform",
"key": "Abp.Account.Nationality",
@ -17454,6 +17472,12 @@
"en": "URL",
"tr": "URL"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.UserType",
"en": "User Type",
"tr": "Kullanıcı Tipi"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.UserName",

View file

@ -891,6 +891,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
},
}),
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
new() { FieldName = "UserType", FieldDbType = DbType.Int32, Value = "0", CustomValueType = FieldCustomValueTypeEnum.Value },
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value }
}),
WidgetsJson = JsonSerializer.Serialize(new List<WidgetEditDto>
@ -946,8 +947,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
#region Users Fields
await _listFormFieldRepository.InsertManyAsync(
[
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -964,8 +964,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -988,8 +987,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1012,8 +1010,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1036,8 +1033,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1060,8 +1056,41 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Int16,
FieldName = "UserType",
EditGroupOrderNo = 1,
EditOrderNo = 5,
EditorType2 = EditorTypes.dxSelectBox,
ColSpan = 1,
CaptionName = "App.Listform.ListformField.UserType",
Width = 0,
ListOrderNo = 5,
Visible = true,
IsActive = true,
LookupJson = JsonSerializer.Serialize(new LookupDto
{
DataSourceType = UiLookupDataSourceTypeEnum.StaticData,
DisplayExpr = "name",
ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
new () { Key=PlatformConsts.AbpIdentity.User.InternalUserTypeDefaultValue, Name="Internal" },
new () { Key=PlatformConsts.AbpIdentity.User.ExternalUserTypeDefaultValue, Name="External" },
}),
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1069,12 +1098,12 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
SourceDbType = DbType.String,
FieldName = "PhoneNumber",
EditGroupOrderNo = 1,
EditOrderNo = 5,
EditOrderNo = 6,
EditorType2 = EditorTypes.dxTextBox,
ColSpan = 1,
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
Width = 0,
ListOrderNo = 5,
ListOrderNo = 6,
Visible = true,
IsActive = true,
@ -1085,8 +1114,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1094,7 +1122,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
SourceDbType = DbType.String,
FieldName = "WorkHour",
EditGroupOrderNo = 1,
EditOrderNo = 6,
EditOrderNo = 7,
EditorType2 = EditorTypes.dxSelectBox,
ColSpan = 1,
EditorOptions = EditorOptionValues.ShowClearButton,
@ -1112,8 +1140,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1121,7 +1148,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
SourceDbType = DbType.Guid,
FieldName = "DepartmentId",
EditGroupOrderNo = 1,
EditOrderNo = 7,
EditOrderNo = 8,
EditorType2 = EditorTypes.dxSelectBox,
ColSpan = 1,
EditorOptions = EditorOptionValues.ShowClearButton,
@ -1145,8 +1172,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1154,7 +1180,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
SourceDbType = DbType.Guid,
FieldName = "JobPositionId",
EditGroupOrderNo = 1,
EditOrderNo = 8,
EditOrderNo = 9,
EditorType2 = EditorTypes.dxSelectBox,
ColSpan = 1,
EditorOptions = EditorOptionValues.ShowClearButton,
@ -1180,8 +1206,29 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Password",
EditGroupOrderNo = 1,
EditOrderNo = 10,
EditorType2 = EditorTypes.dxTextBox,
ColSpan = 1,
CaptionName = "App.Listform.ListformField.Password",
Width = 0,
ListOrderNo = 10,
Visible = false,
IsActive = false,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
@ -1189,29 +1236,11 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
SourceDbType = DbType.Boolean,
FieldName = "IsActive",
EditGroupOrderNo = 1,
EditOrderNo = 10,
EditOrderNo = 11,
EditorType2 = EditorTypes.dxCheckBox,
ColSpan = 1,
CaptionName = "App.Listform.ListformField.IsActive",
Width = 0,
ListOrderNo = 10,
Visible = true,
IsActive = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean,
FieldName = "IsVerified",
CaptionName = "App.Listform.ListformField.IsVerified",
Width = 0,
ListOrderNo = 11,
Visible = true,
IsActive = true,
@ -1220,15 +1249,14 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean,
FieldName = "EmailConfirmed",
CaptionName = "App.Listform.ListformField.EmailConfirmed",
FieldName = "IsVerified",
CaptionName = "App.Listform.ListformField.IsVerified",
Width = 0,
ListOrderNo = 12,
Visible = true,
@ -1238,15 +1266,14 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Boolean,
FieldName = "TwoFactorEnabled",
CaptionName = "App.Listform.ListformField.TwoFactorEnabled",
FieldName = "EmailConfirmed",
CaptionName = "App.Listform.ListformField.EmailConfirmed",
Width = 0,
ListOrderNo = 13,
Visible = true,
@ -1256,23 +1283,18 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{
new ListFormField {
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Password",
EditGroupOrderNo = 1,
EditOrderNo = 9,
EditorType2 = EditorTypes.dxTextBox,
ColSpan = 1,
CaptionName = "App.Listform.ListformField.Password",
SourceDbType = DbType.Boolean,
FieldName = "TwoFactorEnabled",
CaptionName = "App.Listform.ListformField.TwoFactorEnabled",
Width = 0,
ListOrderNo = 14,
Visible = false,
IsActive = false,
Visible = true,
IsActive = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),

View file

@ -155,6 +155,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
adminUser.SetDepartmentId(PlatformConsts.AbpIdentity.User.AdminDepartmentIdDefaultValue);
adminUser.SetJobPositionId(PlatformConsts.AbpIdentity.User.AdminJobPositionIdDefaultValue);
adminUser.SetAvatar(GetAvatar(tenantId?.ToString(), adminUser.Id.ToString()));
adminUser.SetUserType(PlatformConsts.AbpIdentity.User.InternalUserTypeDefaultValue);
(await UserManager.CreateAsync(adminUser, adminPassword, validatePassword: false)).CheckErrors();
result.CreatedAdminUser = true;

View file

@ -15,6 +15,12 @@ public static class PlatformConsts
public const string React = "UI";
public const char MultiValueDelimiter = '|';
public enum UserType
{
Internal,
External
}
public static class EditorOptionValues
{
public static string Disabled = "{ \"disabled\" : true }";
@ -119,6 +125,7 @@ public static class PlatformConsts
public const string Surname = "Surname";
public const string LoginEndDate = "LoginEndDate";
public const string RoleNames = "RoleNames";
public const string UserType = "UserType";
public const string IsVerified = "IsVerified";
public const string Avatar = "Avatar";
public const string RocketUsername = "RocketUsername";
@ -188,6 +195,9 @@ public static class PlatformConsts
public static readonly DateTime AdminBirthDateDefaultValue = new(1981, 8, 27);
public const string AdminBirthPlaceDefaultValue = "Kadıköy, İstanbul";
public const string AdminMaritalStatusDefaultValue = "Evli";
public const UserType InternalUserTypeDefaultValue = PlatformConsts.UserType.Internal;
public const UserType ExternalUserTypeDefaultValue = PlatformConsts.UserType.External;
}
public static class OrganizationUnits

View file

@ -11,6 +11,16 @@ public static class AbpIdentityUserExtensions
return $"{user.Name} {user.Surname}".Trim();
}
//UserType
public static void SetUserType(this IdentityUser user, PlatformConsts.UserType userType)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.UserType, userType);
}
public static PlatformConsts.UserType GetUserType(this IdentityUser user)
{
return user.GetProperty<PlatformConsts.UserType>(PlatformConsts.AbpIdentity.User.UserType);
}
//IsVerified
public static void SetIsVerified(this IdentityUser user, bool isVerified)
{

View file

@ -5,6 +5,7 @@ using Volo.Abp.ObjectExtending;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.Threading;
using static Sozsoft.Platform.PlatformConsts;
namespace Sozsoft.Platform.EntityFrameworkCore;
@ -19,6 +20,15 @@ public static class PlatformEfCoreEntityExtensionMappings
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, UserType>(
PlatformConsts.AbpIdentity.User.UserType,
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.IsRequired().HasDefaultValue(PlatformConsts.AbpIdentity.User.InternalUserTypeDefaultValue);
}
);
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, bool>(
PlatformConsts.AbpIdentity.User.IsVerified,

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260730174943_Initial")]
[Migration("20260731143824_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -7801,6 +7801,11 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("nvarchar(256)")
.HasColumnName("UserName");
b.Property<int>("UserType")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("Village")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");

View file

@ -462,6 +462,7 @@ namespace Sozsoft.Platform.Migrations
SerialNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
SskNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
TerminationDate = table.Column<DateTime>(type: "datetime2", nullable: true),
UserType = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
Village = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
VolumeNo = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
WorkHour = table.Column<string>(type: "nvarchar(max)", nullable: true),

View file

@ -7798,6 +7798,11 @@ namespace Sozsoft.Platform.Migrations
.HasColumnType("nvarchar(256)")
.HasColumnName("UserName");
b.Property<int>("UserType")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasDefaultValue(0);
b.Property<string>("Village")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");

View file

@ -153,6 +153,7 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
adminUser.SetDepartmentId(PlatformConsts.AbpIdentity.User.AdminDepartmentIdDefaultValue);
adminUser.SetJobPositionId(PlatformConsts.AbpIdentity.User.AdminJobPositionIdDefaultValue);
adminUser.SetAvatar(GetAvatar(tenantId?.ToString(), adminUser.Id.ToString()));
adminUser.SetUserType(PlatformConsts.AbpIdentity.User.InternalUserTypeDefaultValue);
(await UserManager.CreateAsync(adminUser, adminPassword, validatePassword: false)).CheckErrors();
result.CreatedAdminUser = true;

View file

@ -308,13 +308,19 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false);
var sessions = await identitySessionRepository.GetListAsync();
// Bu kullanıcı hariç diğer distinct aktif kullanıcı sayısını hesapla.
// Kullanıcının kendi oturumu varsa (refresh) o kişi tekrar sayılmaz.
var otherActiveUserCount = sessions
var activeUserIds = sessions
.Where(s => s.UserId != user.Id)
.Select(s => s.UserId)
.Distinct()
.Count();
.ToList();
// Aktif session kullanıcılarını IdentityUserManager üzerinden topluca yükle
// ve yalnızca UserType = 0 (Internal) olanları concurrent limite dahil et.
var otherActiveUserCount = (await userManager.Users
.Where(u => activeUserIds.Contains(u.Id))
.ToListAsync())
.Count(u => u.GetUserType() == PlatformConsts.UserType.Internal);
if (otherActiveUserCount >= maxConcurrentUsers)
{

View file

@ -122,6 +122,7 @@ export interface UserInfoViewModel extends ExtensibleObject {
lockoutEnabled: boolean
lockoutEnd?: string
loginEndDate?: Date | string
userType: number
isVerified: boolean
userRoleNames: string[]
roles: AssignedRoleViewModel[]

View file

@ -8,3 +8,8 @@ export type SelectBoxOptionWithGroup = {
label?: string
group?: string
}
export type SelectBoxOptionByNumber = {
value?: number
label?: string
}

View file

@ -76,7 +76,11 @@ import {
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 {
SelectBoxOption,
SelectBoxOptionWithGroup,
SelectBoxOptionByNumber,
} from '@/types/shared'
import { AdaptableCard, ConfirmDialog } from '@/components/shared'
import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models'
import { APP_NAME, AVATAR_URL } from '@/constants/app.constant'
@ -254,7 +258,7 @@ function UserDetails() {
</TabNav>
</TabList>
<TabContent value="user">
<div className="px-4 py-6">
<div className="px-4 py-2">
<Formik
enableReinitialize
initialValues={userDetails}
@ -376,14 +380,33 @@ function UserDetails() {
/>
</FormItem>
<FormItem size="sm" label={translate('::RocketUsername')}>
<Field
type="text"
name="rocketUsername"
placeholder={translate('::RocketUsername')}
component={Input}
prefix={<FaFacebookMessenger className="text-xl" />}
<FormItem label={translate('::Abp.Account.UserType')}>
<Field type="number" name="userType">
{({ field, form }: FieldProps<SelectBoxOptionByNumber>) => {
const userTypes: SelectBoxOptionByNumber[] = [
{
value: 0,
label: translate('::Abp.Account.UserType.Internal'),
},
{
value: 1,
label: translate('::Abp.Account.UserType.External'),
},
]
return (
<Select
menuPlacement='top'
field={field}
form={form}
options={userTypes}
value={userTypes.filter((o) => o.value === values.userType)}
onChange={(option) =>
form.setFieldValue(field.name, option?.value ?? null)
}
/>
)
}}
</Field>
</FormItem>
</div>
@ -510,6 +533,7 @@ function UserDetails() {
]
return (
<Select
menuPlacement='top'
field={field}
form={form}
options={bloodTypeOptions}
@ -600,7 +624,7 @@ function UserDetails() {
</TabContent>
<TabContent value="permission">
<div className="px-4 py-6">
<div className="px-4 py-2">
<Formik
enableReinitialize
initialValues={userDetails}
@ -712,7 +736,7 @@ function UserDetails() {
</TabContent>
<TabContent value="work">
<div className="px-4 py-6">
<div className="px-4 py-">
<Formik
enableReinitialize
initialValues={userDetails}
@ -808,6 +832,18 @@ function UserDetails() {
</FormItem>
</div>
<div>
<FormItem size="sm" label={translate('::RocketUsername')}>
<Field
type="text"
name="rocketUsername"
placeholder={translate('::RocketUsername')}
component={Input}
prefix={<FaFacebookMessenger className="text-xl" />}
/>
</FormItem>
</div>
<div>
<FormItem
label={translate('::Abp.Identity.User.UserInformation.SskNo')}
@ -894,7 +930,7 @@ function UserDetails() {
</TabContent>
<TabContent value="identity">
<div className="px-4 py-6">
<div className="px-4 py-2">
<Formik
enableReinitialize
initialValues={userDetails}
@ -1251,7 +1287,7 @@ function UserDetails() {
</TabContent>
<TabContent value="lockout">
<div className="px-4 py-6">
<div className="px-4 py-2">
<Formik
enableReinitialize
initialValues={userDetails}
@ -1639,7 +1675,7 @@ function UserDetails() {
</TabContent>
<TabContent value="claimTypes">
<div className="px-4 py-6">
<div className="px-4 py-2">
<Table compact>
<THead>
<Tr>

View file

@ -6,11 +6,12 @@ import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
import {
ColumnFormatDto,
DbTypeEnum,
FieldCustomValueTypeEnum,
GridDto,
PlatformEditorTypes,
SubFormTabTypeEnum,
} from '@/proxy/form/models'
import { getList } from '@/services/form.service'
import { getList, getNextSequenceValue } from '@/services/form.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { usePermission } from '@/utils/hooks/usePermission'
import { useStoreActions, useStoreState } from '@/store'
@ -32,6 +33,7 @@ import DxCardView, {
ToolbarItem,
} from 'devextreme-react/card-view'
import CustomStore from 'devextreme/data/custom_store'
import type { DataType } from 'devextreme/common'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createRoot } from 'react-dom/client'
import { Helmet } from 'react-helmet'
@ -42,6 +44,7 @@ import { GridExtraFilterToolbar } from './GridExtraFilterToolbar'
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
import {
autoNumber,
extractSearchParamsFields,
getEditingFormGroups,
GridExtraFilterState,
@ -417,7 +420,9 @@ const CardView = (props: CardViewProps) => {
const [isPopupFullScreen, setIsPopupFullScreen] = useState(false)
const [formData, setFormData] = useState<Record<string, any>>()
const formDataRef = useRef<Record<string, any>>()
useEffect(() => {
formDataRef.current = formData
}, [formData])
const lookupDisplayValuesRef = useRef<Record<string, Map<string, string>>>({})
const [lookupReadyState, setLookupReadyState] = useState({ listFormCode, ready: false })
const [notePanelTarget, setNotePanelTarget] = useState<{
@ -689,7 +694,6 @@ const CardView = (props: CardViewProps) => {
nextData[childField] = null
})
formDataRef.current = nextData
setFormData(nextData)
},
[cascadeChildrenByParent],
)
@ -859,7 +863,7 @@ const CardView = (props: CardViewProps) => {
...editorOptions,
dataSource:
typeof lookup.dataSource === 'function'
? lookup.dataSource({ data: formData ?? {} })
? lookup.dataSource({ data: formDataRef.current ?? {} })
: lookup.dataSource,
displayExpr: lookup.displayExpr,
valueExpr: lookup.valueExpr,
@ -892,7 +896,7 @@ const CardView = (props: CardViewProps) => {
: undefined,
}
},
[formData, gridDto, imageUploadFormTemplate, imageViewerFormTemplate, mode, translate],
[gridDto, imageUploadFormTemplate, imageViewerFormTemplate, mode, translate],
)
const editingFormItems = useMemo(() => {
@ -1009,6 +1013,86 @@ const CardView = (props: CardViewProps) => {
const editingOptions = gridDto?.gridOptions.editingOptionDto
const selectionOptions = gridDto?.gridOptions.selectionDto
const onInitNewCard = useCallback(
(event: CardViewTypes.InitNewCardEvent<Record<string, any>>) => {
const nextFormData = event.data as Record<string, any>
formDataRef.current = nextFormData
setMode('new')
setIsPopupFullScreen(useMobileEditPopup || (editingOptions?.popup?.fullScreen ?? false))
event.promise = (async () => {
const rawFilter = searchParamsRef.current?.get('filter')
let filters: any[] = []
if (rawFilter) {
try {
filters = extractSearchParamsFields(JSON.parse(rawFilter))
} catch (error) {
console.error('Filter parse edilemedi:', error)
}
}
for (const column of gridDto?.columnFormats ?? []) {
const fieldName = column.fieldName
if (!fieldName || extraFilters.some((filter) => filter.fieldName === fieldName)) {
continue
}
if (column.defaultValue !== null && column.defaultValue !== undefined) {
if (typeof column.defaultValue === 'string' && column.defaultValue === '@AUTONUMBER') {
nextFormData[fieldName] = autoNumber()
} else if (column.defaultValueType === FieldCustomValueTypeEnum.Sequence) {
try {
const response = await getNextSequenceValue(String(column.defaultValue))
nextFormData[fieldName] = response.data
} catch (error) {
console.error('Sequence default value alınamadı:', {
fieldName,
defaultValue: column.defaultValue,
error,
})
}
} else {
nextFormData[fieldName] = column.defaultValue
}
}
const fieldMatch = filters.find(([field]) => field === fieldName)
if (!fieldMatch) continue
const value = fieldMatch[2]
switch (column.dataType as DataType) {
case 'date':
case 'datetime':
nextFormData[fieldName] = new Date(value)
break
case 'number':
nextFormData[fieldName] = Number(value)
break
case 'boolean':
nextFormData[fieldName] = value === true || value === 'true'
break
case 'object':
try {
nextFormData[fieldName] = JSON.parse(value)
} catch {
nextFormData[fieldName] = value
}
break
default:
nextFormData[fieldName] = value
break
}
}
formDataRef.current = nextFormData
setFormData({ ...nextFormData })
})()
},
[editingOptions?.popup?.fullScreen, extraFilters, gridDto, useMobileEditPopup],
)
const onSelectionChanged = useCallback(
(event: CardViewTypes.SelectionChangedEvent) => {
const selectedCardsData = event.selectedCardsData as Record<string, any>[]
@ -1074,15 +1158,7 @@ const CardView = (props: CardViewProps) => {
wordWrapEnabled={gridDto.gridOptions.rowDto?.whiteSpace !== 'nowrap'}
onContentReady={onContentReady}
onSelectionChanged={onSelectionChanged}
onInitNewCard={(event) => {
const nextFormData = (event.data as Record<string, any>) ?? {}
formDataRef.current = nextFormData
setFormData(nextFormData)
setMode('new')
setIsPopupFullScreen(
useMobileEditPopup || (editingOptions?.popup?.fullScreen ?? false),
)
}}
onInitNewCard={onInitNewCard}
onEditingStart={(event) => {
const nextFormData = (event.data as Record<string, any>) ?? {}
formDataRef.current = nextFormData