Admin tools: minor improvements

- enable Projects button in navbar for admin projects page
- enable copy of user ID in user info modal
- add null check in UserListController (thanks @moschlar)
This commit is contained in:
yu-i-i
2026-03-14 00:54:29 +01:00
parent 486460f599
commit 9b3b00a740
5 changed files with 47 additions and 22 deletions

View File

@@ -0,0 +1,7 @@
.project-ds-nav-page {
.manage-projects-page {
.nav-item-projects {
display: initial !important;
}
}
}

View File

@@ -11,3 +11,4 @@
@import 'labs';
@import 'admin-tools/user-list';
@import 'admin-tools/user-list-ds-nav';
@import 'admin-tools/manage-projects-page';

View File

@@ -290,8 +290,8 @@ function _sortAndPaginate(users, sort, page) {
function _formatUserInfo(user, maxDate) {
let authMethods = []
if (availableAuthMethods.includes('local') && user.hashedPassword) authMethods.push('local')
if (availableAuthMethods.includes('saml') && user.samlIdentifiers.length > 0) authMethods.push('saml')
if (availableAuthMethods.includes('oidc') && user.thirdPartyIdentifiers.length > 0) authMethods.push('oidc')
if (availableAuthMethods.includes('saml') && user.samlIdentifiers?.length) authMethods.push('saml')
if (availableAuthMethods.includes('oidc') && user.thirdPartyIdentifiers?.length) authMethods.push('oidc')
// If none of the above, mark as LDAP
if (availableAuthMethods.includes('ldap') && authMethods.length === 0 && user.loginCount !== 0) authMethods.push('ldap')

View File

@@ -56,11 +56,13 @@ export function ProjectListDsNav() {
return (
<div className="project-ds-nav-page website-redesign">
<DefaultNavbar
{...navbarProps}
overleafLogo={overleafLogo}
showCloseIcon
/>
<div className="manage-projects-page">
<DefaultNavbar
{...navbarProps}
overleafLogo={overleafLogo}
showCloseIcon
/>
</div>
<div className="project-list-wrapper">
<SidebarDsNav />
<div className="project-ds-nav-content-and-messages">

View File

@@ -42,7 +42,8 @@ function ShowUserInfoModal({
const user = users[0]
const [activationLink, setActivationLink] = useState<string | null>(null)
const [copied, setCopied] = useState(false)
const [copiedId, setCopiedId] = useState(false)
const [copiedActivationLink, setCopiedActivationLink] = useState(false)
useEffect(() => {
if (!showModal) return
@@ -56,22 +57,22 @@ function ShowUserInfoModal({
})
}, [showModal, user.id])
const handleCopy = () => {
if (!activationLink) return
const markCopied = (setter: React.Dispatch<React.SetStateAction<boolean>>) => {
setter(true)
setTimeout(() => setter(false), 1500)
}
const markCopied = () => {
setCopied(true)
setTimeout(() => setCopied(false), 1500)
}
const handleCopy = (text: string, setter: React.Dispatch<React.SetStateAction<boolean>>) => {
if (!text) return
if (navigator.clipboard?.writeText) {
navigator.clipboard.writeText(activationLink).then(markCopied)
navigator.clipboard.writeText(text).then(() => markCopied(setter))
return
}
// fallback for older browsers
const tempInput = document.createElement('input')
tempInput.value = activationLink
tempInput.value = text
tempInput.style.position = 'fixed'
tempInput.style.opacity = '0'
document.body.appendChild(tempInput)
@@ -79,7 +80,7 @@ function ShowUserInfoModal({
document.execCommand('copy')
document.body.removeChild(tempInput)
markCopied()
markCopied(setter)
}
return (
@@ -95,7 +96,22 @@ function ShowUserInfoModal({
<>
<Card.Header>{t('Account')}</Card.Header>
<Body>
<InfoRow label={'ID'} value={user.id} />
<InfoRow
label={'ID'}
value={
<span
style={{ cursor: 'pointer', textDecoration: 'underline' }}
onClick={() => handleCopy(user.id, setCopiedId)}
>
{user.id}
{copiedId && (
<span className="ms-2 text-success">
({t('copied')})
</span>
)}
</span>
}
/>
<InfoRow label={t('email_address')} value={user.email} />
<InfoRow label={t('first_name')} value={user.firstName || '—'} />
<InfoRow label={t('last_name')} value={user.lastName || '—'} />
@@ -116,10 +132,10 @@ function ShowUserInfoModal({
value={
<span
style={{ cursor: 'pointer', textDecoration: 'underline' }}
onClick={handleCopy}
onClick={() => handleCopy(activationLink, setCopiedActivationLink)}
>
{activationLink}
{copied && (
{copiedActivationLink && (
<span className="ms-2 text-success">
({t('copied')})
</span>
@@ -171,4 +187,3 @@ function ShowUserInfoModal({
}
export default ShowUserInfoModal