updates
This commit is contained in:
@@ -13,6 +13,7 @@ import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const AboutPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -206,7 +207,7 @@ const AboutPage: React.FC = () => {
|
||||
{pageContent?.story_content ? (
|
||||
<div
|
||||
className="text-lg md:text-xl leading-relaxed font-light tracking-wide"
|
||||
dangerouslySetInnerHTML={{ __html: pageContent.story_content.replace(/\n/g, '<br />') }}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(pageContent.story_content.replace(/\n/g, '<br />'))}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const AccessibilityPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -153,9 +154,9 @@ const AccessibilityPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const CancellationPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -154,9 +155,9 @@ const CancellationPolicyPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const FAQPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -153,9 +154,9 @@ const FAQPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const PrivacyPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -161,9 +162,9 @@ const PrivacyPolicyPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const RefundsPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -161,9 +162,9 @@ const RefundsPolicyPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
|
||||
const TermsPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -161,9 +162,9 @@ const TermsPage: React.FC = () => {
|
||||
[&_*]:text-gray-300 [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_h5]:text-white [&_h6]:text-white
|
||||
[&_strong]:text-[#d4af37] [&_b]:text-[#d4af37] [&_a]:text-[#d4af37]"
|
||||
style={{ color: '#d1d5db' }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
}}
|
||||
dangerouslySetInnerHTML={createSanitizedHtml(
|
||||
pageContent.content || pageContent.description || '<p style="color: #d1d5db;">No content available.</p>'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import InspectionManagement from '../../components/shared/InspectionManagement';
|
||||
import Pagination from '../../components/common/Pagination';
|
||||
import apiClient from '../../services/api/apiClient';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
type Tab = 'status-board' | 'maintenance' | 'housekeeping' | 'inspections' | 'rooms';
|
||||
|
||||
@@ -107,7 +108,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
setFloors(uniqueFloors);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch floors:', error);
|
||||
logger.error('Failed to fetch floors', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +218,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
setAvailableAmenities(response.data.amenities);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch amenities:', error);
|
||||
logger.error('Failed to fetch amenities', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -294,7 +295,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Failed to fetch page ${page}:`, err);
|
||||
logger.error(`Failed to fetch page ${page}`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +309,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
return prev;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch room types:', error);
|
||||
logger.error('Failed to fetch room types', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -335,7 +336,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
const updatedRoom = await roomService.getRoomByNumber(editingRoom.room_number);
|
||||
setEditingRoom(updatedRoom.data.room);
|
||||
} catch (err) {
|
||||
console.error('Failed to refresh room data:', err);
|
||||
logger.error('Failed to refresh room data', err);
|
||||
}
|
||||
} else {
|
||||
const createData = {
|
||||
@@ -476,7 +477,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
|
||||
setEditingRoom(roomData);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch full room details:', error);
|
||||
logger.error('Failed to fetch full room details', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -617,7 +618,7 @@ const AdvancedRoomManagementPage: React.FC = () => {
|
||||
const response = await roomService.getRoomByNumber(editingRoom.room_number);
|
||||
setEditingRoom(response.data.room);
|
||||
} catch (error: any) {
|
||||
console.error('Error deleting image:', error);
|
||||
logger.error('Error deleting image', error);
|
||||
toast.error(error.response?.data?.message || error.response?.data?.detail || 'Unable to delete image');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ import { reportService, ReportData, reviewService, Review } from '../../services
|
||||
import { auditService, AuditLog, AuditLogFilters } from '../../services/api/auditService';
|
||||
import { formatDate } from '../../utils/format';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { logger } from '../../utils/logger';
|
||||
import analyticsService, {
|
||||
ComprehensiveAnalyticsData,
|
||||
RevPARData,
|
||||
@@ -250,7 +251,7 @@ const AnalyticsDashboardPage: React.FC = () => {
|
||||
setTotalItems(response.data.pagination.total);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching audit logs:', error);
|
||||
logger.error('Error fetching audit logs', error);
|
||||
toast.error(error.response?.data?.message || 'Unable to load audit logs');
|
||||
} finally {
|
||||
setAuditLoading(false);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { parseDateLocal } from '../../utils/format';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import CreateBookingModal from '../../components/shared/CreateBookingModal';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
const BookingManagementPage: React.FC = () => {
|
||||
const { formatCurrency } = useFormatCurrency();
|
||||
@@ -105,7 +106,7 @@ const BookingManagementPage: React.FC = () => {
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || 'Unable to create invoice';
|
||||
toast.error(errorMessage);
|
||||
console.error('Invoice creation error:', error);
|
||||
logger.error('Invoice creation error', error);
|
||||
} finally {
|
||||
setCreatingInvoice(false);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useAsync } from '../../hooks/useAsync';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
const DashboardPage: React.FC = () => {
|
||||
const { formatCurrency } = useFormatCurrency();
|
||||
@@ -36,7 +37,7 @@ const DashboardPage: React.FC = () => {
|
||||
await logout();
|
||||
navigate('/');
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
logger.error('Logout error', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,7 +72,7 @@ const DashboardPage: React.FC = () => {
|
||||
setRecentPayments(response.data.payments);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching payments:', err);
|
||||
logger.error('Error fetching payments', err);
|
||||
} finally {
|
||||
setLoadingPayments(false);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { formatDate } from '../../utils/format';
|
||||
import TaskDetailModal from '../../components/tasks/TaskDetailModal';
|
||||
import CreateTaskModal from '../../components/tasks/CreateTaskModal';
|
||||
import TaskFilters from '../../components/tasks/TaskFilters';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
type TaskStatus = 'pending' | 'assigned' | 'in_progress' | 'completed' | 'cancelled' | 'overdue';
|
||||
type TaskPriority = 'low' | 'medium' | 'high' | 'urgent';
|
||||
@@ -61,7 +62,7 @@ const TaskManagementPage: React.FC = () => {
|
||||
const tasksArray = responseData?.data || responseData || [];
|
||||
return Array.isArray(tasksArray) ? tasksArray : [];
|
||||
}).catch(error => {
|
||||
console.error('Error fetching tasks:', error);
|
||||
logger.error('Error fetching tasks', error);
|
||||
return [];
|
||||
}),
|
||||
{ immediate: true }
|
||||
|
||||
@@ -3,15 +3,40 @@ import { Plus, Search, Edit, Trash2, X } from 'lucide-react';
|
||||
import { userService, User } from '../../services/api';
|
||||
import { toast } from 'react-toastify';
|
||||
import Loading from '../../components/common/Loading';
|
||||
import LoadingButton from '../../components/common/LoadingButton';
|
||||
import ErrorMessage from '../../components/common/ErrorMessage';
|
||||
import Pagination from '../../components/common/Pagination';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { useApiCall } from '../../hooks/useApiCall';
|
||||
|
||||
const UserManagementPage: React.FC = () => {
|
||||
const { userInfo } = useAuthStore();
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [editingUser, setEditingUser] = useState<User | null>(null);
|
||||
const [deletingUserId, setDeletingUserId] = useState<number | null>(null);
|
||||
|
||||
const { execute: executeSubmit, isLoading: isSubmitting } = useApiCall(
|
||||
async (data: any, isEdit: boolean) => {
|
||||
if (isEdit && editingUser) {
|
||||
return await userService.updateUser(editingUser.id, data);
|
||||
} else {
|
||||
return await userService.createUser(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
showSuccessToast: true,
|
||||
successMessage: (editingUser ? 'User updated' : 'User added') + ' successfully',
|
||||
onSuccess: () => {
|
||||
setShowModal(false);
|
||||
resetForm();
|
||||
setTimeout(() => fetchUsers(), 300);
|
||||
},
|
||||
}
|
||||
);
|
||||
const [filters, setFilters] = useState({
|
||||
search: '',
|
||||
role: '',
|
||||
@@ -42,21 +67,23 @@ const UserManagementPage: React.FC = () => {
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
console.log('Fetching users with filters:', filters, 'page:', currentPage);
|
||||
setError(null);
|
||||
logger.debug('Fetching users', { filters, page: currentPage });
|
||||
const response = await userService.getUsers({
|
||||
...filters,
|
||||
page: currentPage,
|
||||
limit: itemsPerPage,
|
||||
});
|
||||
console.log('Users response:', response);
|
||||
setUsers(response.data.users);
|
||||
if (response.data.pagination) {
|
||||
setTotalPages(response.data.pagination.totalPages);
|
||||
setTotalItems(response.data.pagination.total);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching users:', error);
|
||||
toast.error(error.response?.data?.message || 'Unable to load users list');
|
||||
logger.error('Error fetching users', error);
|
||||
const errorMessage = error.response?.data?.message || 'Unable to load users list';
|
||||
setError(errorMessage);
|
||||
toast.error(errorMessage);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -64,49 +91,34 @@ const UserManagementPage: React.FC = () => {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!editingUser && (!formData.password || formData.password.trim() === '')) {
|
||||
toast.error('Please enter password');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const submitData: any = {
|
||||
full_name: formData.full_name,
|
||||
email: formData.email,
|
||||
phone_number: formData.phone_number,
|
||||
role: formData.role,
|
||||
status: formData.status,
|
||||
};
|
||||
|
||||
if (editingUser) {
|
||||
|
||||
const updateData: any = {
|
||||
full_name: formData.full_name,
|
||||
email: formData.email,
|
||||
phone_number: formData.phone_number,
|
||||
role: formData.role,
|
||||
status: formData.status,
|
||||
};
|
||||
|
||||
|
||||
if (formData.password && formData.password.trim() !== '') {
|
||||
updateData.password = formData.password;
|
||||
submitData.password = formData.password;
|
||||
}
|
||||
|
||||
console.log('Updating user:', editingUser.id, 'with data:', updateData);
|
||||
const response = await userService.updateUser(editingUser.id, updateData);
|
||||
console.log('Update response:', response);
|
||||
toast.success('User updated successfully');
|
||||
logger.debug('Updating user', { userId: editingUser.id, updateData: submitData });
|
||||
} else {
|
||||
|
||||
if (!formData.password || formData.password.trim() === '') {
|
||||
toast.error('Please enter password');
|
||||
return;
|
||||
}
|
||||
console.log('Creating user with data:', formData);
|
||||
const response = await userService.createUser(formData);
|
||||
console.log('Create response:', response);
|
||||
toast.success('User added successfully');
|
||||
submitData.password = formData.password;
|
||||
logger.debug('Creating user', { formData: submitData });
|
||||
}
|
||||
|
||||
|
||||
setShowModal(false);
|
||||
resetForm();
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
fetchUsers();
|
||||
}, 300);
|
||||
await executeSubmit(submitData, !!editingUser);
|
||||
} catch (error: any) {
|
||||
console.error('Error submitting user:', error);
|
||||
toast.error(error.response?.data?.message || 'An error occurred');
|
||||
logger.error('Error submitting user', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,7 +136,6 @@ const UserManagementPage: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
|
||||
if (userInfo?.id === id) {
|
||||
toast.error('You cannot delete your own account');
|
||||
return;
|
||||
@@ -133,13 +144,16 @@ const UserManagementPage: React.FC = () => {
|
||||
if (!window.confirm('Are you sure you want to delete this user?')) return;
|
||||
|
||||
try {
|
||||
console.log('Deleting user:', id);
|
||||
setDeletingUserId(id);
|
||||
logger.debug('Deleting user', { userId: id });
|
||||
await userService.deleteUser(id);
|
||||
toast.success('User deleted successfully');
|
||||
fetchUsers();
|
||||
} catch (error: any) {
|
||||
console.error('Error deleting user:', error);
|
||||
logger.error('Error deleting user', error);
|
||||
toast.error(error.response?.data?.message || 'Unable to delete user');
|
||||
} finally {
|
||||
setDeletingUserId(null);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -248,6 +262,14 @@ const UserManagementPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<ErrorMessage
|
||||
message={error}
|
||||
onDismiss={() => setError(null)}
|
||||
className="animate-fade-in"
|
||||
/>
|
||||
)}
|
||||
|
||||
{}
|
||||
<div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl border border-slate-200/60 overflow-hidden animate-fade-in" style={{ animationDelay: '0.2s' }}>
|
||||
<div className="overflow-x-auto">
|
||||
@@ -307,14 +329,17 @@ const UserManagementPage: React.FC = () => {
|
||||
>
|
||||
<Edit className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
<LoadingButton
|
||||
onClick={() => handleDelete(user.id)}
|
||||
className="p-2 rounded-lg text-rose-600 hover:text-rose-700 hover:bg-rose-50 transition-all duration-200 shadow-sm hover:shadow-md border border-rose-200 hover:border-rose-300 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={userInfo?.id === user.id}
|
||||
isLoading={deletingUserId === user.id}
|
||||
disabled={userInfo?.id === user.id || deletingUserId === user.id}
|
||||
variant="danger"
|
||||
size="sm"
|
||||
className="p-2"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -439,16 +464,20 @@ const UserManagementPage: React.FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowModal(false)}
|
||||
className="flex-1 px-6 py-3 border-2 border-slate-300 rounded-xl text-slate-700 font-semibold hover:bg-slate-50 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
disabled={isSubmitting}
|
||||
className="flex-1 px-6 py-3 border-2 border-slate-300 rounded-xl text-slate-700 font-semibold hover:bg-slate-50 transition-all duration-200 shadow-sm hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
className="flex-1 px-6 py-3 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl font-semibold hover:from-amber-600 hover:to-amber-700 transition-all duration-200 shadow-lg hover:shadow-xl"
|
||||
isLoading={isSubmitting}
|
||||
loadingText={editingUser ? 'Updating...' : 'Creating...'}
|
||||
variant="primary"
|
||||
className="flex-1"
|
||||
>
|
||||
{editingUser ? 'Update' : 'Create'}
|
||||
</button>
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user