Tenantlı uygulama için Login düzenlemesi

This commit is contained in:
Sedat ÖZTÜRK 2026-06-02 21:47:01 +03:00
parent 67286232da
commit f9c5910813
25 changed files with 226 additions and 131 deletions

View file

@ -19,7 +19,6 @@
<ItemGroup>
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>
</Project>

View file

@ -14,7 +14,6 @@
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>

View file

@ -31,6 +31,8 @@ public class MailTrackingManager : DomainService
/// <returns></returns>
public async Task StartAsync()
{
// https://us-east-1.console.aws.amazon.com/iam/home?region=eu-central-1#/users
// https://eu-central-1.console.aws.amazon.com/ses/home?region=eu-central-1#/identities/system%40sozsoft.com?tabId=authentication
var accessKey = configuration.GetValue<string>(AmazonSesEmailSettingNames.AccessKey);
var accessKeyId = configuration.GetValue<string>(AmazonSesEmailSettingNames.AccessKeyId);
var region = configuration.GetValue<string>(AmazonSesEmailSettingNames.Region);

View file

@ -30,7 +30,6 @@
<PackageReference Include="Volo.Abp.TextTemplating.Razor" Version="10.0.0" />
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>

View file

@ -14,7 +14,6 @@
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>

View file

@ -65,6 +65,7 @@ public class AmazonSesEmailSender : EmailSenderBase, ISozsoftEmailSender, ITrans
await BackgroundJobManager.EnqueueAsync(
new ErpBackgroundEmailSendingJobArgs
{
TenantId = CurrentTenant.Id,
To = to,
Sender = sender,
Params = @params,

View file

@ -1,5 +1,6 @@
using Volo.Abp.BackgroundJobs;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Sender.Mail;
@ -7,13 +8,19 @@ public class ErpBackgroundEmailSendingJob :
AsyncBackgroundJob<ErpBackgroundEmailSendingJobArgs>, ITransientDependency
{
protected ISozsoftEmailSender EmailSender { get; }
protected ICurrentTenant CurrentTenant { get; }
public ErpBackgroundEmailSendingJob(ISozsoftEmailSender emailSender)
public ErpBackgroundEmailSendingJob(
ISozsoftEmailSender emailSender,
ICurrentTenant currentTenant)
{
EmailSender = emailSender;
CurrentTenant = currentTenant;
}
public override async Task ExecuteAsync(ErpBackgroundEmailSendingJobArgs args)
{
using (CurrentTenant.Change(args.TenantId))
{
//await EmailSender.SendEmailAsync(args.Template, args.To, args.Params, args.Subject);
await EmailSender.SendEmailAsync(
@ -25,4 +32,4 @@ public class ErpBackgroundEmailSendingJob :
args.Attachments);
}
}
}

View file

@ -1,8 +1,12 @@
namespace Sozsoft.Sender.Mail;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.Sender.Mail;
[Serializable]
public class ErpBackgroundEmailSendingJobArgs
public class ErpBackgroundEmailSendingJobArgs : IMultiTenant
{
public Guid? TenantId { get; set; }
public string[] To { get; set; }
public KeyValuePair<string, string>? Sender { get; set; }
@ -14,6 +18,4 @@ public class ErpBackgroundEmailSendingJobArgs
public Dictionary<string, string>? Attachments { get; set; }
public string? TextContent { get; set; }
}

View file

@ -14,7 +14,6 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>
</Project>

View file

@ -5,10 +5,9 @@ using System.Threading.Tasks;
using Sozsoft.Languages;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
using Volo.Abp.SettingManagement;
using Volo.Abp.Settings;
using SettingDefinition = Sozsoft.Settings.Entities.SettingDefinition;
namespace Sozsoft.Settings;
@ -20,17 +19,20 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
private readonly ISettingDefinitionManager settingDefinitionManager;
private readonly ISettingManager settingManager;
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
private readonly ICurrentTenant currentTenant;
public SettingUiAppService(
ILanguageKeyIntegrationService languageKeyIntegrationService,
ISettingDefinitionManager settingDefinitionManager,
ISettingManager settingManager,
ErpSettingDefinitionManager ErpSettingDefinitionManager)
ErpSettingDefinitionManager ErpSettingDefinitionManager,
ICurrentTenant currentTenant)
{
this.languageKeyIntegrationService = languageKeyIntegrationService;
this.settingDefinitionManager = settingDefinitionManager;
this.settingManager = settingManager;
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
this.currentTenant = currentTenant;
}
public virtual async Task<List<MainGroupedSettingDto>> GetListAsync()
@ -95,15 +97,18 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
{
if (setting.Providers.IsNullOrEmpty())
{
await settingManager.SetForCurrentUserAsync(setting.Name, value);
if (currentTenant.Id.HasValue)
{
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
}
else
{
if (setting.Providers.Any(p => p == UserSettingValueProvider.ProviderName))
{
await settingManager.SetForCurrentUserAsync(setting.Name, value);
await settingManager.SetGlobalAsync(setting.Name, value);
}
else if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName))
}
else
{
if (setting.Providers.Any(p => p == TenantSettingValueProvider.ProviderName) && currentTenant.Id.HasValue)
{
await settingManager.SetForCurrentTenantAsync(setting.Name, value);
}
@ -111,6 +116,10 @@ public class SettingUiAppService : ApplicationService, ISettingUiAppService
{
await settingManager.SetGlobalAsync(setting.Name, value);
}
else if (setting.Providers.Any(p => p == UserSettingValueProvider.ProviderName))
{
await settingManager.SetForCurrentUserAsync(setting.Name, value);
}
}
}

View file

@ -14,7 +14,6 @@
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>

View file

@ -14,7 +14,6 @@
</PackageReference>
<PackageReference Include="MailKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="MimeKit" Version="4.16.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="7.2.0" PrivateAssets="all" />
</ItemGroup>

View file

@ -258,7 +258,7 @@
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
"descriptionKey": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
"defaultValue": "SYSTEM@SOZSOFT.COM",
"defaultValue": "system@sozsoft.com",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -274,7 +274,7 @@
"code": "App.SiteManagement.General.TimedLoginEmails",
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
"descriptionKey": "App.SiteManagement.General.TimedLoginEmails.Description",
"defaultValue": "SYSTEM@SOZSOFT.COM",
"defaultValue": "system@sozsoft.com",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -466,7 +466,7 @@
"code": "Abp.Mailing.DefaultFromAddress",
"nameKey": "Abp.Mailing.DefaultFromAddress",
"descriptionKey": "Abp.Mailing.DefaultFromAddress.Description",
"defaultValue": "SYSTEM@SOZSOFT.COM",
"defaultValue": "system@sozsoft.com",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -482,7 +482,7 @@
"code": "Abp.Mailing.Smtp.UserName",
"nameKey": "Abp.Mailing.Smtp.UserName",
"descriptionKey": "Abp.Mailing.Smtp.UserName.Description",
"defaultValue": "SYSTEM@SOZSOFT.COM",
"defaultValue": "system@sozsoft.com",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -610,7 +610,7 @@
"code": "Abp.Mailing.AWS.AccessKey",
"nameKey": "Abp.Mailing.AWS.AccessKey",
"descriptionKey": "Abp.Mailing.AWS.AccessKey.Description",
"defaultValue": "aXW8L21rP6dPO6Txj76Be2FCpWRBa25EMrSAVL76",
"defaultValue": "SibFBAMiSApvz+NChYmlgZmx25JNbximemIDOFps",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -626,7 +626,7 @@
"code": "Abp.Mailing.AWS.AccessKeyId",
"nameKey": "Abp.Mailing.AWS.AccessKeyId",
"descriptionKey": "Abp.Mailing.AWS.AccessKeyId.Description",
"defaultValue": "AKIATULUYBLX4IY3S2P1",
"defaultValue": "AKIA5OCSDJB5KOQY74NV",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@ -722,7 +722,7 @@
"code": "Abp.Account.Captcha.SiteKey",
"nameKey": "Abp.Account.Captcha.SiteKey",
"descriptionKey": "Abp.Account.Captcha.SiteKey.Description",
"defaultValue": "0x4AAAAAAAGadwQME-GSYuJU",
"defaultValue": "0x4AAAAAABdEjmiXxcl0j7jp",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,
@ -738,7 +738,7 @@
"code": "Abp.Account.Captcha.SecretKey",
"nameKey": "Abp.Account.Captcha.SecretKey",
"descriptionKey": "Abp.Account.Captcha.SecretKey.Description",
"defaultValue": "0x4AAAAAAAGad_f_WP47IcNBs9FTu5DhNX8",
"defaultValue": "0x4AAAAAABdEjhw1A8sJZUvQX8-CgqvB3mE",
"isVisibleToClients": false,
"providers": "G|D",
"isInherited": false,

View file

@ -26,7 +26,7 @@ public class PlatformBackgroundWorkerTemplateDefinitionProvider : TemplateDefini
foreach (var worker in workers.Where(a =>
a.IsActive &&
a.WorkerType == WorkerTypeEnum.MailQueueWorker &&
a.Options != null && a.Options != string.Empty).ToList())
a.Options != null).ToList().Where(a => !a.Options.IsNullOrWhiteSpace()))
{
var Options = JsonSerializer.Deserialize<MailQueueWorkerOptions>(worker.Options);

View file

@ -12,7 +12,6 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
const { className, onError, onExpire, onSuccess } = props
return (
<>
<Turnstile
ref={ref}
className={className ?? 'mb-4 mx-auto'}
@ -21,7 +20,6 @@ const Captcha = forwardRef((props: CaptchaProps, ref: Ref<TurnstileInstance>) =>
onExpire={onExpire}
onSuccess={onSuccess}
/>
</>
)
})
Captcha.displayName = 'Captcha'

