diff --git a/scripts/pi-hole/js/groups-adlists.js b/scripts/pi-hole/js/groups-adlists.js
index d5363c01..f00d387d 100644
--- a/scripts/pi-hole/js/groups-adlists.js
+++ b/scripts/pi-hole/js/groups-adlists.js
@@ -525,14 +525,9 @@ function addAdlist(event) {
method: "post",
dataType: "json",
data: JSON.stringify({ address: addresses, comment: comment, type: type }),
- success: function () {
+ success: function (data) {
utils.enableAll();
- utils.showAlert(
- "success",
- "fas fa-plus",
- "Successfully added " + type + "list(s)",
- addressestr
- );
+ utils.listsAlert("list", addresses, data);
table.ajax.reload(null, false);
table.rows().deselect();
diff --git a/scripts/pi-hole/js/groups-clients.js b/scripts/pi-hole/js/groups-clients.js
index 013bab8b..d16df1b3 100644
--- a/scripts/pi-hole/js/groups-clients.js
+++ b/scripts/pi-hole/js/groups-clients.js
@@ -454,9 +454,9 @@ function addClient() {
method: "post",
dataType: "json",
data: JSON.stringify({ client: ips, comment: comment }),
- success: function () {
+ success: function (data) {
utils.enableAll();
- utils.showAlert("success", "fas fa-plus", "Successfully added client(s)", ipStr);
+ utils.listsAlert("client", ips, data);
reloadClientSuggestions();
table.ajax.reload(null, false);
table.rows().deselect();
diff --git a/scripts/pi-hole/js/groups-domains.js b/scripts/pi-hole/js/groups-domains.js
index a4591253..2185ef06 100644
--- a/scripts/pi-hole/js/groups-domains.js
+++ b/scripts/pi-hole/js/groups-domains.js
@@ -541,9 +541,9 @@ function addDomain() {
type: type,
kind: kind,
}),
- success: function () {
+ success: function (data) {
utils.enableAll();
- utils.showAlert("success", "fas fa-plus", "Successfully added domain(s)", domainStr);
+ utils.listsAlert("domain", domains, data);
table.ajax.reload(null, false);
table.rows().deselect();
diff --git a/scripts/pi-hole/js/groups.js b/scripts/pi-hole/js/groups.js
index eb14b0eb..c61b0668 100644
--- a/scripts/pi-hole/js/groups.js
+++ b/scripts/pi-hole/js/groups.js
@@ -310,9 +310,9 @@ function addGroup() {
comment: comment,
enabled: true,
}),
- success: function () {
+ success: function (data) {
utils.enableAll();
- utils.showAlert("success", "fas fa-plus", "Successfully added group(s)", groupStr);
+ utils.listsAlert("group", names, data);
$("#new_name").val("");
$("#new_comment").val("");
table.ajax.reload();
diff --git a/scripts/pi-hole/js/utils.js b/scripts/pi-hole/js/utils.js
index cbeb6304..b1823a5d 100644
--- a/scripts/pi-hole/js/utils.js
+++ b/scripts/pi-hole/js/utils.js
@@ -536,6 +536,81 @@ function hexDecode(string) {
return back;
}
+function listAlert(type, items, data) {
+ // Show simple success message if there is no "processed" object in "data" or
+ // if all items were processed successfully
+ if (data.processed === undefined || data.processed.success.length === items.length) {
+ showAlert(
+ "success",
+ "fas fa-plus",
+ "Successfully added " + type + (items.length !== 1 ? "s" : ""),
+ items.join(", ")
+ );
+ return;
+ }
+
+ // Show a more detailed message if there is a "processed" object in "data" and
+ // not all items were processed successfully
+ let message = "";
+
+ // Show a list of successful items if there are any
+ if (data.processed.success.length > 0) {
+ message +=
+ "Successfully added " +
+ data.processed.success.length +
+ " " +
+ type +
+ (data.processed.success.length !== 1 ? "s" : "") +
+ ":";
+
+ // Loop over data.processed.success and print "item"
+ for (const item in data.processed.success) {
+ if (Object.prototype.hasOwnProperty.call(data.processed.success, item)) {
+ message += "
- " + data.processed.success[item].item + "";
+ }
+ }
+ }
+
+ // Add a line break if there are both successful and failed items
+ if (data.processed.success.length > 0 && data.processed.errors.length > 0) {
+ message += "
";
+ }
+
+ // Show a list of failed items if there are any
+ if (data.processed.errors.length > 0) {
+ message +=
+ "Failed to add " +
+ data.processed.errors.length +
+ " " +
+ type +
+ (data.processed.errors.length !== 1 ? "s" : "") +
+ ":\n";
+
+ // Loop over data.processed.errors and print "item: error"
+ for (const item in data.processed.errors) {
+ if (Object.prototype.hasOwnProperty.call(data.processed.errors, item)) {
+ let error = data.processed.errors[item].error;
+ if (error === "UNIQUE constraint failed: domainlist.domain, domainlist.type") {
+ // Replace the error message with a more user-friendly one
+ error = "Already present";
+ }
+
+ message += "
- " + data.processed.errors[item].item + ": " + error;
+ }
+ }
+ }
+
+ // Show the warning message
+ const total = data.processed.success.length + data.processed.errors.length;
+ const processed = "(" + total + " " + type + (total !== 1 ? "s" : "") + " processed)";
+ showAlert(
+ "warning",
+ "fas fa-exclamation-triangle",
+ "Some " + type + (items.length !== 1 ? "s" : "") + " could not be added " + processed,
+ message
+ );
+}
+
window.utils = (function () {
return {
escapeHtml: escapeHtml,
@@ -569,5 +644,6 @@ window.utils = (function () {
parseQueryString: parseQueryString,
hexEncode: hexEncode,
hexDecode: hexDecode,
+ listsAlert: listAlert,
};
})();