larxius commited on
Commit
97266bb
·
verified ·
1 Parent(s): 7a063b0

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
- setPromptValues({ role: 'soc_analyst', email: '', org_id: '' });
 
 
 
 
 
598
  setPromptModal({
599
  isOpen: true,
600
- title: 'Add Global Member',
601
- desc: 'Invite a user to an organization.',
602
  inputs: [
603
  { key: 'email', label: 'User Email', placeholder: 'user@example.com' },
604
- { key: 'role', label: 'Role', placeholder: 'soc_analyst, executive, org_admin, etc' },
605
- { key: 'org_id', label: 'Organization ID', placeholder: 'Optional' }
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) fetchStats();
615
- } catch (err) { }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- className="bg-surface-container border border-outline-variant rounded-lg px-3 py-2 focus:border-primary outline-none text-on-surface"
 
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>