diff --git a/components/layout.templ b/components/layout.templ
index edc6fb9..80e25ad 100644
--- a/components/layout.templ
+++ b/components/layout.templ
@@ -237,14 +237,20 @@ templ LayoutWithContext(title string, ctx context.Context) {
}
@@ -458,8 +464,8 @@ templ LayoutWithContext(title string, ctx context.Context) {
}
// Initialize admin dropdown toggle if available
- const adminDropdownToggle = document.querySelector('[data-collapse-toggle="dropdown-admin"]');
- const adminDropdown = document.getElementById('dropdown-admin');
+ const adminDropdownToggle = document.querySelector('[data-collapse-toggle="dropdown-settings"]');
+ const adminDropdown = document.getElementById('dropdown-settings');
if (adminDropdownToggle && adminDropdown) {
// Check if we should show the dropdown (if current page is under admin section)
diff --git a/components/notifications.templ b/components/notifications.templ
index b90cf53..77ee14a 100644
--- a/components/notifications.templ
+++ b/components/notifications.templ
@@ -5,6 +5,66 @@ import (
"fmt"
)
+// Dialog component for confirmation dialogs using Flowbite modal
+templ NotificationDialog(id string, title string, message string, confirmClass string, confirmText string, action string, serviceID uint, serviceName string) {
+
+
+
+
+ if action == "delete" {
+
+ } else {
+
+ }
+
{ message }
+
+
+
+
+
+
+}
+
+script hideNotificationDialog(id string) {
+ document.getElementById(id).classList.add("hidden");
+ document.getElementById(id).classList.remove("flex");
+}
+
+script showNotificationDialog(id string) {
+ document.getElementById(id).classList.remove("hidden");
+ document.getElementById(id).classList.add("flex");
+}
+
+script triggerServiceDelete(dialogId string, serviceID uint, serviceName string) {
+ // Hide the dialog
+ document.getElementById(dialogId).classList.add("hidden");
+ document.getElementById(dialogId).classList.remove("flex");
+
+ // Add debugging info
+ console.log(`Notification service deletion triggered for: ${serviceName} (ID: ${serviceID})`);
+
+ // Store data in a way that's accessible to event handlers
+ window.lastDeletedService = {
+ id: serviceID,
+ name: serviceName
+ };
+
+ // Add custom marker to track this deletion
+ window.currentlyDeletingService = true;
+}
+
templ Notifications(ctx context.Context, data SettingsNotificationsData) {
@LayoutWithContext("Notification Services", ctx) {
@@ -85,31 +145,113 @@ templ Notifications(ctx context.Context, data SettingsNotificationsData) {
// Track all HTMX events for debugging
document.addEventListener('htmx:beforeRequest', function(event) {
console.log("HTMX before request:", event.detail);
+
+ // Check if this is a DELETE request for a notification service
+ const path = event.detail.path;
+ const method = event.detail.verb;
+
+ console.log(`Request path: ${path}, method: ${method}`);
+
+ // Pattern match for notification service deletions (e.g., /admin/settings/notifications/123)
+ if (path && method === 'DELETE' && path.match(/^\/admin\/settings\/notifications\/\d+$/)) {
+ console.log("Detected notification service deletion request via URL pattern");
+
+ // This is definitely a delete request - store this information
+ window.isServiceDeleteRequest = true;
+ }
});
document.addEventListener('htmx:afterRequest', function(event) {
console.log("HTMX after request:", event.detail);
- // Check if this is a successful notification service deletion
- if (event.detail.pathInfo &&
- event.detail.pathInfo.requestPath &&
- event.detail.pathInfo.requestPath.match(/^\/admin\/settings\/notifications\/\d+$/) &&
- event.detail.verb === 'DELETE' &&
- event.detail.successful) {
+ // Check for notification service deletion multiple ways
+ const isDeleteRequest =
+ // Check global flag from the triggerServiceDelete function
+ window.currentlyDeletingService ||
+ // Check flag from beforeRequest handler
+ window.isServiceDeleteRequest ||
+ // Check URL pattern directly from this event
+ (event.detail.pathInfo &&
+ event.detail.pathInfo.requestPath &&
+ event.detail.pathInfo.requestPath.match(/^\/admin\/settings\/notifications\/\d+$/) &&
+ event.detail.verb === 'DELETE');
+
+ console.log(`Is delete request: ${isDeleteRequest}`);
+
+ // If this is a successful delete request, show notification
+ if (isDeleteRequest && event.detail.successful) {
+ console.log("Delete request was successful");
- showToast('Notification service deleted successfully', 'success');
+ let serviceName = "Unknown";
+
+ // Try multiple sources for service name
+ if (event.detail.elt && event.detail.elt.getAttribute) {
+ serviceName = event.detail.elt.getAttribute('data-service-name') || serviceName;
+ }
+
+ if (serviceName === "Unknown" && window.lastDeletedService) {
+ // Fallback to our stored service info
+ serviceName = window.lastDeletedService.name;
+ }
+
+ console.log(`Showing success notification for deleted service: ${serviceName}`);
+ showToast(`Notification service "${serviceName}" deleted successfully`, 'success');
+
+ // Clear flags
+ window.currentlyDeletingService = false;
+ window.isServiceDeleteRequest = false;
+ window.lastDeletedService = null;
}
});
document.addEventListener('htmx:responseError', function(event) {
console.log("HTMX response error:", event.detail);
+ // Similar logic as success but for errors
+ const isDeleteRequest =
+ window.currentlyDeletingService ||
+ window.isServiceDeleteRequest ||
+ (event.detail.pathInfo &&
+ event.detail.pathInfo.requestPath &&
+ event.detail.pathInfo.requestPath.match(/^\/admin\/settings\/notifications\/\d+$/) &&
+ event.detail.verb === 'DELETE');
+
let errorMsg = 'An error occurred';
if (event.detail.xhr && event.detail.xhr.responseText) {
errorMsg = event.detail.xhr.responseText;
}
- showToast(errorMsg, 'error');
+ if (isDeleteRequest) {
+ console.log("Delete request failed");
+
+ let serviceName = "Unknown";
+
+ // Try multiple sources for service name
+ if (event.detail.elt && event.detail.elt.getAttribute) {
+ serviceName = event.detail.elt.getAttribute('data-service-name') || serviceName;
+ }
+
+ if (serviceName === "Unknown" && window.lastDeletedService) {
+ // Fallback to our stored service info
+ serviceName = window.lastDeletedService.name;
+ }
+
+ let errorMsg = `Failed to delete notification service "${serviceName}"`;
+
+ if (event.detail.xhr && event.detail.xhr.responseText) {
+ errorMsg = `Error: ${event.detail.xhr.responseText}`;
+ }
+
+ console.log(`Showing error notification: ${errorMsg}`);
+ showToast(errorMsg, 'error');
+
+ // Clear flags
+ window.currentlyDeletingService = false;
+ window.isServiceDeleteRequest = false;
+ window.lastDeletedService = null;
+ } else {
+ showToast(errorMsg, 'error');
+ }
});
// Handle modal hide buttons
@@ -214,12 +356,21 @@ templ Notifications(ctx context.Context, data SettingsNotificationsData) {
>
+
+ @NotificationDialog(
+ fmt.Sprintf("delete-notification-dialog-%d", service.ID),
+ "Delete Notification Service",
+ fmt.Sprintf("Are you sure you want to delete the notification service '%s'? This cannot be undone.", service.Name),
+ "text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800",
+ "Delete",
+ "delete",
+ service.ID,
+ service.Name,
+ )
diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go
index 5b6a13c..d20fab3 100644
--- a/internal/scheduler/scheduler.go
+++ b/internal/scheduler/scheduler.go
@@ -1579,12 +1579,23 @@ func (s *Scheduler) sendServiceWebhookNotification(service *db.NotificationServi
// generateDefaultPayload creates a standard webhook payload
func generateDefaultPayload(job *db.Job, history *db.JobHistory, config *db.TransferConfig, eventType string) map[string]interface{} {
+ // get event type
+ switch eventType {
+ case "job_start":
+ eventType = "Job Started"
+ case "job_complete":
+ eventType = "Job Completed"
+ case "job_fail":
+ eventType = "Job Failed"
+ }
+
payload := map[string]interface{}{
"event": eventType,
"job": map[string]interface{}{
"id": job.ID,
"name": job.Name,
"status": history.Status,
+ "event": eventType,
"message": history.ErrorMessage,
"started_at": history.StartTime.Format(time.RFC3339),
"config_id": config.ID,
@@ -2400,6 +2411,8 @@ func (s *Scheduler) sendNtfyNotification(service *db.NotificationService, job *d
func (s *Scheduler) sendGotifyNotification(service *db.NotificationService, job *db.Job, history *db.JobHistory, config *db.TransferConfig, eventType string) error {
s.log.LogDebug("Sending Gotify notification for job %d", job.ID)
+ // pretty print job
+ fmt.Printf("Job: %+v\n", job)
// Get Gotify server URL and token from service config
serverURL, ok := service.Config["url"]
if !ok || serverURL == "" {
diff --git a/internal/web/handlers/settings_handlers.go b/internal/web/handlers/settings_handlers.go
index 30299c5..aa28adf 100644
--- a/internal/web/handlers/settings_handlers.go
+++ b/internal/web/handlers/settings_handlers.go
@@ -74,12 +74,6 @@ func (h *Handlers) HandleCreateNotificationService(c *gin.Context) {
description := c.PostForm("description")
isEnabled := c.PostForm("is_enabled") == "on"
- // print the form data
- fmt.Println("name", name)
- fmt.Println("serviceType", serviceType)
- fmt.Println("description", description)
- fmt.Println("isEnabled", isEnabled)
-
// Validate required fields
if name == "" || serviceType == "" {
// Return to notifications page with error message
@@ -727,6 +721,8 @@ func (h *Handlers) HandleTestNotification(c *gin.Context) {
config["priority"] = c.PostForm("gotify_priority")
config["title"] = c.PostForm("gotify_title_template")
+ fmt.Println("config", config)
+
// Validate required fields
if config["url"] == "" || config["token"] == "" {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "Gotify Server URL and Application Token are required"})