Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Update frontend/src/pages/SuperAdminPanel.jsx
Browse files
frontend/src/pages/SuperAdminPanel.jsx
CHANGED
|
@@ -898,6 +898,8 @@ const SuperAdminPanel = () => {
|
|
| 898 |
if (match) initialOrgId = String(match.id);
|
| 899 |
}
|
| 900 |
|
|
|
|
|
|
|
| 901 |
setPromptValues({
|
| 902 |
email: u.email,
|
| 903 |
role: u.role || 'admin',
|
|
@@ -905,19 +907,33 @@ const SuperAdminPanel = () => {
|
|
| 905 |
password: ''
|
| 906 |
});
|
| 907 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 908 |
setPromptModal({
|
| 909 |
isOpen: true,
|
| 910 |
-
title: 'Edit Member & Password',
|
| 911 |
-
desc: `Update details & credentials for ${u.email}`,
|
| 912 |
-
inputs:
|
| 913 |
-
{ key: 'email', label: 'User Email', disabled: true },
|
| 914 |
-
{ key: 'password', label: 'New Password (Leave blank to keep current password)', placeholder: 'Type new password to update...', type: 'password', showRules: true },
|
| 915 |
-
{ key: 'role', label: 'Role', type: 'select', options: MEMBER_ROLE_OPTIONS },
|
| 916 |
-
{ key: 'org_id', label: 'Organization', type: 'select', options: orgOptions }
|
| 917 |
-
],
|
| 918 |
onConfirm: async (vals) => {
|
| 919 |
const pwd = vals.password ? vals.password.trim() : '';
|
| 920 |
-
if (pwd && (pwd.length < 8 || !/[A-Z]/.test(pwd) || !/[a-z]/.test(pwd) || !/[^A-Za-z0-9]/.test(pwd))) {
|
| 921 |
toast.error('Password must meet all 4 security requirements (8+ chars, 1 uppercase, 1 lowercase, 1 special character).');
|
| 922 |
return;
|
| 923 |
}
|
|
@@ -928,12 +944,12 @@ const SuperAdminPanel = () => {
|
|
| 928 |
body: JSON.stringify({
|
| 929 |
role: vals.role,
|
| 930 |
org_id: vals.org_id || null,
|
| 931 |
-
password: pwd
|
| 932 |
})
|
| 933 |
});
|
| 934 |
const data = await res.json().catch(() => ({}));
|
| 935 |
if (res.ok) {
|
| 936 |
-
toast.success('Member details
|
| 937 |
fetchStats();
|
| 938 |
} else {
|
| 939 |
toast.error(data.message || 'Failed to update member');
|
|
@@ -1595,36 +1611,38 @@ const SuperAdminPanel = () => {
|
|
| 1595 |
</td>
|
| 1596 |
<td className="px-md py-sm text-[13px] font-semibold text-on-surface-variant">{u.org_name}</td>
|
| 1597 |
<td className="px-md py-sm text-right flex justify-end gap-2">
|
| 1598 |
-
|
| 1599 |
-
|
| 1600 |
-
|
| 1601 |
-
|
| 1602 |
-
|
| 1603 |
-
|
| 1604 |
-
|
| 1605 |
-
|
| 1606 |
-
|
| 1607 |
-
|
| 1608 |
-
|
| 1609 |
-
|
| 1610 |
-
|
| 1611 |
-
|
| 1612 |
-
|
| 1613 |
-
|
| 1614 |
-
|
| 1615 |
-
|
| 1616 |
-
|
| 1617 |
-
|
| 1618 |
-
|
| 1619 |
-
|
|
|
|
|
|
|
| 1620 |
}
|
| 1621 |
-
|
|
|
|
| 1622 |
}
|
| 1623 |
-
|
| 1624 |
-
|
| 1625 |
-
|
| 1626 |
-
|
| 1627 |
-
<span className="text-on-surface-variant text-xs italic">Read Only</span>
|
| 1628 |
)}
|
| 1629 |
</td>
|
| 1630 |
</tr>
|
|
|
|
| 898 |
if (match) initialOrgId = String(match.id);
|
| 899 |
}
|
| 900 |
|
| 901 |
+
const isSupport = user?.role === 'support_engineer';
|
| 902 |
+
|
| 903 |
setPromptValues({
|
| 904 |
email: u.email,
|
| 905 |
role: u.role || 'admin',
|
|
|
|
| 907 |
password: ''
|
| 908 |
});
|
| 909 |
|
| 910 |
+
const modalInputs = [
|
| 911 |
+
{ key: 'email', label: 'User Email', disabled: true }
|
| 912 |
+
];
|
| 913 |
+
|
| 914 |
+
if (!isSupport) {
|
| 915 |
+
modalInputs.push({
|
| 916 |
+
key: 'password',
|
| 917 |
+
label: 'New Password (Leave blank to keep current password)',
|
| 918 |
+
placeholder: 'Type new password to update...',
|
| 919 |
+
type: 'password',
|
| 920 |
+
showRules: true
|
| 921 |
+
});
|
| 922 |
+
}
|
| 923 |
+
|
| 924 |
+
modalInputs.push(
|
| 925 |
+
{ key: 'role', label: 'Role', type: 'select', options: MEMBER_ROLE_OPTIONS },
|
| 926 |
+
{ key: 'org_id', label: 'Organization', type: 'select', options: orgOptions }
|
| 927 |
+
);
|
| 928 |
+
|
| 929 |
setPromptModal({
|
| 930 |
isOpen: true,
|
| 931 |
+
title: isSupport ? 'Edit Member Role & Organization' : 'Edit Member & Password',
|
| 932 |
+
desc: isSupport ? `Update role & organization mapping for ${u.email}` : `Update details & credentials for ${u.email}`,
|
| 933 |
+
inputs: modalInputs,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
onConfirm: async (vals) => {
|
| 935 |
const pwd = vals.password ? vals.password.trim() : '';
|
| 936 |
+
if (pwd && !isSupport && (pwd.length < 8 || !/[A-Z]/.test(pwd) || !/[a-z]/.test(pwd) || !/[^A-Za-z0-9]/.test(pwd))) {
|
| 937 |
toast.error('Password must meet all 4 security requirements (8+ chars, 1 uppercase, 1 lowercase, 1 special character).');
|
| 938 |
return;
|
| 939 |
}
|
|
|
|
| 944 |
body: JSON.stringify({
|
| 945 |
role: vals.role,
|
| 946 |
org_id: vals.org_id || null,
|
| 947 |
+
password: (!isSupport && pwd) ? pwd : null
|
| 948 |
})
|
| 949 |
});
|
| 950 |
const data = await res.json().catch(() => ({}));
|
| 951 |
if (res.ok) {
|
| 952 |
+
toast.success('Member details updated successfully');
|
| 953 |
fetchStats();
|
| 954 |
} else {
|
| 955 |
toast.error(data.message || 'Failed to update member');
|
|
|
|
| 1611 |
</td>
|
| 1612 |
<td className="px-md py-sm text-[13px] font-semibold text-on-surface-variant">{u.org_name}</td>
|
| 1613 |
<td className="px-md py-sm text-right flex justify-end gap-2">
|
| 1614 |
+
<button
|
| 1615 |
+
onClick={() => handleEditMember(u)}
|
| 1616 |
+
className="text-on-surface-variant hover:text-primary transition-colors bg-transparent border-0 cursor-pointer p-1"
|
| 1617 |
+
title={isSupportEngineer ? "Edit Member Role & Organization" : "Edit Member Details & Password"}
|
| 1618 |
+
>
|
| 1619 |
+
<Edit className="w-4 h-4" />
|
| 1620 |
+
</button>
|
| 1621 |
+
{!isSupportEngineer && (
|
| 1622 |
+
<button onClick={() => {
|
| 1623 |
+
setConfirmModal({
|
| 1624 |
+
isOpen: true,
|
| 1625 |
+
title: 'Delete Member',
|
| 1626 |
+
desc: `Are you sure you want to delete ${u.email}?`,
|
| 1627 |
+
type: 'error',
|
| 1628 |
+
onConfirm: async () => {
|
| 1629 |
+
try {
|
| 1630 |
+
const res = await authFetch(`/api/auth/users/${u.id}`, {
|
| 1631 |
+
method: 'DELETE'
|
| 1632 |
+
});
|
| 1633 |
+
if (res.ok) {
|
| 1634 |
+
toast.success('Member deleted successfully');
|
| 1635 |
+
fetchStats();
|
| 1636 |
+
} else {
|
| 1637 |
+
toast.error('Failed to delete member');
|
| 1638 |
}
|
| 1639 |
+
} catch (err) {
|
| 1640 |
+
toast.error('Network error deleting member');
|
| 1641 |
}
|
| 1642 |
+
closeConfirm();
|
| 1643 |
+
}
|
| 1644 |
+
});
|
| 1645 |
+
}} className="text-on-surface-variant hover:text-error transition-colors bg-transparent border-0 cursor-pointer p-1" title="Delete Member"><Trash2 className="w-4 h-4" /></button>
|
|
|
|
| 1646 |
)}
|
| 1647 |
</td>
|
| 1648 |
</tr>
|