FEATURE: Initial admin sidebar navigation (#24789)

This is v0 of admin sidebar navigation, which moves
all of the top-level admin nav from the top of the page
into a sidebar. This is hidden behind a enable_admin_sidebar_navigation
site setting, and is opt-in for now.

This sidebar is dynamically shown whenever the user enters an
admin route in the UI, and is hidden and replaced with either
the:

* Main forum sidebar
* Chat sidebar

Depending on where they navigate to. For now, custom sections
are not supported in the admin sidebar.

This commit removes the experimental admin sidebar generation rake
task but keeps the experimental sidebar UI for now for further
testing; it just uses the real nav as the default now.
This commit is contained in:
Martin Brennan
2023-12-18 11:48:25 +10:00
committed by GitHub
parent 194c84b217
commit 6de00f89c2
17 changed files with 252 additions and 367 deletions

View File

@ -10,6 +10,7 @@ import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
import { resetPanelSections } from "discourse/lib/sidebar/custom-sections";
import { ADMIN_PANEL } from "discourse/services/sidebar-state";
// TODO (martin) (2024-02-01) Remove this experimental UI.
export default class AdminConfigAreaSidebarExperiment extends Component {
@service adminSidebarExperimentStateManager;
@service toasts;

View File

@ -1,4 +1,5 @@
import Controller from "@ember/controller";
import { readOnly } from "@ember/object/computed";
import { inject as service } from "@ember/service";
import { dasherize } from "@ember/string";
import discourseComputed from "discourse-common/utils/decorators";
@ -6,6 +7,8 @@ import discourseComputed from "discourse-common/utils/decorators";
export default class AdminController extends Controller {
@service router;
@readOnly("siteSettings.enable_admin_sidebar_navigation") showAdminSidebar;
@discourseComputed("siteSettings.enable_group_directory")
showGroups(enableGroupDirectory) {
return !enableGroupDirectory;

View File

@ -14,12 +14,7 @@ export default class AdminRoute extends DiscourseRoute {
}
activate() {
if (
!this.siteSettings.userInAnyGroups(
"enable_experimental_admin_ui_groups",
this.currentUser
)
) {
if (!this.siteSettings.enable_admin_sidebar_navigation) {
return DiscourseURL.redirectTo("/admin");
}

View File

@ -1,23 +1,35 @@
import { inject as service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
import { MAIN_PANEL } from "discourse/services/sidebar-state";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/services/sidebar-state";
import I18n from "discourse-i18n";
export default class AdminRoute extends DiscourseRoute {
@service sidebarState;
@service siteSettings;
titleToken() {
return I18n.t("admin_title");
}
activate() {
if (this.siteSettings.enable_admin_sidebar_navigation) {
this.sidebarState.setPanel(ADMIN_PANEL);
this.sidebarState.setSeparatedMode();
this.sidebarState.hideSwitchPanelButtons();
}
this.controllerFor("application").setProperties({
showTop: false,
});
}
deactivate() {
deactivate(transition) {
this.controllerFor("application").set("showTop", true);
this.sidebarState.setPanel(MAIN_PANEL);
if (this.siteSettings.enable_admin_sidebar_navigation) {
if (!transition?.to.name.startsWith("admin")) {
this.sidebarState.setPanel(MAIN_PANEL);
}
}
}
}

View File

@ -4,37 +4,40 @@
<div class="row">
<div class="full-width">
<div class="admin-main-nav">
<ul class="nav nav-pills">
<NavItem @route="admin.dashboard" @label="admin.dashboard.title" />
{{#if this.currentUser.admin}}
<NavItem
@route="adminSiteSettings"
@label="admin.site_settings.title"
/>
{{/if}}
<NavItem @route="adminUsers" @label="admin.users.title" />
{{#if this.showGroups}}
<NavItem @route="groups" @label="admin.groups.title" />
{{/if}}
{{#if this.showBadges}}
<NavItem @route="adminBadges" @label="admin.badges.title" />
{{/if}}
{{#if this.currentUser.admin}}
<NavItem @route="adminEmail" @label="admin.email.title" />
{{/if}}
<NavItem @route="adminLogs" @label="admin.logs.title" />
<NavItem @route="adminCustomize" @label="admin.customize.title" />
{{#if this.currentUser.admin}}
<NavItem @route="adminApi" @label="admin.api.title" />
{{#if this.siteSettings.enable_backups}}
<NavItem @route="admin.backups" @label="admin.backups.title" />
{{#unless this.showAdminSidebar}}
<div class="admin-main-nav">
<ul class="nav nav-pills">
<NavItem @route="admin.dashboard" @label="admin.dashboard.title" />
{{#if this.currentUser.admin}}
<NavItem
@route="adminSiteSettings"
@label="admin.site_settings.title"
/>
{{/if}}
<NavItem @route="adminPlugins" @label="admin.plugins.title" />
{{/if}}
<PluginOutlet @name="admin-menu" />
</ul>
</div>
<NavItem @route="adminUsers" @label="admin.users.title" />
{{#if this.showGroups}}
<NavItem @route="groups" @label="admin.groups.title" />
{{/if}}
{{#if this.showBadges}}
<NavItem @route="adminBadges" @label="admin.badges.title" />
{{/if}}
{{#if this.currentUser.admin}}
<NavItem @route="adminEmail" @label="admin.email.title" />
{{/if}}
<NavItem @route="adminLogs" @label="admin.logs.title" />
<NavItem @route="adminCustomize" @label="admin.customize.title" />
{{#if this.currentUser.admin}}
<NavItem @route="adminApi" @label="admin.api.title" />
{{#if this.siteSettings.enable_backups}}
<NavItem @route="admin.backups" @label="admin.backups.title" />
{{/if}}
<NavItem @route="adminPlugins" @label="admin.plugins.title" />
{{/if}}
{{! TODO: What do we do with this?? How many plugins are using? }}
<PluginOutlet @name="admin-menu" />
</ul>
</div>
{{/unless}}
<div class="boxed white admin-content">
<div class="admin-contents {{this.adminContentsClassName}}">

View File

@ -4,6 +4,7 @@ import {
addSidebarSection,
} from "discourse/lib/sidebar/custom-sections";
import { ADMIN_PANEL } from "discourse/services/sidebar-state";
import I18n from "discourse-i18n";
function defineAdminSectionLink(BaseCustomSidebarSectionLink) {
const SidebarAdminSectionLink = class extends BaseCustomSidebarSectionLink {
@ -33,7 +34,9 @@ function defineAdminSectionLink(BaseCustomSidebarSectionLink) {
}
get text() {
return this.adminSidebarNavLink.text;
return this.adminSidebarNavLink.label
? I18n.t(this.adminSidebarNavLink.label)
: this.adminSidebarNavLink.text;
}
get prefixType() {
@ -77,7 +80,9 @@ function defineAdminSection(
}
get text() {
return this.adminNavSectionData.text;
return this.adminNavSectionData.label
? I18n.t(this.adminNavSectionData.label)
: this.adminNavSectionData.text;
}
get links() {
@ -103,22 +108,47 @@ export function useAdminNavConfig(navMap) {
hideSectionHeader: true,
links: [
{
name: "Back to Forum",
name: "back_to_forum",
route: "discovery.latest",
text: "Back to Forum",
label: "admin.back_to_forum",
icon: "arrow-left",
},
{
name: "Lobby",
route: "admin-revamp.lobby",
text: "Lobby",
name: "admin_dashboard",
route: "admin.dashboard",
label: "admin.dashboard.title",
icon: "home",
},
{
name: "legacy",
route: "admin",
text: "Legacy Admin",
icon: "wrench",
name: "admin_site_settings",
route: "adminSiteSettings",
label: "admin.site_settings.title",
icon: "cog",
},
{
name: "admin_users",
route: "adminUsers",
label: "admin.users.title",
icon: "users",
},
{
name: "admin_reports",
route: "adminReports",
label: "admin.dashboard.reports_tab",
icon: "chart-pie",
},
{
name: "admin_plugins",
route: "adminPlugins",
label: "admin.plugins.title",
icon: "puzzle-piece",
},
{
name: "admin_badges",
route: "adminBadges",
label: "admin.badges.title",
icon: "certificate",
},
],
},
@ -157,12 +187,7 @@ export default {
return;
}
if (
!this.siteSettings.userInAnyGroups(
"enable_experimental_admin_ui_groups",
this.currentUser
)
) {
if (!this.siteSettings.enable_admin_sidebar_navigation) {
return;
}
@ -179,7 +204,19 @@ export default {
);
const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
const navConfig = useAdminNavConfig(savedConfig || ADMIN_NAV_MAP);
const navMap = savedConfig || ADMIN_NAV_MAP;
if (this.siteSettings.experimental_form_templates) {
navMap.findBy("name", "customize").links.push({
name: "admin_customize_form_templates",
route: "adminCustomizeFormTemplates",
label: "admin.form_templates.nav_title",
icon: "list",
});
}
const navConfig = useAdminNavConfig(navMap);
buildAdminSidebar(navConfig, adminSectionLinkClass);
},
};

View File

@ -1,232 +1,182 @@
// DO NOT EDIT THIS FILE!!!
// Update it by running `rake javascript:update_constants`
export const ADMIN_NAV_MAP = [
{
name: "root",
text: "Root",
links: [
{ name: "admin-revamp", route: "admin-revamp", text: "Revamp" },
{ name: "admin", route: "admin", text: "Admin" },
],
},
{
name: "plugins",
text: "Plugins",
links: [{ name: "admin_plugins", route: "adminPlugins", text: "Plugins" }],
},
{
name: "site_settings",
text: "Site Settings",
links: [
{
name: "admin_site_settings",
route: "adminSiteSettings",
text: "Site Settings",
},
],
},
{
name: "reports",
text: "Reports",
links: [{ name: "admin_reports", route: "adminReports", text: "Reports" }],
},
{
name: "users",
text: "Users",
links: [
{ name: "admin_users_list", route: "adminUsersList", text: "List" },
{ name: "admin_users", route: "adminUsers", text: "Users" },
],
},
{
name: "email",
text: "Email",
links: [
{ name: "admin_email_sent", route: "adminEmail.sent", text: "Sent" },
{
name: "admin_email",
route: "adminEmail.index",
label: "admin.email.settings",
icon: "cog",
},
{
name: "admin_email_sent",
route: "adminEmail.sent",
label: "admin.email.sent",
icon: "arrow-right",
},
{
name: "admin_email_skipped",
route: "adminEmail.skipped",
text: "Skipped",
label: "admin.email.skipped",
icon: "angle-double-right",
},
{
name: "admin_email_bounced",
route: "adminEmail.bounced",
text: "Bounced",
label: "admin.email.bounced",
icon: "times",
},
{
name: "admin_email_received",
route: "adminEmail.received",
text: "Received",
label: "admin.email.received",
icon: "inbox",
},
{
name: "admin_email_rejected",
route: "adminEmail.rejected",
text: "Rejected",
label: "admin.email.rejected",
icon: "ban",
},
{
name: "admin_email_preview-digest",
route: "adminEmail.previewDigest",
text: "Preview Digest",
},
{
name: "admin_email_advanced-test",
route: "adminEmail.advancedTest",
text: "Advanced Test",
},
{ name: "admin_email", route: "adminEmail", text: "Email" },
],
},
{
name: "logs",
text: "Logs",
label: "admin.logs.title",
links: [
{
name: "admin_logs_staff_action_logs",
route: "adminLogs.staffActionLogs",
text: "Staff Action Logs",
label: "admin.logs.staff_actions.title",
icon: "user-shield",
},
{
name: "admin_logs_screened_emails",
route: "adminLogs.screenedEmails",
text: "Screened Emails",
label: "admin.logs.screened_emails.title",
icon: "envelope",
},
{
name: "admin_logs_screened_ip_addresses",
route: "adminLogs.screenedIpAddresses",
text: "Screened Ip Addresses",
label: "admin.logs.screened_ips.title",
icon: "globe",
},
{
name: "admin_logs_screened_urls",
route: "adminLogs.screenedUrls",
text: "Screened Urls",
label: "admin.logs.screened_urls.title",
icon: "globe",
},
{
name: "admin_logs_search_logs",
route: "adminSearchLogs",
text: "Search Logs",
label: "admin.logs.search_logs.title",
icon: "search",
},
{
name: "admin_logs_search_logs_term",
route: "adminSearchLogs.term",
text: "Search Term",
},
{ name: "admin_logs", route: "adminLogs", text: "Logs" },
],
},
{
name: "customize",
text: "Customize",
label: "admin.customize.title",
links: [
{ name: "admin_customize", route: "adminCustomize", text: "Customize" },
{
name: "admin_customize_themes",
route: "adminCustomizeThemes",
text: "Themes",
label: "admin.customize.theme.title",
icon: "paint-brush",
},
{
name: "admin_customize_colors",
route: "adminCustomize.colors",
text: "Colors",
},
{
name: "admin_customize_permalinks",
route: "adminPermalinks",
text: "Permalinks",
},
{
name: "admin_customize_embedding",
route: "adminEmbedding",
text: "Embedding",
},
{
name: "admin_customize_user_fields",
route: "adminUserFields",
text: "User Fields",
},
{ name: "admin_customize_emojis", route: "adminEmojis", text: "Emojis" },
{
name: "admin_customize_form-templates",
route: "adminCustomizeFormTemplates",
text: "Form Templates",
},
{
name: "admin_customize_form-templates_new",
route: "adminCustomizeFormTemplates.new",
text: "Form Templates New",
label: "admin.customize.colors.title",
icon: "palette",
},
{
name: "admin_customize_site_texts",
route: "adminSiteText",
text: "Site Texts",
label: "admin.site_text.title",
icon: "language",
},
{
name: "admin_customize_email_templates",
route: "adminCustomizeEmailTemplates",
text: "Email Templates",
},
{
name: "admin_customize_robots",
route: "adminCustomizeRobotsTxt",
text: "Robots",
label: "admin.email.templates_title",
icon: "envelope",
},
{
name: "admin_customize_email_style",
route: "adminCustomizeEmailStyle",
text: "Email Style",
label: "admin.customize.email_style.title",
icon: "envelope",
},
{
name: "admin_customize_user_fields",
route: "adminUserFields",
label: "admin.user_fields.title",
icon: "user-edit",
},
{
name: "admin_customize_emojis",
route: "adminEmojis",
label: "admin.emoji.title",
icon: "discourse-emojis",
},
{
name: "admin_customize_permalinks",
route: "adminPermalinks",
label: "admin.permalink.title",
icon: "link",
},
{
name: "admin_customize_embedding",
route: "adminEmbedding",
label: "admin.embedding.title",
icon: "code",
},
{
name: "admin_customize_watched_words",
route: "adminWatchedWords",
text: "Watched Words",
},
],
},
{
name: "dashboard",
text: "Dashboard",
links: [
{
name: "admin_dashboard_moderation",
route: "admin.dashboardModeration",
text: "Moderation",
},
{
name: "admin_dashboard_security",
route: "admin.dashboardSecurity",
text: "Security",
},
{
name: "admin_dashboard_reports",
route: "admin.dashboardReports",
text: "Reports",
label: "admin.watched_words.title",
icon: "eye",
},
],
},
{
name: "api",
text: "Api",
label: "admin.api.title",
links: [
{ name: "admin_api_keys", route: "adminApiKeys", text: "Keys" },
{
name: "admin_api_keys",
route: "adminApiKeys",
icon: "key",
label: "admin.api.keys",
},
{
name: "admin_api_web_hooks",
route: "adminWebHooks",
text: "Web Hooks",
label: "admin.web_hooks.title",
icon: "globe",
},
{ name: "admin_api", route: "adminApi", text: "Api" },
],
},
{
name: "backups",
text: "Backups",
label: "admin.backups.menu.backups",
links: [
{ name: "admin_backups_logs", route: "admin.backups.logs", text: "Logs" },
{ name: "admin_backups", route: "admin.backups", text: "Backups" },
{
name: "admin_backups",
route: "admin.backups.index",
label: "admin.backups.menu.backups",
icon: "archive",
},
{
name: "admin_backups_logs",
route: "admin.backups.logs",
label: "admin.backups.menu.logs",
icon: "stream",
},
],
},
{
name: "badges",
text: "Badges",
links: [{ name: "admin_badges", route: "adminBadges", text: "Badges" }],
},
];

View File

@ -31,10 +31,7 @@ export default class AdminRevampSectionLink extends BaseSectionLink {
return (
this.currentUser.staff &&
this.siteSettings.userInAnyGroups(
"enable_experimental_admin_ui_groups",
this.currentUser
)
this.siteSettings.enable_admin_sidebar_navigation
);
}

View File

@ -22,12 +22,6 @@ class SidebarUrl < ActiveRecord::Base
},
{ name: "Review", path: "/review", icon: "flag", segment: SidebarUrl.segments["primary"] },
{ name: "Admin", path: "/admin", icon: "wrench", segment: SidebarUrl.segments["primary"] },
{
name: "Admin Revamp",
path: "/admin-revamp",
icon: "star",
segment: SidebarUrl.segments["primary"],
},
{ name: "Users", path: "/u", icon: "users", segment: SidebarUrl.segments["secondary"] },
{
name: "About",