UX: Move category editing/creation to its own page (#10973)

* Move new/edit category modals to its own page

* Fix JS tests

* Minor fixes to new-category UI

* Add mobile toggle

* Use global pretender endpoint so plugins can benefit too

* Alignment fix

* Minor review fixes

* Styling refactor

* Move some SCSS out of the modal
This commit is contained in:
Penar Musaraj
2020-10-23 12:49:02 -04:00
committed by GitHub
parent 2bcca46cc5
commit 6f5d8cad51
25 changed files with 505 additions and 351 deletions

View File

@@ -106,9 +106,13 @@ export default buildCategoryPanel("general", {
return Category.list().filterBy("parent_category_id", categoryId); return Category.list().filterBy("parent_category_id", categoryId);
}, },
@discourseComputed("category.isUncategorizedCategory", "category.id") @discourseComputed(
showDescription(isUncategorizedCategory, categoryId) { "category.isUncategorizedCategory",
return !isUncategorizedCategory && categoryId; "category.id",
"category.topic_url"
)
showDescription(isUncategorizedCategory, categoryId, topicUrl) {
return !isUncategorizedCategory && categoryId && topicUrl;
}, },
@action @action

View File

@@ -9,7 +9,7 @@ export function buildCategoryPanel(tab, extras) {
{ {
activeTab: equal("selectedTab", tab), activeTab: equal("selectedTab", tab),
classNameBindings: [ classNameBindings: [
":modal-tab", ":edit-category-tab",
"activeTab::hide", "activeTab::hide",
`:edit-category-tab-${tab}`, `:edit-category-tab-${tab}`,
], ],

View File

@@ -1,22 +1,20 @@
import I18n from "I18n"; import I18n from "I18n";
import { isEmpty } from "@ember/utils";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import discourseComputed, { on } from "discourse-common/utils/decorators";
import DiscourseURL from "discourse/lib/url";
import { extractError } from "discourse/lib/ajax-error";
import discourseComputed, {
on,
observes,
} from "discourse-common/utils/decorators";
import Category from "discourse/models/category";
import bootbox from "bootbox"; import bootbox from "bootbox";
import { extractError } from "discourse/lib/ajax-error";
import DiscourseURL from "discourse/lib/url";
import { readOnly } from "@ember/object/computed";
export default Controller.extend(ModalFunctionality, { export default Controller.extend({
selectedTab: null, selectedTab: "general",
saving: false, saving: false,
deleting: false, deleting: false,
panels: null, panels: null,
hiddenTooltip: true, hiddenTooltip: true,
createdCategory: false,
expandedMenu: false,
mobileView: readOnly("site.mobileView"),
@on("init") @on("init")
_initPanels() { _initPanels() {
@@ -26,36 +24,6 @@ export default Controller.extend(ModalFunctionality, {
}); });
}, },
onShow() {
this.changeSize();
this.titleChanged();
this.set("hiddenTooltip", true);
},
@observes("model.description")
changeSize() {
if (!isEmpty(this.get("model.description"))) {
this.set("modal.modalClass", "edit-category-modal full");
} else {
this.set("modal.modalClass", "edit-category-modal small");
}
},
@discourseComputed("model.{id,name}")
title(model) {
if (model.id) {
return I18n.t("category.edit_dialog_title", {
categoryName: model.name,
});
}
return I18n.t("category.create");
},
@observes("title")
titleChanged() {
this.set("modal.title", this.title);
},
@discourseComputed("saving", "model.name", "model.color", "deleting") @discourseComputed("saving", "model.name", "model.color", "deleting")
disabled(saving, name, color, deleting) { disabled(saving, name, color, deleting) {
if (saving || deleting) { if (saving || deleting) {
@@ -89,6 +57,15 @@ export default Controller.extend(ModalFunctionality, {
return id ? "category.save" : "category.create"; return id ? "category.save" : "category.create";
}, },
@discourseComputed("model.id", "model.name")
title(id, name) {
return id
? I18n.t("category.edit_dialog_title", {
categoryName: name,
})
: I18n.t("category.create");
},
actions: { actions: {
registerValidator(validator) { registerValidator(validator) {
this.validators.push(validator); this.validators.push(validator);
@@ -111,23 +88,22 @@ export default Controller.extend(ModalFunctionality, {
.save() .save()
.then((result) => { .then((result) => {
this.set("saving", false); this.set("saving", false);
this.send("closeModal"); if (!model.id) {
model.setProperties({ model.setProperties({
slug: result.category.slug, slug: result.category.slug,
id: result.category.id, id: result.category.id,
}); createdCategory: true,
DiscourseURL.redirectTo(`/c/${Category.slugFor(model)}/${model.id}`); });
}
}) })
.catch((error) => { .catch((error) => {
this.flash(extractError(error), "error"); bootbox.alert(extractError(error));
this.set("saving", false); this.set("saving", false);
}); });
}, },
deleteCategory() { deleteCategory() {
this.set("deleting", true); this.set("deleting", true);
this.send("hideModal");
bootbox.confirm( bootbox.confirm(
I18n.t("category.delete_confirm"), I18n.t("category.delete_confirm"),
I18n.t("no_value"), I18n.t("no_value"),
@@ -136,19 +112,14 @@ export default Controller.extend(ModalFunctionality, {
if (result) { if (result) {
this.model.destroy().then( this.model.destroy().then(
() => { () => {
// success this.transitionToRoute("discovery.categories");
this.send("closeModal");
DiscourseURL.redirectTo("/categories");
}, },
(error) => { () => {
this.flash(extractError(error), "error");
this.send("reopenModal");
this.displayErrors([I18n.t("category.delete_error")]); this.displayErrors([I18n.t("category.delete_error")]);
this.set("deleting", false); this.set("deleting", false);
} }
); );
} else { } else {
this.send("reopenModal");
this.set("deleting", false); this.set("deleting", false);
} }
} }
@@ -158,5 +129,17 @@ export default Controller.extend(ModalFunctionality, {
toggleDeleteTooltip() { toggleDeleteTooltip() {
this.toggleProperty("hiddenTooltip"); this.toggleProperty("hiddenTooltip");
}, },
goBack() {
if (this.model.createdCategory) {
DiscourseURL.redirectTo(this.model.url);
} else {
DiscourseURL.routeTo(this.model.url);
}
},
toggleMenu() {
this.toggleProperty("expandedMenu");
},
}, },
}); });

View File

@@ -21,6 +21,8 @@ export default function () {
this.route("topicBySlugOrId", { path: "/t/:slugOrId", resetNamespace: true }); this.route("topicBySlugOrId", { path: "/t/:slugOrId", resetNamespace: true });
this.route("newCategory", { path: "/new-category" });
this.route("discovery", { path: "/", resetNamespace: true }, function () { this.route("discovery", { path: "/", resetNamespace: true }, function () {
// legacy route // legacy route
this.route("topParentCategory", { path: "/c/:slug/l/top" }); this.route("topParentCategory", { path: "/c/:slug/l/top" });
@@ -63,6 +65,8 @@ export default function () {
}); });
this.route("categories"); this.route("categories");
this.route("editCategory", { path: "/c/:slug/edit" });
this.route("editChildCategory", { path: "/c/:parentSlug/:slug/edit" });
// legacy routes // legacy routes
this.route("parentCategory", { path: "/c/:slug" }); this.route("parentCategory", { path: "/c/:slug" });

View File

@@ -10,7 +10,7 @@ import Category from "discourse/models/category";
import mobile from "discourse/lib/mobile"; import mobile from "discourse/lib/mobile";
import { findAll } from "discourse/models/login-method"; import { findAll } from "discourse/models/login-method";
import { getOwner } from "discourse-common/lib/get-owner"; import { getOwner } from "discourse-common/lib/get-owner";
import { userPath } from "discourse/lib/url"; import DiscourseURL, { userPath } from "discourse/lib/url";
import Composer from "discourse/models/composer"; import Composer from "discourse/models/composer";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import bootbox from "bootbox"; import bootbox from "bootbox";
@@ -203,13 +203,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
}, },
editCategory(category) { editCategory(category) {
Category.reloadById(category.get("id")).then((atts) => { DiscourseURL.routeTo(`/c/${Category.slugFor(category)}/edit`);
const model = this.store.createRecord("category", atts.category);
model.setupGroupsAndPermissions();
this.site.updateCategory(model);
showModal("edit-category", { model });
this.controllerFor("edit-category").set("selectedTab", "general");
});
}, },
checkEmail(user) { checkEmail(user) {

View File

@@ -9,7 +9,6 @@ import { defaultHomepage } from "discourse/lib/utilities";
import TopicList from "discourse/models/topic-list"; import TopicList from "discourse/models/topic-list";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import PreloadStore from "discourse/lib/preload-store"; import PreloadStore from "discourse/lib/preload-store";
import { SEARCH_PRIORITIES } from "discourse/lib/constants";
import { hash } from "rsvp"; import { hash } from "rsvp";
import Site from "discourse/models/site"; import Site from "discourse/models/site";
@@ -111,7 +110,7 @@ const DiscoveryCategoriesRoute = DiscourseRoute.extend(OpenComposer, {
}, },
createCategory() { createCategory() {
openNewCategoryModal(this); this.transitionTo("newCategory");
}, },
reorderCategories() { reorderCategories() {
@@ -134,22 +133,4 @@ const DiscoveryCategoriesRoute = DiscourseRoute.extend(OpenComposer, {
}, },
}); });
export function openNewCategoryModal(context) {
const groups = context.site.groups,
everyoneName = groups.findBy("id", 0).name;
const model = context.store.createRecord("category", {
color: "0088CC",
text_color: "FFFFFF",
group_permissions: [{ group_name: everyoneName, permission_type: 1 }],
available_groups: groups.map((g) => g.name),
allow_badges: true,
topic_featured_link_allowed: true,
custom_fields: {},
search_priority: SEARCH_PRIORITIES.normal,
});
showModal("edit-category", { model }).set("selectedTab", "general");
}
export default DiscoveryCategoriesRoute; export default DiscoveryCategoriesRoute;

View File

@@ -0,0 +1,28 @@
import I18n from "I18n";
import DiscourseRoute from "discourse/routes/discourse";
import Category from "discourse/models/category";
export default DiscourseRoute.extend({
model(params) {
return Category.reloadBySlugPath(params.slug).then((result) => {
const record = this.store.createRecord("category", result.category);
record.setupGroupsAndPermissions();
this.site.updateCategory(record);
return record;
});
},
titleToken() {
return I18n.t("category.edit_dialog_title", {
categoryName: this.currentModel.name,
});
},
renderTemplate() {
this.render("edit-category", {
controller: "edit-category",
outlet: "list-container",
model: this.currentModel,
});
},
});

View File

@@ -0,0 +1,30 @@
import I18n from "I18n";
import DiscourseRoute from "discourse/routes/discourse";
import Category from "discourse/models/category";
export default DiscourseRoute.extend({
model(params) {
return Category.reloadBySlug(params.slug, params.parentSlug).then(
(result) => {
const record = this.store.createRecord("category", result.category);
record.setupGroupsAndPermissions();
this.site.updateCategory(record);
return record;
}
);
},
titleToken() {
return I18n.t("category.edit_dialog_title", {
categoryName: this.currentModel.name,
});
},
renderTemplate() {
this.render("edit-category", {
controller: "edit-category",
outlet: "list-container",
model: this.currentModel,
});
},
});

View File

@@ -0,0 +1,32 @@
import I18n from "I18n";
import DiscourseRoute from "discourse/routes/discourse";
import { SEARCH_PRIORITIES } from "discourse/lib/constants";
export default DiscourseRoute.extend({
model() {
const groups = this.site.groups,
everyoneName = groups.findBy("id", 0).name;
return this.store.createRecord("category", {
color: "0088CC",
text_color: "FFFFFF",
group_permissions: [{ group_name: everyoneName, permission_type: 1 }],
available_groups: groups.map((g) => g.name),
allow_badges: true,
topic_featured_link_allowed: true,
custom_fields: {},
search_priority: SEARCH_PRIORITIES.normal,
});
},
titleToken() {
return I18n.t("category.create");
},
renderTemplate() {
this.render("edit-category", {
controller: "edit-category",
model: this.currentModel,
});
},
});

View File

@@ -19,7 +19,7 @@
id="category-allowed-tag-groups" id="category-allowed-tag-groups"
tagGroups=category.allowed_tag_groups tagGroups=category.allowed_tag_groups
}} }}
{{#link-to "tagGroups"}}{{i18n "category.manage_tag_groups_link"}}{{/link-to}} {{#link-to "tagGroups" class="manage-tag-groups"}}{{i18n "category.manage_tag_groups_link"}}{{/link-to}}
</section> </section>
<section class="field"> <section class="field">

View File

@@ -0,0 +1,60 @@
<div class="edit-category {{if expandedMenu "expanded-menu"}}">
<ul class="nav nav-stacked">
{{edit-category-tab panels=panels selectedTab=selectedTab tab="general"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="security"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="settings"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="images"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="topic-template"}}
{{#if siteSettings.tagging_enabled}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="tags"}}
{{/if}}
</ul>
<div class="edit-category-title">
<h2>{{title}}</h2>
{{#if mobileView}}
{{d-button action=(action "toggleMenu") class="menu-toggle" icon="bars"}}
{{else}}
{{#if model.id}}
{{d-button
class="category-back"
action=(action "goBack")
label="category.back"
icon="caret-left"
}}
{{/if}}
{{/if}}
</div>
{{#each panels as |tab|}}
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
{{/each}}
<div class="edit-category-footer">
{{d-button id="save-category" class="btn-primary" disabled=disabled action=(action "saveCategory") label=saveLabel}}
{{#if model.can_delete}}
{{d-button
class="btn-danger"
disabled=deleteDisabled
action=(action "deleteCategory")
icon="far-trash-alt"
label="category.delete"
}}
{{else if model.id}}
<div class="disable-info">
{{d-button
disabled=deleteDisabled
class="btn-default"
action=(action "toggleDeleteTooltip")
icon="question-circle"
label="category.delete"
}}
<div class="cannot-delete-reason {{if hiddenTooltip "hidden" ""}}">
{{html-safe model.cannot_delete_reason}}
</div>
</div>
{{/if}}
</div>
</div>

View File

@@ -1,42 +0,0 @@
<div>
<ul class="nav nav-pills">
{{edit-category-tab panels=panels selectedTab=selectedTab tab="general"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="security"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="settings"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="images"}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="topic-template"}}
{{#if siteSettings.tagging_enabled}}
{{edit-category-tab panels=panels selectedTab=selectedTab tab="tags"}}
{{/if}}
</ul>
{{#d-modal-body}}
{{#each panels as |tab|}}
{{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
{{/each}}
{{/d-modal-body}}
<div class="modal-footer">
{{d-button id="save-category" class="btn-primary" disabled=disabled action=(action "saveCategory") label=saveLabel}}
{{#if model.can_delete}}
{{d-button class="btn-danger pull-right"
disabled=deleteDisabled
action=(action "deleteCategory")
icon="far-trash-alt"
label="category.delete"}}
{{else if model.id}}
<div class="disable_info_wrap">
{{d-button disabled=deleteDisabled
class="btn-default"
action=(action "toggleDeleteTooltip")
icon="question-circle"
label="category.delete"}}
<div class="cannot_delete_reason {{if hiddenTooltip "hidden" ""}}">
{{html-safe model.cannot_delete_reason}}
</div>
</div>
{{/if}}
</div>
</div>

View File

@@ -1,31 +1,21 @@
import { visit } from "@ember/test-helpers"; import { visit } from "@ember/test-helpers";
import { test } from "qunit"; import { test } from "qunit";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import DiscourseURL from "discourse/lib/url";
import { acceptance } from "discourse/tests/helpers/qunit-helpers"; import { acceptance } from "discourse/tests/helpers/qunit-helpers";
acceptance("Category Edit", function (needs) { acceptance("Category Edit", function (needs) {
needs.user(); needs.user();
needs.settings({ email_in: true }); needs.settings({ email_in: true });
test("Can open the category modal", async (assert) => {
await visit("/c/bug");
await click(".edit-category");
assert.ok(visible(".d-modal"), "it pops up a modal");
await click("button.modal-close");
assert.ok(!visible(".d-modal"), "it closes the modal");
});
test("Editing the category", async (assert) => { test("Editing the category", async (assert) => {
await visit("/c/bug"); await visit("/c/bug");
await click(".edit-category"); await click("button.edit-category");
assert.equal(currentURL(), "/c/bug/edit", "it jumps to the correct screen");
assert.equal(find(".d-modal .badge-category").text(), "bug"); assert.equal(find(".badge-category").text(), "bug");
await fillIn("input.category-name", "testing"); await fillIn("input.category-name", "testing");
assert.equal(find(".d-modal .badge-category").text(), "testing"); assert.equal(find(".badge-category").text(), "testing");
await fillIn("#edit-text-color", "#ff0000"); await fillIn("#edit-text-color", "#ff0000");
@@ -38,24 +28,21 @@ acceptance("Category Edit", function (needs) {
await searchPriorityChooser.selectRowByValue(1); await searchPriorityChooser.selectRowByValue(1);
await click("#save-category"); await click("#save-category");
assert.equal(currentURL(), "/c/bug/edit", "it stays on the edit screen");
assert.ok(!visible(".d-modal"), "it closes the modal");
assert.equal(
DiscourseURL.redirectedTo,
"/c/bug/1",
"it does one of the rare full page redirects"
);
}); });
test("Error Saving", async (assert) => { test("Error Saving", async (assert) => {
await visit("/c/bug"); await visit("/c/bug");
await click("button.edit-category");
await click(".edit-category");
await click(".edit-category-settings"); await click(".edit-category-settings");
await fillIn(".email-in", "duplicate@example.com"); await fillIn(".email-in", "duplicate@example.com");
await click("#save-category"); await click("#save-category");
assert.ok(visible("#modal-alert"));
assert.equal(find("#modal-alert").html(), "duplicate email"); assert.ok(visible(".bootbox"));
assert.equal(find(".bootbox .modal-body").html(), "duplicate email");
await click(".bootbox .btn-primary");
assert.ok(!visible(".bootbox"));
}); });
test("Subcategory list settings", async (assert) => { test("Subcategory list settings", async (assert) => {
@@ -64,7 +51,7 @@ acceptance("Category Edit", function (needs) {
); );
await visit("/c/bug"); await visit("/c/bug");
await click(".edit-category"); await click("button.edit-category");
await click(".edit-category-settings a"); await click(".edit-category-settings a");
assert.ok( assert.ok(

View File

@@ -0,0 +1,34 @@
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import I18n from "I18n";
import DiscourseURL from "discourse/lib/url";
acceptance("Category New", function (needs) {
needs.user();
test("Creating a new category", async (assert) => {
await visit("/new-category");
assert.ok(find(".badge-category"));
await fillIn("input.category-name", "testing");
assert.equal(find(".badge-category").text(), "testing");
await click("#save-category");
assert.equal(
find(".edit-category-title h2").text(),
I18n.t("category.edit_dialog_title", {
categoryName: "testing",
})
);
await click(".category-back");
assert.equal(
DiscourseURL.redirectedTo,
"/c/testing/11",
"it full page redirects after a newly created category"
);
});
});

View File

@@ -14,6 +14,7 @@ export default {
whisper: 4, whisper: 4,
}, },
groups: [ groups: [
{ id: 0, name: "everyone" },
{ id: 1, name: "admins" }, { id: 1, name: "admins" },
{ id: 2, name: "moderators" }, { id: 2, name: "moderators" },
{ id: 3, name: "staff" }, { id: 3, name: "staff" },

View File

@@ -313,6 +313,10 @@ export function applyDefaultHandlers(pretender) {
response(fixturesByUrl["/categories_and_latest.json"]) response(fixturesByUrl["/categories_and_latest.json"])
); );
pretender.get("/c/bug/find_by_slug.json", () =>
response(fixturesByUrl["/c/1/show.json"])
);
pretender.put("/categories/:category_id", (request) => { pretender.put("/categories/:category_id", (request) => {
const category = parsePostData(request.requestBody); const category = parsePostData(request.requestBody);
category.id = parseInt(request.params.category_id, 10); category.id = parseInt(request.params.category_id, 10);
@@ -324,6 +328,18 @@ export function applyDefaultHandlers(pretender) {
return response({ category }); return response({ category });
}); });
pretender.post("/categories", () => {
return response({
category: {
id: 11,
name: "testing",
color: "0088CC",
text_color: "FFFFFF",
slug: "testing",
},
});
});
pretender.get("/draft.json", (request) => { pretender.get("/draft.json", (request) => {
if (request.queryParams.draft_key === "new_topic") { if (request.queryParams.draft_key === "new_topic") {
return response(fixturesByUrl["/draft.json"]); return response(fixturesByUrl["/draft.json"]);

View File

@@ -78,14 +78,13 @@ $mobile-breakpoint: 700px;
} }
} }
.nav-stacked {
@media screen and (max-width: 700px) {
margin: 0 15px 0 -10px;
}
}
.admin-contents { .admin-contents {
position: relative; position: relative;
.nav-stacked {
@media screen and (max-width: 700px) {
margin: 0 15px 0 -10px;
}
}
} }
.admin-contents table { .admin-contents table {

View File

@@ -0,0 +1,165 @@
$category-settings-width: unquote("min(500px, 90%)");
$number-input-width: 75px;
div.edit-category {
display: grid;
grid-template-columns: 1fr 5fr;
grid-template-rows: auto auto auto;
grid-row-gap: 1em;
grid-column-gap: 1.5em;
grid-template-areas: "sidebar header" "sidebar content" "sidebar footer";
.edit-category-title {
grid-area: header;
display: flex;
justify-content: space-between;
align-self: start;
}
.nav-stacked {
grid-area: sidebar;
grid-row: 1 / span 3;
background: transparent;
}
#list-area & h2 {
margin: 0;
}
section.field {
margin-bottom: 1em;
}
.edit-category-tab-general {
.category-chooser {
width: unquote("min(340px, 90%)");
}
}
.edit-category-tab-security {
.permission-selector {
margin-right: 0.25em;
}
.pending-permission-change-alert {
margin-left: auto;
max-width: 250px;
background: var(--primary-very-high);
color: var(--secondary);
margin-top: 10px;
padding: 5px 10px;
position: relative;
.arrow-div {
border: solid transparent;
content: " ";
position: absolute;
border-bottom-color: var(--primary-very-high);
border-width: 7px;
top: -13px;
left: 200px;
}
}
.add-permission {
position: relative;
top: 0.1em;
}
.permission-list {
list-style: none;
margin: 0 0 30px;
padding: 0;
.name {
margin-right: 20px;
display: inline-block;
min-width: 100px;
}
.permission {
margin-left: 20px;
}
.d-icon-times-circle {
margin-left: 5px;
color: var(--danger);
}
li {
margin-bottom: 10px;
}
}
}
.edit-category-tab-settings {
> section {
margin-bottom: 1.5em;
}
input[type="text"],
.select-kit {
width: $category-settings-width;
}
label {
max-width: $category-settings-width;
}
input[type="number"] {
width: $number-input-width;
}
}
.edit-category-tab-tags {
#category-min-tags-from-group {
width: $number-input-width;
min-height: 36px;
}
.select-kit {
&.tag-chooser {
width: $category-settings-width;
.select-kit-filter,
.filter-input {
min-width: 250px;
}
.select-kit-body {
max-width: 100%;
}
}
}
.manage-tag-groups {
display: block;
}
}
.edit-category-footer {
grid-area: footer;
display: flex;
justify-content: space-between;
align-self: start;
.disable-info {
position: relative;
.cannot-delete-reason {
position: absolute;
bottom: 125%;
right: 0px;
width: 250px;
background: var(--primary);
color: var(--secondary);
text-align: center;
border-radius: 2px;
padding: 12px 8px;
&::after {
top: 100%;
left: 57%;
border: solid transparent;
content: " ";
position: absolute;
border-top-color: var(--primary);
border-width: 8px;
}
}
}
}
}

View File

@@ -172,19 +172,6 @@
} }
} }
.modal.edit-category-modal {
.modal-body {
textarea {
height: 10em;
}
}
@media screen and (min-width: 524px) {
.modal-inner-container {
min-width: 525px;
}
}
}
.modal { .modal {
.nav { .nav {
padding: 10px 30px 10px 15px; padding: 10px 30px 10px 15px;
@@ -354,141 +341,6 @@
} }
} }
.permission-list {
// Category security tab
list-style: none;
margin: 0 0 30px;
padding: 0;
.name {
margin-right: 20px;
display: inline-block;
min-width: 100px;
}
.permission {
margin-left: 20px;
}
.d-icon-times-circle {
margin-left: 5px;
color: var(--danger);
}
li {
margin-bottom: 10px;
}
}
.edit-category-modal {
input:not([type="checkbox"]),
.controls {
display: block;
margin-bottom: 0.5em;
}
.disable_info_wrap {
position: relative;
display: inline-block;
float: right;
.cannot_delete_reason {
position: absolute;
background: var(--primary);
color: var(--secondary);
text-align: center;
border-radius: 2px;
padding: 12px 8px;
&::after {
top: 100%;
left: 57%;
border: solid transparent;
content: " ";
position: absolute;
border-top-color: var(--primary);
border-width: 8px;
}
}
}
.permission-selector {
margin-right: 0.25em;
}
.pending-permission-change-alert {
margin-left: auto;
max-width: 250px;
background: var(--primary-very-high);
color: var(--secondary);
margin-top: 10px;
padding: 5px 10px;
position: relative;
.arrow-div {
border: solid transparent;
content: " ";
position: absolute;
border-bottom-color: var(--primary-very-high);
border-width: 7px;
top: -13px;
left: 200px;
}
}
.add-permission {
position: relative;
top: 0.1em;
}
.edit-category-tab-settings {
$category-settings-width: 280px; // Consistent width makes this all easier to read
> section {
margin-bottom: 1.5em;
}
input[type="number"],
input[type="text"],
.select-kit {
width: $category-settings-width;
}
label {
max-width: $category-settings-width;
}
.category-email-in-outlet {
label {
flex-wrap: wrap; // Todo: fix all multi-element labels
a {
margin-left: 1.3em;
}
}
}
}
#category-min-tags-from-group {
width: 100px;
}
}
.edit-category-tab-tags {
.select-kit {
&.tag-chooser {
width: 100%;
.select-kit-filter,
.filter-input {
display: flex;
flex: 1 0 250px;
}
.mobile-view & {
.no-content,
.is-loading,
&.select-kit.is-expanded .select-kit-collection {
width: 95%;
}
}
}
}
}
#category-min-tags-from-group {
min-height: 36px;
}
.incoming-email-modal { .incoming-email-modal {
.btn { .btn {
transition: none; transition: none;

View File

@@ -0,0 +1,11 @@
.edit-category {
section.field {
.field-item {
display: inline-block;
vertical-align: top;
+ .field-item {
margin-left: 1em;
}
}
}
}

View File

@@ -48,28 +48,6 @@
} }
} }
.edit-category-modal {
.modal-body {
position: relative;
height: 420px;
}
.edit-category-tab-general {
.category-chooser {
width: 434px;
}
}
.disable_info_wrap {
.cannot_delete_reason {
top: -100px;
right: 4px;
max-width: 380px;
min-width: 300px;
}
}
}
.choose-topic-modal { .choose-topic-modal {
.modal-body { .modal-body {
position: relative; position: relative;

View File

@@ -0,0 +1,49 @@
div.edit-category {
display: block;
position: relative;
.nav-stacked {
position: absolute;
top: 3.2em;
left: -80%;
transition: left 0.2s ease;
}
.edit-category-tab,
.edit-category-footer {
background-color: var(--secondary);
transition: transform 0.2s ease;
transform: translateX(0);
}
&.expanded-menu {
.edit-category-tab,
.edit-category-footer {
transform: translateX(45%);
}
.nav-stacked {
left: 0px;
}
}
.edit-category-title {
justify-content: start;
align-items: center;
padding-bottom: 1em;
.menu-toggle {
order: 1;
margin-right: 1em;
}
h2 {
order: 2;
margin-bottom: 0;
@include ellipsis;
}
}
.edit-category-footer {
padding-bottom: 2em;
}
}

View File

@@ -84,22 +84,6 @@
} }
} }
.edit-category-modal {
.modal-body {
position: relative;
height: 350px;
}
&.small .modal-body {
height: 310px;
}
.disable_info_wrap .cannot_delete_reason {
top: -114px;
right: 8px;
min-width: 200px;
}
}
/* fixes for the new account confirm dialog on mobile */ /* fixes for the new account confirm dialog on mobile */
.modal-inner-container { .modal-inner-container {

View File

@@ -2908,6 +2908,7 @@ en:
edit: "Edit" edit: "Edit"
edit_dialog_title: "Edit: %{categoryName}" edit_dialog_title: "Edit: %{categoryName}"
view: "View Topics in Category" view: "View Topics in Category"
back: "Back to category"
general: "General" general: "General"
settings: "Settings" settings: "Settings"
topic_template: "Topic Template" topic_template: "Topic Template"
@@ -2917,7 +2918,7 @@ en:
tags_placeholder: "(Optional) list of allowed tags" tags_placeholder: "(Optional) list of allowed tags"
tags_tab_description: "Tags and tag groups specified above will only be available in this category and other categories that also specify them. They won't be available for use in other categories." tags_tab_description: "Tags and tag groups specified above will only be available in this category and other categories that also specify them. They won't be available for use in other categories."
tag_groups_placeholder: "(Optional) list of allowed tag groups" tag_groups_placeholder: "(Optional) list of allowed tag groups"
manage_tag_groups_link: "Manage tag groups here." manage_tag_groups_link: "Manage tag groups"
allow_global_tags_label: "Also allow other tags" allow_global_tags_label: "Also allow other tags"
tag_group_selector_placeholder: "(Optional) Tag group" tag_group_selector_placeholder: "(Optional) Tag group"
required_tag_group_description: "Require new topics to have tags from a tag group:" required_tag_group_description: "Require new topics to have tags from a tag group:"

View File

@@ -682,6 +682,9 @@ Discourse::Application.routes.draw do
get "c/:category_slug/find_by_slug" => "categories#find_by_slug" get "c/:category_slug/find_by_slug" => "categories#find_by_slug"
get "c/:parent_category_slug/:category_slug/find_by_slug" => "categories#find_by_slug" get "c/:parent_category_slug/:category_slug/find_by_slug" => "categories#find_by_slug"
get "c/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' }
get "c/:parent_category_slug/:category_slug/edit" => "categories#find_by_slug", constraints: { format: 'html' }
get "/new-category" => "categories#show", constraints: { format: 'html' }
get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss get "c/*category_slug_path_with_id.rss" => "list#category_feed", format: :rss
scope path: 'c/*category_slug_path_with_id' do scope path: 'c/*category_slug_path_with_id' do