mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Add admin password reset option for users (#5180)
## Summary - add backend support for admins to reset user passwords and optionally email notifications when SMTP is enabled - surface mail capability in admin settings data for the UI - add a shared change-password modal hooked into People and Team user actions with random password generation and email options ## Testing - not run (not requested) ------ [Codex Task](https://chatgpt.com/codex/tasks/task_b_6934b978fe3c83289b5b95dec79b3d38) --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
parent
c980ee10c0
commit
9c03914edd
@@ -30,6 +30,7 @@ import LoginRequiredBanner from '@app/components/shared/config/LoginRequiredBann
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UpdateSeatsButton from '@app/components/shared/UpdateSeatsButton';
|
||||
import { useLicense } from '@app/contexts/LicenseContext';
|
||||
import ChangeUserPasswordModal from '@app/components/shared/ChangeUserPasswordModal';
|
||||
|
||||
export default function PeopleSection() {
|
||||
const { t } = useTranslation();
|
||||
@@ -43,8 +44,11 @@ export default function PeopleSection() {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [inviteModalOpened, setInviteModalOpened] = useState(false);
|
||||
const [editUserModalOpened, setEditUserModalOpened] = useState(false);
|
||||
const [changePasswordModalOpened, setChangePasswordModalOpened] = useState(false);
|
||||
const [passwordUser, setPasswordUser] = useState<User | null>(null);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [mailEnabled, setMailEnabled] = useState(false);
|
||||
|
||||
// License information
|
||||
const [licenseInfo, setLicenseInfo] = useState<{
|
||||
@@ -119,6 +123,7 @@ export default function PeopleSection() {
|
||||
premiumEnabled: adminData.premiumEnabled,
|
||||
totalUsers: adminData.totalUsers,
|
||||
});
|
||||
setMailEnabled(adminData.mailEnabled);
|
||||
} else {
|
||||
// Provide example data when login is disabled
|
||||
const exampleUsers: User[] = [
|
||||
@@ -179,6 +184,7 @@ export default function PeopleSection() {
|
||||
|
||||
setUsers(exampleUsers);
|
||||
setTeams(exampleTeams);
|
||||
setMailEnabled(false);
|
||||
|
||||
// Example license information
|
||||
setLicenseInfo({
|
||||
@@ -267,6 +273,16 @@ export default function PeopleSection() {
|
||||
setEditUserModalOpened(true);
|
||||
};
|
||||
|
||||
const openChangePasswordModal = (user: User) => {
|
||||
setPasswordUser(user);
|
||||
setChangePasswordModalOpened(true);
|
||||
};
|
||||
|
||||
const closeChangePasswordModal = () => {
|
||||
setChangePasswordModalOpened(false);
|
||||
setPasswordUser(null);
|
||||
};
|
||||
|
||||
const closeEditModal = () => {
|
||||
setEditUserModalOpened(false);
|
||||
setSelectedUser(null);
|
||||
@@ -562,7 +578,20 @@ export default function PeopleSection() {
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown style={{ zIndex: Z_INDEX_OVER_CONFIG_MODAL }}>
|
||||
<Menu.Item onClick={() => openEditModal(user)} disabled={!loginEnabled}>{t('workspace.people.editRole')}</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<LocalIcon icon="edit" width="1rem" height="1rem" />}
|
||||
onClick={() => openEditModal(user)}
|
||||
disabled={!loginEnabled}
|
||||
>
|
||||
{t('workspace.people.editRole')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<LocalIcon icon="lock" width="1rem" height="1rem" />}
|
||||
onClick={() => openChangePasswordModal(user)}
|
||||
disabled={!loginEnabled}
|
||||
>
|
||||
{t('workspace.people.changePassword.action', 'Change password')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={user.enabled ? <LocalIcon icon="person-off" width="1rem" height="1rem" /> : <LocalIcon icon="person-check" width="1rem" height="1rem" />}
|
||||
onClick={() => handleToggleEnabled(user)}
|
||||
@@ -591,6 +620,14 @@ export default function PeopleSection() {
|
||||
onSuccess={fetchData}
|
||||
/>
|
||||
|
||||
<ChangeUserPasswordModal
|
||||
opened={changePasswordModalOpened}
|
||||
onClose={closeChangePasswordModal}
|
||||
user={passwordUser}
|
||||
onSuccess={fetchData}
|
||||
mailEnabled={mailEnabled}
|
||||
/>
|
||||
|
||||
{/* Edit User Modal */}
|
||||
<Modal
|
||||
opened={editUserModalOpened}
|
||||
|
||||
+30
@@ -22,6 +22,7 @@ import { alert } from '@app/components/toast';
|
||||
import { teamService, Team } from '@app/services/teamService';
|
||||
import { User, userManagementService } from '@app/services/userManagementService';
|
||||
import { Z_INDEX_OVER_CONFIG_MODAL } from '@app/styles/zIndex';
|
||||
import ChangeUserPasswordModal from '@app/components/shared/ChangeUserPasswordModal';
|
||||
|
||||
interface TeamDetailsSectionProps {
|
||||
teamId: number;
|
||||
@@ -38,6 +39,8 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
const [userLastRequest, setUserLastRequest] = useState<Record<string, number>>({});
|
||||
const [addMemberModalOpened, setAddMemberModalOpened] = useState(false);
|
||||
const [changeTeamModalOpened, setChangeTeamModalOpened] = useState(false);
|
||||
const [changePasswordModalOpened, setChangePasswordModalOpened] = useState(false);
|
||||
const [passwordUser, setPasswordUser] = useState<User | null>(null);
|
||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||
const [selectedUserId, setSelectedUserId] = useState<string>('');
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string>('');
|
||||
@@ -47,6 +50,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
const [licenseInfo, setLicenseInfo] = useState<{
|
||||
availableSlots: number;
|
||||
} | null>(null);
|
||||
const [mailEnabled, setMailEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTeamDetails();
|
||||
@@ -70,6 +74,7 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
setLicenseInfo({
|
||||
availableSlots: adminData.availableSlots,
|
||||
});
|
||||
setMailEnabled(adminData.mailEnabled);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch team details:', error);
|
||||
alert({ alertType: 'error', title: t('workspace.teams.loadError', 'Failed to load team details') });
|
||||
@@ -172,6 +177,16 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
setChangeTeamModalOpened(true);
|
||||
};
|
||||
|
||||
const openChangePasswordModal = (user: User) => {
|
||||
setPasswordUser(user);
|
||||
setChangePasswordModalOpened(true);
|
||||
};
|
||||
|
||||
const closeChangePasswordModal = () => {
|
||||
setChangePasswordModalOpened(false);
|
||||
setPasswordUser(null);
|
||||
};
|
||||
|
||||
const handleChangeTeam = async () => {
|
||||
if (!selectedUser || !selectedTeamId) {
|
||||
alert({ alertType: 'error', title: t('workspace.teams.changeTeam.selectTeamRequired', 'Please select a team') });
|
||||
@@ -398,6 +413,13 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
>
|
||||
{t('workspace.teams.changeTeam.label', 'Change Team')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<LocalIcon icon="lock" width="1rem" height="1rem" />}
|
||||
onClick={() => openChangePasswordModal(user)}
|
||||
disabled={processing}
|
||||
>
|
||||
{t('workspace.people.changePassword.action', 'Change password')}
|
||||
</Menu.Item>
|
||||
{team.name !== 'Internal' && team.name !== 'Default' && (
|
||||
<Menu.Item
|
||||
leftSection={<LocalIcon icon="person-remove" width="1rem" height="1rem" />}
|
||||
@@ -427,6 +449,14 @@ export default function TeamDetailsSection({ teamId, onBack }: TeamDetailsSectio
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
|
||||
<ChangeUserPasswordModal
|
||||
opened={changePasswordModalOpened}
|
||||
onClose={closeChangePasswordModal}
|
||||
user={passwordUser}
|
||||
onSuccess={fetchTeamDetails}
|
||||
mailEnabled={mailEnabled}
|
||||
/>
|
||||
|
||||
{/* Add Member Modal */}
|
||||
<Modal
|
||||
opened={addMemberModalOpened}
|
||||
|
||||
Reference in New Issue
Block a user