larxius commited on
Commit
5c792a8
·
verified ·
1 Parent(s): 449a0bf

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 || null
932
  })
933
  });
934
  const data = await res.json().catch(() => ({}));
935
  if (res.ok) {
936
- toast.success('Member details & password updated successfully');
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
- {!isSupportEngineer ? (
1599
- <>
1600
- <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>
1601
- <button onClick={() => {
1602
- setConfirmModal({
1603
- isOpen: true,
1604
- title: 'Delete Member',
1605
- desc: `Are you sure you want to delete ${u.email}?`,
1606
- type: 'error',
1607
- onConfirm: async () => {
1608
- try {
1609
- const res = await authFetch(`/api/auth/users/${u.id}`, {
1610
- method: 'DELETE'
1611
- });
1612
- if (res.ok) {
1613
- toast.success('Member deleted successfully');
1614
- fetchStats();
1615
- } else {
1616
- toast.error('Failed to delete member');
1617
- }
1618
- } catch (err) {
1619
- toast.error('Network error deleting member');
 
 
1620
  }
1621
- closeConfirm();
 
1622
  }
1623
- });
1624
- }} className="text-on-surface-variant hover:text-error transition-colors bg-transparent border-0 cursor-pointer p-1"><Trash2 className="w-4 h-4" /></button>
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>