diff --git a/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.hbs b/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.hbs
new file mode 100644
index 00000000000..45964d85fd6
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.hbs
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.js b/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.js
new file mode 100644
index 00000000000..ff1571c5088
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/components/admin-config-area-sidebar-experiment.js
@@ -0,0 +1,66 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+import {
+ buildAdminSidebar,
+ useAdminNavConfig,
+} from "discourse/instance-initializers/admin-sidebar";
+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";
+
+export default class AdminConfigAreaSidebarExperiment extends Component {
+ @service adminSidebarExperimentStateManager;
+ @service toasts;
+ @tracked editedNavConfig;
+
+ get defaultAdminNav() {
+ return JSON.stringify(ADMIN_NAV_MAP, null, 2);
+ }
+
+ @action
+ loadDefaultNavConfig() {
+ const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
+ this.editedNavConfig = savedConfig
+ ? JSON.stringify(savedConfig, null, 2)
+ : this.defaultAdminNav;
+ }
+
+ @action
+ resetToDefault() {
+ this.editedNavConfig = this.defaultAdminNav;
+ this.#saveConfig(ADMIN_NAV_MAP);
+ }
+
+ @action
+ applyConfig() {
+ let config = null;
+ try {
+ config = JSON.parse(this.editedNavConfig);
+ } catch {
+ this.toasts.error({
+ duration: 3000,
+ data: {
+ message: "There was an error, make sure the structure is valid JSON.",
+ },
+ });
+ return;
+ }
+
+ this.#saveConfig(config);
+ }
+
+ #saveConfig(config) {
+ this.adminSidebarExperimentStateManager.navConfig = config;
+ resetPanelSections(
+ ADMIN_PANEL,
+ useAdminNavConfig(config),
+ buildAdminSidebar
+ );
+ this.toasts.success({
+ duration: 3000,
+ data: { message: "Sidebar navigation applied successfully!" },
+ });
+ }
+}
diff --git a/app/assets/javascripts/admin/addon/routes/admin-revamp-config-area.js b/app/assets/javascripts/admin/addon/routes/admin-revamp-config-area.js
index 6751bb0aa61..438c67a950b 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-revamp-config-area.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-revamp-config-area.js
@@ -1,10 +1,19 @@
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";
+import { dasherize } from "@ember/string";
+import AdminConfigAreaSidebarExperiment from "admin/components/admin-config-area-sidebar-experiment";
+
+const CONFIG_AREA_COMPONENT_MAP = {
+ "sidebar-experiment": AdminConfigAreaSidebarExperiment,
+};
export default class AdminRevampConfigAreaRoute extends Route {
@service router;
async model(params) {
- return { area: params.area };
+ return {
+ area: params.area,
+ configAreaComponent: CONFIG_AREA_COMPONENT_MAP[dasherize(params.area)],
+ };
}
}
diff --git a/app/assets/javascripts/admin/addon/routes/admin-revamp.js b/app/assets/javascripts/admin/addon/routes/admin-revamp.js
index 9f6ec2ec6a0..880d47d40e9 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-revamp.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-revamp.js
@@ -32,8 +32,10 @@ export default class AdminRoute extends DiscourseRoute {
});
}
- deactivate() {
+ deactivate(transition) {
this.controllerFor("application").set("showTop", true);
- this.sidebarState.setPanel(MAIN_PANEL);
+ if (!transition?.to.name.startsWith("admin")) {
+ this.sidebarState.setPanel(MAIN_PANEL);
+ }
}
}
diff --git a/app/assets/javascripts/admin/addon/routes/admin.js b/app/assets/javascripts/admin/addon/routes/admin.js
index 87ccb1a888c..ded5cf48e37 100644
--- a/app/assets/javascripts/admin/addon/routes/admin.js
+++ b/app/assets/javascripts/admin/addon/routes/admin.js
@@ -1,7 +1,11 @@
+import { inject as service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
+import { MAIN_PANEL } from "discourse/services/sidebar-state";
import I18n from "discourse-i18n";
export default class AdminRoute extends DiscourseRoute {
+ @service sidebarState;
+
titleToken() {
return I18n.t("admin_title");
}
@@ -14,5 +18,6 @@ export default class AdminRoute extends DiscourseRoute {
deactivate() {
this.controllerFor("application").set("showTop", true);
+ this.sidebarState.setPanel(MAIN_PANEL);
}
}
diff --git a/app/assets/javascripts/admin/addon/services/admin-sidebar-experiment-state-manager.js b/app/assets/javascripts/admin/addon/services/admin-sidebar-experiment-state-manager.js
new file mode 100644
index 00000000000..e86677435fe
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/services/admin-sidebar-experiment-state-manager.js
@@ -0,0 +1,16 @@
+import Service from "@ember/service";
+import KeyValueStore from "discourse/lib/key-value-store";
+
+export default class AdminSidebarExperimentStateManager extends Service {
+ STORE_NAMESPACE = "discourse_admin_sidebar_experiment_";
+
+ store = new KeyValueStore(this.STORE_NAMESPACE);
+
+ get navConfig() {
+ return this.store.getObject("navConfig");
+ }
+
+ set navConfig(value) {
+ this.store.setObject({ key: "navConfig", value });
+ }
+}
diff --git a/app/assets/javascripts/admin/addon/templates/admin-revamp-config-area.hbs b/app/assets/javascripts/admin/addon/templates/admin-revamp-config-area.hbs
index 13526e8a57e..71b3e7a52ca 100644
--- a/app/assets/javascripts/admin/addon/templates/admin-revamp-config-area.hbs
+++ b/app/assets/javascripts/admin/addon/templates/admin-revamp-config-area.hbs
@@ -1,3 +1,5 @@
- Config Area ({{@model.area}})
+ {{#if @model.configAreaComponent}}
+ <@model.configAreaComponent />
+ {{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/addon/templates/admin-revamp-config.hbs b/app/assets/javascripts/admin/addon/templates/admin-revamp-config.hbs
index a70240c2105..8f90c9441ca 100644
--- a/app/assets/javascripts/admin/addon/templates/admin-revamp-config.hbs
+++ b/app/assets/javascripts/admin/addon/templates/admin-revamp-config.hbs
@@ -1,5 +1,3 @@
- Config
-
{{outlet}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/instance-initializers/admin-sidebar.js b/app/assets/javascripts/discourse/app/instance-initializers/admin-sidebar.js
index d3b448050a1..97a0b99a849 100644
--- a/app/assets/javascripts/discourse/app/instance-initializers/admin-sidebar.js
+++ b/app/assets/javascripts/discourse/app/instance-initializers/admin-sidebar.js
@@ -1,3 +1,4 @@
+import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
import {
addSidebarPanel,
addSidebarSection,
@@ -23,6 +24,10 @@ function defineAdminSectionLink(BaseCustomSidebarSectionLink) {
return this.adminSidebarNavLink.route;
}
+ get href() {
+ return this.adminSidebarNavLink.href;
+ }
+
get models() {
return this.adminSidebarNavLink.routeModels;
}
@@ -90,14 +95,81 @@ function defineAdminSection(
return AdminNavSection;
}
+export function useAdminNavConfig(navMap) {
+ const adminNavSections = [
+ {
+ text: "",
+ name: "root",
+ hideSectionHeader: true,
+ links: [
+ {
+ name: "Back to Forum",
+ route: "discovery.latest",
+ text: "Back to Forum",
+ icon: "arrow-left",
+ },
+ {
+ name: "Lobby",
+ route: "admin-revamp.lobby",
+ text: "Lobby",
+ icon: "home",
+ },
+ {
+ name: "legacy",
+ route: "admin",
+ text: "Legacy Admin",
+ icon: "wrench",
+ },
+ ],
+ },
+ ];
+
+ return adminNavSections.concat(navMap);
+}
+
+let adminSectionLinkClass = null;
+export function buildAdminSidebar(navConfig) {
+ navConfig.forEach((adminNavSectionData) => {
+ addSidebarSection(
+ (BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
+ // We only want to define the link class once even though we have many different sections.
+ adminSectionLinkClass =
+ adminSectionLinkClass ||
+ defineAdminSectionLink(BaseCustomSidebarSectionLink);
+
+ return defineAdminSection(
+ adminNavSectionData,
+ BaseCustomSidebarSection,
+ adminSectionLinkClass
+ );
+ },
+ ADMIN_PANEL
+ );
+ });
+}
+
export default {
initialize(owner) {
this.currentUser = owner.lookup("service:currentUser");
+ this.siteSettings = owner.lookup("service:site-settings");
if (!this.currentUser?.staff) {
return;
}
+ if (
+ !this.siteSettings.userInAnyGroups(
+ "enable_experimental_admin_ui_groups",
+ this.currentUser
+ )
+ ) {
+ return;
+ }
+
+ this.adminSidebarExperimentStateManager = owner.lookup(
+ "service:admin-sidebar-experiment-state-manager"
+ );
+
addSidebarPanel(
(BaseCustomSidebarPanel) =>
class AdminSidebarPanel extends BaseCustomSidebarPanel {
@@ -106,71 +178,8 @@ export default {
}
);
- let adminSectionLinkClass = null;
-
- // HACK: This is just an example, we need a better way of defining this data.
- const adminNavSections = [
- {
- text: "",
- name: "root",
- hideSectionHeader: true,
- links: [
- {
- name: "Back to Forum",
- route: "discovery.latest",
- text: "Back to Forum",
- icon: "arrow-left",
- },
- {
- name: "Lobby",
- route: "admin-revamp.lobby",
- text: "Lobby",
- icon: "home",
- },
- {
- name: "legacy",
- route: "admin",
- text: "Legacy Admin",
- icon: "wrench",
- },
- ],
- },
- {
- text: "Community",
- name: "community",
- links: [
- {
- name: "Item 1",
- route: "admin-revamp.config.area",
- routeModels: [{ area: "item-1" }],
- text: "Item 1",
- },
- {
- name: "Item 2",
- route: "admin-revamp.config.area",
- routeModels: [{ area: "item-2" }],
- text: "Item 2",
- },
- ],
- },
- ];
-
- adminNavSections.forEach((adminNavSectionData) => {
- addSidebarSection(
- (BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
- // We only want to define the link class once even though we have many different sections.
- adminSectionLinkClass =
- adminSectionLinkClass ||
- defineAdminSectionLink(BaseCustomSidebarSectionLink);
-
- return defineAdminSection(
- adminNavSectionData,
- BaseCustomSidebarSection,
- adminSectionLinkClass
- );
- },
- ADMIN_PANEL
- );
- });
+ const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
+ const navConfig = useAdminNavConfig(savedConfig || ADMIN_NAV_MAP);
+ buildAdminSidebar(navConfig, adminSectionLinkClass);
},
};
diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js b/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js
new file mode 100644
index 00000000000..ffb3c696ac2
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/lib/sidebar/admin-nav-map.js
@@ -0,0 +1,245 @@
+// 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: "admin_plugins_chat", route: "adminPlugins.chat", text: "Chat" },
+ {
+ name: "admin_plugins_discourse-automation",
+ route: "adminPlugins.discourse-automation",
+ text: "Discourse Automation",
+ },
+ {
+ name: "admin_plugins_discourse-automation_new",
+ route: "adminPlugins.discourse-automation.new",
+ text: "Discourse Automation New",
+ },
+ ],
+ },
+ {
+ 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_skipped",
+ route: "adminEmail.skipped",
+ text: "Skipped",
+ },
+ {
+ name: "admin_email_bounced",
+ route: "adminEmail.bounced",
+ text: "Bounced",
+ },
+ {
+ name: "admin_email_received",
+ route: "adminEmail.received",
+ text: "Received",
+ },
+ {
+ name: "admin_email_rejected",
+ route: "adminEmail.rejected",
+ text: "Rejected",
+ },
+ {
+ 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",
+ links: [
+ {
+ name: "admin_logs_staff_action_logs",
+ route: "adminLogs.staffActionLogs",
+ text: "Staff Action Logs",
+ },
+ {
+ name: "admin_logs_screened_emails",
+ route: "adminLogs.screenedEmails",
+ text: "Screened Emails",
+ },
+ {
+ name: "admin_logs_screened_ip_addresses",
+ route: "adminLogs.screenedIpAddresses",
+ text: "Screened Ip Addresses",
+ },
+ {
+ name: "admin_logs_screened_urls",
+ route: "adminLogs.screenedUrls",
+ text: "Screened Urls",
+ },
+ {
+ name: "admin_logs_search_logs",
+ route: "adminSearchLogs",
+ text: "Search Logs",
+ },
+ {
+ name: "admin_logs_search_logs_term",
+ route: "adminSearchLogs.term",
+ text: "Search Term",
+ },
+ { name: "admin_logs", route: "adminLogs", text: "Logs" },
+ ],
+ },
+ {
+ name: "customize",
+ text: "Customize",
+ links: [
+ { name: "admin_customize", route: "adminCustomize", text: "Customize" },
+ {
+ name: "admin_customize_themes",
+ route: "adminCustomizeThemes",
+ text: "Themes",
+ },
+ {
+ 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",
+ },
+ {
+ name: "admin_customize_site_texts",
+ route: "adminSiteText",
+ text: "Site Texts",
+ },
+ {
+ name: "admin_customize_email_templates",
+ route: "adminCustomizeEmailTemplates",
+ text: "Email Templates",
+ },
+ {
+ name: "admin_customize_robots",
+ route: "adminCustomizeRobotsTxt",
+ text: "Robots",
+ },
+ {
+ name: "admin_customize_email_style",
+ route: "adminCustomizeEmailStyle",
+ text: "Email Style",
+ },
+ {
+ 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",
+ },
+ ],
+ },
+ {
+ name: "api",
+ text: "Api",
+ links: [
+ { name: "admin_api_keys", route: "adminApiKeys", text: "Keys" },
+ {
+ name: "admin_api_web_hooks",
+ route: "adminWebHooks",
+ text: "Web Hooks",
+ },
+ { name: "admin_api", route: "adminApi", text: "Api" },
+ ],
+ },
+ {
+ name: "backups",
+ text: "Backups",
+ links: [
+ { name: "admin_backups_logs", route: "admin.backups.logs", text: "Logs" },
+ { name: "admin_backups", route: "admin.backups", text: "Backups" },
+ ],
+ },
+ {
+ name: "badges",
+ text: "Badges",
+ links: [{ name: "admin_badges", route: "adminBadges", text: "Badges" }],
+ },
+];
diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/base-custom-sidebar-panel.js b/app/assets/javascripts/discourse/app/lib/sidebar/base-custom-sidebar-panel.js
index c941a792292..b98850b5141 100644
--- a/app/assets/javascripts/discourse/app/lib/sidebar/base-custom-sidebar-panel.js
+++ b/app/assets/javascripts/discourse/app/lib/sidebar/base-custom-sidebar-panel.js
@@ -1,8 +1,10 @@
+import { tracked } from "@glimmer/tracking";
+
/**
* Base class representing a sidebar section header interface.
*/
export default class BaseCustomSidebarPanel {
- sections = [];
+ @tracked sections = [];
/**
* @returns {boolean} Controls whether the panel is hidden, which means that
diff --git a/app/assets/javascripts/discourse/app/lib/sidebar/custom-sections.js b/app/assets/javascripts/discourse/app/lib/sidebar/custom-sections.js
index 2fa1025b9fc..0a3d98fe30d 100644
--- a/app/assets/javascripts/discourse/app/lib/sidebar/custom-sections.js
+++ b/app/assets/javascripts/discourse/app/lib/sidebar/custom-sections.js
@@ -33,7 +33,7 @@ export function addSidebarPanel(func) {
}
export function addSidebarSection(func, panelKey) {
- const panel = customPanels.find((p) => p.key === panelKey);
+ const panel = customPanels.findBy("key", panelKey);
if (!panel) {
// eslint-disable-next-line no-console
return console.warn(
@@ -45,6 +45,20 @@ export function addSidebarSection(func, panelKey) {
);
}
+export function resetPanelSections(
+ panelKey,
+ newSections = null,
+ sectionBuilder = null
+) {
+ const panel = customPanels.findBy("key", panelKey);
+ if (newSections) {
+ panel.sections = [];
+ sectionBuilder(newSections);
+ } else {
+ panel.sections = [];
+ }
+}
+
export function resetSidebarPanels() {
customPanels = [new MainSidebarPanel()];
currentPanelKey = "main";
diff --git a/app/assets/stylesheets/common/admin/admin_revamp.scss b/app/assets/stylesheets/common/admin/admin_revamp.scss
index f23e723854c..160a3e512da 100644
--- a/app/assets/stylesheets/common/admin/admin_revamp.scss
+++ b/app/assets/stylesheets/common/admin/admin_revamp.scss
@@ -1,12 +1,27 @@
.admin-revamp {
- &__config {
- padding: 1em;
- background-color: var(--primary-low);
- }
-
&__config-area {
padding: 1em;
margin: 1em 0;
background-color: var(--primary-very-low);
}
}
+
+.admin-config-area-sidebar-experiment {
+ &__editor {
+ margin-top: 1em;
+
+ .ace-wrapper {
+ position: relative;
+ width: 100%;
+ height: calc(100vh);
+ min-height: 500px;
+ .ace_editor {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ }
+ }
+ }
+}
diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake
index 915411d3a90..4a3559b5b3f 100644
--- a/lib/tasks/javascript.rake
+++ b/lib/tasks/javascript.rake
@@ -130,6 +130,135 @@ def absolute_sourcemap(dest)
end
end
+def generate_admin_sidebar_nav_map
+ vague_categories = { "root" => [] }
+
+ admin_routes =
+ Rails
+ .application
+ .routes
+ .routes
+ .map do |route|
+ next if route.verb != "GET"
+ path = route.path.spec.to_s.gsub("(.:format)", "")
+ next if !path.include?("admin")
+ next if path.include?("/:") || path.include?("admin-login")
+ path
+ end
+ .compact
+
+ engine_routes =
+ Rails::Engine
+ .subclasses
+ .map do |engine|
+ engine
+ .routes
+ .routes
+ .map do |route|
+ next if route.verb != "GET"
+ path = route.path.spec.to_s.gsub("(.:format)", "")
+ next if !path.include?("admin")
+ next if path.include?("/:") || path.include?("admin-login")
+ path
+ end
+ .compact
+ end
+ .flatten
+
+ admin_routes = admin_routes.concat(engine_routes)
+
+ admin_routes.each do |path|
+ split_path = path.split("/")
+ if split_path.length >= 3
+ vague_categories[split_path[2]] ||= []
+ vague_categories[split_path[2]] << { path: path }
+ else
+ vague_categories["root"] << { path: path }
+ end
+ end
+
+ # Copy this JS to your browser to get the Ember routes.
+ #
+ <<~JS
+ let routeMap = {}
+ for (const [key, value] of Object.entries(
+ Object.fromEntries(
+ Object.entries(
+ Discourse.__container__.lookup("service:router")._router._routerMicrolib
+ .recognizer.names
+ ).filter(([key]) => key.includes("admin"))
+ )
+ )) {
+ let route = value.segments
+ .map((s) => s.value)
+ .join("/")
+ .replace("//", "/");
+ if (
+ route.includes("dummy") ||
+ route.includes("loading") ||
+ route.includes("_id") ||
+ route.includes("admin-invite")
+ ) {
+ continue;
+ }
+ routeMap[key] = route;
+ }
+ console.log(JSON.stringify(routeMap));
+JS
+
+ # Paste the output below between ROUTE_MAP.
+ #
+ ember_route_map = <<~ROUTE_MAP
+ {"admin.dashboard.general":"/admin/","admin.dashboard":"/admin/","admin":"/admin/","admin.dashboardModeration":"/admin/dashboard/moderation","admin.dashboardSecurity":"/admin/dashboard/security","admin.dashboardReports":"/admin/dashboard/reports","adminSiteSettings.index":"/admin/site_settings/","adminSiteSettings":"/admin/site_settings/","adminEmail.sent":"/admin/email/sent","adminEmail.skipped":"/admin/email/skipped","adminEmail.bounced":"/admin/email/bounced","adminEmail.received":"/admin/email/received","adminEmail.rejected":"/admin/email/rejected","adminEmail.previewDigest":"/admin/email/preview-digest","adminEmail.advancedTest":"/admin/email/advanced-test","adminEmail.index":"/admin/email/","adminEmail":"/admin/email/","adminCustomize.colors.index":"/admin/customize/colors/","adminCustomize.colors":"/admin/customize/colors/","adminCustomizeThemes.index":"/admin/customize/themes/","adminCustomizeThemes":"/admin/customize/themes/","adminSiteText.edit":"/admin/customize/site_texts/id","adminSiteText.index":"/admin/customize/site_texts/","adminSiteText":"/admin/customize/site_texts/","adminUserFields":"/admin/customize/user_fields","adminEmojis":"/admin/customize/emojis","adminPermalinks":"/admin/customize/permalinks","adminEmbedding":"/admin/customize/embedding","adminCustomizeEmailTemplates.edit":"/admin/customize/email_templates/id","adminCustomizeEmailTemplates.index":"/admin/customize/email_templates/","adminCustomizeEmailTemplates":"/admin/customize/email_templates/","adminCustomizeRobotsTxt":"/admin/customize/robots","adminCustomizeEmailStyle.edit":"/admin/customize/email_style/field_name","adminCustomizeEmailStyle.index":"/admin/customize/email_style/","adminCustomizeEmailStyle":"/admin/customize/email_style/","adminCustomizeFormTemplates.new":"/admin/customize/form-templates/new","adminCustomizeFormTemplates.edit":"/admin/customize/form-templates/id","adminCustomizeFormTemplates.index":"/admin/customize/form-templates/","adminCustomizeFormTemplates":"/admin/customize/form-templates/","adminWatchedWords.index":"/admin/customize/watched_words/","adminWatchedWords":"/admin/customize/watched_words/","adminCustomize.index":"/admin/customize/","adminCustomize":"/admin/customize/","adminApiKeys.new":"/admin/api/keys/new","adminApiKeys.index":"/admin/api/keys/","adminApiKeys":"/admin/api/keys/","adminWebHooks.index":"/admin/api/web_hooks/","adminWebHooks":"/admin/api/web_hooks/","adminApi.index":"/admin/api/","adminApi":"/admin/api/","admin.backups.logs":"/admin/backups/logs","admin.backups.index":"/admin/backups/","admin.backups":"/admin/backups/","adminReports.show":"/admin/reports/type","adminReports.index":"/admin/reports/","adminReports":"/admin/reports/","adminLogs.staffActionLogs":"/admin/logs/staff_action_logs","adminLogs.screenedEmails":"/admin/logs/screened_emails","adminLogs.screenedIpAddresses":"/admin/logs/screened_ip_addresses","adminLogs.screenedUrls":"/admin/logs/screened_urls","adminSearchLogs.index":"/admin/logs/search_logs/","adminSearchLogs":"/admin/logs/search_logs/","adminSearchLogs.term":"/admin/logs/search_logs/term","adminLogs.index":"/admin/logs/","adminLogs":"/admin/logs/","adminUsersList.show":"/admin/users/list/filter","adminUsersList.index":"/admin/users/list/","adminUsersList":"/admin/users/list/","adminUsers.index":"/admin/users/","adminUsers":"/admin/users/","adminBadges.index":"/admin/badges/","adminBadges":"/admin/badges/","adminPlugins.index":"/admin/plugins/","adminPlugins":"/admin/plugins/","adminPlugins.chat":"/admin/plugins/chat","adminPlugins.discourse-automation.new":"/admin/plugins/discourse-automation/new","adminPlugins.discourse-automation.edit":"/admin/plugins/discourse-automation/id","adminPlugins.discourse-automation.index":"/admin/plugins/discourse-automation/","adminPlugins.discourse-automation":"/admin/plugins/discourse-automation/","admin-revamp.lobby":"/admin-revamp/","admin-revamp":"/admin-revamp/","admin-revamp.config.area":"/admin-revamp/config/area","admin-revamp.config.index":"/admin-revamp/config/","admin-revamp.config":"/admin-revamp/config/"}
+ ROUTE_MAP
+ ember_route_map = JSON.parse(ember_route_map)
+
+ # Match the Ember routes to the rails routes.
+ vague_categories.each do |category, route_data|
+ route_data.each do |rails_route|
+ ember_route_map.each do |ember_route_name, ember_path|
+ rails_route[:ember_route] = ember_route_name if ember_path == rails_route[:path] ||
+ ember_path == rails_route[:path] + "/"
+ end
+ end
+ end
+
+ # Remove all rails routes that don't have an Ember equivalent.
+ vague_categories.each do |category, route_data|
+ vague_categories[category] = route_data.reject { |rails_route| !rails_route.key?(:ember_route) }
+ end
+
+ # Remove all categories that don't have any routes (meaning they are all rails-only).
+ vague_categories.each do |category, route_data|
+ vague_categories.delete(category) if route_data.length == 0
+ end
+
+ # Output in the format needed for sidebar sections and links.
+ vague_categories.map do |category, route_data|
+ category_text = category.titleize.gsub("Admin ", "")
+ {
+ name: category,
+ text: category_text,
+ links:
+ route_data.map do |rails_route|
+ {
+ name: rails_route[:path].split("/").compact_blank.join("_").chomp,
+ route: rails_route[:ember_route],
+ text:
+ rails_route[:path]
+ .split("/")
+ .compact_blank
+ .join(" ")
+ .chomp
+ .titleize
+ .gsub("Admin ", "")
+ .gsub("#{category_text} ", ""),
+ }
+ end,
+ }
+ end
+end
+
task "javascript:update_constants" => :environment do
task_name = "update_constants"
@@ -163,6 +292,10 @@ task "javascript:update_constants" => :environment do
export const AUTO_GROUPS = #{auto_groups.to_json};
JS
+ write_template("discourse/app/lib/sidebar/admin-nav-map.js", task_name, <<~JS)
+ export const ADMIN_NAV_MAP = #{generate_admin_sidebar_nav_map.to_json}
+ JS
+
pretty_notifications = Notification.types.map { |n| " #{n[0]}: #{n[1]}," }.join("\n")
write_template("discourse/tests/fixtures/concerns/notification-types.js", task_name, <<~JS)
diff --git a/spec/system/admin_revamp_sidebar_navigation_spec.rb b/spec/system/admin_revamp_sidebar_navigation_spec.rb
index 08bfe633a10..3f24c8c6ab0 100644
--- a/spec/system/admin_revamp_sidebar_navigation_spec.rb
+++ b/spec/system/admin_revamp_sidebar_navigation_spec.rb
@@ -13,6 +13,7 @@ describe "Admin Revamp | Sidebar Naviagion", type: :system do
it "navigates to the admin revamp from the sidebar" do
visit("/latest")
sidebar_page.click_section_link("Admin Revamp")
- expect(page).to have_content("Admin Revamp Lobby")
+ expect(page).to have_content("Lobby")
+ expect(page).to have_content("Legacy Admin")
end
end