Skip to content

Adds Delete Trigger action#529

Closed
ramsaysewell wants to merge 4 commits intorajnandan1:mainfrom
ramsaysewell:feature/delete-trigger-action
Closed

Adds Delete Trigger action#529
ramsaysewell wants to merge 4 commits intorajnandan1:mainfrom
ramsaysewell:feature/delete-trigger-action

Conversation

@ramsaysewell
Copy link

Adds a "Delete Trigger" button next to the "Test Trigger" and "Settings" buttons that removes the Trigger from the database.

There is currently no "confirm" implemented for this destructive action.

Copilot AI review requested due to automatic review settings January 4, 2026 19:30
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds the ability to delete triggers through a new "Delete Trigger" button in the UI. The implementation includes backend API endpoints, database operations, and frontend UI changes, but notably lacks a confirmation dialog for this destructive action.

Key Changes

  • Added DeleteTrigger action handler in the API server with proper role-based authorization
  • Implemented database method deleteTrigger to remove triggers by ID
  • Added "Delete Trigger" button with loading/success/error states in the trigger management UI

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/routes/(manage)/manage/(app)/app/api/+server.js Added API route handler for "deleteTrigger" action with AdminEditor authorization check; also reorganized imports alphabetically
src/lib/server/db/dbimpl.js Added deleteTrigger database method to delete triggers by ID; minor whitespace cleanup
src/lib/server/controllers/controller.js Added DeleteTrigger controller function with ID validation; reorganized imports alphabetically
src/lib/components/manage/triggerInfo.svelte Added delete button UI, deleteTrigger async function with loader states, and updated trigger list rendering; removed unused import and simplified testTrigger function signature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


if (resp.error) {
triggers[i].deleteLoaders = "error";
alert(resp.error);
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using alert() for error messages is not consistent with modern UI/UX practices and can be jarring for users. Consider using a toast notification or inline error message that matches the existing design system, similar to how other parts of the application handle errors.

Copilot uses AI. Check for mistakes.
}, 1000);
}
} catch (error) {
alert("Error: " + error);
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using alert() for error messages is not consistent with modern UI/UX practices. Consider using a toast notification or inline error message that matches the existing design system.

Copilot uses AI. Check for mistakes.
});
}

//delete alert
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "//delete alert" is inconsistent with the function name. The function is called deleteTrigger and works with triggers, not alerts. The comment should say "//delete trigger" to match the function's purpose.

Suggested change
//delete alert
//delete trigger

Copilot uses AI. Check for mistakes.

//delete alert
async deleteTrigger(data) {
return await this.knex("triggers").where({ id: data.id }).delete();
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting a trigger without handling references to it in the monitors table could result in orphaned references. The monitors table has down_trigger and degraded_trigger fields that may reference trigger IDs. Consider either: 1) Setting up a foreign key with CASCADE on the database level, 2) Checking if the trigger is referenced by any monitors before deletion and preventing the delete or clearing those references, or 3) Automatically clearing monitor references when the trigger is deleted.

Suggested change
return await this.knex("triggers").where({ id: data.id }).delete();
const triggerId = data.id;
return await this.knex.transaction(async (trx) => {
// Clear references from monitors that point to this trigger
await trx("monitors")
.where("down_trigger", triggerId)
.update({ down_trigger: null });
await trx("monitors")
.where("degraded_trigger", triggerId)
.update({ degraded_trigger: null });
// Delete the trigger itself
return await trx("triggers").where({ id: triggerId }).delete();
});

Copilot uses AI. Check for mistakes.
Comment on lines +290 to +293
setTimeout(() => {
triggers[i].deleteLoaders = "idle";
}, 3000);
}
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a potential timing issue with the loader state management. The finally block sets a 3-second timeout to reset the loader state, but on successful deletion, loadData() is called after 1 second, which will replace the triggers array. This means the loader state reset on line 291 will try to update a stale reference that's no longer in the triggers array. Consider clearing the timeout when data is reloaded, or manage the loader state differently.

Copilot uses AI. Check for mistakes.
"Content-Type": "application/json"
},
body: JSON.stringify({ action: "deleteTrigger", data: { id: trigger_id } })
});
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delete function doesn't check the HTTP response status code. Even if the server returns an error status code (e.g., 500, 403), the code attempts to parse JSON and only checks for resp.error. If the server returns a non-JSON error response, this will fail. Consider checking data.ok or data.status before parsing the JSON response.

Suggested change
});
});
if (!data.ok) {
triggers[i].deleteLoaders = "error";
let message = "Request failed with status " + data.status;
try {
const errorBody = await data.text();
if (errorBody) {
message += ": " + errorBody;
}
} catch (e) {
// ignore body parsing errors, we already have status
}
alert(message);
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +329
if (alertData.id) {
return await db.deleteTrigger(alertData);
}
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition on line 327 is redundant. The check on line 323 already ensures that alertData.id exists, so the second check on line 327 will always be true. Either remove the second condition and unconditionally call db.deleteTrigger(alertData), or combine both error cases into a single validation block.

Suggested change
if (alertData.id) {
return await db.deleteTrigger(alertData);
}
return await db.deleteTrigger(alertData);

Copilot uses AI. Check for mistakes.
Comment on lines +266 to +294
async function deleteTrigger(i, trigger_id) {
triggers[i].deleteLoaders = "loading";
try {
let data = await fetch(base + "/manage/app/api/", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ action: "deleteTrigger", data: { id: trigger_id } })
});
let resp = await data.json();

if (resp.error) {
triggers[i].deleteLoaders = "error";
alert(resp.error);
} else {
triggers[i].deleteLoaders = "success";
setTimeout(async () => {
await loadData();
}, 1000);
}
} catch (error) {
alert("Error: " + error);
} finally {
setTimeout(() => {
triggers[i].deleteLoaders = "idle";
}, 3000);
}
}
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This destructive action lacks user confirmation before deleting the trigger. As noted in the PR description, there is no confirmation dialog implemented. Consider adding a confirmation prompt (e.g., using a modal or browser confirm) before proceeding with the deletion to prevent accidental data loss.

Copilot uses AI. Check for mistakes.
@ramsaysewell
Copy link
Author

This pr will resolve #522

@rajnandan1 rajnandan1 moved this from Backlog to In progress in Kener v4 Launch Feb 2, 2026
@rajnandan1 rajnandan1 moved this from In progress to Done in Kener v4 Launch Feb 24, 2026
@rajnandan1
Copy link
Owner

This request/issue/feature has been added to v4.x
The release of this new version was a lot of effort. If you find this project then please support me by either of the below options

  1. GitHub Sponsors
  2. Buy Me a Coffee

@rajnandan1 rajnandan1 closed this Feb 24, 2026
@github-project-automation github-project-automation bot moved this from Done to Dev Done in Kener v4 Launch Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Dev Done

Development

Successfully merging this pull request may close these issues.

3 participants