DEV: Automatically generate all admin links for app for new sidebar (#24175)

NOTE: Most of this is experimental and will be removed at a later
time, which is why things like translations have not been added.

The new /admin-revamp UI uses a sidebar for admin nav. This initial
step adds a script to generate a map of all the current admin nav
into a format the sidebar to read. Then, people can experiment
with different changes to this structure.

The structure can then be edited from `/admin-revamp/config/sidebar-experiment`,
and it is saved to local storage so people can visually experiment with different ways
of showing the admin sidebar links.
This commit is contained in:
Martin Brennan
2023-11-02 10:34:37 +10:00
committed by GitHub
parent 1c395e1a01
commit b53449eac9
15 changed files with 632 additions and 80 deletions

View File

@@ -0,0 +1,35 @@
<div
class="admin-config-area-sidebar-experiment"
{{did-insert this.loadDefaultNavConfig}}
>
<h4>Sidebar Experiment</h4>
<p>Changes you make here will be applied to the admin sidebar and persist
between reloads
<em>on this device only</em>. Note that in addition to the
<code>text</code>
and
<code>route</code>
options, you can also specify a
<code>icon</code>
or a
<code>href</code>, if you want to link to a specific page but don't know the
Ember route.</p>
<DButton
@action={{this.resetToDefault}}
@translatedLabel="Reset to Default"
/>
<DButton
class="btn-primary"
@action={{this.applyConfig}}
@translatedLabel="Apply Config"
/>
<div class="admin-config-area-sidebar-experiment__editor">
<AceEditor
@content={{this.editedNavConfig}}
@editorId="admin-config-area-sidebar-experiment"
@save={{this.applyNav}}
/>
</div>
</div>

View File

@@ -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!" },
});
}
}

View File

@@ -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)],
};
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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 });
}
}

View File

@@ -1,3 +1,5 @@
<div class="admin-revamp__config-area">
Config Area ({{@model.area}})
{{#if @model.configAreaComponent}}
<@model.configAreaComponent />
{{/if}}
</div>

View File

@@ -1,5 +1,3 @@
<div class="admin-revamp__config">
Config
{{outlet}}
</div>