bot-url: Add event filtering UI to generate bot URL modal.

Adds an option to the generate bot URL modal to filter for events
that will trigger notifications. This option is conditionally
displayed when only `all_event_types` is defined for the selected
integration.

If selected, the URL will display all events by default. There
are "check all" and "uncheck all" buttons to easily include or
remove all the events from the URL.

Fixes #27628.
This commit is contained in:
sujal 2023-12-19 22:53:17 +05:30 committed by Tim Abbott
parent a61c06c425
commit fbcf31c9e0
4 changed files with 129 additions and 2 deletions

View File

@ -3,6 +3,7 @@ import $ from "jquery";
import type {Instance} from "tippy.js"; import type {Instance} from "tippy.js";
import render_generate_integration_url_modal from "../templates/settings/generate_integration_url_modal.hbs"; import render_generate_integration_url_modal from "../templates/settings/generate_integration_url_modal.hbs";
import render_integration_events from "../templates/settings/integration_events.hbs";
import {show_copied_confirmation} from "./copied_tooltip"; import {show_copied_confirmation} from "./copied_tooltip";
import * as dialog_widget from "./dialog_widget"; import * as dialog_widget from "./dialog_widget";
@ -39,6 +40,7 @@ export function show_generate_integration_url_modal(api_key: string): void {
const $topic_input = $<HTMLInputElement>("input#integration-url-topic-input"); const $topic_input = $<HTMLInputElement>("input#integration-url-topic-input");
const $integration_url = $("#generate-integration-url-modal .integration-url"); const $integration_url = $("#generate-integration-url-modal .integration-url");
const $dialog_submit_button = $("#generate-integration-url-modal .dialog_submit_button"); const $dialog_submit_button = $("#generate-integration-url-modal .dialog_submit_button");
const $show_integration_events = $("#show-integration-events");
$dialog_submit_button.prop("disabled", true); $dialog_submit_button.prop("disabled", true);
$("#integration-url-stream_widget").prop("disabled", true); $("#integration-url-stream_widget").prop("disabled", true);
@ -57,11 +59,33 @@ export function show_generate_integration_url_modal(api_key: string): void {
$topic_input.parent().toggleClass("hide", !checked); $topic_input.parent().toggleClass("hide", !checked);
}); });
$show_integration_events.on("change", () => {
$("#integrations-event-container").toggleClass(
"hide",
!$show_integration_events.prop("checked"),
);
update_url(true);
});
$(document).on("change", "#integrations-event-container .integration-event", () => {
update_url();
});
$("#add-all-integration-events").on("click", () => {
$("#integrations-event-container .integration-event").prop("checked", true);
update_url();
});
$("#remove-all-integration-events").on("click", () => {
$("#integrations-event-container .integration-event").prop("checked", false);
update_url();
});
$("#generate-integration-url-modal .integration-url-parameter").on("change input", () => { $("#generate-integration-url-modal .integration-url-parameter").on("change input", () => {
update_url(); update_url();
}); });
function update_url(): void { function update_url(render_events = false): void {
selected_integration = integration_input_dropdown_widget.value()!.toString(); selected_integration = integration_input_dropdown_widget.value()!.toString();
if (selected_integration === default_integration_option.unique_id) { if (selected_integration === default_integration_option.unique_id) {
$("#integration-url-stream_widget").prop("disabled", true); $("#integration-url-stream_widget").prop("disabled", true);
@ -73,6 +97,35 @@ export function show_generate_integration_url_modal(api_key: string): void {
const stream_id = stream_input_dropdown_widget.value(); const stream_id = stream_input_dropdown_widget.value();
const topic_name = $topic_input.val()!; const topic_name = $topic_input.val()!;
const selected_integration_data = realm.realm_incoming_webhook_bots.find(
(bot) => bot.name === selected_integration,
);
const all_event_types = selected_integration_data?.all_event_types;
if (all_event_types !== null) {
$("#integration-events-parameter").removeClass("hide");
} else {
$("#integration-events-parameter").addClass("hide");
$("#integrations-event-container").addClass("hide");
$("#integrations-event-options").empty();
$show_integration_events.prop("checked", false);
}
if ($show_integration_events.prop("checked") && render_events) {
const events_with_ids = all_event_types?.map((event) => {
const event_id = event.replaceAll(/\s+/g, "-");
return {
event,
event_id,
};
});
events_with_ids?.sort((a, b) => a.event.localeCompare(b.event));
const events = render_integration_events({
events: events_with_ids,
});
$("#integrations-event-options").empty().append(events);
}
const params = new URLSearchParams({api_key}); const params = new URLSearchParams({api_key});
if (stream_id !== -1) { if (stream_id !== -1) {
params.set("stream", stream_id!.toString()); params.set("stream", stream_id!.toString());
@ -80,13 +133,17 @@ export function show_generate_integration_url_modal(api_key: string): void {
params.set("topic", topic_name); params.set("topic", topic_name);
} }
} }
const selected_events = set_events_param(params);
const realm_url = realm.realm_uri; const realm_url = realm.realm_uri;
const base_url = `${realm_url}/api/v1/external/`; const base_url = `${realm_url}/api/v1/external/`;
$integration_url.text(`${base_url}${selected_integration}?${params.toString()}`); $integration_url.text(`${base_url}${selected_integration}?${params.toString()}`);
$dialog_submit_button.prop("disabled", false); $dialog_submit_button.prop("disabled", false);
if ($override_topic.prop("checked") && topic_name === "") { if (
($override_topic.prop("checked") && topic_name === "") ||
($show_integration_events.prop("checked") && !selected_events)
) {
$dialog_submit_button.prop("disabled", true); $dialog_submit_button.prop("disabled", true);
} }
} }
@ -174,6 +231,26 @@ export function show_generate_integration_url_modal(api_key: string): void {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }
function set_events_param(params: URLSearchParams): boolean {
if (!$show_integration_events.prop("checked")) {
return false;
}
const $selected_integration_events = $(
"#integrations-event-container .integration-event:checked",
);
const selected_events = $selected_integration_events
.map(function () {
return $(this).val();
})
.get();
if (selected_events.length > 0) {
params.set("only_events", JSON.stringify(selected_events));
return true;
}
return false;
}
} }
dialog_widget.launch({ dialog_widget.launch({

View File

@ -482,3 +482,23 @@
border: 1px solid var(--color-hotkey-hint); border: 1px solid var(--color-hotkey-hint);
} }
} }
#generate-integration-url-modal {
#integrations-event-container {
.integration-all-events-buttons {
display: flex;
gap: 10px;
margin: 5px 0 10px;
}
#integrations-event-options {
.integration-event-wrapper {
margin: 5px 0;
}
.integration-event-name {
word-break: break-all;
}
}
}
}

