UX: Show a warning when admin clicks save without adding group when changing category permissions (#7947)

UX: Show a warning when admin clicks save without adding group when changing category permissions (#7947)

  • UX: Show a warning when admin clicks save without adding group when changing category permissions

  • Use div rather than pseudo element

  • Don’t pass controller to component

  • Use observer instead of a delay

  • Refactor

  • Rename to validator

  • validator is much better than saveValidator

diff --git a/app/assets/javascripts/discourse/components/category-panel-base.js.es6 b/app/assets/javascripts/discourse/components/category-panel-base.js.es6
deleted file mode 100644
index e38c2b7..0000000
--- a/app/assets/javascripts/discourse/components/category-panel-base.js.es6
+++ /dev/null
@@ -1,14 +0,0 @@
-const CategoryPanelBase = Ember.Component.extend({
-  classNameBindings: [":modal-tab", "activeTab::invisible"]
-});
-
-export default CategoryPanelBase;
-
-export function buildCategoryPanel(tab, extras) {
-  return CategoryPanelBase.extend(
-    {
-      activeTab: Ember.computed.equal("selectedTab", tab)
-    },
-    extras || {}
-  );
-}
diff --git a/app/assets/javascripts/discourse/components/edit-category-security.js.es6 b/app/assets/javascripts/discourse/components/edit-category-security.js.es6
index 129fc82..be41b0a 100644
--- a/app/assets/javascripts/discourse/components/edit-category-security.js.es6
+++ b/app/assets/javascripts/discourse/components/edit-category-security.js.es6
@@ -1,12 +1,33 @@
 import { buildCategoryPanel } from "discourse/components/edit-category-panel";
 import PermissionType from "discourse/models/permission-type";
+import { on } from "ember-addons/ember-computed-decorators";
 
 export default buildCategoryPanel("security", {
   editingPermissions: false,
   selectedGroup: null,
   selectedPermission: null,
+  showPendingGroupChangesAlert: false,
+  interactedWithDropdowns: false,
+
+  @on("init")
+  _registerValidator() {
+    this.registerValidator(() => {
+      if (
+        !this.showPendingGroupChangesAlert &&
+        this.interactedWithDropdowns &&
+        this.activeTab
+      ) {
+        this.set("showPendingGroupChangesAlert", true);
+        return true;
+      }
+    });
+  },
 
   actions: {
+    onDropdownChange() {
+      this.set("interactedWithDropdowns", true);
+    },
+
     editPermissions() {
       if (!this.get("category.is_special")) {
         this.set("editingPermissions", true);
@@ -25,6 +46,10 @@ export default buildCategoryPanel("security", {
         "selectedGroup",
         this.get("category.availableGroups.firstObject")
       );
+      this.setProperties({
+        showPendingGroupChangesAlert: false,
+        interactedWithDropdowns: false
+      });
     },
 
     removePermission(permission) {
diff --git a/app/assets/javascripts/discourse/controllers/edit-category.js.es6 b/app/assets/javascripts/discourse/controllers/edit-category.js.es6
index 0e18b63..381292b 100644
--- a/app/assets/javascripts/discourse/controllers/edit-category.js.es6
+++ b/app/assets/javascripts/discourse/controllers/edit-category.js.es6
@@ -16,7 +16,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
 
   @on("init")
   _initPanels() {
-    this.set("panels", []);
+    this.setProperties({
+      panels: [],
+      validators: []
+    });
   },
 
   onShow() {
@@ -75,7 +78,14 @@ export default Ember.Controller.extend(ModalFunctionality, {
   },
 
   actions: {
+    registerValidator(validator) {
+      this.validators.push(validator);
+    },
+
     saveCategory() {
+      if (this.validators.some(validator => validator())) {
+        return;
+      }
       const model = this.model;
       const parentCategory = this.site.categories.findBy(
         "id",
diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs
index a7efeec..c7e1555 100644
--- a/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs
+++ b/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs
@@ -26,15 +26,24 @@
                   allowInitialValueMutation=true
                   allowContentReplacement=true
                   content=category.availableGroups
+                  onSelect=(action "onDropdownChange")
                   value=selectedGroup}}
       {{combo-box allowInitialValueMutation=true
                   class="permission-selector"
                   nameProperty="description"
                   content=category.availablePermissions
+                  onSelect=(action "onDropdownChange")
                   value=selectedPermission}}
-      <button {{action "addPermission" selectedGroup selectedPermission}} class="btn btn-default add-permission">
-        {{d-icon 'plus' class="add-permission-icon"}}
-      </button>
+      {{d-button
+        action=(action "addPermission" selectedGroup selectedPermission)
+        class="btn-primary add-permission"
+        icon="plus"}}
+      {{#if showPendingGroupChangesAlert}}
+        <div class="pending-permission-change-alert">
+          <div class="arrow-div"></div>
+          {{i18n "category.pending_permission_change_alert" group=selectedGroup}}
+        </div>
+      {{/if}}
     {{/if}}
   {{else}}
     {{#unless category.is_special}}
diff --git a/app/assets/javascripts/discourse/templates/modal/edit-category.hbs b/app/assets/javascripts/discourse/templates/modal/edit-category.hbs
index d0029b0..6abe868 100644
--- a/app/assets/javascripts/discourse/templates/modal/edit-category.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/edit-category.hbs
@@ -12,7 +12,7 @@
 
   {{#d-modal-body}}
     {{#each panels as |tab|}}
-      {{component tab selectedTab=selectedTab category=model}}
+      {{component tab selectedTab=selectedTab category=model registerValidator=(action "registerValidator")}}
     {{/each}}
   {{/d-modal-body}}
 
diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss
index f49fb23..cd42181 100644
--- a/app/assets/stylesheets/common/base/modal.scss
+++ b/app/assets/stylesheets/common/base/modal.scss
@@ -350,13 +350,28 @@
     margin-right: 0.25em;
   }
 
-  .add-permission {
+  .pending-permission-change-alert {
+    margin-left: auto;
+    max-width: 250px;
+    background: $primary-very-high;
+    color: $secondary;
+    margin-top: 10px;
+    padding: 5px 10px;
     position: relative;
-    top: 0.1em;
+    .arrow-div {
+      border: solid transparent;
+      content: " ";
+      position: absolute;
+      border-bottom-color: $primary-very-high;
+      border-width: 7px;
+      top: -13px;
+      left: 200px;
+    }
   }
 
-  .add-permission-icon {
-    margin: 0;
+  .add-permission {
+    position: relative;
+    top: 0.1em;
   }
 
   .edit-category-tab-settings {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 431e25a..d00ba1c 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2577,6 +2577,7 @@ en:
       special_warning: "Warning: This category is a pre-seeded category and the security settings cannot be edited. If you do not wish to use this category, delete it instead of repurposing it."
       uncategorized_security_warning: "This category is special. It is intended as holding area for topics that have no category; it cannot have security settings."
       uncategorized_general_warning: 'This category is special. It is used as the default category for new topics that do not have a category selected. If you want to prevent this behavior and force category selection, <a href="%{settingLink}">please disable the setting here</a>. If you want to change the name or description, go to <a href="%{customizeLink}">Customize / Text Content</a>.'
+      pending_permission_change_alert: "You haven't added %{group} to this category; click this button to add them."
       images: "Images"
       email_in: "Custom incoming email address:"
       email_in_allow_strangers: "Accept emails from anonymous users with no accounts"

GitHub sha: 70bd8e2d