View file

@ -0,0 +1,148 @@
import Input from '@/components/ui/Input'
import { getTenantByNameDetail } from '@/services/tenant.service'
import { useStoreActions, useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { defaultDomain, getSubdomain } from '@/utils/subdomain'
import type { CSSProperties } from 'react'
import { useCallback, useEffect, useRef } from 'react'
const hiddenTenantStyle: CSSProperties = {
opacity: 0,
position: 'absolute',
pointerEvents: 'none',
height: 0,
margin: 0,
padding: 0,
border: 'none',
}
const TenantSelector = () => {
const { translate } = useLocalization()
const isMultiTenant = useStoreState((state) => state.abpConfig.config?.multiTenancy.isEnabled)
const tenantName = useStoreState((state) => state.locale.currentTenantName)
const { setTenantName } = useStoreActions((actions) => actions.locale)
const { setTenant } = useStoreActions((actions) => actions.auth.tenant)
const { setWarning } = useStoreActions((actions) => actions.base.messages)
const requestIdRef = useRef(0)
const lastRequestedTenantNameRef = useRef<string>()
const subDomainName = getSubdomain()
const isSubdomainTenant = !!subDomainName && subDomainName !== defaultDomain
const tenantStyle = isSubdomainTenant ? hiddenTenantStyle : undefined
const setWarningTimeout = useCallback(
(message: string) => {
setTimeout(() => {
setWarning(message)
}, 100)
},
[setWarning],
)
const redirectToMainDomain = useCallback(
(name: string) => {
setTenantName(undefined)
const parts = window.location.hostname.split('.')
const mainDomain = parts.length >= 3 ? parts.slice(1).join('.') : window.location.hostname
setWarningTimeout(
`"${name}" kurumuna ait kayıt bulunamadı.\nAna sayfaya yönlendiriliyorsunuz...`,
)
setTimeout(() => {
window.location.href = `${window.location.protocol}//${mainDomain}`
}, 3000)
},
[setTenantName, setWarningTimeout],
)
const fetchDataByName = useCallback(
async (name: string, isSubdomain = false) => {
if (!isSubdomain && name === lastRequestedTenantNameRef.current) {
return
}
lastRequestedTenantNameRef.current = name
const requestId = requestIdRef.current + 1
requestIdRef.current = requestId
if (name) {
try {
const response = await getTenantByNameDetail(name)
if (requestId !== requestIdRef.current) {
return
}
if (response.data) {
setTenant({
tenantId: response.data.id,
tenantName: response.data.name,
menuGroup: response.data.menuGroup,
})
} else {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} catch {
if (requestId !== requestIdRef.current) {
return
}
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} else {
setTenant(undefined)
}
},
[redirectToMainDomain, setTenant],
)
const handleTenantNameChange = (value: string) => {
setTenantName(value)
}
const handleTenantNameBlur = () => {
if (subDomainName) {
return
}
fetchDataByName(tenantName || '')
}
useEffect(() => {
if (!isMultiTenant) {
setTenant(undefined)
return
}
if (subDomainName) {
setTenantName(subDomainName)
fetchDataByName(subDomainName, true)
}
}, [fetchDataByName, isMultiTenant, setTenant, setTenantName, subDomainName])
if (!isMultiTenant) {
return null
}
return (
<>
<label className="form-label mb-2" style={tenantStyle}>
{translate('::Organization')}
</label>
<div className="mb-4">
<Input
placeholder={translate('::Organization')}
value={tenantName ?? ''}
onChange={(event) => handleTenantNameChange(event.target.value)}
onBlur={handleTenantNameBlur}
style={tenantStyle}
aria-hidden={isSubdomainTenant ? 'true' : 'false'}
autoFocus={!isSubdomainTenant}
/>
</div>
</>
)
}
export default TenantSelector

View file

@ -19,6 +19,7 @@ export { default as SegmentItemOption } from './SegmentItemOption'
export { default as StickyFooter } from './StickyFooter'
export { default as SvgIcon } from './SvgIcon'
export { default as TableRowSkeleton } from './loaders/TableRowSkeleton'
export { default as TenantSelector } from './TenantSelector'
export { default as TextBlockSkeleton } from './loaders/TextBlockSkeleton'
export { default as TextEllipsis } from './TextEllipsis'
export { default as UsersAvatarGroup } from './UsersAvatarGroup'

View file

@ -456,13 +456,15 @@ function OrgChartNode({
style={{ cursor: dragging ? 'grabbing' : 'grab' }}
>
{/* Header bar */}
<div data-header="" className={`${headerBg} rounded-t-xl px-3 py-2 flex items-center gap-2`}>
<div data-header="" className={`${headerBg} rounded-t-xl px-3 py-2 flex items-center gap-2 dark:bg-gray-900 dark:text-gray-100`}>
{mode === 'department' ? (
<FaBuilding className="w-3 h-3 text-white opacity-80 flex-shrink-0" />
) : (
<FaBriefcase className="w-3 h-3 text-white opacity-80 flex-shrink-0" />
)}
<span data-node-name="" className="text-white font-semibold text-xs truncate flex-1">{node.name}</span>
<span data-node-name="" className="text-white font-semibold text-xs truncate flex-1 dark:bg-gray-900 dark:text-gray-100">
{node.name}
</span>
{hasChildren && (
<button
data-stop-drag="true"
@ -514,7 +516,7 @@ function OrgChartNode({
{/* Child count badge */}
{hasChildren && (
<div className="absolute -bottom-2.5 left-1/2 -translate-x-1/2 bg-white border border-slate-200 rounded-full px-2 py-0.5 text-xs text-slate-500 shadow-sm whitespace-nowrap z-10">
<div className="absolute -bottom-2.5 left-1/2 -translate-x-1/2 bg-white border border-slate-200 rounded-full px-2 py-0.5 text-xs text-slate-500 shadow-sm whitespace-nowrap z-10 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-100">
{node.children.length}
</div>
)}

View file

@ -38,9 +38,9 @@ const LoginHistoryIcon = ({ type }: { type: string }) => {
}
const validationSchema = Yup.object().shape({
password: Yup.string().required('Password Required'),
password: Yup.string().required(),
newPassword: Yup.string()
.required('Enter your new password')
.required()
.min(6, 'Too Short!')
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{6,})/,

View file

@ -1,5 +1,6 @@
import ActionLink from '@/components/shared/ActionLink'
import Captcha from '@/components/shared/Captcha'
import TenantSelector from '@/components/shared/TenantSelector'
import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
@ -84,6 +85,7 @@ const ExtendLogin = () => {
{message}
</Alert>
)}
<TenantSelector />
<Formik
initialValues={{
email: userName,

View file

@ -1,3 +1,4 @@
import { TenantSelector } from '@/components/shared'
import ActionLink from '@/components/shared/ActionLink'
import Captcha from '@/components/shared/Captcha'
import Alert from '@/components/ui/Alert'
@ -87,6 +88,7 @@ const ForgotPassword = () => {
{message}
</Alert>
)}
<TenantSelector />
<Formik
initialValues={{
email: userName,

View file

@ -2,6 +2,7 @@ import { FailedSignInResponse } from '@/proxy/auth/models'
import ActionLink from '@/components/shared/ActionLink'
import Captcha from '@/components/shared/Captcha'
import PasswordInput from '@/components/shared/PasswordInput'
import TenantSelector from '@/components/shared/TenantSelector'
import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import Checkbox from '@/components/ui/Checkbox'
@ -9,7 +10,6 @@ import { FormContainer, FormItem } from '@/components/ui/Form'
import Input from '@/components/ui/Input'
import PlatformLoginResultType from '@/constants/login.result.enum'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { getTenantByNameDetail } from '@/services/tenant.service'
import { useStoreActions, useStoreState } from '@/store'
import useAuth from '@/utils/hooks/useAuth'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -17,10 +17,9 @@ import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
import { TurnstileInstance } from '@marsidev/react-turnstile'
import { Field, Form, Formik } from 'formik'
import { motion } from 'framer-motion'
import { useEffect, useRef, useState } from 'react'
import { useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import * as Yup from 'yup'
import { defaultDomain, getSubdomain } from '@/utils/subdomain'
import { Helmet } from 'react-helmet'
import { APP_NAME } from '@/constants/app.constant'
@ -33,28 +32,23 @@ type SignInFormSchema = {
}
const validationSchema = Yup.object().shape({
userName: Yup.string().required('Please enter your user name'),
password: Yup.string().required('Please enter your password'),
userName: Yup.string().required(),
password: Yup.string().required(),
rememberMe: Yup.bool(),
twoFactor: Yup.boolean(),
twoFactorCode: Yup.string().when('twoFactor', {
is: true,
then: (schema) => schema.required('Mail adresinize gönderilen doğrulama kodunu giriniz'),
then: (schema) => schema.required(),
otherwise: (schema) => schema.notRequired(),
}),
})
const Login = () => {
const navigate = useNavigate()
const isMultiTenant = useStoreState((a) => a.abpConfig.config?.multiTenancy.isEnabled)
const { setTenant } = useStoreActions((a) => a.auth.tenant)
const UiVersion = useStoreState((state) => state.locale.currentUiVersion)
const { setUiVersion } = useStoreActions((a) => a.locale)
const tenantName = useStoreState((state) => state.locale.currentTenantName)
const { setTenantName } = useStoreActions((actions) => actions.locale)
const [message, setMessage] = useState('')
const [error, setError] = useTimeOutMessage(300000)
const [twoFactor, setTwoFactor] = useState(false)
@ -93,9 +87,6 @@ const Login = () => {
setError(result.message)
} else {
setError('')
//Tenant belirlenmişse
fetchDataByName(tenantName || '')
}
if (result.status === 'failed') {
@ -165,57 +156,6 @@ const Login = () => {
setSubmitting(false)
}
const fetchDataByName = async (name: string, isSubdomain = false) => {
if (name) {
try {
const response = await getTenantByNameDetail(name)
if (response.data) {
setTenant({ tenantId: response.data.id, tenantName: response.data.name, menuGroup: response.data.menuGroup });
} else {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} catch {
setTenant(undefined)
if (isSubdomain) redirectToMainDomain(name)
}
} else {
setTenant(undefined)
}
}
const redirectToMainDomain = (name: string) => {
setTenantName(undefined)
const parts = window.location.hostname.split('.')
const mainDomain = parts.length >= 3 ? parts.slice(1).join('.') : window.location.hostname
setWarningTimeout(`"${name}" kurumuna ait kayıt bulunamadı. Ana sayfaya yönlendiriliyorsunuz...`)
setTimeout(() => {
window.location.href = `${window.location.protocol}//${mainDomain}`
}, 3000)
}
const subDomainName = getSubdomain()
useEffect(() => {
if (subDomainName) {
setTenantName(subDomainName)
fetchDataByName(subDomainName, true)
}
}, [subDomainName])
const tenantStyle: React.CSSProperties | undefined =
subDomainName && subDomainName !== defaultDomain
? {
opacity: 0,
position: 'absolute',
pointerEvents: 'none',
height: 0,
margin: 0,
padding: 0,
border: 'none',
}
: undefined
const findUiVersion = async () => {
try {
const res = await fetch(`/version.json?ts=${Date.now()}`)
@ -246,22 +186,7 @@ const Login = () => {
<p>{translate('::Abp.Account.WelcomeBack.Message')}</p>
</div>
{isMultiTenant && (
<>
<label className="form-label mb-2" style={tenantStyle}>
{translate('::Organization')}
</label>
<div className="mb-4">
<Input
placeholder={translate('::Organization')}
value={tenantName}
onChange={(e) => setTenantName(e.target.value)}
style={tenantStyle}
aria-hidden={subDomainName && subDomainName !== defaultDomain ? 'true' : 'false'}
/>
</div>
</>
)}
<TenantSelector />
<div>
<Formik
initialValues={{
@ -289,7 +214,7 @@ const Login = () => {
name="userName"
placeholder={translate('::Abp.Account.EmailAddress')}
component={Input}
inputClassName="dark:bg-gray-900 dark:text-gray-100"
className="dark:bg-gray-900 dark:text-gray-100"
/>
</FormItem>
)}

View file

@ -1,5 +1,6 @@
import ActionLink from '@/components/shared/ActionLink'
import PasswordInput from '@/components/shared/PasswordInput'
import TenantSelector from '@/components/shared/TenantSelector'
import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
@ -83,6 +84,7 @@ const Register = () => {
{error}
</Alert>
)}
<TenantSelector />
<Formik
initialValues={{
email: '',

View file

@ -21,7 +21,7 @@ type ResetPasswordFormSchema = {
}
const validationSchema = Yup.object().shape({
password: Yup.string().required('Please enter your password'),
password: Yup.string().required(),
confirmPassword: Yup.string().oneOf([Yup.ref('password')], 'Your passwords do not match'),
})

View file

@ -2,7 +2,7 @@ import useAccount from '@/utils/hooks/useAccount'
import { Alert, Button, FormContainer, FormItem, Input } from '@/components/ui'
import { Field, Form, Formik } from 'formik'
import * as Yup from 'yup'
import { ActionLink } from '@/components/shared'
import { ActionLink, TenantSelector } from '@/components/shared'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { store } from '@/store'
import Captcha from '@/components/shared/Captcha'
@ -57,6 +57,7 @@ const SendConfirmationCode = () => {
{error}
</Alert>
)}
<TenantSelector />
<Formik
initialValues={{
email: userName,