UX: Use DPageHeader on the Emails page (#30781)

There are a few changes here to make the Emails admin page more consistent with the rest of the admin UI.

- The header and navigation menu have been updated.
- The sidebar now stays highlighted when visiting the email admin sub-pages.
- Moved the Template editor from /admin/customize/email_templates to /admin/email/templates, so it fit as a sub-page.
- Removed the link to the Template editor from the Customize section of the old top menu, since it's accessible from the Emails section, instead.
This commit is contained in:
Gary Pendergast 2025-01-15 15:36:16 +11:00 committed by GitHub
parent 698e7a17dd
commit 5da6a06ce3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 84 additions and 82 deletions

View File

@ -6,11 +6,11 @@ import discourseComputed from "discourse/lib/decorators";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import { i18n } from "discourse-i18n";
export default class AdminCustomizeEmailTemplatesEditController extends Controller.extend(
export default class AdminEmailTemplatesEditController extends Controller.extend(
bufferedProperty("emailTemplate")
) {
@service dialog;
@controller adminCustomizeEmailTemplates;
@controller adminEmailTemplates;
emailTemplate = null;
saved = false;

View File

@ -3,7 +3,7 @@ import { action } from "@ember/object";
import { sort } from "@ember/object/computed";
import { service } from "@ember/service";
export default class AdminCustomizeEmailTemplatesController extends Controller {
export default class AdminEmailTemplatesController extends Controller {
@service router;
titleSorting = ["title"];
@ -11,6 +11,6 @@ export default class AdminCustomizeEmailTemplatesController extends Controller {
@action
onSelectTemplate(template) {
this.router.transitionTo("adminCustomizeEmailTemplates.edit", template);
this.router.transitionTo("adminEmailTemplates.edit", template);
}
}

View File

@ -4,7 +4,7 @@ import RestModel from "discourse/models/rest";
export default class EmailTemplate extends RestModel {
revert() {
return ajax(`/admin/customize/email_templates/${this.id}`, {
return ajax(`/admin/email/templates/${this.id}`, {
type: "DELETE",
}).then((result) =>
getProperties(result.email_template, "subject", "body", "can_revert")

View File

@ -1,9 +1,9 @@
import Route from "@ember/routing/route";
import { scrollTop } from "discourse/mixins/scroll-top";
export default class AdminCustomizeEmailTemplatesEditRoute extends Route {
export default class AdminEmailTemplatesEditRoute extends Route {
model(params) {
const all = this.modelFor("adminCustomizeEmailTemplates");
const all = this.modelFor("adminEmailTemplates");
return all.findBy("id", params.id);
}

View File

@ -2,7 +2,7 @@ import { action } from "@ember/object";
import Route from "@ember/routing/route";
import { service } from "@ember/service";
export default class AdminCustomizeEmailTemplatesRoute extends Route {
export default class AdminEmailTemplatesRoute extends Route {
@service router;
model() {
@ -15,13 +15,11 @@ export default class AdminCustomizeEmailTemplatesRoute extends Route {
@action
didTransition() {
const editController = this.controllerFor(
"adminCustomizeEmailTemplates.edit"
);
const editController = this.controllerFor("adminEmailTemplates.edit");
if (!editController.emailTemplate) {
this.router.transitionTo(
"adminCustomizeEmailTemplates.edit",
"adminEmailTemplates.edit",
this.controller.get("sortedTemplates.firstObject")
);
}

View File

@ -38,6 +38,13 @@ export default function () {
this.route("rejected");
this.route("previewDigest", { path: "/preview-digest" });
this.route("advancedTest", { path: "/advanced-test" });
this.route(
"adminEmailTemplates",
{ path: "/templates", resetNamespace: true },
function () {
this.route("edit", { path: "/:id" });
}
);
}
);
@ -80,13 +87,6 @@ export default function () {
this.route("edit", { path: "/:id" });
}
);
this.route(
"adminCustomizeEmailTemplates",
{ path: "/email_templates", resetNamespace: true },
function () {
this.route("edit", { path: "/:id" });
}
);
this.route("adminCustomizeRobotsTxt", {
path: "/robots",
resetNamespace: true,

View File

@ -17,11 +17,6 @@
@label="admin.site_text.title"
class="admin-customize-site-text"
/>
<NavItem
@route="adminCustomizeEmailTemplates"
@label="admin.customize.email_templates.title"
class="admin-customize-email-templates"
/>
<NavItem
@route="adminCustomizeEmailStyle"
@label="admin.customize.email_style.title"

View File

@ -1,7 +1,7 @@
<ComboBox
@value={{this.emailTemplate.id}}
@content={{this.adminCustomizeEmailTemplates.sortedTemplates}}
@onChange={{this.adminCustomizeEmailTemplates.onSelectTemplate}}
@content={{this.adminEmailTemplates.sortedTemplates}}
@onChange={{this.adminEmailTemplates.onSelectTemplate}}
@nameProperty="title"
/>

View File

@ -1,23 +1,33 @@
<AdminNav>
<NavItem @route="adminEmail.index" @label="admin.email.settings" />
<NavItem
@route="adminEmail.previewDigest"
@label="admin.email.preview_digest"
/>
<NavItem
@route="adminEmail.advancedTest"
@label="admin.email.advanced_test.title"
/>
<NavItem
@route="adminCustomizeEmailTemplates"
@label="admin.email.templates"
/>
<NavItem @route="adminEmail.sent" @label="admin.email.sent" />
<NavItem @route="adminEmail.skipped" @label="admin.email.skipped" />
<NavItem @route="adminEmail.bounced" @label="admin.email.bounced" />
<NavItem @route="adminEmail.received" @label="admin.email.received" />
<NavItem @route="adminEmail.rejected" @label="admin.email.rejected" />
</AdminNav>
<DPageHeader
@titleLabel={{i18n "admin.email.title"}}
@descriptionLabel={{i18n "admin.email.description"}}
@shouldDisplay={{true}}
>
<:breadcrumbs>
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
<DBreadcrumbsItem
@path="/admin/email"
@label={{i18n "admin.email.title"}}
/>
</:breadcrumbs>
<:tabs>
<NavItem @route="adminEmail.index" @label="admin.email.settings" />
<NavItem
@route="adminEmail.previewDigest"
@label="admin.email.preview_digest"
/>
<NavItem
@route="adminEmail.advancedTest"
@label="admin.email.advanced_test.title"
/>
<NavItem @route="adminEmailTemplates" @label="admin.email.templates" />
<NavItem @route="adminEmail.sent" @label="admin.email.sent" />
<NavItem @route="adminEmail.skipped" @label="admin.email.skipped" />
<NavItem @route="adminEmail.bounced" @label="admin.email.bounced" />
<NavItem @route="adminEmail.received" @label="admin.email.received" />
<NavItem @route="adminEmail.rejected" @label="admin.email.rejected" />
</:tabs>
</DPageHeader>
<div class="admin-container">
{{outlet}}

View File

@ -1,7 +1,9 @@
import RestAdapter from "discourse/adapters/rest";
export default class EmailTemplateAdapter extends RestAdapter {
basePath() {
return "/admin/customize/";
pathFor(store, type, id) {
return id === undefined
? "/admin/email/templates"
: `/admin/email/templates/${id}`;
}
}

View File

@ -162,7 +162,7 @@ export const ADMIN_NAV_MAP = [
links: [
{
name: "admin_server_setup",
route: "adminEmail.index",
route: "adminEmail",
label: "admin.email_settings.sidebar_link.server_setup.title",
icon: "gear",
keywords: "admin.email_settings.sidebar_link.server_setup.keywords",

View File

@ -955,7 +955,7 @@
}
}
.admin-customize-email-templates {
.admin-email-templates {
.email-template,
.controls {
margin-top: 1em;

View File

@ -6334,7 +6334,8 @@ en:
email:
title: "Emails"
settings: "Email Settings"
description: "Customize the templates used to create emails, preview summary emails that will be sent out, and view email logs."
settings: "Settings"
templates: "Templates"
templates_title: "Email Templates"
preview_digest: "Preview Summary"

View File

@ -194,6 +194,13 @@ Discourse::Application.routes.draw do
post "handle_mail"
get "advanced-test"
post "advanced-test" => "email#advanced_test"
get "templates" => "email_templates#index"
get "templates/(:id)" => "email_templates#show", :constraints => { id: /[0-9a-z_.]+/ }
delete "templates/(:id)" => "email_templates#revert",
:constraints => {
id: /[0-9a-z_.]+/,
}
put "templates/(:id)" => "email_templates#update", :constraints => { id: /[0-9a-z_.]+/ }
end
end
@ -279,17 +286,6 @@ Discourse::Application.routes.draw do
get "reseed" => "site_texts#get_reseed_options"
post "reseed" => "site_texts#reseed"
get "email_templates" => "email_templates#index"
get "email_templates/(:id)" => "email_templates#show", :constraints => { id: /[0-9a-z_.]+/ }
put "email_templates/(:id)" => "email_templates#update",
:constraints => {
id: /[0-9a-z_.]+/,
}
delete "email_templates/(:id)" => "email_templates#revert",
:constraints => {
id: /[0-9a-z_.]+/,
}
get "robots" => "robots_txt#show"
put "robots.json" => "robots_txt#update"
delete "robots.json" => "robots_txt#reset"

View File

@ -23,7 +23,7 @@ RSpec.describe Admin::EmailTemplatesController do
before { sign_in(admin) }
it "should work if you are an admin" do
get "/admin/customize/email_templates.json"
get "/admin/email/templates.json"
expect(response.status).to eq(200)
@ -32,7 +32,7 @@ RSpec.describe Admin::EmailTemplatesController do
end
it "returns overridden = true if subject or body has translation_overrides record" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: original_subject,
@ -42,7 +42,7 @@ RSpec.describe Admin::EmailTemplatesController do
headers: headers
expect(response.status).to eq(200)
get "/admin/customize/email_templates.json"
get "/admin/email/templates.json"
expect(response.status).to eq(200)
templates = response.parsed_body["email_templates"]
template = templates.find { |t| t["id"] == "user_notifications.admin_login" }
@ -50,7 +50,7 @@ RSpec.describe Admin::EmailTemplatesController do
TranslationOverride.destroy_all
get "/admin/customize/email_templates.json"
get "/admin/email/templates.json"
expect(response.status).to eq(200)
templates = response.parsed_body["email_templates"]
template = templates.find { |t| t["id"] == "user_notifications.admin_login" }
@ -60,7 +60,7 @@ RSpec.describe Admin::EmailTemplatesController do
shared_examples "email templates inaccessible" do
it "denies access with a 404 response" do
get "/admin/customize/email_templates.json"
get "/admin/email/templates.json"
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
@ -89,7 +89,7 @@ RSpec.describe Admin::EmailTemplatesController do
before { sign_in(admin) }
it "returns 'not found' when an unknown email template id is used" do
put "/admin/customize/email_templates/non_existent_template",
put "/admin/email/templates/non_existent_template",
params: {
email_template: {
subject: "Foo",
@ -106,7 +106,7 @@ RSpec.describe Admin::EmailTemplatesController do
shared_examples "invalid email template" do
it "returns the right error messages" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -124,7 +124,7 @@ RSpec.describe Admin::EmailTemplatesController do
end
it "doesn't create translation overrides" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -138,7 +138,7 @@ RSpec.describe Admin::EmailTemplatesController do
end
it "doesn't create entries in the Staff Log" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -224,7 +224,7 @@ RSpec.describe Admin::EmailTemplatesController do
let(:email_body) { "The body contains [%{site_name}](%{base_url}) and %{email_token}." }
it "returns the successfully updated email template" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -249,7 +249,7 @@ RSpec.describe Admin::EmailTemplatesController do
end
it "creates translation overrides" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -263,7 +263,7 @@ RSpec.describe Admin::EmailTemplatesController do
end
it "creates entries in the Staff Log" do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -293,7 +293,7 @@ RSpec.describe Admin::EmailTemplatesController do
old_subject = I18n.t("system_messages.pending_users_reminder.subject_template")
expect(old_subject).to be_a(Hash)
put "/admin/customize/email_templates/system_messages.pending_users_reminder",
put "/admin/email/templates/system_messages.pending_users_reminder",
params: {
email_template: {
subject: "",
@ -316,7 +316,7 @@ RSpec.describe Admin::EmailTemplatesController do
shared_examples "email template update not allowed" do
it "prevents updates with a 404 response" do
put "/admin/customize/email_templates/some_id",
put "/admin/email/templates/some_id",
params: {
email_template: {
subject: "Subject",
@ -352,7 +352,7 @@ RSpec.describe Admin::EmailTemplatesController do
before { sign_in(admin) }
it "returns 'not found' when an unknown email template id is used" do
delete "/admin/customize/email_templates/non_existent_template", headers: headers
delete "/admin/email/templates/non_existent_template", headers: headers
expect(response).not_to be_successful
json = response.parsed_body
@ -364,7 +364,7 @@ RSpec.describe Admin::EmailTemplatesController do
let(:email_body) { "The body contains [%{site_name}](%{base_url}) and %{email_token}." }
before do
put "/admin/customize/email_templates/user_notifications.admin_login",
put "/admin/email/templates/user_notifications.admin_login",
params: {
email_template: {
subject: email_subject,
@ -378,14 +378,14 @@ RSpec.describe Admin::EmailTemplatesController do
expect(I18n.t("user_notifications.admin_login.subject_template")).to eq(email_subject)
expect(I18n.t("user_notifications.admin_login.text_body_template")).to eq(email_body)
delete "/admin/customize/email_templates/user_notifications.admin_login", headers: headers
delete "/admin/email/templates/user_notifications.admin_login", headers: headers
expect(I18n.t("user_notifications.admin_login.subject_template")).to eq(original_subject)
expect(I18n.t("user_notifications.admin_login.text_body_template")).to eq(original_body)
end
it "returns the restored email template" do
delete "/admin/customize/email_templates/user_notifications.admin_login", headers: headers
delete "/admin/email/templates/user_notifications.admin_login", headers: headers
expect(response.status).to eq(200)
json = response.parsed_body
@ -403,7 +403,7 @@ RSpec.describe Admin::EmailTemplatesController do
it "creates entries in the Staff Log" do
UserHistory.delete_all
delete "/admin/customize/email_templates/user_notifications.admin_login", headers: headers
delete "/admin/email/templates/user_notifications.admin_login", headers: headers
log = UserHistory.find_by_subject("user_notifications.admin_login.subject_template")
@ -424,7 +424,7 @@ RSpec.describe Admin::EmailTemplatesController do
shared_examples "email template reversal not allowed" do
it "prevents reversals with a 404 response" do
delete "/admin/customize/email_templates/some_id", headers: headers
delete "/admin/email/templates/some_id", headers: headers
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))