Merge pull request 'Fix staff invitation acceptance & add delete/deactivate buttons' (#7) from dev into loaf-prod
Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
@@ -50,6 +50,7 @@ import Resources from './pages/Resources';
|
|||||||
import ContactUs from './pages/ContactUs';
|
import ContactUs from './pages/ContactUs';
|
||||||
import TermsOfService from './pages/TermsOfService';
|
import TermsOfService from './pages/TermsOfService';
|
||||||
import PrivacyPolicy from './pages/PrivacyPolicy';
|
import PrivacyPolicy from './pages/PrivacyPolicy';
|
||||||
|
import AcceptInvitation from './pages/AcceptInvitation';
|
||||||
|
|
||||||
const PrivateRoute = ({ children, adminOnly = false }) => {
|
const PrivateRoute = ({ children, adminOnly = false }) => {
|
||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
@@ -82,6 +83,7 @@ function App() {
|
|||||||
<Route path="/register" element={<Register />} />
|
<Route path="/register" element={<Register />} />
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||||
|
<Route path="/accept-invitation" element={<AcceptInvitation />} />
|
||||||
<Route path="/forgot-password" element={<ForgotPassword />} />
|
<Route path="/forgot-password" element={<ForgotPassword />} />
|
||||||
<Route path="/reset-password" element={<ResetPassword />} />
|
<Route path="/reset-password" element={<ResetPassword />} />
|
||||||
<Route path="/change-password-required" element={
|
<Route path="/change-password-required" element={
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import CreateStaffDialog from '../../components/CreateStaffDialog';
|
|||||||
import InviteStaffDialog from '../../components/InviteStaffDialog';
|
import InviteStaffDialog from '../../components/InviteStaffDialog';
|
||||||
import PendingInvitationsTable from '../../components/PendingInvitationsTable';
|
import PendingInvitationsTable from '../../components/PendingInvitationsTable';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { UserCog, Search, Shield, UserPlus, Mail, Edit, Eye } from 'lucide-react';
|
import { UserCog, Search, Shield, UserPlus, Mail, Edit, Eye, Trash2, UserCheck, UserX } from 'lucide-react';
|
||||||
|
|
||||||
const AdminStaff = () => {
|
const AdminStaff = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -71,6 +71,32 @@ const AdminStaff = () => {
|
|||||||
setFilteredUsers(filtered);
|
setFilteredUsers(filtered);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleStatus = async (userId, currentStatus) => {
|
||||||
|
const newStatus = currentStatus === 'active' ? 'inactive' : 'active';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put(`/admin/users/${userId}/status`, { status: newStatus });
|
||||||
|
toast.success(`User ${newStatus === 'active' ? 'activated' : 'deactivated'} successfully`);
|
||||||
|
fetchStaff(); // Refresh list
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.response?.data?.detail || 'Failed to update user status');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteUser = async (userId, userName) => {
|
||||||
|
if (!window.confirm(`Are you sure you want to delete ${userName}? This action cannot be undone.`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.delete(`/admin/users/${userId}`);
|
||||||
|
toast.success('User deleted successfully');
|
||||||
|
fetchStaff(); // Refresh list
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.response?.data?.detail || 'Failed to delete user');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getRoleBadge = (role) => {
|
const getRoleBadge = (role) => {
|
||||||
const config = {
|
const config = {
|
||||||
superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' },
|
superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' },
|
||||||
@@ -250,7 +276,7 @@ const AdminStaff = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2 flex-wrap">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => navigate(`/admin/users/${user.id}`)}
|
onClick={() => navigate(`/admin/users/${user.id}`)}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -259,6 +285,41 @@ const AdminStaff = () => {
|
|||||||
<Edit className="h-4 w-4 mr-2" />
|
<Edit className="h-4 w-4 mr-2" />
|
||||||
Manage
|
Manage
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{hasPermission('users.status') && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleToggleStatus(user.id, user.status)}
|
||||||
|
variant="outline"
|
||||||
|
className={`border-2 rounded-full px-4 py-2 ${
|
||||||
|
user.status === 'active'
|
||||||
|
? 'border-orange-500 text-orange-600 hover:bg-orange-50'
|
||||||
|
: 'border-green-500 text-green-600 hover:bg-green-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{user.status === 'active' ? (
|
||||||
|
<>
|
||||||
|
<UserX className="h-4 w-4 mr-2" />
|
||||||
|
Deactivate
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<UserCheck className="h-4 w-4 mr-2" />
|
||||||
|
Activate
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasPermission('users.delete') && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleDeleteUser(user.id, `${user.first_name} ${user.last_name}`)}
|
||||||
|
variant="outline"
|
||||||
|
className="border-2 border-red-500 text-red-600 hover:bg-red-50 rounded-full px-4 py-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user