feat(platform): Add confirmation modal for email verification
- Create reusable ConfirmationModal component with variants (info, warning, danger, success) - Replace browser confirm() dialogs with styled modal for email verification - Update PlatformBusinesses and PlatformUsers to use the new modal - Add translation keys for verification messages - Unverify test@example.com for testing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import PlatformListing from './components/PlatformListing';
|
||||
import PlatformTable from './components/PlatformTable';
|
||||
import PlatformListRow from './components/PlatformListRow';
|
||||
import EditPlatformEntityModal from './components/EditPlatformEntityModal';
|
||||
import ConfirmationModal from '../../components/ConfirmationModal';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
interface PlatformBusinessesProps {
|
||||
@@ -25,6 +26,8 @@ const PlatformBusinesses: React.FC<PlatformBusinessesProps> = ({ onMasquerade })
|
||||
const [showInviteModal, setShowInviteModal] = useState(false);
|
||||
const [editingBusiness, setEditingBusiness] = useState<PlatformBusiness | null>(null);
|
||||
const [showInactiveBusinesses, setShowInactiveBusinesses] = useState(false);
|
||||
const [verifyEmailUser, setVerifyEmailUser] = useState<{ id: number; email: string; name: string } | null>(null);
|
||||
const [isVerifying, setIsVerifying] = useState(false);
|
||||
|
||||
// Filter and separate businesses
|
||||
const filteredBusinesses = (businesses || []).filter(b =>
|
||||
@@ -47,14 +50,22 @@ const PlatformBusinesses: React.FC<PlatformBusinessesProps> = ({ onMasquerade })
|
||||
}
|
||||
};
|
||||
|
||||
const handleVerifyEmail = async (userId: number) => {
|
||||
if (confirm(t('platform.confirmVerifyEmail'))) {
|
||||
try {
|
||||
await verifyUserEmail(userId);
|
||||
queryClient.invalidateQueries({ queryKey: ['platform', 'businesses'] });
|
||||
} catch (error) {
|
||||
alert(t('errors.generic'));
|
||||
}
|
||||
const handleVerifyEmailClick = (userId: number, email: string, name: string) => {
|
||||
setVerifyEmailUser({ id: userId, email, name });
|
||||
};
|
||||
|
||||
const handleVerifyEmailConfirm = async () => {
|
||||
if (!verifyEmailUser) return;
|
||||
|
||||
setIsVerifying(true);
|
||||
try {
|
||||
await verifyUserEmail(verifyEmailUser.id);
|
||||
queryClient.invalidateQueries({ queryKey: ['platform', 'businesses'] });
|
||||
setVerifyEmailUser(null);
|
||||
} catch (error) {
|
||||
alert(t('errors.generic'));
|
||||
} finally {
|
||||
setIsVerifying(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,9 +81,13 @@ const PlatformBusinesses: React.FC<PlatformBusinessesProps> = ({ onMasquerade })
|
||||
tertiaryText={business.owner?.email || '-'}
|
||||
actions={
|
||||
<>
|
||||
{business.owner && !business.owner.email_verified && ( // Assuming PlatformBusiness owner object has email_verified, if not we might need to fetch it or update interface
|
||||
{business.owner && !business.owner.email_verified && (
|
||||
<button
|
||||
onClick={() => handleVerifyEmail(business.owner!.id)}
|
||||
onClick={() => handleVerifyEmailClick(
|
||||
business.owner!.id,
|
||||
business.owner!.email,
|
||||
business.owner!.full_name || business.owner!.username || business.name
|
||||
)}
|
||||
className="text-green-600 hover:text-green-500 dark:text-green-400 dark:hover:text-green-300 font-medium text-xs inline-flex items-center gap-1 px-3 py-1 border border-green-200 dark:border-green-800 rounded-lg hover:bg-green-50 dark:hover:bg-green-900/30 transition-colors"
|
||||
title={t('platform.verifyEmail')}
|
||||
>
|
||||
@@ -194,6 +209,30 @@ const PlatformBusinesses: React.FC<PlatformBusinessesProps> = ({ onMasquerade })
|
||||
isOpen={!!editingBusiness}
|
||||
onClose={() => setEditingBusiness(null)}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
isOpen={!!verifyEmailUser}
|
||||
onClose={() => setVerifyEmailUser(null)}
|
||||
onConfirm={handleVerifyEmailConfirm}
|
||||
title={t('platform.verifyEmail')}
|
||||
message={
|
||||
<div className="space-y-3">
|
||||
<p>{t('platform.confirmVerifyEmailMessage')}</p>
|
||||
{verifyEmailUser && (
|
||||
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-3">
|
||||
<p className="font-medium text-gray-900 dark:text-white">{verifyEmailUser.name}</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{verifyEmailUser.email}</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{t('platform.verifyEmailNote')}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
confirmText={t('platform.verify')}
|
||||
cancelText={t('common.cancel')}
|
||||
variant="success"
|
||||
isLoading={isVerifying}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import PlatformListing from './components/PlatformListing';
|
||||
import PlatformListRow from './components/PlatformListRow';
|
||||
import EditPlatformEntityModal from './components/EditPlatformEntityModal';
|
||||
import ConfirmationModal from '../../components/ConfirmationModal';
|
||||
|
||||
interface PlatformUsersProps {
|
||||
onMasquerade: (targetUser: { id: number; username?: string; name?: string; email?: string; role?: string }) => void;
|
||||
@@ -19,6 +20,8 @@ const PlatformUsers: React.FC<PlatformUsersProps> = ({ onMasquerade }) => {
|
||||
const [roleFilter, setRoleFilter] = useState<string>('all');
|
||||
const { data: users, isLoading, error } = usePlatformUsers();
|
||||
const [editingUser, setEditingUser] = useState<PlatformUser | null>(null);
|
||||
const [verifyEmailUser, setVerifyEmailUser] = useState<{ id: number; email: string; name: string } | null>(null);
|
||||
const [isVerifying, setIsVerifying] = useState(false);
|
||||
|
||||
const filteredUsers = (users || []).filter(u => {
|
||||
const isPlatformUser = ['superuser', 'platform_manager', 'platform_sales', 'platform_support'].includes(u.role);
|
||||
@@ -51,14 +54,22 @@ const PlatformUsers: React.FC<PlatformUsersProps> = ({ onMasquerade }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleVerifyEmail = async (userId: number) => {
|
||||
if (confirm(t('platform.confirmVerifyEmail'))) {
|
||||
try {
|
||||
await verifyUserEmail(userId);
|
||||
queryClient.invalidateQueries({ queryKey: ['platform', 'users'] });
|
||||
} catch (error) {
|
||||
alert(t('errors.generic'));
|
||||
}
|
||||
const handleVerifyEmailClick = (userId: number, email: string, name: string) => {
|
||||
setVerifyEmailUser({ id: userId, email, name });
|
||||
};
|
||||
|
||||
const handleVerifyEmailConfirm = async () => {
|
||||
if (!verifyEmailUser) return;
|
||||
|
||||
setIsVerifying(true);
|
||||
try {
|
||||
await verifyUserEmail(verifyEmailUser.id);
|
||||
queryClient.invalidateQueries({ queryKey: ['platform', 'users'] });
|
||||
setVerifyEmailUser(null);
|
||||
} catch (error) {
|
||||
alert(t('errors.generic'));
|
||||
} finally {
|
||||
setIsVerifying(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,7 +91,7 @@ const PlatformUsers: React.FC<PlatformUsersProps> = ({ onMasquerade }) => {
|
||||
<>
|
||||
{!u.email_verified && (
|
||||
<button
|
||||
onClick={() => handleVerifyEmail(u.id)}
|
||||
onClick={() => handleVerifyEmailClick(u.id, u.email, u.name || u.username)}
|
||||
className="text-green-600 hover:text-green-500 dark:text-green-400 dark:hover:text-green-300 font-medium text-xs inline-flex items-center gap-1 px-3 py-1 border border-green-200 dark:border-green-800 rounded-lg hover:bg-green-50 dark:hover:bg-green-900/30 transition-colors"
|
||||
title={t('platform.verifyEmail')}
|
||||
>
|
||||
@@ -137,6 +148,30 @@ const PlatformUsers: React.FC<PlatformUsersProps> = ({ onMasquerade }) => {
|
||||
isOpen={!!editingUser}
|
||||
onClose={() => setEditingUser(null)}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
isOpen={!!verifyEmailUser}
|
||||
onClose={() => setVerifyEmailUser(null)}
|
||||
onConfirm={handleVerifyEmailConfirm}
|
||||
title={t('platform.verifyEmail')}
|
||||
message={
|
||||
<div className="space-y-3">
|
||||
<p>{t('platform.confirmVerifyEmailMessage')}</p>
|
||||
{verifyEmailUser && (
|
||||
<div className="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-3">
|
||||
<p className="font-medium text-gray-900 dark:text-white">{verifyEmailUser.name}</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{verifyEmailUser.email}</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{t('platform.verifyEmailNote')}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
confirmText={t('platform.verify')}
|
||||
cancelText={t('common.cancel')}
|
||||
variant="success"
|
||||
isLoading={isVerifying}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user