erp-platform/company/src/pages/LoginWithTenant.tsx
2025-06-20 23:17:38 +03:00

237 lines
9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useAuthStore } from '../store/authStore';
import { LogIn, Mail, Lock, Building } from 'lucide-react';
import { apiClient } from '../services/api/config';
import { useLanguage } from '../context/LanguageContext';
interface LoginFormData {
tenantName?: string;
userNameOrEmailAddress: string;
password: string;
rememberMe?: boolean;
}
interface TenantInfo {
success: boolean;
tenantId?: string;
name?: string;
isActive?: boolean;
}
const LoginWithTenant: React.FC = () => {
const navigate = useNavigate();
const { login, isLoading } = useAuthStore();
const { t } = useLanguage();
const [isMultiTenancyEnabled, setIsMultiTenancyEnabled] = useState(false);
const [tenantInfo, setTenantInfo] = useState<TenantInfo | null>(null);
const {
register,
handleSubmit,
formState: { errors },
watch,
} = useForm<LoginFormData>({
defaultValues: {
rememberMe: true
}
});
const tenantName = watch('tenantName');
useEffect(() => {
checkMultiTenancy();
}, []);
useEffect(() => {
if (tenantName) {
checkTenant(tenantName);
} else {
setTenantInfo(null);
localStorage.removeItem('tenant_id');
}
}, [tenantName]);
const checkMultiTenancy = async () => {
try {
const response = await apiClient.get('/api/abp/application-configuration');
setIsMultiTenancyEnabled(response.data.multiTenancy?.isEnabled || false);
} catch (error) {
console.error('Failed to check multi-tenancy:', error);
}
};
const checkTenant = async (name: string) => {
try {
const response = await apiClient.post<TenantInfo>('/api/abp/multi-tenancy/tenants/by-name/' + name);
if (response.data.success && response.data.tenantId) {
setTenantInfo(response.data);
localStorage.setItem('tenant_id', response.data.tenantId);
} else {
setTenantInfo({ success: false });
localStorage.removeItem('tenant_id');
}
} catch (error) {
setTenantInfo({ success: false });
localStorage.removeItem('tenant_id');
}
};
const onSubmit = async (data: LoginFormData) => {
try {
await login({
username: data.userNameOrEmailAddress,
password: data.password,
});
navigate('/');
} catch (error) {
// Error is handled in the store
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center py-24 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full">
<div className="bg-white rounded-lg shadow-xl p-8">
<div className="text-center mb-4">
<div className="inline-flex items-center justify-center w-16 h-16 bg-blue-600 rounded-full mb-4">
<LogIn className="h-8 w-8 text-white" />
</div>
<h2 className="text-3xl font-bold text-gray-900">
{t('login.welcome')}
</h2>
<p className="mt-2 text-sm text-gray-600">
{t('login.subtitle')}{' '}
<Link
to="/register"
className="font-medium text-blue-600 hover:text-blue-500 transition-colors"
>
{t('login.createAccount')}
</Link>
</p>
</div>
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
{/* Tenant Field - Only show if multi-tenancy is enabled */}
{isMultiTenancyEnabled && (
<div>
<label htmlFor="tenant" className="block text-sm font-medium text-gray-700 mb-2">
{t('login.organization')}
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Building className="h-5 w-5 text-gray-400" />
</div>
<input
{...register('tenantName')}
type="text"
autoComplete="organization"
className="appearance-none relative block w-full px-3 py-3 pl-3 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder={t('login.organizationPlaceholder')}
/>
</div>
{tenantInfo && !tenantInfo.success && tenantName && (
<p className="mt-2 text-sm text-red-600">
{t('login.organizationNotFound')}
</p>
)}
{tenantInfo && tenantInfo.success && (
<p className="mt-2 text-sm text-green-600">
{tenantInfo.name} {t('login.organizationFound')}
</p>
)}
</div>
)}
{/* Username/Email Field */}
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
{t('login.email')}
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Mail className="h-5 w-5 text-gray-400" />
</div>
<input
{...register('userNameOrEmailAddress', {
required: t('login.emailRequired'),
})}
type="text"
autoComplete="username"
className="appearance-none relative block w-full px-3 py-3 pl-3 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder={t('login.emailPlaceholder')}
/>
</div>
{errors.userNameOrEmailAddress && (
<p className="mt-2 text-sm text-red-600">
{errors.userNameOrEmailAddress.message}
</p>
)}
</div>
{/* Password Field */}
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
{t('login.password')}
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
{...register('password', {
required: t('login.passwordRequired'),
})}
type="password"
autoComplete="current-password"
className="appearance-none relative block w-full px-3 py-3 pl-3 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
placeholder={t('login.passwordPlaceholder')}
/>
</div>
{errors.password && (
<p className="mt-2 text-sm text-red-600">
{errors.password.message}
</p>
)}
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<input
{...register('rememberMe')}
id="remember-me"
type="checkbox"
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label htmlFor="remember-me" className="ml-2 block text-sm text-gray-700">
{t('login.rememberMe')}
</label>
</div>
<div className="text-sm">
<a href="#" className="font-medium text-blue-600 hover:text-blue-500 transition-colors">
{t('login.forgotPassword')}
</a>
</div>
</div>
<div>
<button
type="submit"
disabled={isLoading || (tenantInfo !== null && !tenantInfo.success && !!tenantName)}
className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
>
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
<LogIn className="h-5 w-5 text-blue-500 group-hover:text-blue-400" />
</span>
{isLoading ? t('login.signingIn') : t('login.signIn')}
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default LoginWithTenant;