From cf84ba650afbaa2a5a3a358fbf7771d6bbda317b Mon Sep 17 00:00:00 2001 From: yu-i-i Date: Thu, 19 Mar 2026 02:34:33 +0100 Subject: [PATCH] Admin tools: themed dashboard --- services/web/config/settings.defaults.js | 4 +- .../modules/admin-tools/entries-per-page.scss | 26 +++ .../admin-tools/manage-projects-page.scss | 9 + .../admin-tools/manage-users-page.scss | 15 ++ .../modules/admin-tools/pagination-dark.scss | 33 ++++ .../modules/admin-tools/user-list-ds-nav.scss | 184 ++++++++++++------ .../modules/admin-tools/user-list.scss | 78 ++------ .../web/frontend/stylesheets/modules/all.scss | 3 + .../app/src/ProjectListController.mjs | 35 +++- .../app/src/UserListController.mjs | 20 +- .../app/views/manage-projects-react.pug | 2 + .../app/views/manage-users-react.pug | 2 + .../frontend/js/manage-projects-root.tsx | 16 +- .../frontend/js/manage-users-root.tsx | 16 +- .../components/project-list-ds-nav.tsx | 7 +- .../components/project-list-summary.tsx | 56 +++--- .../components/sidebar/sidebar-ds-nav.tsx | 2 +- .../user-list/components/user-list-ds-nav.tsx | 7 +- .../components/user-list-summary.tsx | 58 +++--- 19 files changed, 383 insertions(+), 190 deletions(-) create mode 100644 services/web/frontend/stylesheets/modules/admin-tools/entries-per-page.scss create mode 100644 services/web/frontend/stylesheets/modules/admin-tools/manage-users-page.scss create mode 100644 services/web/frontend/stylesheets/modules/admin-tools/pagination-dark.scss diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index 43fcd0bd2b..2dd6ac3f21 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -1169,7 +1169,9 @@ module.exports = { }, splitTestOverrides: { - // new-fancy-feature': 'enabled', + ...(process.env.OVERLEAF_THEMED_DASHBOARD?.toLowerCase() === 'true' ? { + 'themed-project-dashboard': 'enabled', + } : {}), ...(process.env.OVERLEAF_HISTORY_RESTORE?.toLowerCase() === 'true' ? { 'history-ranges-support': 'enabled', 'revert-file': 'enabled', diff --git a/services/web/frontend/stylesheets/modules/admin-tools/entries-per-page.scss b/services/web/frontend/stylesheets/modules/admin-tools/entries-per-page.scss new file mode 100644 index 0000000000..97f6a272ce --- /dev/null +++ b/services/web/frontend/stylesheets/modules/admin-tools/entries-per-page.scss @@ -0,0 +1,26 @@ +.entries-per-page-toggle { + cursor: pointer; + background-color: var(--bg-light-tertiary); + padding: 0 var(--spacing-02); + border-radius: var(--border-radius-medium); + &:hover { + background-color: var(--bg-accent-03); + } + & + .dropdown-menu { + min-width: auto; + width: auto; + } +} + +.entries-per-page-toggle::after { + display: none; +} + +@include theme('default') { + .entries-per-page-toggle { + background-color: var(--bg-dark-tertiary); + &:hover { + background-color: var(--green-70); + } + } +} diff --git a/services/web/frontend/stylesheets/modules/admin-tools/manage-projects-page.scss b/services/web/frontend/stylesheets/modules/admin-tools/manage-projects-page.scss index 358047669b..77402d3a4b 100644 --- a/services/web/frontend/stylesheets/modules/admin-tools/manage-projects-page.scss +++ b/services/web/frontend/stylesheets/modules/admin-tools/manage-projects-page.scss @@ -5,3 +5,12 @@ } } } + +@include theme('default') { + #manage-projects-root { + .website-redesign-navbar, + .website-redesign .navbar-default { + @include navbar-dark; + } + } +} diff --git a/services/web/frontend/stylesheets/modules/admin-tools/manage-users-page.scss b/services/web/frontend/stylesheets/modules/admin-tools/manage-users-page.scss new file mode 100644 index 0000000000..67bb061162 --- /dev/null +++ b/services/web/frontend/stylesheets/modules/admin-tools/manage-users-page.scss @@ -0,0 +1,15 @@ +@include theme('default') { + #manage-users-root { + .website-redesign-navbar, + .website-redesign .navbar-default { + @include navbar-dark; + } + } + .user-ds-nav-page { + @include dark-dropdown-menu; + .table, + .table-container { + @include dark-table; + } + } +} diff --git a/services/web/frontend/stylesheets/modules/admin-tools/pagination-dark.scss b/services/web/frontend/stylesheets/modules/admin-tools/pagination-dark.scss new file mode 100644 index 0000000000..d18c0a58a8 --- /dev/null +++ b/services/web/frontend/stylesheets/modules/admin-tools/pagination-dark.scss @@ -0,0 +1,33 @@ +@include theme('default') { + .project-ds-nav-page, + .user-ds-nav-page { + .pagination { + > li { + > a, + > span, + > button { + color: var(--content-primary-inverse); + background-color: var(--bg-secondary); + border-color: var(--border-primary); + + &:hover, + &:focus { + color: var(--content-primary-inverse); + background-color: var(--bg-tertiary); + } + } + } + + > .active > a, + > .active > span, + > .active > button { + &, + &:hover, + &:focus { + color: var(--content-primary-inverse); + background-color: var(--green-70); + } + } + } + } +} diff --git a/services/web/frontend/stylesheets/modules/admin-tools/user-list-ds-nav.scss b/services/web/frontend/stylesheets/modules/admin-tools/user-list-ds-nav.scss index 0deefe096c..f5d900481f 100644 --- a/services/web/frontend/stylesheets/modules/admin-tools/user-list-ds-nav.scss +++ b/services/web/frontend/stylesheets/modules/admin-tools/user-list-ds-nav.scss @@ -1,9 +1,42 @@ +:root { + --ds-nav-active-bg: var(--bg-accent-03); + --ds-nav-active-color: var(--green-60); + --theme-toggle-selected-background: var(--green-20); + --ds-nav-content-bg-secondary: var(--bg-light-secondary); + --table-icon-bg-hover: 27 34 44; + --themed-dashboard-popover-bg: var(--bg-dark-primary); + --themed-dashboard-popover-color: var(--content-primary-dark); + --themed-dashboard-popover-link-color: var(--link-ui-dark); + --themed-dashboard-popover-link-hover-color: var(--link-ui-hover-dark); + --themed-dashboard-popover-link-visited-color: var(--link-ui-visited-dark); + --ds-nav-color-scheme: light; + + @include theme('default') { + --ds-nav-active-bg: var(--green-70); + --ds-nav-active-color: var(--green-10); + --theme-toggle-selected-background: var(--green-70); + --ds-nav-content-bg-secondary: var(--bg-dark-secondary); + --table-icon-bg-hover: 255 255 255; + --themed-dashboard-popover-bg: var(--bg-light-primary); + --themed-dashboard-popover-color: var(--content-primary); + --themed-dashboard-popover-link-color: var(--link-ui); + --themed-dashboard-popover-link-hover-color: var(--link-ui-hover); + --themed-dashboard-popover-link-visited-color: var(--link-ui-visited); + --ds-nav-color-scheme: dark; + } +} + +body { + --ds-nav-bg-color: var(--bg-primary-themed); + --ds-nav-hover-bg: var(--bg-secondary-themed); + --ds-nav-color: var(--content-secondary-themed); +} + .user-ds-nav-page { - display: flex; - flex-direction: column; - height: 100vh; - height: 100dvh; - color: var(--content-secondary); + @include full-height-stacked-page; + + color-scheme: var(--ds-nav-color-scheme); + color: var(--content-secondary-themed); // NOTE-AC: This code can be eliminated when we remove sidebar-navigation-ui-update --navbar-btn-padding-h: var(--spacing-06); @@ -15,9 +48,6 @@ .navbar-default { position: relative; - --navbar-toggler-expanded-bg: none; - --navbar-toggler-expanded-color: var(--content-secondary); - .navbar-header .navbar-logo { @include media-breakpoint-up(md) { position: relative; @@ -48,7 +78,7 @@ .nav-item-help::before { content: ''; display: block; - border-top: 1px solid var(--border-divider); + border-top: 1px solid var(--border-divider-themed); margin: var(--spacing-07) var(--spacing-06); } @@ -63,8 +93,8 @@ border-radius: var(--border-radius-medium); &.show { - background-color: var(--bg-accent-03); - color: var(--green-60); + background-color: var(--ds-nav-active-bg); + color: var(--ds-nav-active-color); } } } @@ -78,6 +108,7 @@ } .user-list-wrapper { + background-color: var(--ds-nav-bg-color); flex-grow: 1; display: flex; overflow-y: hidden; @@ -95,6 +126,10 @@ .create-account-button-wrapper { padding: 0 var(--spacing-08) var(--spacing-05) var(--spacing-05); border-bottom: solid 1px transparent; + + &.show-shadow { + border-bottom-color: var(--border-divider-themed); + } } nav { @@ -117,39 +152,13 @@ border-top: solid 1px transparent; &.show-shadow { - border-top-color: var(--border-divider); + border-top-color: var(--border-divider-themed); } } + } - .user-list-sidebar-survey-link { - color: var(--content-secondary) !important; - } - - .survey-notification { - background-color: var(--bg-light-secondary); - color: var(--content-secondary); - box-shadow: none; - border-radius: var(--border-radius-large); - position: relative; - - .user-notification-close { - border: none; - padding: 0; - background: none; - position: absolute; - top: var(--spacing-07); - right: var(--spacing-07); - color: inherit; - } - - p { - margin-bottom: var(--spacing-03); - } - } - - .user-list-sidebar-survey-wrapper .user-notifications { - margin-bottom: var(--spacing-05); - } + hr { + border-top: 1px solid var(--border-divider-themed); } ul.user-list-filters { @@ -163,7 +172,7 @@ > button { width: 100%; text-align: left; - color: var(--content-secondary); + color: var(--ds-nav-color); background: none; border-radius: var(--border-radius-medium); border: none; @@ -171,18 +180,19 @@ } &:hover button { - background-color: var(--bg-light-secondary); + background-color: var(--ds-nav-hover-bg); } &.active button { - background-color: var(--bg-accent-03); - color: var(--green-60); + background-color: var(--ds-nav-active-bg); + color: var(--ds-nav-active-color); font-weight: bold; } } .dropdown-header { font-weight: bold; + color: var(--ds-nav-color); } > li.tag { @@ -195,7 +205,7 @@ button.dropdown-toggle { border-radius: var(--border-radius-full); border: none; - color: var(--content-secondary); + color: var(--ds-nav-color); height: 20px; width: 20px; padding: 0; @@ -224,7 +234,7 @@ .user-dash-table { .btn-link { - color: var(--content-secondary); + color: var(--content-secondary-themed); height: var(--spacing-08); width: var(--spacing-08); border-radius: 100%; @@ -233,12 +243,12 @@ &:hover, &:focus { - background-color: #d9d9d9 !important; + background-color: rgb(var(--table-icon-bg-hover) / 8%) !important; } } .dash-cell-name a { - color: var(--content-secondary) !important; + color: var(--content-primary-themed) !important; } } @@ -249,7 +259,7 @@ > * { @include media-breakpoint-up(md) { - border-left: 1px solid var(--border-divider); + border-left: 1px solid var(--border-divider-themed); } } @@ -257,17 +267,16 @@ flex-grow: 1; overflow-y: auto; position: relative; - background-color: var(--bg-light-secondary); + background-color: var(--ds-nav-content-bg-secondary); @include media-breakpoint-up(md) { border-top-left-radius: var(--border-radius-large); - border-top: 1px solid var(--border-divider); + border-top: 1px solid var(--border-divider-themed); } } .cookie-banner { position: static; - background-color: var(--bg-light-primary); // Remove the parts of the shadow that stick out of the sides clip-path: inset(-13px 0 0 0); @@ -276,11 +285,16 @@ z-index: auto; } } + + .btn-link { + --link-color: var(--link-web-themed); + --link-hover-color: var(--link-web-hover-themed); + } } .ds-nav-icon-dropdown { .dropdown-toggle { - color: var(--content-secondary); + color: var(--ds-nav-color); background: none; height: 44px; width: 44px; @@ -288,12 +302,12 @@ overflow: hidden; &:hover { - background-color: var(--bg-light-secondary); + background-color: var(--ds-nav-hover-bg); } &.show { - background-color: var(--bg-accent-03); - color: var(--green-60); + background-color: var(--ds-nav-active-bg); + color: var(--ds-nav-active-color); } &::after { @@ -314,6 +328,7 @@ } .ds-nav-ds-name { + color: var(--ds-nav-color); margin-bottom: var(--spacing-05); span { @@ -323,4 +338,59 @@ @include body-xs; } } + + .add-affiliation { + color: var(--ds-nav-color); + } +} + +.theme-toggle { + display: flex; + justify-content: space-between; + cursor: default !important; + align-items: center; + + &:hover { + background-color: transparent !important; + color: var(--content-primary-themed); + } + + legend { + font-size: var(--font-size-02); + line-height: var(--line-height-02); + margin-bottom: 0; + } +} + +.theme-toggle-radios { + display: flex; + border-radius: var(--border-radius-full); + background-color: var(--bg-secondary-themed); + padding: var(--spacing-01); + gap: var(--spacing-01); +} + +.theme-toggle-radio { + display: flex; + + input { + all: unset; + } + + label { + display: flex; + padding: var(--spacing-03); + margin-bottom: 0; + border-radius: var(--border-radius-full); + cursor: pointer; + color: var(--content-primary-themed); + } + + .material-symbols { + font-size: var(--font-size-03); + } + + input:checked + label { + background-color: var(--theme-toggle-selected-background); + } } diff --git a/services/web/frontend/stylesheets/modules/admin-tools/user-list.scss b/services/web/frontend/stylesheets/modules/admin-tools/user-list.scss index 6e7a6f6640..75c3856d0b 100644 --- a/services/web/frontend/stylesheets/modules/admin-tools/user-list.scss +++ b/services/web/frontend/stylesheets/modules/admin-tools/user-list.scss @@ -17,7 +17,7 @@ padding: 0 var(--spacing-02); } -#user-list-root .user-notifications ul { +#manage-users-root .user-notifications ul { margin-bottom: 0; } @@ -50,7 +50,7 @@ .user-list-title { @include heading-sm; - color: $content-secondary; + color: var(--content-secondary-themed); font-weight: bold; min-width: 0; } @@ -241,7 +241,7 @@ ul.user-list-filters { @include media-breakpoint-down(md) { tr:not(:last-child) { - border-bottom: 1px solid $table-border-color; + border-bottom: 1px solid var(--border-divider-themed); } td { @@ -260,7 +260,7 @@ ul.user-list-filters { .table-header-sort-btn { border: 0; text-align: left; - color: var(--content-secondary); + color: var(--content-secondary-themed); background-color: transparent; padding: 0; font-weight: bold; @@ -271,7 +271,7 @@ ul.user-list-filters { &:hover, &:focus { - color: var(--content-secondary); + color: var(--content-secondary-themed); text-decoration: none; } @@ -283,11 +283,8 @@ ul.user-list-filters { .dash-row-admin { font-weight: bold; - td { - a { - color: darkred !important; - --bs-link-color: darkred !important; - } + a { + color: var(--content-danger) !important; } } @@ -474,7 +471,7 @@ ul.user-list-filters { } .dash-cell-actions { - width: 12; + width: 12%; } } @@ -532,57 +529,18 @@ ul.user-list-filters { } } -.survey-notification { - display: flex; - flex-wrap: wrap; - padding: var(--spacing-06); - background-color: var(--bg-dark-tertiary); - border-color: transparent; - color: var(--neutral-20); - box-shadow: 2px 4px 6px rgb(0 0 0 / 25%); - border-radius: var(--border-radius-base); - - @include media-breakpoint-up(md) { - flex-wrap: nowrap; - } - - button.close { - @extend .text-white; - - padding: 0; - } -} - -.user-list-sidebar-survey-wrapper { - .survey-notification { - font-size: var(--font-size-02); - - a { - text-decoration: none; - } - } - - @include media-breakpoint-down(md) { - .survey-notification { - font-size: unset; - - .user-list-sidebar-survey-link { - display: block; - align-items: center; - min-width: 48px; - min-height: 48px; - padding-top: var(--spacing-07); - } - } - } -} - -.user-list-load-more-button { - margin-bottom: var(--spacing-05); -} - form.user-search { .form-group { margin-bottom: 0; } } + +@include theme('default') { + .dash-cell-email a, + .dash-cell-email-date a { + color: var(--green-40); + } + .dash-row-admin a { + color: var(--content-danger-dark) !important; + } +} diff --git a/services/web/frontend/stylesheets/modules/all.scss b/services/web/frontend/stylesheets/modules/all.scss index a98ccb6122..23462389e6 100644 --- a/services/web/frontend/stylesheets/modules/all.scss +++ b/services/web/frontend/stylesheets/modules/all.scss @@ -9,6 +9,9 @@ @import 'symbol-palette'; @import 'writefull'; @import 'labs'; +@import 'admin-tools/entries-per-page'; @import 'admin-tools/user-list'; @import 'admin-tools/user-list-ds-nav'; @import 'admin-tools/manage-projects-page'; +@import 'admin-tools/manage-users-page'; +@import 'admin-tools/pagination-dark'; diff --git a/services/web/modules/admin-tools/app/src/ProjectListController.mjs b/services/web/modules/admin-tools/app/src/ProjectListController.mjs index 215fc53347..2c85fde71c 100644 --- a/services/web/modules/admin-tools/app/src/ProjectListController.mjs +++ b/services/web/modules/admin-tools/app/src/ProjectListController.mjs @@ -4,21 +4,32 @@ import { fileURLToPath } from 'node:url' import { expressify } from '@overleaf/promise-utils' import logger from '@overleaf/logger' import Metrics from '@overleaf/metrics' +import SessionManager from '../../../../app/src/Features/Authentication/SessionManager.mjs' +import PrivilegeLevels from '../../../../app/src/Features/Authorization/PrivilegeLevels.mjs' import ProjectHelper from '../../../../app/src/Features/Project/ProjectHelper.mjs' import ProjectGetter from '../../../../app/src/Features/Project/ProjectGetter.mjs' -import PrivilegeLevels from '../../../../app/src/Features/Authorization/PrivilegeLevels.mjs' -import SessionManager from '../../../../app/src/Features/Authentication/SessionManager.mjs' +import ProjectDeleter from '../../../../app/src/Features/Project/ProjectDeleter.mjs' +import UserSettingsHelper from '../../../../app/src/Features/Project/UserSettingsHelper.mjs' import UserGetter from '../../../../app/src/Features/User/UserGetter.mjs' -import { OError } from '../../../../app/src/Features/Errors/Errors.js' import { User } from '../../../../app/src/models/User.mjs' import { Project } from '../../../../app/src/models/Project.mjs' import { DeletedProject } from '../../../../app/src/models/DeletedProject.mjs' -import ProjectDeleter from '../../../../app/src/Features/Project/ProjectDeleter.mjs' +import { OError } from '../../../../app/src/Features/Errors/Errors.js' import HttpErrorHandler from '../../../../app/src/Features/Errors/HttpErrorHandler.mjs' +import SplitTestHandler from '../../../../app/src/Features/SplitTests/SplitTestHandler.mjs' const __dirname = Path.dirname(fileURLToPath(import.meta.url)) +function cleanupSession(req) { + // cleanup redirects at the end of the redirect chain + delete req.session.postCheckoutRedirect + delete req.session.postLoginRedirect + delete req.session.postOnboardingRedirect +} + async function manageProjectsPage(req, res, next) { + cleanupSession(req) + const projectsBlobPending = _getProjects().catch(err => { logger.err({ err }, 'projects listing in background failed') return undefined @@ -30,8 +41,24 @@ async function manageProjectsPage(req, res, next) { status: prefetchedProjectsBlob ? 'success' : 'error', }) + const userId = SessionManager.getLoggedInUserId(req.session) + const user = await User.findById(userId, 'ace') + + const userSettings = await UserSettingsHelper.buildUserSettings( + req, + res, + user + ) + + await SplitTestHandler.promises.getAssignment( + req, + res, + 'themed-project-dashboard' + ) + res.render(Path.resolve(__dirname, '../views/manage-projects-react'), { title: 'Manage Projects', + userSettings, prefetchedProjectsBlob, }) } diff --git a/services/web/modules/admin-tools/app/src/UserListController.mjs b/services/web/modules/admin-tools/app/src/UserListController.mjs index 8b775d8235..101cd82dc8 100644 --- a/services/web/modules/admin-tools/app/src/UserListController.mjs +++ b/services/web/modules/admin-tools/app/src/UserListController.mjs @@ -16,12 +16,14 @@ import OneTimeTokenHandler from '../../../../app/src/Features/Security/OneTimeTo import UserGetter from '../../../../app/src/Features/User/UserGetter.mjs' import UserUpdater from '../../../../app/src/Features/User/UserUpdater.mjs' import UserDeleter from '../../../../app/src/Features/User/UserDeleter.mjs' +import UserSettingsHelper from '../../../../app/src/Features/Project/UserSettingsHelper.mjs' import ProjectDeleter from '../../../../app/src/Features/Project/ProjectDeleter.mjs' import OwnershipTransferHandler from '../../../../app/src/Features/Collaborators/OwnershipTransferHandler.mjs' import HttpErrorHandler from '../../../../app/src/Features/Errors/HttpErrorHandler.mjs' import ErrorController from '../../../../app/src/Features/Errors/ErrorController.mjs' import Errors, { OError } from '../../../../app/src/Features/Errors/Errors.js' import { db } from '../../../../app/src/infrastructure/mongodb.mjs' +import SplitTestHandler from '../../../../app/src/Features/SplitTests/SplitTestHandler.mjs' const __dirname = Path.dirname(fileURLToPath(import.meta.url)) @@ -71,8 +73,6 @@ function cleanupSession(req) { async function manageUsersPage(req, res, next) { cleanupSession(req) - const userId = SessionManager.getLoggedInUserId(req.session) - const usersBlobPending = _getUsers().catch(err => { logger.err({ err }, 'users listing in background failed') return undefined @@ -84,8 +84,24 @@ async function manageUsersPage(req, res, next) { status: prefetchedUsersBlob ? 'success' : 'error', }) + await SplitTestHandler.promises.getAssignment( + req, + res, + 'themed-project-dashboard' + ) + + const userId = SessionManager.getLoggedInUserId(req.session) + const user = await User.findById(userId, 'ace') + + const userSettings = await UserSettingsHelper.buildUserSettings( + req, + res, + user + ) + res.render(Path.resolve(__dirname, '../views/manage-users-react'), { title: 'Manage Users', + userSettings, prefetchedUsersBlob, availableAuthMethods, userDetailsUpdatedOnLogin, diff --git a/services/web/modules/admin-tools/app/views/manage-projects-react.pug b/services/web/modules/admin-tools/app/views/manage-projects-react.pug index e90bf0cf35..8be5bfa442 100644 --- a/services/web/modules/admin-tools/app/views/manage-projects-react.pug +++ b/services/web/modules/admin-tools/app/views/manage-projects-react.pug @@ -15,6 +15,8 @@ block append meta data-type='json' content=prefetchedProjectsBlob ) + meta(name='ol-userSettings' data-type='json' content=userSettings) + meta(name='ol-overallThemes' data-type='json' content=overallThemes) if suggestedLanguageSubdomainConfig meta( name='ol-suggestedLanguage' diff --git a/services/web/modules/admin-tools/app/views/manage-users-react.pug b/services/web/modules/admin-tools/app/views/manage-users-react.pug index fa77f9e2ce..9b9f1e8ae7 100644 --- a/services/web/modules/admin-tools/app/views/manage-users-react.pug +++ b/services/web/modules/admin-tools/app/views/manage-users-react.pug @@ -16,6 +16,8 @@ block append meta data-type='json' content=prefetchedUsersBlob ) + meta(name='ol-userSettings' data-type='json' content=userSettings) + meta(name='ol-overallThemes' data-type='json' content=overallThemes) if suggestedLanguageSubdomainConfig meta( name='ol-suggestedLanguage' diff --git a/services/web/modules/admin-tools/frontend/js/manage-projects-root.tsx b/services/web/modules/admin-tools/frontend/js/manage-projects-root.tsx index d8338ca3b0..f586ecc053 100644 --- a/services/web/modules/admin-tools/frontend/js/manage-projects-root.tsx +++ b/services/web/modules/admin-tools/frontend/js/manage-projects-root.tsx @@ -13,15 +13,15 @@ function ManageProjectsRoot() { if (!isReady) return null return ( - - - - + + + + - - - - + + + + ) } diff --git a/services/web/modules/admin-tools/frontend/js/manage-users-root.tsx b/services/web/modules/admin-tools/frontend/js/manage-users-root.tsx index 55b1f941b2..6172903199 100644 --- a/services/web/modules/admin-tools/frontend/js/manage-users-root.tsx +++ b/services/web/modules/admin-tools/frontend/js/manage-users-root.tsx @@ -28,15 +28,15 @@ function ManageUsersRoot() { if (!isReady) return null return ( - - - - + + + + - - - - + + + + ) } diff --git a/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-ds-nav.tsx b/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-ds-nav.tsx index d4f4a0a78c..f54854aad4 100644 --- a/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-ds-nav.tsx +++ b/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-ds-nav.tsx @@ -15,11 +15,13 @@ import DefaultNavbar from '@/shared/components/navbar/default-navbar' import Footer from '@/shared/components/footer/footer' import SidebarDsNav from './sidebar/sidebar-ds-nav' import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg' +import overleafLogoDark from '@/shared/svgs/overleaf-a-ds-solution-mallard-dark.svg' import { getUserName } from '../util/user' import { useProjectListContext } from '../context/project-list-context' import { useUserIdentityContext } from '../../user-list/context/user-identity-context' import Pagination from '@/shared/components/pagination-cep' import ProjectListSummary from './project-list-summary' +import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme' export function ProjectListDsNav() { @@ -39,6 +41,7 @@ export function ProjectListDsNav() { totalPages, } = useProjectListContext() const { getUserNameById } = useUserIdentityContext() + const activeOverallTheme = useActiveOverallTheme('themed-project-dashboard') const userName = projectsOwnerId ? getUserNameById(projectsOwnerId) : t('all_users') const tableTopArea = ( @@ -59,7 +62,9 @@ export function ProjectListDsNav() {
diff --git a/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-summary.tsx b/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-summary.tsx index 156e5a2059..f5ed58db69 100644 --- a/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-summary.tsx +++ b/services/web/modules/admin-tools/frontend/js/project-list/components/project-list-summary.tsx @@ -1,7 +1,14 @@ import { useTranslation } from 'react-i18next' -import OLFormSelect from '@/shared/components/ol/ol-form-select' +import { + Dropdown, + DropdownMenu, + DropdownItem, + DropdownToggle, +} from '@/shared/components/dropdown/dropdown-menu' import { useProjectListContext } from '../context/project-list-context' +const OPTIONS = [20, 40, 80] + export default function ProjectListSummary() { const { visibleProjects, @@ -14,37 +21,40 @@ export default function ProjectListSummary() { return (
-

{t('showing_x_out_of_n_projects', { x: visibleProjects.length, n: visibleProjects.length + hiddenProjectsCount, })} + · + - setProjectsPerPage(Number(e.target.value))} - style={{ - width: 'auto', - border: '1px solid #ccc', - background: 'var(--green-10)', - padding: '0 0.2rem', - boxShadow: 'none', - cursor: 'pointer', - }} - > - - - - - - {t('per_page')} - + + + + {projectsPerPage} + + + + {OPTIONS.map((value) => ( + setProjectsPerPage(value)} + > + {value} + + ))} + + + + {t('per_page')} -

) } diff --git a/services/web/modules/admin-tools/frontend/js/project-list/components/sidebar/sidebar-ds-nav.tsx b/services/web/modules/admin-tools/frontend/js/project-list/components/sidebar/sidebar-ds-nav.tsx index 4ae17f5181..e27f1a5add 100644 --- a/services/web/modules/admin-tools/frontend/js/project-list/components/sidebar/sidebar-ds-nav.tsx +++ b/services/web/modules/admin-tools/frontend/js/project-list/components/sidebar/sidebar-ds-nav.tsx @@ -27,7 +27,7 @@ function SidebarDsNav() { const { sessionUser } = getMeta('ol-navbar') const { containerRef, scrolledUp } = useScrolled() const themedDsNav = useFeatureFlag('themed-project-dashboard') - + const { getUserNameById } = useUserIdentityContext() const { projectsOwnerId } = useProjectListContext() diff --git a/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-ds-nav.tsx b/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-ds-nav.tsx index 50867c744f..4f658eef1c 100644 --- a/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-ds-nav.tsx +++ b/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-ds-nav.tsx @@ -17,7 +17,9 @@ import DefaultNavbar from '@/shared/components/navbar/default-navbar' import Footer from '@/shared/components/footer/footer' import SidebarDsNav from './sidebar/sidebar-ds-nav' import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg' +import overleafLogoDark from '@/shared/svgs/overleaf-a-ds-solution-mallard-dark.svg' import CookieBanner from '@/shared/components/cookie-banner' +import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme' import Pagination from '@/shared/components/pagination-cep' import UserListSummary from './user-list-summary' @@ -36,6 +38,7 @@ export function UserListDsNav() { setCurrentPage, totalPages, } = useUserListContext() + const activeOverallTheme = useActiveOverallTheme('themed-project-dashboard') const tableTopArea = (
@@ -54,7 +57,9 @@ export function UserListDsNav() {
diff --git a/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-summary.tsx b/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-summary.tsx index d6743f86a9..558d548556 100644 --- a/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-summary.tsx +++ b/services/web/modules/admin-tools/frontend/js/user-list/components/user-list-summary.tsx @@ -1,7 +1,14 @@ import { useTranslation } from 'react-i18next' -import OLFormSelect from '@/shared/components/ol/ol-form-select' +import { + Dropdown, + DropdownMenu, + DropdownItem, + DropdownToggle, +} from '@/shared/components/dropdown/dropdown-menu' import { useUserListContext } from '../context/user-list-context' +const OPTIONS = [20, 40, 80] + export default function UserListSummary() { const { visibleUsers, @@ -9,42 +16,45 @@ export default function UserListSummary() { usersPerPage, setUsersPerPage, } = useUserListContext() - const { t } = useTranslation() return (
-

{t('showing_x_out_of_n_users', { x: visibleUsers.length, n: visibleUsers.length + hiddenUsersCount, })} + · + - setUsersPerPage(Number(e.target.value))} - style={{ - width: 'auto', - border: '1px solid #ccc', - background: 'var(--green-10)', - padding: '0 0.2rem', - boxShadow: 'none', - cursor: 'pointer', - }} - > - - - - - - {t('per_page')} - + + + + {usersPerPage} + + + + {OPTIONS.map((value) => ( + setUsersPerPage(value)} + > + {value} + + ))} + + + + + {t('per_page')} -

) }