mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: created edit and delete flags (#27484)
Allow admins to create edit and delete flags.
This commit is contained in:
committed by
GitHub
parent
a86590ffd6
commit
c3fadc7330
@@ -6,9 +6,10 @@ import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import AdminConfigHeader from "admin/components/admin-config-header";
|
||||
import AdminFlagItem from "admin/components/admin-flag-item";
|
||||
|
||||
export default class AdminFlags extends Component {
|
||||
export default class AdminConfigAreasFlags extends Component {
|
||||
@service site;
|
||||
@tracked flags = this.site.flagTypes;
|
||||
|
||||
@@ -46,19 +47,39 @@ export default class AdminFlags extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
deleteFlagCallback(flag) {
|
||||
return ajax(`/admin/config/flags/${flag.id}`, {
|
||||
type: "DELETE",
|
||||
})
|
||||
.then(() => {
|
||||
this.flags.removeObject(flag);
|
||||
})
|
||||
.catch((error) => popupAjaxError(error));
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="container admin-flags">
|
||||
<h1>{{i18n "admin.flags.title"}}</h1>
|
||||
<table class="flags grid">
|
||||
<AdminConfigHeader
|
||||
@name="flags"
|
||||
@heading="admin.config_areas.flags.header"
|
||||
@subheading="admin.config_areas.flags.subheader"
|
||||
@primaryActionRoute="adminConfig.flags.new"
|
||||
@primaryActionCssClass="admin-flags__header-add-flag"
|
||||
@primaryActionIcon="plus"
|
||||
@primaryActionLabel="admin.config_areas.flags.add"
|
||||
/>
|
||||
<table class="admin-flags__items grid">
|
||||
<thead>
|
||||
<th>{{i18n "admin.flags.description"}}</th>
|
||||
<th>{{i18n "admin.flags.enabled"}}</th>
|
||||
<th>{{i18n "admin.config_areas.flags.description"}}</th>
|
||||
<th>{{i18n "admin.config_areas.flags.enabled"}}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.flags as |flag|}}
|
||||
<AdminFlagItem
|
||||
@flag={{flag}}
|
||||
@moveFlagCallback={{this.moveFlagCallback}}
|
||||
@deleteFlagCallback={{this.deleteFlagCallback}}
|
||||
@isFirstFlag={{this.isFirstFlag flag}}
|
||||
@isLastFlag={{this.isLastFlag flag}}
|
||||
/>
|
||||
@@ -0,0 +1,34 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
|
||||
export default class AdminFlagItem extends Component {
|
||||
get headerCssClass() {
|
||||
return `admin-${this.args.name}__header`;
|
||||
}
|
||||
<template>
|
||||
<div class={{this.headerCssClass}}>
|
||||
<h2>{{i18n @heading}}</h2>
|
||||
{{#if @primaryActionRoute}}
|
||||
<LinkTo
|
||||
@route={{@primaryActionRoute}}
|
||||
class={{concatClass
|
||||
"btn-primary"
|
||||
"btn"
|
||||
"btn-icon-text"
|
||||
@primaryActionCssClass
|
||||
}}
|
||||
>
|
||||
{{dIcon @primaryActionIcon}}
|
||||
{{i18n @primaryActionLabel}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if @subheading}}
|
||||
<h3>{{i18n @subheading}}</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
@@ -3,7 +3,9 @@ import { tracked } from "@glimmer/tracking";
|
||||
import { fn } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { not } from "truth-helpers";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||
import DropdownMenu from "discourse/components/dropdown-menu";
|
||||
@@ -14,22 +16,48 @@ import i18n from "discourse-common/helpers/i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
|
||||
export default class AdminFlagItem extends Component {
|
||||
@service dialog;
|
||||
@service router;
|
||||
|
||||
@tracked enabled = this.args.flag.enabled;
|
||||
|
||||
get canMove() {
|
||||
return this.args.flag.id !== SYSTEM_FLAG_IDS.notify_user;
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
return (
|
||||
!Object.values(SYSTEM_FLAG_IDS).includes(this.args.flag.id) &&
|
||||
!this.args.flag.is_used
|
||||
);
|
||||
}
|
||||
|
||||
get editTitle() {
|
||||
return this.canEdit
|
||||
? "admin.config_areas.flags.form.edit_flag"
|
||||
: "admin.config_areas.flags.form.non_editable";
|
||||
}
|
||||
|
||||
get deleteTitle() {
|
||||
return this.canEdit
|
||||
? "admin.config_areas.flags.form.edit_flag"
|
||||
: "admin.config_areas.flags.form.non_editable";
|
||||
}
|
||||
|
||||
@action
|
||||
toggleFlagEnabled(flag) {
|
||||
this.enabled = !this.enabled;
|
||||
|
||||
return ajax(`/admin/config/flags/${flag.id}/toggle`, {
|
||||
type: "PUT",
|
||||
}).catch((error) => {
|
||||
this.enabled = !this.enabled;
|
||||
return popupAjaxError(error);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.args.flag.enabled = this.enabled;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.enabled = !this.enabled;
|
||||
return popupAjaxError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -48,6 +76,23 @@ export default class AdminFlagItem extends Component {
|
||||
this.args.moveFlagCallback(this.args.flag, "down");
|
||||
this.dMenu.close();
|
||||
}
|
||||
@action
|
||||
edit() {
|
||||
this.router.transitionTo("adminConfig.flags.edit", this.args.flag);
|
||||
}
|
||||
|
||||
@action
|
||||
delete() {
|
||||
this.dialog.yesNoConfirm({
|
||||
message: i18n("admin.config_areas.flags.delete_confirm", {
|
||||
name: this.args.flag.name,
|
||||
}),
|
||||
didConfirm: () => {
|
||||
this.args.deleteFlagCallback(this.args.flag);
|
||||
},
|
||||
});
|
||||
this.dMenu.close();
|
||||
}
|
||||
|
||||
<template>
|
||||
<tr class="admin-flag-item {{@flag.name_key}}">
|
||||
@@ -64,10 +109,19 @@ export default class AdminFlagItem extends Component {
|
||||
class="admin-flag-item__toggle {{@flag.name_key}}"
|
||||
{{on "click" (fn this.toggleFlagEnabled @flag)}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
class="btn btn-secondary admin-flag-item__edit"
|
||||
@action={{this.edit}}
|
||||
@label="admin.config_areas.flags.edit"
|
||||
@disabled={{not this.canEdit}}
|
||||
@title={{this.editTitle}}
|
||||
/>
|
||||
|
||||
{{#if this.canMove}}
|
||||
<DMenu
|
||||
@identifier="flag-menu"
|
||||
@title={{i18n "admin.flags.more_options.title"}}
|
||||
@title={{i18n "admin.config_areas.flags.more_options.title"}}
|
||||
@icon="ellipsis-v"
|
||||
@onRegisterApi={{this.onRegisterApi}}
|
||||
>
|
||||
@@ -76,9 +130,9 @@ export default class AdminFlagItem extends Component {
|
||||
{{#unless @isFirstFlag}}
|
||||
<dropdown.item>
|
||||
<DButton
|
||||
@label="admin.flags.more_options.move_up"
|
||||
@label="admin.config_areas.flags.more_options.move_up"
|
||||
@icon="arrow-up"
|
||||
@class="btn-transparent move-up"
|
||||
@class="btn-transparent admin-flag-item__move-up"
|
||||
@action={{this.moveUp}}
|
||||
/>
|
||||
</dropdown.item>
|
||||
@@ -86,13 +140,24 @@ export default class AdminFlagItem extends Component {
|
||||
{{#unless @isLastFlag}}
|
||||
<dropdown.item>
|
||||
<DButton
|
||||
@label="admin.flags.more_options.move_down"
|
||||
@label="admin.config_areas.flags.more_options.move_down"
|
||||
@icon="arrow-down"
|
||||
@class="btn-transparent move-down"
|
||||
@class="btn-transparent admin-flag-item__move-down"
|
||||
@action={{this.moveDown}}
|
||||
/>
|
||||
</dropdown.item>
|
||||
{{/unless}}
|
||||
|
||||
<dropdown.item>
|
||||
<DButton
|
||||
@label="admin.config_areas.flags.delete"
|
||||
@icon="trash-alt"
|
||||
class="btn-transparent admin-flag-item__delete"
|
||||
@action={{this.delete}}
|
||||
@disabled={{not this.canEdit}}
|
||||
@title={{this.deleteTitle}}
|
||||
/>
|
||||
</dropdown.item>
|
||||
</DropdownMenu>
|
||||
</:content>
|
||||
</DMenu>
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { TextArea } from "@ember/legacy-built-in-components";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { not } from "truth-helpers";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
|
||||
import MultiSelect from "select-kit/components/multi-select";
|
||||
|
||||
export default class AdminFlagsForm extends Component {
|
||||
@service router;
|
||||
@service site;
|
||||
|
||||
@tracked enabled = true;
|
||||
@tracked name;
|
||||
@tracked description;
|
||||
@tracked appliesTo;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
if (this.isUpdate) {
|
||||
this.name = this.args.flag.name;
|
||||
this.description = this.args.flag.description;
|
||||
this.appliesTo = this.args.flag.applies_to;
|
||||
this.enabled = this.args.flag.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
get isUpdate() {
|
||||
return this.args.flag;
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
return (
|
||||
!isEmpty(this.name) &&
|
||||
!isEmpty(this.description) &&
|
||||
!isEmpty(this.appliesTo)
|
||||
);
|
||||
}
|
||||
|
||||
get header() {
|
||||
return this.isUpdate
|
||||
? "admin.config_areas.flags.form.edit_header"
|
||||
: "admin.config_areas.flags.form.add_header";
|
||||
}
|
||||
|
||||
get appliesToValues() {
|
||||
return this.site.valid_flag_applies_to_types.map((type) => {
|
||||
return {
|
||||
name: I18n.t(
|
||||
`admin.config_areas.flags.form.${type
|
||||
.toLowerCase()
|
||||
.replace("::", "_")}`
|
||||
),
|
||||
id: type,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
save() {
|
||||
this.isUpdate ? this.update() : this.create();
|
||||
}
|
||||
|
||||
@bind
|
||||
create() {
|
||||
return ajax(`/admin/config/flags`, {
|
||||
type: "POST",
|
||||
data: this.#formData,
|
||||
})
|
||||
.then((response) => {
|
||||
this.site.flagTypes.push(response.flag);
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
})
|
||||
.catch((error) => {
|
||||
return popupAjaxError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@bind
|
||||
update() {
|
||||
return ajax(`/admin/config/flags/${this.args.flag.id}`, {
|
||||
type: "PUT",
|
||||
data: this.#formData,
|
||||
})
|
||||
.then((response) => {
|
||||
this.args.flag.name = response.flag.name;
|
||||
this.args.flag.description = response.flag.description;
|
||||
this.args.flag.applies_to = response.flag.applies_to;
|
||||
this.args.flag.enabled = response.flag.enabled;
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
})
|
||||
.catch((error) => {
|
||||
return popupAjaxError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@bind
|
||||
get #formData() {
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
applies_to: this.appliesTo,
|
||||
enabled: this.enabled,
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="admin-config-area">
|
||||
<h2>{{i18n "admin.config_areas.flags.header"}}</h2>
|
||||
<LinkTo
|
||||
@route="adminConfig.flags"
|
||||
class="btn-default btn btn-icon-text btn-back"
|
||||
>
|
||||
{{dIcon "chevron-left"}}
|
||||
{{i18n "admin.config_areas.flags.back"}}
|
||||
</LinkTo>
|
||||
<div class="admin-config-area__primary-content admin-flag-form">
|
||||
<AdminConfigAreaCard @heading={{this.header}}>
|
||||
<div class="control-group">
|
||||
<label for="name">
|
||||
{{i18n "admin.config_areas.flags.form.name"}}
|
||||
</label>
|
||||
<Input
|
||||
name="name"
|
||||
@type="text"
|
||||
@value={{this.name}}
|
||||
maxlength="200"
|
||||
class="admin-flag-form__name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="description">
|
||||
{{i18n "admin.config_areas.flags.form.description"}}
|
||||
</label>
|
||||
<TextArea
|
||||
@value={{this.description}}
|
||||
maxlength="1000"
|
||||
class="admin-flag-form__description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="applies-to">
|
||||
{{i18n "admin.config_areas.flags.form.applies_to"}}
|
||||
</label>
|
||||
<MultiSelect
|
||||
@value={{this.appliesTo}}
|
||||
@content={{this.appliesToValues}}
|
||||
@options={{hash allowAny=false}}
|
||||
class="admin-flag-form__applies-to"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="checkbox-label admin-flag-form__enabled">
|
||||
<Input @type="checkbox" @checked={{this.enabled}} />
|
||||
{{i18n "admin.config_areas.flags.form.enabled"}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info admin_flag_form__info">
|
||||
{{dIcon "info-circle"}}
|
||||
{{i18n "admin.config_areas.flags.form.alert"}}
|
||||
</div>
|
||||
|
||||
<DButton
|
||||
@action={{this.save}}
|
||||
@label="admin.config_areas.flags.form.save"
|
||||
@ariaLabel="admin.config_areas.flags.form.save"
|
||||
@disabled={{not this.isValid}}
|
||||
class="btn-primary admin-flag-form__save"
|
||||
/>
|
||||
</AdminConfigAreaCard>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import Route from "@ember/routing/route";
|
||||
import { service } from "@ember/service";
|
||||
|
||||
export default class AdminConfigFlagsEditRoute extends Route {
|
||||
@service site;
|
||||
|
||||
model(params) {
|
||||
return this.site.flagTypes.findBy("id", parseInt(params.flag_id, 10));
|
||||
}
|
||||
}
|
||||
@@ -215,6 +215,8 @@ export default function () {
|
||||
function () {
|
||||
this.route("flags", function () {
|
||||
this.route("index", { path: "/" });
|
||||
this.route("new");
|
||||
this.route("edit", { path: "/:flag_id" });
|
||||
});
|
||||
|
||||
this.route("about");
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<AdminFlagsForm @flag={{@model}} />
|
||||
@@ -1 +1 @@
|
||||
<AdminFlags />
|
||||
<AdminConfigAreas::Flags />
|
||||
@@ -0,0 +1 @@
|
||||
<AdminFlagsForm />
|
||||
Reference in New Issue
Block a user