2026-02-24 20:44:16 +00:00
|
|
|
|
import { getTenantByNameDetail } from '@/services/tenant.service'
|
|
|
|
|
|
import { CustomTenantDto } from '@/proxy/config/models'
|
|
|
|
|
|
import {
|
|
|
|
|
|
FaArrowLeft,
|
|
|
|
|
|
FaArrowRight,
|
|
|
|
|
|
FaDollarSign,
|
|
|
|
|
|
FaMoneyBillWave,
|
|
|
|
|
|
FaBuilding,
|
|
|
|
|
|
FaGlobe,
|
|
|
|
|
|
FaEnvelope,
|
|
|
|
|
|
FaMap,
|
|
|
|
|
|
FaMapPin,
|
|
|
|
|
|
FaPhone,
|
|
|
|
|
|
FaSearch,
|
|
|
|
|
|
FaUser,
|
|
|
|
|
|
FaUserPlus,
|
2026-07-07 13:41:07 +00:00
|
|
|
|
FaCheckCircle,
|
2026-07-08 11:53:23 +00:00
|
|
|
|
FaSitemap,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
} from 'react-icons/fa'
|
2026-07-06 21:53:45 +00:00
|
|
|
|
import React, { useEffect, useState } from 'react'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
2026-07-06 21:53:45 +00:00
|
|
|
|
import { Button, Select } from '@/components/ui'
|
|
|
|
|
|
import {
|
|
|
|
|
|
CityDto,
|
|
|
|
|
|
CountryDto,
|
|
|
|
|
|
DistrictDto,
|
|
|
|
|
|
getCities,
|
|
|
|
|
|
getCountries,
|
|
|
|
|
|
getDistricts,
|
|
|
|
|
|
} from '@/services/location.service'
|
|
|
|
|
|
|
|
|
|
|
|
interface LocationOption<T> {
|
|
|
|
|
|
label: string
|
|
|
|
|
|
value: string
|
|
|
|
|
|
data: T
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
2026-07-07 13:41:07 +00:00
|
|
|
|
const TENANT_DATA_STORAGE_KEY = 'tenantData'
|
|
|
|
|
|
const TENANT_FORM_DRAFT_STORAGE_KEY = 'tenantFormDraft'
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const DEFAULT_PHONE_FORMAT = '000 000 000 000 000'
|
|
|
|
|
|
const TURKEY_PHONE_FORMAT = '(000) 000-0000'
|
|
|
|
|
|
const REQUIRED_NEW_CUSTOMER_FIELDS: Array<keyof CustomTenantDto> = [
|
|
|
|
|
|
'organizationName',
|
|
|
|
|
|
'founder',
|
|
|
|
|
|
'country',
|
|
|
|
|
|
'city',
|
|
|
|
|
|
'district',
|
2026-07-07 21:00:35 +00:00
|
|
|
|
'township',
|
2026-07-06 21:53:45 +00:00
|
|
|
|
'postalCode',
|
|
|
|
|
|
'address1',
|
|
|
|
|
|
'phoneNumber',
|
|
|
|
|
|
'email',
|
|
|
|
|
|
'taxOffice',
|
|
|
|
|
|
'vknTckn',
|
|
|
|
|
|
]
|
|
|
|
|
|
const REQUIRED_EXISTING_CUSTOMER_FIELDS: Array<keyof CustomTenantDto> = [
|
|
|
|
|
|
'id',
|
|
|
|
|
|
'name',
|
|
|
|
|
|
'organizationName',
|
|
|
|
|
|
'founder',
|
|
|
|
|
|
'country',
|
|
|
|
|
|
'city',
|
|
|
|
|
|
'district',
|
2026-07-07 21:00:35 +00:00
|
|
|
|
'township',
|
2026-07-06 21:53:45 +00:00
|
|
|
|
'address1',
|
|
|
|
|
|
'email',
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const getPhoneFormat = (country?: CountryDto) =>
|
|
|
|
|
|
country?.phoneNumberFormat ||
|
|
|
|
|
|
(country?.phoneCode === 90 ? TURKEY_PHONE_FORMAT : DEFAULT_PHONE_FORMAT)
|
|
|
|
|
|
|
|
|
|
|
|
const getPhoneMaxLength = (country?: CountryDto) =>
|
|
|
|
|
|
country?.phoneNumberMaxLength ?? getPhoneFormat(country).replace(/[^0]/g, '').length
|
|
|
|
|
|
|
|
|
|
|
|
const getPhoneMinLength = (country?: CountryDto) =>
|
|
|
|
|
|
country?.phoneNumberMinLength ?? getPhoneMaxLength(country)
|
|
|
|
|
|
|
|
|
|
|
|
const getNationalPhoneDigits = (value: string, country?: CountryDto) => {
|
|
|
|
|
|
let digits = value.replace(/\D/g, '')
|
|
|
|
|
|
if (!country) return digits
|
|
|
|
|
|
|
|
|
|
|
|
const code = String(country.phoneCode)
|
|
|
|
|
|
if (value.trimStart().startsWith('+') && digits.startsWith(code)) {
|
|
|
|
|
|
digits = digits.slice(code.length)
|
|
|
|
|
|
}
|
|
|
|
|
|
return digits
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getPhoneValidationError = (value: string, country?: CountryDto) => {
|
|
|
|
|
|
if (!country) return 'Lütfen önce ülke seçin.'
|
|
|
|
|
|
|
|
|
|
|
|
const length = getNationalPhoneDigits(value, country).length
|
|
|
|
|
|
const minLength = getPhoneMinLength(country)
|
|
|
|
|
|
const maxLength = getPhoneMaxLength(country)
|
|
|
|
|
|
if (length >= minLength && length <= maxLength) return ''
|
|
|
|
|
|
|
|
|
|
|
|
return minLength === maxLength
|
|
|
|
|
|
? `Telefon numarası ${minLength} haneli olmalıdır.`
|
|
|
|
|
|
: `Telefon numarası ${minLength}-${maxLength} hane arasında olmalıdır.`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const formatPhoneNumber = (value: string, country?: CountryDto) => {
|
|
|
|
|
|
if (!country) return value.replace(/\D/g, '').slice(0, 15)
|
|
|
|
|
|
|
|
|
|
|
|
const maxLength = getPhoneMaxLength(country)
|
|
|
|
|
|
const digits = getNationalPhoneDigits(value, country).slice(0, maxLength)
|
|
|
|
|
|
const format = getPhoneFormat(country)
|
|
|
|
|
|
let digitIndex = 0
|
|
|
|
|
|
let nationalNumber = ''
|
|
|
|
|
|
|
|
|
|
|
|
for (const character of format) {
|
|
|
|
|
|
if (character === '0') {
|
|
|
|
|
|
if (digitIndex >= digits.length) break
|
|
|
|
|
|
nationalNumber += digits[digitIndex]
|
|
|
|
|
|
digitIndex += 1
|
|
|
|
|
|
} else if (digitIndex === 0) {
|
|
|
|
|
|
if (digits.length > 0) nationalNumber += character
|
|
|
|
|
|
} else if (digitIndex < digits.length || character === ')') {
|
|
|
|
|
|
nationalNumber += character
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return `+${country.phoneCode}${nationalNumber ? ` ${nationalNumber}` : ''}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
const normalizePhoneForStorage = (value?: string, country?: CountryDto) =>
|
|
|
|
|
|
value ? getNationalPhoneDigits(value, country) : value
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const isBlankValue = (value: unknown) =>
|
|
|
|
|
|
value === undefined || value === null || String(value).trim() === ''
|
|
|
|
|
|
|
|
|
|
|
|
const hasRequiredFields = (
|
|
|
|
|
|
tenant: Partial<CustomTenantDto>,
|
|
|
|
|
|
fields: Array<keyof CustomTenantDto>,
|
|
|
|
|
|
) => fields.every((field) => !isBlankValue(tenant[field]))
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeCode = (value?: string) => value?.trim().toLowerCase() || ''
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-07-07 21:00:35 +00:00
|
|
|
|
const getDistrictPostalCode = (district?: DistrictDto) =>
|
|
|
|
|
|
district?.postalCode ||
|
|
|
|
|
|
(district as (DistrictDto & { PostalCode?: string }) | undefined)?.PostalCode ||
|
|
|
|
|
|
''
|
|
|
|
|
|
|
|
|
|
|
|
const getUniqueLocationOptions = <T,>(
|
|
|
|
|
|
items: T[],
|
|
|
|
|
|
getKey: (item: T) => string,
|
|
|
|
|
|
getLabel: (item: T) => string,
|
|
|
|
|
|
): LocationOption<T>[] => {
|
|
|
|
|
|
const seen = new Set<string>()
|
|
|
|
|
|
|
|
|
|
|
|
return items.reduce<LocationOption<T>[]>((options, item) => {
|
|
|
|
|
|
const key = getKey(item)
|
|
|
|
|
|
if (!key || seen.has(key)) return options
|
|
|
|
|
|
|
|
|
|
|
|
seen.add(key)
|
|
|
|
|
|
options.push({
|
|
|
|
|
|
label: getLabel(item),
|
|
|
|
|
|
value: key,
|
|
|
|
|
|
data: item,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return options
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
type TenantFormDraft = Partial<CustomTenantDto> & {
|
|
|
|
|
|
isExisting?: boolean
|
|
|
|
|
|
foundTenantName?: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
interface TenantFormProps {
|
|
|
|
|
|
onSubmit: (tenant: CustomTenantDto) => void
|
|
|
|
|
|
onBack: () => void
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
|
|
|
|
|
const [isExisting, setIsExisting] = useState<boolean>(true)
|
|
|
|
|
|
const [formData, setFormData] = useState<Partial<CustomTenantDto>>({})
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const [countries, setCountries] = useState<CountryDto[]>([])
|
|
|
|
|
|
const [cities, setCities] = useState<CityDto[]>([])
|
|
|
|
|
|
const [districts, setDistricts] = useState<DistrictDto[]>([])
|
|
|
|
|
|
const [isLoadingCountries, setIsLoadingCountries] = useState(false)
|
|
|
|
|
|
const [isLoadingCities, setIsLoadingCities] = useState(false)
|
|
|
|
|
|
const [isLoadingDistricts, setIsLoadingDistricts] = useState(false)
|
|
|
|
|
|
const [isLoadingTenant, setIsLoadingTenant] = useState(false)
|
|
|
|
|
|
const [emailError, setEmailError] = useState('')
|
|
|
|
|
|
const [phoneError, setPhoneError] = useState('')
|
|
|
|
|
|
const [existingTenantError, setExistingTenantError] = useState('')
|
|
|
|
|
|
const [formError, setFormError] = useState('')
|
|
|
|
|
|
const [foundTenantName, setFoundTenantName] = useState('')
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const savedDraft = localStorage.getItem(TENANT_FORM_DRAFT_STORAGE_KEY)
|
|
|
|
|
|
const savedTenant = localStorage.getItem(TENANT_DATA_STORAGE_KEY)
|
|
|
|
|
|
const savedData = savedDraft || savedTenant
|
|
|
|
|
|
if (!savedData) return
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsed = JSON.parse(savedData) as TenantFormDraft
|
|
|
|
|
|
setIsExisting(parsed.isExisting ?? true)
|
|
|
|
|
|
setFoundTenantName(parsed.foundTenantName || parsed.name || '')
|
|
|
|
|
|
setFormData(parsed)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Müşteri form taslağı okunamadı:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const loadCountries = async () => {
|
|
|
|
|
|
setIsLoadingCountries(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getCountries()
|
|
|
|
|
|
setCountries(response.data)
|
|
|
|
|
|
|
|
|
|
|
|
const defaultCountry = response.data.find((country) => country.name === 'Türkiye')
|
|
|
|
|
|
if (defaultCountry) {
|
|
|
|
|
|
setFormData((prev) =>
|
|
|
|
|
|
prev.country
|
|
|
|
|
|
? prev
|
|
|
|
|
|
: {
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
country: defaultCountry.name,
|
|
|
|
|
|
city: '',
|
|
|
|
|
|
district: '',
|
2026-07-07 21:00:35 +00:00
|
|
|
|
township: '',
|
|
|
|
|
|
postalCode: '',
|
2026-07-06 21:53:45 +00:00
|
|
|
|
phoneNumber: `+${defaultCountry.phoneCode}`,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
setIsLoadingCities(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const cityResponse = await getCities(defaultCountry.name)
|
|
|
|
|
|
setCities(cityResponse.data)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Şehirler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingCities(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Ülkeler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingCountries(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void loadCountries()
|
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (Object.keys(formData).length === 0) return
|
|
|
|
|
|
|
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
|
TENANT_FORM_DRAFT_STORAGE_KEY,
|
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
|
...formData,
|
|
|
|
|
|
isExisting,
|
|
|
|
|
|
foundTenantName,
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
}, [formData, foundTenantName, isExisting])
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const loadLocationOptions = async () => {
|
|
|
|
|
|
if (!formData.country) return
|
|
|
|
|
|
|
|
|
|
|
|
setIsLoadingCities(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const cityResponse = await getCities(formData.country)
|
|
|
|
|
|
setCities(cityResponse.data)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Şehirler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingCities(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!formData.city) return
|
|
|
|
|
|
setIsLoadingDistricts(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const districtResponse = await getDistricts(formData.country, formData.city)
|
|
|
|
|
|
setDistricts(districtResponse.data)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('İlçeler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingDistricts(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void loadLocationOptions()
|
|
|
|
|
|
}, [formData.country, formData.city])
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const selectedCountry = countries.find((country) => country.name === formData.country)
|
|
|
|
|
|
const phoneMask = selectedCountry
|
|
|
|
|
|
? `+${selectedCountry.phoneCode} ${getPhoneFormat(selectedCountry)}`
|
|
|
|
|
|
: DEFAULT_PHONE_FORMAT
|
|
|
|
|
|
const canSubmitExistingTenant =
|
|
|
|
|
|
Boolean(
|
|
|
|
|
|
formData.id &&
|
|
|
|
|
|
formData.organizationName &&
|
|
|
|
|
|
normalizeCode(foundTenantName) === normalizeCode(formData.name),
|
|
|
|
|
|
) && !isLoadingTenant
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
|
|
|
|
e.preventDefault()
|
2026-07-06 21:53:45 +00:00
|
|
|
|
let hasValidationError = false
|
|
|
|
|
|
setFormError('')
|
|
|
|
|
|
|
|
|
|
|
|
if (isExisting) {
|
|
|
|
|
|
if (!formData.name?.trim()) {
|
|
|
|
|
|
setExistingTenantError('Lütfen önce organization code girin.')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
!formData.id ||
|
|
|
|
|
|
normalizeCode(foundTenantName) !== normalizeCode(formData.name) ||
|
|
|
|
|
|
!formData.organizationName
|
|
|
|
|
|
) {
|
|
|
|
|
|
setExistingTenantError('Devam etmek için önce organization code ile kurum bilgisini bulun.')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasRequiredFields(formData, REQUIRED_EXISTING_CUSTOMER_FIELDS)) {
|
|
|
|
|
|
setExistingTenantError('Kurum bilgileri eksik geldi. Lütfen kurum kodunu tekrar aratın.')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
onSubmit({
|
|
|
|
|
|
...formData,
|
|
|
|
|
|
phoneNumber: normalizePhoneForStorage(formData.phoneNumber, selectedCountry),
|
|
|
|
|
|
mobileNumber: normalizePhoneForStorage(formData.mobileNumber, selectedCountry),
|
|
|
|
|
|
faxNumber: normalizePhoneForStorage(formData.faxNumber, selectedCountry),
|
|
|
|
|
|
isExisting: true,
|
|
|
|
|
|
} as CustomTenantDto)
|
2026-07-06 21:53:45 +00:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasRequiredFields(formData, REQUIRED_NEW_CUSTOMER_FIELDS)) {
|
|
|
|
|
|
setFormError('Lütfen zorunlu alanları eksiksiz doldurun.')
|
|
|
|
|
|
hasValidationError = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const nextPhoneError = getPhoneValidationError(formData.phoneNumber || '', selectedCountry)
|
|
|
|
|
|
setPhoneError(nextPhoneError)
|
|
|
|
|
|
if (nextPhoneError) {
|
|
|
|
|
|
hasValidationError = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isExisting && !EMAIL_PATTERN.test(formData.email || '')) {
|
|
|
|
|
|
setEmailError('Lütfen geçerli bir e-posta adresi girin.')
|
|
|
|
|
|
hasValidationError = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (hasValidationError) return
|
2026-07-07 13:41:07 +00:00
|
|
|
|
onSubmit({
|
|
|
|
|
|
...formData,
|
|
|
|
|
|
phoneNumber: normalizePhoneForStorage(formData.phoneNumber, selectedCountry),
|
|
|
|
|
|
mobileNumber: normalizePhoneForStorage(formData.mobileNumber, selectedCountry),
|
|
|
|
|
|
faxNumber: normalizePhoneForStorage(formData.faxNumber, selectedCountry),
|
|
|
|
|
|
isExisting: false,
|
|
|
|
|
|
} as CustomTenantDto)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleInputChange = (field: keyof CustomTenantDto, value: string) => {
|
|
|
|
|
|
setFormData((prev) => ({ ...prev, [field]: value }))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
const getDefaultNewCustomerData = (): Partial<CustomTenantDto> => {
|
|
|
|
|
|
const defaultCountry = countries.find((country) => country.name === 'Türkiye')
|
|
|
|
|
|
return defaultCountry
|
|
|
|
|
|
? {
|
|
|
|
|
|
country: defaultCountry.name,
|
|
|
|
|
|
city: '',
|
|
|
|
|
|
district: '',
|
2026-07-07 21:00:35 +00:00
|
|
|
|
township: '',
|
|
|
|
|
|
postalCode: '',
|
2026-07-07 13:41:07 +00:00
|
|
|
|
phoneNumber: `+${defaultCountry.phoneCode}`,
|
|
|
|
|
|
}
|
|
|
|
|
|
: {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCustomerTypeChange = (nextIsExisting: boolean) => {
|
|
|
|
|
|
if (nextIsExisting === isExisting) return
|
|
|
|
|
|
|
|
|
|
|
|
setIsExisting(nextIsExisting)
|
|
|
|
|
|
setFoundTenantName('')
|
|
|
|
|
|
setExistingTenantError('')
|
|
|
|
|
|
setFormError('')
|
|
|
|
|
|
setEmailError('')
|
|
|
|
|
|
setPhoneError('')
|
|
|
|
|
|
setDistricts([])
|
|
|
|
|
|
setFormData(nextIsExisting ? {} : getDefaultNewCustomerData())
|
|
|
|
|
|
localStorage.removeItem(TENANT_DATA_STORAGE_KEY)
|
|
|
|
|
|
localStorage.removeItem(TENANT_FORM_DRAFT_STORAGE_KEY)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const resetExistingTenantDetails = (name: string) => {
|
|
|
|
|
|
setFoundTenantName('')
|
|
|
|
|
|
setExistingTenantError('')
|
|
|
|
|
|
setFormError('')
|
|
|
|
|
|
setFormData({ name })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCountryChange = async (option: LocationOption<CountryDto> | null) => {
|
|
|
|
|
|
const country = option?.data
|
|
|
|
|
|
setFormData((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
country: country?.name || '',
|
|
|
|
|
|
city: '',
|
|
|
|
|
|
district: '',
|
2026-07-07 21:00:35 +00:00
|
|
|
|
township: '',
|
|
|
|
|
|
postalCode: '',
|
2026-07-06 21:53:45 +00:00
|
|
|
|
phoneNumber: country ? `+${country.phoneCode}` : '',
|
|
|
|
|
|
}))
|
|
|
|
|
|
setCities([])
|
|
|
|
|
|
setDistricts([])
|
|
|
|
|
|
setPhoneError('')
|
|
|
|
|
|
|
|
|
|
|
|
if (!country) return
|
|
|
|
|
|
setIsLoadingCities(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getCities(country.name)
|
|
|
|
|
|
setCities(response.data)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Şehirler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingCities(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleCityChange = async (option: LocationOption<CityDto> | null) => {
|
|
|
|
|
|
const city = option?.data
|
2026-07-07 21:00:35 +00:00
|
|
|
|
setFormData((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
city: city?.name || '',
|
|
|
|
|
|
district: '',
|
|
|
|
|
|
township: '',
|
|
|
|
|
|
postalCode: '',
|
|
|
|
|
|
}))
|
2026-07-06 21:53:45 +00:00
|
|
|
|
setDistricts([])
|
|
|
|
|
|
|
|
|
|
|
|
if (!city || !formData.country) return
|
|
|
|
|
|
setIsLoadingDistricts(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getDistricts(formData.country, city.name)
|
|
|
|
|
|
setDistricts(response.data)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('İlçeler alınırken hata:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingDistricts(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 21:00:35 +00:00
|
|
|
|
const handleDistrictChange = (option: LocationOption<DistrictDto> | null) => {
|
|
|
|
|
|
setFormData((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
district: option?.data.name || '',
|
|
|
|
|
|
township: '',
|
|
|
|
|
|
postalCode: '',
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleTownshipChange = (option: LocationOption<DistrictDto> | null) => {
|
|
|
|
|
|
setFormData((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
township: option?.data.township || '',
|
|
|
|
|
|
postalCode: getDistrictPostalCode(option?.data),
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const countryOptions: LocationOption<CountryDto>[] = countries.map((country) => ({
|
|
|
|
|
|
label: country.name,
|
|
|
|
|
|
value: country.name,
|
|
|
|
|
|
data: country,
|
|
|
|
|
|
}))
|
|
|
|
|
|
const cityOptions: LocationOption<CityDto>[] = cities.map((city) => ({
|
|
|
|
|
|
label: city.name,
|
|
|
|
|
|
value: city.name,
|
|
|
|
|
|
data: city,
|
|
|
|
|
|
}))
|
2026-07-07 21:00:35 +00:00
|
|
|
|
const districtOptions = getUniqueLocationOptions(
|
|
|
|
|
|
districts,
|
|
|
|
|
|
(district) => district.name,
|
|
|
|
|
|
(district) => district.name,
|
|
|
|
|
|
)
|
|
|
|
|
|
const townshipOptions = getUniqueLocationOptions(
|
|
|
|
|
|
districts.filter((district) => district.name === formData.district && district.township),
|
|
|
|
|
|
(district) => `${district.township || ''}:${getDistrictPostalCode(district)}`,
|
|
|
|
|
|
(district) => district.township || '',
|
|
|
|
|
|
)
|
2026-07-06 21:53:45 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
const getTenantInfo = async () => {
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const tenantName = formData.name?.trim()
|
|
|
|
|
|
if (!tenantName) {
|
|
|
|
|
|
setExistingTenantError('Lütfen önce organization code girin.')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setIsLoadingTenant(true)
|
|
|
|
|
|
setExistingTenantError('')
|
|
|
|
|
|
setFormError('')
|
2026-02-24 20:44:16 +00:00
|
|
|
|
try {
|
2026-07-06 21:53:45 +00:00
|
|
|
|
const tenant = await getTenantByNameDetail(tenantName)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
setFormData((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
id: tenant.data.id,
|
|
|
|
|
|
name: tenant.data.name,
|
|
|
|
|
|
organizationName: tenant.data.organizationName,
|
|
|
|
|
|
founder: tenant.data.founder,
|
|
|
|
|
|
vknTckn: tenant.data.vknTckn,
|
|
|
|
|
|
taxOffice: tenant.data.taxOffice,
|
|
|
|
|
|
country: tenant.data.country,
|
|
|
|
|
|
city: tenant.data.city,
|
2026-07-07 13:41:07 +00:00
|
|
|
|
district: tenant.data.district,
|
2026-07-07 21:00:35 +00:00
|
|
|
|
township: tenant.data.township,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
postalCode: tenant.data.postalCode,
|
|
|
|
|
|
phoneNumber: tenant.data.phoneNumber,
|
|
|
|
|
|
mobileNumber: tenant.data.mobileNumber,
|
|
|
|
|
|
faxNumber: tenant.data.faxNumber,
|
2026-07-07 13:41:07 +00:00
|
|
|
|
address1: tenant.data.address1,
|
|
|
|
|
|
address2: tenant.data.address2,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
email: tenant.data.email,
|
|
|
|
|
|
website: tenant.data.website,
|
|
|
|
|
|
menuGroup: tenant.data.menuGroup,
|
|
|
|
|
|
}))
|
2026-07-07 13:41:07 +00:00
|
|
|
|
|
|
|
|
|
|
if (tenant.data.name) setFoundTenantName(tenant.data.name)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
} catch (error) {
|
2026-07-06 21:53:45 +00:00
|
|
|
|
setFoundTenantName('')
|
|
|
|
|
|
setExistingTenantError('Kurum bulunamadı. Lütfen organization code bilgisini kontrol edin.')
|
2026-02-24 20:44:16 +00:00
|
|
|
|
console.error('Kurum bilgisi alınırken hata:', error)
|
2026-07-06 21:53:45 +00:00
|
|
|
|
} finally {
|
|
|
|
|
|
setIsLoadingTenant(false)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex flex-col lg:flex-row gap-6 mb-6">
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<div className="w-full lg:w-1/3 bg-white rounded-xl shadow-lg border border-gray-200 p-6 dark:border-gray-700 dark:bg-gray-900 dark:shadow-gray-950/40">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="mb-6">
|
|
|
|
|
|
<h2 className="text-lg font-semibold mb-4 flex items-center">
|
|
|
|
|
|
<FaUser className="w-5 h-5 text-green-600 mr-2" />{' '}
|
|
|
|
|
|
{translate('::Public.payment.customerInfo')}
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
<div className="grid grid-cols-1 gap-4">
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<Button
|
2026-07-07 13:41:07 +00:00
|
|
|
|
onClick={() => handleCustomerTypeChange(true)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
variant="plain"
|
2026-07-07 13:41:07 +00:00
|
|
|
|
className={`relative block h-auto w-full p-4 border-2 rounded-xl transition-all text-left select-none shadow-sm ${
|
2026-02-24 20:44:16 +00:00
|
|
|
|
isExisting === true
|
2026-07-07 13:41:07 +00:00
|
|
|
|
? 'border-blue-600 bg-blue-100 shadow-lg ring-4 ring-blue-200 scale-[1.02] dark:border-sky-400 dark:bg-sky-950/60 dark:ring-sky-500/30'
|
|
|
|
|
|
: 'border-gray-200 bg-white hover:border-blue-400 hover:bg-blue-50/60 dark:border-gray-700 dark:bg-gray-800/70 dark:hover:border-sky-500 dark:hover:bg-sky-950/30'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}`}
|
|
|
|
|
|
>
|
2026-07-07 13:41:07 +00:00
|
|
|
|
{isExisting === true && (
|
|
|
|
|
|
<FaCheckCircle className="absolute right-4 top-4 h-5 w-5 text-blue-600 dark:text-sky-300" />
|
|
|
|
|
|
)}
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<div className="font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.products.tenantForm.existing.title')}
|
|
|
|
|
|
</div>
|
2026-07-07 13:41:07 +00:00
|
|
|
|
<div className="text-sm text-gray-700 dark:text-gray-300">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.products.tenantForm.existing.desc')}
|
|
|
|
|
|
</div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
</Button>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<Button
|
2026-07-07 13:41:07 +00:00
|
|
|
|
onClick={() => handleCustomerTypeChange(false)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
variant="plain"
|
2026-07-07 13:41:07 +00:00
|
|
|
|
className={`relative block h-auto w-full p-4 border-2 rounded-xl transition-all text-left select-none shadow-sm ${
|
2026-02-24 20:44:16 +00:00
|
|
|
|
isExisting === false
|
2026-07-07 13:41:07 +00:00
|
|
|
|
? 'border-blue-600 bg-blue-100 shadow-lg ring-4 ring-blue-200 scale-[1.02] dark:border-sky-400 dark:bg-sky-950/60 dark:ring-sky-500/30'
|
|
|
|
|
|
: 'border-gray-200 bg-white hover:border-blue-400 hover:bg-blue-50/60 dark:border-gray-700 dark:bg-gray-800/70 dark:hover:border-sky-500 dark:hover:bg-sky-950/30'
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}`}
|
|
|
|
|
|
>
|
2026-07-07 13:41:07 +00:00
|
|
|
|
{isExisting === false && (
|
|
|
|
|
|
<FaCheckCircle className="absolute right-4 top-4 h-5 w-5 text-blue-600 dark:text-sky-300" />
|
|
|
|
|
|
)}
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<div className="font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.products.tenantForm.new.title')}
|
|
|
|
|
|
</div>
|
2026-07-07 13:41:07 +00:00
|
|
|
|
<div className="text-sm text-gray-700 dark:text-gray-300">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.products.tenantForm.new.desc')}
|
|
|
|
|
|
</div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
</Button>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<div className="w-full lg:w-2/3 bg-white rounded-xl shadow-lg border border-gray-200 p-6 dark:border-gray-700 dark:bg-gray-900 dark:shadow-gray-950/40">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{isExisting !== null && (
|
|
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
|
|
|
|
{isExisting ? (
|
|
|
|
|
|
<div>
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.payment.customer.code')}
|
|
|
|
|
|
</label>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<div className="relative flex flex-col sm:flex-row sm:items-stretch gap-3">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="relative w-full">
|
|
|
|
|
|
<FaBuilding className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
placeholder="Enter your organization code"
|
|
|
|
|
|
value={formData.name || ''}
|
2026-07-06 21:53:45 +00:00
|
|
|
|
onChange={(e) => resetExistingTenantDetails(e.target.value)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
onKeyDown={async (e) => {
|
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
await getTenantInfo()
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
className="h-[40px] w-full pl-10 pr-4 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 sm:rounded-l-lg sm:rounded-r-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<Button
|
2026-02-24 20:44:16 +00:00
|
|
|
|
type="button"
|
2026-07-06 21:53:45 +00:00
|
|
|
|
disabled={isLoadingTenant || !formData.name?.trim()}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
onClick={getTenantInfo}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
icon={<FaSearch className="w-4 h-4" />}
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
className="!h-[40px] min-w-[180px] sm:mr-2 sm:rounded-r-lg sm:rounded-l-none [&>span]:gap-2"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
>
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{isLoadingTenant
|
|
|
|
|
|
? 'Aranıyor...'
|
|
|
|
|
|
: translate('::Public.products.tenantForm.searchOrg')}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
</Button>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{existingTenantError && (
|
|
|
|
|
|
<p className="mt-2 text-sm text-red-600">{existingTenantError}</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{!existingTenantError &&
|
|
|
|
|
|
formData.name &&
|
|
|
|
|
|
!canSubmitExistingTenant &&
|
|
|
|
|
|
!isLoadingTenant && (
|
|
|
|
|
|
<p className="mt-2 text-sm text-amber-600">
|
|
|
|
|
|
Devam etmek için önce Find Organization ile kurum bilgilerini getirin.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{formData.organizationName && (
|
2026-05-26 16:09:26 +00:00
|
|
|
|
<div className="grid grid-cols-1 gap-y-3 text-sm text-gray-700 dark:text-gray-300 p-3">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaBuilding className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.payment.customer.company')}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.organizationName}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-07 13:41:07 +00:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaUser className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">{translate('::LoginPanel.Profil')}</span>
|
|
|
|
|
|
<span>{formData.founder}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaEnvelope className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Abp.Account.EmailAddress')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.email}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaPhone className="w-4 h-4 text-gray-500" />
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<span className="font-medium">{translate('::blog.categories.mobile')}:</span>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<span>{formData.mobileNumber}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-start gap-2">
|
|
|
|
|
|
<FaMapPin className="w-4 h-4 text-gray-500 mt-0.5" />
|
|
|
|
|
|
<div>
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<span className="font-medium">{translate('::App.Address')}:</span>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div>{formData.address1}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaGlobe className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.payment.customer.country')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.country}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaGlobe className="w-4 h-4 text-gray-500" />
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<span className="font-medium">{translate('::Public.common.city')}:</span>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<span>{formData.city}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaMap className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.payment.customer.district')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.district}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-07 21:00:35 +00:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaMap className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::App.Listform.ListformField.Township')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.township}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaMapPin className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.payment.customer.postalCode')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.postalCode}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaMoneyBillWave className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.contact.taxOffice')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.taxOffice}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaDollarSign className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.contact.taxNumber')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.vknTckn}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{formData.reference && (
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaUserPlus className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<span className="font-medium">
|
|
|
|
|
|
{translate('::Public.payment.customer.reference')}:
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span>{formData.reference}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="space-y-4">
|
2026-07-07 21:00:35 +00:00
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
|
|
|
{translate('::Public.products.tenantForm.orgName')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaBuilding className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
placeholder="Enter your organization name"
|
|
|
|
|
|
value={formData.organizationName || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('organizationName', e.target.value)}
|
|
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
|
|
|
|
|
|
/>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
2026-07-07 21:00:35 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::LoginPanel.Profil')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaUser className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
placeholder="Enter founder's name"
|
|
|
|
|
|
value={formData.founder || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('founder', e.target.value)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Public.payment.customer.country')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<Select<LocationOption<CountryDto>>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
required
|
2026-07-06 21:53:45 +00:00
|
|
|
|
isClearable
|
|
|
|
|
|
isLoading={isLoadingCountries}
|
|
|
|
|
|
options={countryOptions}
|
|
|
|
|
|
value={
|
|
|
|
|
|
countryOptions.find((option) => option.value === formData.country) || null
|
|
|
|
|
|
}
|
|
|
|
|
|
onChange={handleCountryChange}
|
|
|
|
|
|
placeholder="Ülke seçin"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-07-07 21:00:35 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Public.common.city')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<Select<LocationOption<CityDto>>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
required
|
2026-07-06 21:53:45 +00:00
|
|
|
|
isClearable
|
|
|
|
|
|
isDisabled={!formData.country}
|
|
|
|
|
|
isLoading={isLoadingCities}
|
|
|
|
|
|
options={cityOptions}
|
|
|
|
|
|
value={cityOptions.find((option) => option.value === formData.city) || null}
|
|
|
|
|
|
onChange={handleCityChange}
|
|
|
|
|
|
placeholder={formData.country ? 'Şehir seçin' : 'Önce ülke seçin'}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Public.payment.customer.district')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<Select<LocationOption<DistrictDto>>
|
|
|
|
|
|
required
|
|
|
|
|
|
isClearable
|
|
|
|
|
|
isDisabled={!formData.city}
|
|
|
|
|
|
isLoading={isLoadingDistricts}
|
|
|
|
|
|
options={districtOptions}
|
|
|
|
|
|
value={
|
|
|
|
|
|
districtOptions.find((option) => option.value === formData.district) ||
|
|
|
|
|
|
null
|
|
|
|
|
|
}
|
2026-07-07 21:00:35 +00:00
|
|
|
|
onChange={handleDistrictChange}
|
2026-07-06 21:53:45 +00:00
|
|
|
|
placeholder={formData.city ? 'İlçe seçin' : 'Önce şehir seçin'}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-07-07 21:00:35 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
|
|
|
{translate('::App.Listform.ListformField.Township')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<Select<LocationOption<DistrictDto>>
|
|
|
|
|
|
required
|
|
|
|
|
|
isClearable
|
|
|
|
|
|
isDisabled={!formData.district}
|
|
|
|
|
|
options={townshipOptions}
|
|
|
|
|
|
value={
|
|
|
|
|
|
townshipOptions.find(
|
|
|
|
|
|
(option) => option.data.township === formData.township,
|
|
|
|
|
|
) || null
|
|
|
|
|
|
}
|
|
|
|
|
|
onChange={handleTownshipChange}
|
|
|
|
|
|
placeholder={formData.district ? 'Township seçin' : 'Önce ilçe seçin'}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Public.payment.customer.postalCode')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<FaMapPin className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
2026-07-06 21:53:45 +00:00
|
|
|
|
placeholder="34782"
|
|
|
|
|
|
value={formData.postalCode || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
|
|
|
{translate('::App.Address')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaMapPin className="absolute left-3 top-3 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
required
|
|
|
|
|
|
placeholder="Enter your address"
|
|
|
|
|
|
value={formData.address1 || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('address1', e.target.value)}
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<FaPhone className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<input
|
2026-07-06 21:53:45 +00:00
|
|
|
|
type="tel"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
required
|
2026-07-06 21:53:45 +00:00
|
|
|
|
inputMode="numeric"
|
|
|
|
|
|
placeholder={phoneMask.replace(/0/g, '_')}
|
|
|
|
|
|
aria-label={`Telefon numarası, format: ${phoneMask}`}
|
|
|
|
|
|
value={formData.phoneNumber || ''}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
handleInputChange(
|
|
|
|
|
|
'phoneNumber',
|
|
|
|
|
|
formatPhoneNumber(e.target.value, selectedCountry),
|
|
|
|
|
|
)
|
|
|
|
|
|
if (phoneError) setPhoneError('')
|
|
|
|
|
|
}}
|
|
|
|
|
|
onBlur={() =>
|
|
|
|
|
|
setPhoneError(
|
|
|
|
|
|
getPhoneValidationError(formData.phoneNumber || '', selectedCountry),
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
aria-invalid={Boolean(phoneError)}
|
|
|
|
|
|
aria-describedby={phoneError ? 'tenant-phone-error' : undefined}
|
|
|
|
|
|
title={`Beklenen format: ${phoneMask}`}
|
|
|
|
|
|
className={`w-full pl-10 pr-4 py-2 border rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
|
|
|
|
|
phoneError
|
|
|
|
|
|
? 'border-red-500 dark:border-red-500'
|
|
|
|
|
|
: 'border-gray-300 dark:border-gray-700'
|
|
|
|
|
|
}`}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{phoneError && (
|
|
|
|
|
|
<p id="tenant-phone-error" className="mt-1 text-sm text-red-600">
|
|
|
|
|
|
{phoneError}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{translate('::Abp.Account.EmailAddress')}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
2026-07-06 21:53:45 +00:00
|
|
|
|
<FaEnvelope className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<input
|
2026-07-06 21:53:45 +00:00
|
|
|
|
type="email"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
required
|
2026-07-06 21:53:45 +00:00
|
|
|
|
placeholder="sample@email.com"
|
|
|
|
|
|
value={formData.email || ''}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
handleInputChange('email', e.target.value)
|
|
|
|
|
|
if (emailError) {
|
|
|
|
|
|
setEmailError(
|
|
|
|
|
|
EMAIL_PATTERN.test(e.target.value)
|
|
|
|
|
|
? ''
|
|
|
|
|
|
: 'Lütfen geçerli bir e-posta adresi girin.',
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
onBlur={() =>
|
|
|
|
|
|
setEmailError(
|
|
|
|
|
|
EMAIL_PATTERN.test(formData.email || '')
|
|
|
|
|
|
? ''
|
|
|
|
|
|
: 'Lütfen geçerli bir e-posta adresi girin.',
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
aria-invalid={Boolean(emailError)}
|
|
|
|
|
|
aria-describedby={emailError ? 'tenant-email-error' : undefined}
|
|
|
|
|
|
className={`w-full pl-10 pr-4 py-2 border rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
|
|
|
|
|
emailError
|
|
|
|
|
|
? 'border-red-500 dark:border-red-500'
|
|
|
|
|
|
: 'border-gray-300 dark:border-gray-700'
|
|
|
|
|
|
}`}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{emailError && (
|
|
|
|
|
|
<p id="tenant-email-error" className="mt-1 text-sm text-red-600">
|
|
|
|
|
|
{emailError}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.contact.taxOffice')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaBuilding className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
placeholder="Kozyatağı"
|
|
|
|
|
|
value={formData.taxOffice}
|
|
|
|
|
|
onChange={(e) => handleInputChange('taxOffice', e.target.value)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{translate('::Public.contact.taxNumber')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaDollarSign className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
placeholder="1234567890"
|
|
|
|
|
|
value={formData.vknTckn}
|
|
|
|
|
|
onChange={(e) => handleInputChange('vknTckn', e.target.value)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-08 11:53:23 +00:00
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
|
|
|
{translate('::Public.payment.customer.website')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaSitemap className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
required
|
|
|
|
|
|
placeholder={translate('::Public.payment.customer.website')}
|
|
|
|
|
|
value={formData.website || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('website', e.target.value)}
|
|
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
|
|
|
|
{translate('::Public.payment.customer.reference')}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<FaUserPlus className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" />
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
placeholder={translate('::Public.payment.customer.reference')}
|
|
|
|
|
|
value={formData.reference || ''}
|
|
|
|
|
|
onChange={(e) => handleInputChange('reference', e.target.value)}
|
|
|
|
|
|
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg bg-white text-gray-900 placeholder-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2026-07-06 21:53:45 +00:00
|
|
|
|
{formError && <p className="mt-2 text-sm text-red-600">{formError}</p>}
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
<div className="flex justify-between items-center mt-6">
|
2026-06-25 22:55:44 +00:00
|
|
|
|
<Button
|
2026-02-24 20:44:16 +00:00
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={() => navigate(ROUTES_ENUM.public.products)}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
icon={<FaArrowLeft className="w-4 h-4" />}
|
|
|
|
|
|
variant="default"
|
|
|
|
|
|
size="sm"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
>
|
2026-03-29 08:59:07 +00:00
|
|
|
|
{translate('::Back')}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
2026-02-24 20:44:16 +00:00
|
|
|
|
type="submit"
|
2026-07-06 21:53:45 +00:00
|
|
|
|
disabled={isExisting && !canSubmitExistingTenant}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
icon={<FaArrowRight className="w-5 h-5" />}
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
color="green-600"
|
|
|
|
|
|
size="sm"
|
2026-02-24 20:44:16 +00:00
|
|
|
|
>
|
|
|
|
|
|
{translate('::Abp.Account.ResetPassword.Continue')}
|
2026-06-25 22:55:44 +00:00
|
|
|
|
</Button>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|