View File

@ -26,6 +26,23 @@
<label for="integration-url-topic-input">{{t "Topic"}}</label> <label for="integration-url-topic-input">{{t "Topic"}}</label>
<input type="text" id="integration-url-topic-input" class="modal_text_input integration-url-parameter" maxlength="{{ max_topic_length }}" /> <input type="text" id="integration-url-topic-input" class="modal_text_input integration-url-parameter" maxlength="{{ max_topic_length }}" />
</div> </div>
<div id="integration-events-parameter" class="input-group hide">
<label class="checkbox">
<input type="checkbox" id="show-integration-events"/>
<span></span>
</label>
<label class="inline" for="show-integration-events">
{{t "Filter events that will trigger notifications?"}}
</label>
</div>
<div class="input-group hide" id="integrations-event-container">
<label for="integrations-event-options">{{t "Events to include:"}}</label>
<div class="integration-all-events-buttons">
<button class="button rounded" id="add-all-integration-events">{{t "Check all"}}</button>
<button class="button rounded" id="remove-all-integration-events">{{t "Uncheck all"}}</button>
</div>
<div id="integrations-event-options"></div>
</div>
<hr /> <hr />
<p class="integration-url-header">{{t "URL for your integration"}}</p> <p class="integration-url-header">{{t "URL for your integration"}}</p>
<div class="integration-url"> <div class="integration-url">

View File

@ -0,0 +1,13 @@
{{#each events }}
<div class="integration-event-wrapper">
<label class="checkbox">
<input type="checkbox" class="integration-event" id="{{this.event_id}}" checked=true
value="{{this.event}}" />
<span>
</span>
</label>
<label for="{{this.event_id}}" class="inline integration-event-name">
{{this.event}}
</label>
</div>
{{/each}}