Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Update frontend/src/pages/SuperAdminPanel.jsx
Browse files
frontend/src/pages/SuperAdminPanel.jsx
CHANGED
|
@@ -593,26 +593,92 @@ const SuperAdminPanel = () => {
|
|
| 593 |
};
|
| 594 |
|
| 595 |
// Member CRUD
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 596 |
const handleAddMember = () => {
|
| 597 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 598 |
setPromptModal({
|
| 599 |
isOpen: true,
|
| 600 |
-
title: 'Add
|
| 601 |
-
desc: 'Invite a
|
| 602 |
inputs: [
|
| 603 |
{ key: 'email', label: 'User Email', placeholder: 'user@example.com' },
|
| 604 |
-
{ key: 'role', label: 'Role',
|
| 605 |
-
{ key: 'org_id', label: 'Organization
|
| 606 |
],
|
| 607 |
onConfirm: async (values) => {
|
| 608 |
try {
|
| 609 |
const res = await fetch('/api/auth/users', {
|
| 610 |
method: 'POST',
|
| 611 |
headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' },
|
| 612 |
-
body: JSON.stringify({ email: values.email, role: values.role, org_id: values.org_id })
|
| 613 |
});
|
| 614 |
-
if (res.ok)
|
| 615 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 616 |
closePrompt();
|
| 617 |
}
|
| 618 |
});
|
|
@@ -1117,26 +1183,7 @@ const SuperAdminPanel = () => {
|
|
| 1117 |
<td className="px-md py-sm text-right flex justify-end gap-2">
|
| 1118 |
{!isSupportEngineer ? (
|
| 1119 |
<>
|
| 1120 |
-
<button onClick={() =>
|
| 1121 |
-
setPromptValues({ role: u.role });
|
| 1122 |
-
setPromptModal({
|
| 1123 |
-
isOpen: true,
|
| 1124 |
-
title: 'Edit Member Role',
|
| 1125 |
-
desc: `Update role for ${u.email}`,
|
| 1126 |
-
inputs: [{ key: 'role', label: 'Role' }],
|
| 1127 |
-
onConfirm: async (vals) => {
|
| 1128 |
-
try {
|
| 1129 |
-
await fetch(`/api/auth/users/${u.id}/role`, {
|
| 1130 |
-
method: 'PUT',
|
| 1131 |
-
headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' },
|
| 1132 |
-
body: JSON.stringify({ role: vals.role })
|
| 1133 |
-
});
|
| 1134 |
-
fetchStats();
|
| 1135 |
-
} catch (err) { }
|
| 1136 |
-
closePrompt();
|
| 1137 |
-
}
|
| 1138 |
-
});
|
| 1139 |
-
}} className="text-on-surface-variant hover:text-primary transition-colors bg-transparent border-0 cursor-pointer p-1"><Edit className="w-4 h-4" /></button>
|
| 1140 |
<button onClick={() => {
|
| 1141 |
setConfirmModal({
|
| 1142 |
isOpen: true,
|
|
@@ -1416,7 +1463,8 @@ const SuperAdminPanel = () => {
|
|
| 1416 |
value={promptValues[input.key] || ''}
|
| 1417 |
onChange={(e) => handlePromptChange(input.key, e.target.value)}
|
| 1418 |
placeholder={input.placeholder}
|
| 1419 |
-
|
|
|
|
| 1420 |
/>
|
| 1421 |
)}
|
| 1422 |
</div>
|
|
|
|
| 593 |
};
|
| 594 |
|
| 595 |
// Member CRUD
|
| 596 |
+
const MEMBER_ROLE_OPTIONS = [
|
| 597 |
+
{ label: 'Admin', value: 'admin' },
|
| 598 |
+
{ label: 'SOC Analyst', value: 'soc_analyst' },
|
| 599 |
+
{ label: 'Organization Admin', value: 'org_admin' },
|
| 600 |
+
{ label: 'Executive', value: 'executive' },
|
| 601 |
+
{ label: 'Super Admin', value: 'super_admin' },
|
| 602 |
+
{ label: 'Support Engineer', value: 'support_engineer' },
|
| 603 |
+
{ label: 'Read Only', value: 'read_only' }
|
| 604 |
+
];
|
| 605 |
+
|
| 606 |
const handleAddMember = () => {
|
| 607 |
+
const orgOptions = [
|
| 608 |
+
{ label: 'None (Global)', value: '' },
|
| 609 |
+
...(organizations || []).map(o => ({ label: o.name, value: o.id }))
|
| 610 |
+
];
|
| 611 |
+
|
| 612 |
+
setPromptValues({ email: '', role: 'admin', org_id: '' });
|
| 613 |
setPromptModal({
|
| 614 |
isOpen: true,
|
| 615 |
+
title: 'Add Member',
|
| 616 |
+
desc: 'Invite or add a new global platform member.',
|
| 617 |
inputs: [
|
| 618 |
{ key: 'email', label: 'User Email', placeholder: 'user@example.com' },
|
| 619 |
+
{ key: 'role', label: 'Role', type: 'select', options: MEMBER_ROLE_OPTIONS },
|
| 620 |
+
{ key: 'org_id', label: 'Organization', type: 'select', options: orgOptions }
|
| 621 |
],
|
| 622 |
onConfirm: async (values) => {
|
| 623 |
try {
|
| 624 |
const res = await fetch('/api/auth/users', {
|
| 625 |
method: 'POST',
|
| 626 |
headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' },
|
| 627 |
+
body: JSON.stringify({ email: values.email, role: values.role, org_id: values.org_id || null })
|
| 628 |
});
|
| 629 |
+
if (res.ok) {
|
| 630 |
+
toast.success('Member added successfully');
|
| 631 |
+
fetchStats();
|
| 632 |
+
} else {
|
| 633 |
+
const data = await res.json();
|
| 634 |
+
toast.error(data.message || 'Failed to add member');
|
| 635 |
+
}
|
| 636 |
+
} catch (err) {
|
| 637 |
+
toast.error('Network error adding member');
|
| 638 |
+
}
|
| 639 |
+
closePrompt();
|
| 640 |
+
}
|
| 641 |
+
});
|
| 642 |
+
};
|
| 643 |
+
|
| 644 |
+
const handleEditMember = (u) => {
|
| 645 |
+
const orgOptions = [
|
| 646 |
+
{ label: 'None (Global)', value: '' },
|
| 647 |
+
...(organizations || []).map(o => ({ label: o.name, value: o.id }))
|
| 648 |
+
];
|
| 649 |
+
|
| 650 |
+
setPromptValues({
|
| 651 |
+
email: u.email,
|
| 652 |
+
role: u.role || 'admin',
|
| 653 |
+
org_id: u.org_id || ''
|
| 654 |
+
});
|
| 655 |
+
|
| 656 |
+
setPromptModal({
|
| 657 |
+
isOpen: true,
|
| 658 |
+
title: 'Edit Member',
|
| 659 |
+
desc: `Update details for ${u.email}`,
|
| 660 |
+
inputs: [
|
| 661 |
+
{ key: 'email', label: 'User Email', disabled: true },
|
| 662 |
+
{ key: 'role', label: 'Role', type: 'select', options: MEMBER_ROLE_OPTIONS },
|
| 663 |
+
{ key: 'org_id', label: 'Organization', type: 'select', options: orgOptions }
|
| 664 |
+
],
|
| 665 |
+
onConfirm: async (vals) => {
|
| 666 |
+
try {
|
| 667 |
+
const res = await fetch(`/api/auth/users/${u.id}/role`, {
|
| 668 |
+
method: 'PUT',
|
| 669 |
+
headers: { 'Authorization': `Bearer ${localStorage.getItem('wss_token')}`, 'Content-Type': 'application/json' },
|
| 670 |
+
body: JSON.stringify({ role: vals.role, org_id: vals.org_id || null })
|
| 671 |
+
});
|
| 672 |
+
if (res.ok) {
|
| 673 |
+
toast.success('Member details updated successfully');
|
| 674 |
+
fetchStats();
|
| 675 |
+
} else {
|
| 676 |
+
const data = await res.json();
|
| 677 |
+
toast.error(data.message || 'Failed to update member');
|
| 678 |
+
}
|
| 679 |
+
} catch (err) {
|
| 680 |
+
toast.error('Network error updating member');
|
| 681 |
+
}
|
| 682 |
closePrompt();
|
| 683 |
}
|
| 684 |
});
|
|
|
|
| 1183 |
<td className="px-md py-sm text-right flex justify-end gap-2">
|
| 1184 |
{!isSupportEngineer ? (
|
| 1185 |
<>
|
| 1186 |
+
<button onClick={() => handleEditMember(u)} className="text-on-surface-variant hover:text-primary transition-colors bg-transparent border-0 cursor-pointer p-1"><Edit className="w-4 h-4" /></button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1187 |
<button onClick={() => {
|
| 1188 |
setConfirmModal({
|
| 1189 |
isOpen: true,
|
|
|
|
| 1463 |
value={promptValues[input.key] || ''}
|
| 1464 |
onChange={(e) => handlePromptChange(input.key, e.target.value)}
|
| 1465 |
placeholder={input.placeholder}
|
| 1466 |
+
disabled={input.disabled}
|
| 1467 |
+
className={`bg-surface-container border border-outline-variant rounded-lg px-3 py-2 focus:border-primary outline-none text-on-surface ${input.disabled ? 'bg-surface-container-high text-on-surface-variant opacity-80 cursor-not-allowed' : ''}`}
|
| 1468 |
/>
|
| 1469 |
)}
|
| 1470 |
</div>
|