feat: Enhance file metadata and notification components

- Added sorting capabilities to file metadata lists, including SortBy and SortDir fields.
- Integrated a toast notification system for user feedback on actions.
- Updated templates and JavaScript for improved user interaction and responsiveness.
- Refactored file metadata handlers to utilize new sorting features and ensure proper data flow.
- Introduced new notification dialog components for better user confirmation on actions.
This commit is contained in:
StarFleetCPTN
2025-03-28 22:22:35 -07:00
parent c52edb75df
commit 28fa65df9d
49 changed files with 6153 additions and 926 deletions
@@ -4,41 +4,40 @@ import (
"context" "context"
"fmt" "fmt"
"strconv" "strconv"
"github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/dialog" // Import dialog package
"github.com/starfleetcptn/gomft/components/layout" "github.com/starfleetcptn/gomft/components/file_metadata/utils" // Import utils package
) )
// FileMetadataDetails renders the details view for a file metadata // FileMetadataDetails renders the details view for a file metadata, matching original structure
templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDetailsData) { templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDetailsData) {
@layout.LayoutWithContext("File Details", ctx) { @components.LayoutWithContext("File Details", ctx) {
<!-- Status and Error Messages --> <!-- Status and Error Messages -->
<div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div> <div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div>
@utils.fileMetadataJS() @utils.FileMetadataJS() // Include JS for toasts, etc.
<div id="details-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900"> <div id="file-details-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900">
<div class="pb-8 w-full"> <div class="pb-8 w-full">
<div class="mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4"> <div class="mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white flex items-center"> <h1 class="text-2xl font-bold text-gray-900 dark:text-white flex items-center">
<i class="fas fa-file-alt w-6 h-6 mr-2 text-blue-500"></i> <i class="fas fa-file-alt w-6 h-6 mr-2 text-blue-500"></i>
File Details File Details: { data.File.FileName }
</h1> </h1>
<div class="flex gap-3"> <a href="/files" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<a href="/files" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"> <i class="fas fa-arrow-left mr-2"></i> Back to Files
<i class="fas fa-arrow-left mr-2"></i> Back to Files </a>
</a>
</div>
</div> </div>
<div class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full"> <div class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full">
<!-- Card header --> <!-- Card header -->
<div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700"> <div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700">
<h5 class="text-xl font-bold leading-none text-gray-900 dark:text-white"> <h5 class="text-xl font-bold leading-none text-gray-900 dark:text-white">
File Information { data.File.FileName }
</h5> </h5>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400"> <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
ID: { strconv.FormatInt(int64(data.File.ID), 10) } File ID: { strconv.FormatUint(uint64(data.File.ID), 10) }
</p> </p>
</div> </div>
@@ -47,85 +46,186 @@ templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDe
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- File Information --> <!-- File Information -->
<div> <div>
<h6 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">File Information</h6> <h6 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white flex items-center">
<dl class="grid grid-cols-1 gap-4"> <i class="fas fa-file-alt mr-2 text-gray-500 dark:text-gray-400"></i> File Information
<div> </h6>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Filename</dt> <div class="overflow-x-auto relative shadow-md sm:rounded-lg">
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ data.File.FileName }</dd> <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
</div> <tbody>
<div> <tr class="border-b dark:border-gray-700">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Size</dt> <th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ strconv.FormatInt(data.File.FileSize, 10) } bytes</dd> Filename
</div> </th>
<div> <td class="py-3 px-4 bg-white dark:bg-gray-800">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Hash</dt> { data.File.FileName }
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ data.File.FileHash }</dd> </td>
</div> </tr>
<div> <tr class="border-b dark:border-gray-700">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Status</dt> <th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
<dd class="mt-1"> Size
<span class={ fmt.Sprintf("px-2 py-1 text-xs font-medium rounded-full %s", getStatusClass(data.File.Status)) }> </th>
{ data.File.Status } <td class="py-3 px-4 bg-white dark:bg-gray-800">
</span> { utils.FormatFileSize(data.File.FileSize) }
</dd> </td>
</div> </tr>
</dl> <tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Hash
</th>
<td class="py-3 px-4 break-all bg-white dark:bg-gray-800">
if data.File.FileHash != "" {
<span class="font-mono">{ data.File.FileHash }</span>
} else {
<span class="text-gray-400 dark:text-gray-500 italic">Not available</span>
}
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Status
</th>
<td class="py-3 px-4 bg-white dark:bg-gray-800">
<span class={ "text-xs font-medium px-2.5 py-0.5 rounded", utils.GetStatusBadgeClass(data.File.Status) }>
{ data.File.Status }
</span>
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Original Path
</th>
<td class="py-3 px-4 break-all bg-white dark:bg-gray-800">
<div class="flex items-center">
<i class="fas fa-folder mr-2 text-yellow-500"></i>
<span>{ data.File.OriginalPath }</span>
</div>
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Destination Path
</th>
<td class="py-3 px-4 break-all bg-white dark:bg-gray-800">
<div class="flex items-center">
<i class="fas fa-folder-open mr-2 text-blue-500"></i>
<span>{ data.File.DestinationPath }</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
<!-- Processing Information --> <!-- Processing Information -->
<div> <div>
<h6 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Processing Information</h6> <h6 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white flex items-center">
<dl class="grid grid-cols-1 gap-4"> <i class="fas fa-cogs mr-2 text-gray-500 dark:text-gray-400"></i> Processing Information
<div> </h6>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Original Path</dt> <div class="overflow-x-auto relative shadow-md sm:rounded-lg">
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ data.File.OriginalPath }</dd> <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
</div> <tbody>
<div> <tr class="border-b dark:border-gray-700">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Destination Path</dt> <th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ data.File.DestinationPath }</dd> Job
</div> </th>
<div> <td class="py-3 px-4 bg-white dark:bg-gray-800">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Processed At</dt> <a href={ templ.SafeURL(fmt.Sprintf("/files/job/%d", data.File.JobID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
<dd class="mt-1 text-sm text-gray-900 dark:text-white">{ data.File.ProcessedTime.Format("2006-01-02 15:04:05") }</dd> { data.File.Job.Name }
</div> </a>
if data.File.Status == "error" && data.File.ErrorMessage != "" { </td>
<div> </tr>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Error</dt> <tr class="border-b dark:border-gray-700">
<dd class="mt-1 text-sm text-red-600 dark:text-red-400">{ data.File.ErrorMessage }</dd> <th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
</div> Processed Time
} </th>
</dl> <td class="py-3 px-4 bg-white dark:bg-gray-800">
<div class="flex items-center">
<i class="far fa-clock mr-2 text-gray-500"></i>
<span>{ data.File.ProcessedTime.Format("2006-01-02 15:04:05") }</span>
</div>
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Creation Time
</th>
<td class="py-3 px-4 bg-white dark:bg-gray-800">
<div class="flex items-center">
<i class="fas fa-calendar-plus mr-2 text-green-500"></i>
<span>{ data.File.CreationTime.Format("2006-01-02 15:04:05") }</span>
</div>
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Modification Time
</th>
<td class="py-3 px-4 bg-white dark:bg-gray-800">
<div class="flex items-center">
<i class="fas fa-calendar-alt mr-2 text-purple-500"></i>
<span>{ data.File.ModTime.Format("2006-01-02 15:04:05") }</span>
</div>
</td>
</tr>
if data.File.Status == "error" && data.File.ErrorMessage != "" {
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Error
</th>
<td class="py-3 px-4 break-all bg-white dark:bg-gray-800">
<div class="flex items-start">
<i class="fas fa-exclamation-triangle mt-1 mr-2 text-red-500"></i>
<span class="text-red-600 dark:text-red-400">{ data.File.ErrorMessage }</span>
</div>
</td>
</tr>
}
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Record Created
</th>
<td class="py-3 px-4 bg-white dark:bg-gray-800">
{ data.File.CreatedAt.Format("2006-01-02 15:04:05") }
</td>
</tr>
<tr class="border-b dark:border-gray-700">
<th scope="row" class="py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800">
Record Updated
</th>
<td class="py-3 px-4 bg-white dark:bg-gray-800">
{ data.File.UpdatedAt.Format("2006-01-02 15:04:05") }
</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
<!-- Actions --> <!-- Delete dialog component call -->
<div class="mt-6 flex justify-end space-x-3"> @dialog.FileMetadataDialog(
<button onclick="showModal('delete-dialog')" class="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"> fmt.Sprintf("delete-file-dialog-%d", data.File.ID),
<i class="fas fa-trash mr-2"></i> Delete File "Delete File Metadata",
</button> fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", data.File.FileName),
</div> "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", // Use correct classes
</div> "Delete",
</div> "delete",
</div> data.File.ID,
data.File.FileName,
"details", // Indicate this is from the details view
)
<!-- Delete Dialog --> <!-- Action buttons -->
<div id="delete-dialog" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"> <div class="mt-6 flex flex-wrap justify-end gap-3">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800"> <a href="/files" class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
<div class="mt-3 text-center"> <i class="fas fa-list mr-2"></i> Back to Files
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Delete File</h3> </a>
<div class="mt-2 px-7 py-3"> <button
<p class="text-sm text-gray-500 dark:text-gray-400"> type="button"
Are you sure you want to delete this file? This action cannot be undone. onclick={ templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", data.File.ID)} }
</p> class="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">
</div> <i class="fas fa-trash mr-2"></i> Delete Record
<div class="items-center px-4 py-3">
<button id="confirm-delete" data-file-id={ strconv.FormatInt(int64(data.File.ID), 10) } class="px-4 py-2 bg-red-600 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500">
Delete
</button>
</div>
<div class="items-center px-4 py-3">
<button onclick="closeModal('delete-dialog')" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500">
Cancel
</button> </button>
</div> </div>
</div> </div>
@@ -135,7 +235,7 @@ templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDe
<script> <script>
// Set dark background color if in dark mode // Set dark background color if in dark mode
if (document.documentElement.classList.contains('dark')) { if (document.documentElement.classList.contains('dark')) {
document.getElementById('details-container').style.backgroundColor = '#111827'; document.getElementById('file-details-container').style.backgroundColor = '#111827';
} }
// Add event listener for theme changes // Add event listener for theme changes
@@ -145,7 +245,7 @@ templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDe
themeToggle.addEventListener('click', function() { themeToggle.addEventListener('click', function() {
setTimeout(function() { setTimeout(function() {
const isDark = document.documentElement.classList.contains('dark'); const isDark = document.documentElement.classList.contains('dark');
document.getElementById('details-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)'; document.getElementById('file-details-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';
}, 50); }, 50);
}); });
} }
@@ -154,21 +254,3 @@ templ FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDe
</div> </div>
} }
} }
// getStatusClass returns the appropriate Tailwind CSS class for the given status
func getStatusClass(status string) string {
switch status {
case "processed":
return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300"
case "archived":
return "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"
case "deleted":
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"
case "archived_and_deleted":
return "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300"
case "error":
return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300"
default:
return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300"
}
}
@@ -11,13 +11,14 @@ import templruntime "github.com/a-h/templ/runtime"
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/dialog" // Import dialog package
"github.com/starfleetcptn/gomft/components/layout" "github.com/starfleetcptn/gomft/components/file_metadata/utils" // Import utils package
"strconv" "strconv"
) )
// FileMetadataDetails renders the details view for a file metadata // FileMetadataDetails renders the details view for a file metadata, matching original structure
func FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDetailsData) templ.Component { func FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDetailsData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@@ -55,179 +56,316 @@ func FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDet
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = utils.fileMetadataJS().Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = utils.FileMetadataJS().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"details-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-file-alt w-6 h-6 mr-2 text-blue-500\"></i> File Details</h1><div class=\"flex gap-3\"><a href=\"/files\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-arrow-left mr-2\"></i> Back to Files</a></div></div><div class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><!-- Card header --><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">File Information</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">ID: ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"file-details-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-file-alt w-6 h-6 mr-2 text-blue-500\"></i> File Details: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(data.File.ID), 10)) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileName)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 41, Col: 55} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 26, Col: 40}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p></div><!-- Card content --><div class=\"p-4 md:p-5\"><div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\"><!-- File Information --><div><h6 class=\"text-lg font-semibold text-gray-900 dark:text-white mb-4\">File Information</h6><dl class=\"grid grid-cols-1 gap-4\"><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Filename</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h1><a href=\"/files\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-arrow-left mr-2\"></i> Back to Files</a></div><div class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><!-- Card header --><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileName) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileName)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 54, Col: 85} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 37, Col: 27}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</dd></div><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Size</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">File ID: ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.File.FileSize, 10)) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatUint(uint64(data.File.ID), 10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 58, Col: 108} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 40, Col: 62}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " bytes</dd></div><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Hash</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</p></div><!-- Card content --><div class=\"p-4 md:p-5\"><div class=\"grid grid-cols-1 md:grid-cols-2 gap-6\"><!-- File Information --><div><h6 class=\"text-lg font-semibold mb-4 text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-file-alt mr-2 text-gray-500 dark:text-gray-400\"></i> File Information</h6><div class=\"overflow-x-auto relative shadow-md sm:rounded-lg\"><table class=\"w-full text-sm text-left text-gray-500 dark:text-gray-400\"><tbody><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Filename</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileHash) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileName)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 62, Col: 85} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 60, Col: 33}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</dd></div><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Status</dt><dd class=\"mt-1\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Size</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var7 = []any{fmt.Sprintf("px-2 py-1 text-xs font-medium rounded-full %s", getStatusClass(data.File.Status))} var templ_7745c5c3_Var7 string
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(utils.FormatFileSize(data.File.FileSize))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 68, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<span class=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Hash</th><td class=\"py-3 px-4 break-all bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var8 string if data.File.FileHash != "" {
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String()) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<span class=\"font-mono\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 1, Col: 0} return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.FileHash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 77, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<span class=\"text-gray-400 dark:text-gray-500 italic\">Not available</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Status</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">") var templ_7745c5c3_Var9 = []any{"text-xs font-medium px-2.5 py-0.5 rounded", utils.GetStatusBadgeClass(data.File.Status)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var9 string templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<span class=\"")
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.Status)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 68, Col: 30}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span></dd></div></dl></div><!-- Processing Information --><div><h6 class=\"text-lg font-semibold text-gray-900 dark:text-white mb-4\">Processing Information</h6><dl class=\"grid grid-cols-1 gap-4\"><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Original Path</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var10 string var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.OriginalPath) templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).String())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 81, Col: 89} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 1, Col: 0}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</dd></div><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Destination Path</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var11 string var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.DestinationPath) templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.Status)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 85, Col: 92} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 89, Col: 32}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</dd></div><div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Processed At</dt><dd class=\"mt-1 text-sm text-gray-900 dark:text-white\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</span></td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Original Path</th><td class=\"py-3 px-4 break-all bg-white dark:bg-gray-800\"><div class=\"flex items-center\"><i class=\"fas fa-folder mr-2 text-yellow-500\"></i> <span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var12 string var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.ProcessedTime.Format("2006-01-02 15:04:05")) templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.OriginalPath)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 89, Col: 120} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 100, Col: 44}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</dd></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span></div></td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Destination Path</th><td class=\"py-3 px-4 break-all bg-white dark:bg-gray-800\"><div class=\"flex items-center\"><i class=\"fas fa-folder-open mr-2 text-blue-500\"></i> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.DestinationPath)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 111, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></div></td></tr></tbody></table></div></div><!-- Processing Information --><div><h6 class=\"text-lg font-semibold mb-4 text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-cogs mr-2 text-gray-500 dark:text-gray-400\"></i> Processing Information</h6><div class=\"overflow-x-auto relative shadow-md sm:rounded-lg\"><table class=\"w-full text-sm text-left text-gray-500 dark:text-gray-400\"><tbody><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Job</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/job/%d", data.File.JobID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var14)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.Job.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 134, Col: 34}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</a></td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Processed Time</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\"><div class=\"flex items-center\"><i class=\"far fa-clock mr-2 text-gray-500\"></i> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.ProcessedTime.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 145, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span></div></td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Creation Time</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\"><div class=\"flex items-center\"><i class=\"fas fa-calendar-plus mr-2 text-green-500\"></i> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.CreationTime.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 156, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span></div></td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Modification Time</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\"><div class=\"flex items-center\"><i class=\"fas fa-calendar-alt mr-2 text-purple-500\"></i> <span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.ModTime.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 167, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span></div></td></tr>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if data.File.Status == "error" && data.File.ErrorMessage != "" { if data.File.Status == "error" && data.File.ErrorMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div><dt class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">Error</dt><dd class=\"mt-1 text-sm text-red-600 dark:text-red-400\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Error</th><td class=\"py-3 px-4 break-all bg-white dark:bg-gray-800\"><div class=\"flex items-start\"><i class=\"fas fa-exclamation-triangle mt-1 mr-2 text-red-500\"></i> <span class=\"text-red-600 dark:text-red-400\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var13 string var templ_7745c5c3_Var19 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.ErrorMessage) templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.ErrorMessage)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 94, Col: 91} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 179, Col: 84}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</dd></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</span></div></td></tr>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</dl></div></div><!-- Actions --><div class=\"mt-6 flex justify-end space-x-3\"><button onclick=\"showModal(&#39;delete-dialog&#39;)\" class=\"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\"><i class=\"fas fa-trash mr-2\"></i> Delete File</button></div></div></div></div><!-- Delete Dialog --><div id=\"delete-dialog\" class=\"hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50\"><div class=\"relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800\"><div class=\"mt-3 text-center\"><h3 class=\"text-lg leading-6 font-medium text-gray-900 dark:text-white\">Delete File</h3><div class=\"mt-2 px-7 py-3\"><p class=\"text-sm text-gray-500 dark:text-gray-400\">Are you sure you want to delete this file? This action cannot be undone.</p></div><div class=\"items-center px-4 py-3\"><button id=\"confirm-delete\" data-file-id=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Record Created</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var14 string var templ_7745c5c3_Var20 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(data.File.ID), 10)) templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.CreatedAt.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 122, Col: 92} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 189, Col: 64}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"px-4 py-2 bg-red-600 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500\">Delete</button></div><div class=\"items-center px-4 py-3\"><button onclick=\"closeModal(&#39;delete-dialog&#39;)\" class=\"px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500\">Cancel</button></div></div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('details-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('details-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</td></tr><tr class=\"border-b dark:border-gray-700\"><th scope=\"row\" class=\"py-3 px-4 font-medium text-gray-900 whitespace-nowrap dark:text-white bg-gray-50 dark:bg-gray-800\">Record Updated</th><td class=\"py-3 px-4 bg-white dark:bg-gray-800\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(data.File.UpdatedAt.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/details/file_metadata_details.templ`, Line: 197, Col: 64}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</td></tr></tbody></table></div></div></div><!-- Delete dialog component call -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dialog.FileMetadataDialog(
fmt.Sprintf("delete-file-dialog-%d", data.File.ID),
"Delete File Metadata",
fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", data.File.FileName),
"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", // Use correct classes
"Delete",
"delete",
data.File.ID,
data.File.FileName,
"details", // Indicate this is from the details view
).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<!-- Action buttons --><div class=\"mt-6 flex flex-wrap justify-end gap-3\"><a href=\"/files\" class=\"py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700\"><i class=\"fas fa-list mr-2\"></i> Back to Files</a> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", data.File.ID)})
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<button type=\"button\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", data.File.ID)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"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\"><i class=\"fas fa-trash mr-2\"></i> Delete Record</button></div></div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('file-details-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('file-details-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
return nil return nil
}) })
templ_7745c5c3_Err = layout.LayoutWithContext("File Details", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) templ_7745c5c3_Err = components.LayoutWithContext("File Details", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -235,22 +373,4 @@ func FileMetadataDetails(ctx context.Context, data file_metadata.FileMetadataDet
}) })
} }
// getStatusClass returns the appropriate Tailwind CSS class for the given status
func getStatusClass(status string) string {
switch status {
case "processed":
return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300"
case "archived":
return "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"
case "deleted":
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"
case "archived_and_deleted":
return "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300"
case "error":
return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300"
default:
return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300"
}
}
var _ = templruntime.GeneratedTemplate var _ = templruntime.GeneratedTemplate
@@ -7,7 +7,7 @@ import (
// FileMetadataDialog renders a confirmation dialog for file metadata actions // FileMetadataDialog renders a confirmation dialog for file metadata actions
templ FileMetadataDialog(id string, title string, message string, confirmClass string, confirmText string, action string, fileID uint, fileName string, section string) { templ FileMetadataDialog(id string, title string, message string, confirmClass string, confirmText string, action string, fileID uint, fileName string, section string) {
@utils.fileMetadataJS() @utils.FileMetadataJS()
<div id={ id } tabindex="-1" aria-hidden="true" class="hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"> <div id={ id } tabindex="-1" aria-hidden="true" class="hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<!-- Backdrop --> <!-- Backdrop -->
<div id={ fmt.Sprintf("%s-backdrop", id) } class="fixed inset-0 bg-gray-900/50 dark:bg-gray-900/80 backdrop-blur-sm"></div> <div id={ fmt.Sprintf("%s-backdrop", id) } class="fixed inset-0 bg-gray-900/50 dark:bg-gray-900/80 backdrop-blur-sm"></div>
@@ -31,7 +31,7 @@ templ FileMetadataDialog(id string, title string, message string, confirmClass s
data-file-name={ fileName } data-file-name={ fileName }
data-file-id={ fmt.Sprint(fileID) } data-file-id={ fmt.Sprint(fileID) }
id={ fmt.Sprintf("delete-file-btn-%d", fileID) } id={ fmt.Sprintf("delete-file-btn-%d", fileID) }
onclick={ triggerFileDelete(id, fileID, fileName) }> onclick={ templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)} }>
{ confirmText } { confirmText }
</button> </button>
} else { } else {
@@ -43,11 +43,11 @@ templ FileMetadataDialog(id string, title string, message string, confirmClass s
data-file-name={ fileName } data-file-name={ fileName }
data-file-id={ fmt.Sprint(fileID) } data-file-id={ fmt.Sprint(fileID) }
id={ fmt.Sprintf("delete-file-btn-%d", fileID) } id={ fmt.Sprintf("delete-file-btn-%d", fileID) }
onclick={ triggerFileDelete(id, fileID, fileName) }> onclick={ templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)} }>
{ confirmText } { confirmText }
</button> </button>
} }
<button type="button" onclick={ closeModal(id) } class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600"> <button type="button" onclick={ templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)} } class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Cancel Cancel
</button> </button>
</div> </div>
@@ -55,18 +55,3 @@ templ FileMetadataDialog(id string, title string, message string, confirmClass s
</div> </div>
</div> </div>
} }
script triggerFileDelete(dialogId string, fileID uint, fileName string) {
// Hide the dialog
document.getElementById(dialogId).classList.add("hidden");
document.getElementById(dialogId).classList.remove("flex");
// Store data in a way that's accessible to event handlers
window.lastDeletedFile = {
id: fileID,
name: fileName
};
// Add custom marker to track this deletion
window.currentlyDeletingFile = true;
}
@@ -35,7 +35,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = utils.fileMetadataJS().Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = utils.FileMetadataJS().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -98,7 +98,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if section == "list" { if section == "list" {
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, triggerFileDelete(id, fileID, fileName)) templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)})
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -171,7 +171,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var10 templ.ComponentScript = triggerFileDelete(id, fileID, fileName) var templ_7745c5c3_Var10 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10.Call) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10.Call)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
@@ -194,7 +194,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} else { } else {
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, triggerFileDelete(id, fileID, fileName)) templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)})
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -254,7 +254,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var16 templ.ComponentScript = triggerFileDelete(id, fileID, fileName) var templ_7745c5c3_Var16 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("triggerFileDelete('%s', %d, '%s')", id, fileID, fileName)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16.Call) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16.Call)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
@@ -277,7 +277,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, closeModal(id)) templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)})
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -285,7 +285,7 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var18 templ.ComponentScript = closeModal(id) var templ_7745c5c3_Var18 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18.Call) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18.Call)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
@@ -298,25 +298,4 @@ func FileMetadataDialog(id string, title string, message string, confirmClass st
}) })
} }
func triggerFileDelete(dialogId string, fileID uint, fileName string) templ.ComponentScript {
return templ.ComponentScript{
Name: `__templ_triggerFileDelete_bdb5`,
Function: `function __templ_triggerFileDelete_bdb5(dialogId, fileID, fileName){// Hide the dialog
document.getElementById(dialogId).classList.add("hidden");
document.getElementById(dialogId).classList.remove("flex");
// Store data in a way that's accessible to event handlers
window.lastDeletedFile = {
id: fileID,
name: fileName
};
// Add custom marker to track this deletion
window.currentlyDeletingFile = true;
}`,
Call: templ.SafeScript(`__templ_triggerFileDelete_bdb5`, dialogId, fileID, fileName),
CallInline: templ.SafeScriptInline(`__templ_triggerFileDelete_bdb5`, dialogId, fileID, fileName),
}
}
var _ = templruntime.GeneratedTemplate var _ = templruntime.GeneratedTemplate
@@ -5,16 +5,16 @@ import (
"strconv" "strconv"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/utils"
"github.com/starfleetcptn/gomft/components/layout" "github.com/starfleetcptn/gomft/components" // Import the main components package
) )
// FileMetadataList renders the list of file metadata // FileMetadataList renders the list of file metadata
templ FileMetadataList(ctx context.Context, data file_metadata.FileMetadataListData) { templ FileMetadataList(ctx context.Context, data file_metadata.FileMetadataListData) {
@layout.LayoutWithContext("Files", ctx) { @components.LayoutWithContext("Files", ctx) { // Call using components package
<!-- Status and Error Messages --> <!-- Status and Error Messages -->
<div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div> <div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div>
@utils.fileMetadataJS() @utils.FileMetadataJS() // Use capitalized function name
<div id="list-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900"> <div id="list-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900">
<div class="pb-8 w-full"> <div class="pb-8 w-full">
@@ -25,11 +25,55 @@ templ FileMetadataList(ctx context.Context, data file_metadata.FileMetadataListD
</h1> </h1>
<div class="flex gap-3"> <div class="flex gap-3">
<a href="/files/search" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"> <a href="/files/search" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<i class="fas fa-search mr-2"></i> Search Files <i class="fas fa-search mr-2"></i> Advanced Search
</a> </a>
</div> </div>
</div> </div>
<!-- Filter Form -->
<div class="p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full">
<h5 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Filter Files</h5>
<form
hx-get="/files/partial"
hx-target="#file-list-container"
hx-swap="innerHTML"
hx-indicator="#filter-loading"
hx-headers='{"X-HX-Request": "true"}'
class="grid grid-cols-1 md:grid-cols-3 gap-4">
if data.Job == nil {
<div>
<label for="job_id" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Job</label>
<input type="text" id="job_id" name="job_id" value={ data.Filter.JobID } placeholder="Job ID" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"/>
</div>
}
<div>
<label for="status" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
<select id="status" name="status" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="">All Statuses</option>
<option value="processed" selected?={ data.Filter.Status == "processed" }>Processed</option>
<option value="archived" selected?={ data.Filter.Status == "archived" }>Archived</option>
<option value="deleted" selected?={ data.Filter.Status == "deleted" }>Deleted</option>
<option value="archived_and_deleted" selected?={ data.Filter.Status == "archived_and_deleted" }>Archived & Deleted</option>
<option value="error" selected?={ data.Filter.Status == "error" }>Error</option>
</select>
</div>
<div>
<label for="filename" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Filename</label>
<input type="text" id="filename" name="filename" value={ data.Filter.FileName } placeholder="Filename or partial match"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div class="md:col-span-3 flex justify-end items-center">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<i class="fas fa-filter mr-2"></i> Apply Filters
</button>
<div id="filter-loading" class="htmx-indicator ml-2 flex items-center">
<i class="fas fa-circle-notch fa-spin text-blue-600"></i>
</div>
</div>
</form>
</div>
<div class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full"> <div class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full">
<!-- Card header --> <!-- Card header -->
<div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700"> <div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700">
@@ -42,8 +86,8 @@ templ FileMetadataList(ctx context.Context, data file_metadata.FileMetadataListD
</div> </div>
<!-- Card content --> <!-- Card content -->
<div class="p-4 md:p-5"> <div id="file-list-container" class="p-4 md:p-5">
@FileMetadataListPartial(ctx, data) @FileMetadataListPartial(ctx, data, "/files/partial", "#file-list-container")
</div> </div>
</div> </div>
</div> </div>
@@ -3,34 +3,77 @@ package list
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"strconv" "strconv"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/dialog"
"github.com/starfleetcptn/gomft/components/file_metadata/utils"
) )
// Helper function to generate sorting links
func sortLink(currentSortBy, currentSortDir, targetSortBy, basePath string, filter file_metadata.FileMetadataFilter, limit int) string {
nextSortDir := "asc"
if currentSortBy == targetSortBy && currentSortDir == "asc" {
nextSortDir = "desc"
}
q := url.Values{}
q.Set("page", "1")
q.Set("limit", strconv.Itoa(limit))
q.Set("sort_by", targetSortBy)
q.Set("sort_dir", nextSortDir)
if filter.Status != "" {
q.Set("status", filter.Status)
}
if filter.FileName != "" {
q.Set("filename", filter.FileName)
}
if filter.JobID != "" {
q.Set("job_id", filter.JobID)
}
if filter.Hash != "" {
q.Set("hash", filter.Hash)
}
if filter.StartDate != "" {
q.Set("start_date", filter.StartDate)
}
if filter.EndDate != "" {
q.Set("end_date", filter.EndDate)
}
return fmt.Sprintf("%s?%s", basePath, q.Encode())
}
// Helper function to get sort icon class
func sortIconClass(currentSortBy, currentSortDir, targetSortBy string) string {
if currentSortBy == targetSortBy {
if currentSortDir == "asc" {
return "fas fa-sort-up ml-1"
}
return "fas fa-sort-down ml-1"
}
return "fas fa-sort text-gray-400 ml-1"
}
// FileMetadataListPartial renders the list of file metadata in a table format // FileMetadataListPartial renders the list of file metadata in a table format
templ FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadataListData) { // Added basePath and targetContainerID parameters
<!-- Delete Dialog --> templ FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadataListData, basePath string, targetContainerID string) {
<div id="delete-dialog" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800"> <!-- Container for dynamically generated dialogs -->
<div class="mt-3 text-center"> <div id="dialog-container">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Delete File</h3> for _, file := range data.Files {
<div class="mt-2 px-7 py-3"> @dialog.FileMetadataDialog(
<p class="text-sm text-gray-500 dark:text-gray-400"> fmt.Sprintf("delete-file-dialog-%d", file.ID),
Are you sure you want to delete this file? This action cannot be undone. "Delete File Metadata",
</p> fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", file.FileName),
</div> "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",
<div class="items-center px-4 py-3"> "Delete",
<button id="confirm-delete" class="px-4 py-2 bg-red-600 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500"> "delete",
Delete file.ID,
</button> file.FileName,
</div> "list",
<div class="items-center px-4 py-3"> )
<button onclick="closeModal('delete-dialog')" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500"> }
Cancel
</button>
</div>
</div>
</div>
</div> </div>
<!-- File List Table --> <!-- File List Table -->
@@ -38,12 +81,62 @@ templ FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetada
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"> <table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"> <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr> <tr>
<th scope="col" class="px-6 py-3">ID</th> <th scope="col" class="px-6 py-3">
<th scope="col" class="px-6 py-3">Filename</th> <a
<th scope="col" class="px-6 py-3">Size</th> href="#"
<th scope="col" class="px-6 py-3">Processed time</th> hx-get={ sortLink(data.SortBy, data.SortDir, "id", basePath, data.Filter, data.Limit) }
<th scope="col" class="px-6 py-3">Status</th> hx-target={ targetContainerID }
if data.Job != nil { hx-swap="innerHTML"
class="flex items-center hover:text-blue-600 dark:hover:text-blue-400"
>
ID <i class={ sortIconClass(data.SortBy, data.SortDir, "id") }></i>
</a>
</th>
<th scope="col" class="px-6 py-3">
<a
href="#"
hx-get={ sortLink(data.SortBy, data.SortDir, "filename", basePath, data.Filter, data.Limit) }
hx-target={ targetContainerID }
hx-swap="innerHTML"
class="flex items-center hover:text-blue-600 dark:hover:text-blue-400"
>
Filename <i class={ sortIconClass(data.SortBy, data.SortDir, "filename") }></i>
</a>
</th>
<th scope="col" class="px-6 py-3">
<a
href="#"
hx-get={ sortLink(data.SortBy, data.SortDir, "size", basePath, data.Filter, data.Limit) }
hx-target={ targetContainerID }
hx-swap="innerHTML"
class="flex items-center hover:text-blue-600 dark:hover:text-blue-400"
>
Size <i class={ sortIconClass(data.SortBy, data.SortDir, "size") }></i>
</a>
</th>
<th scope="col" class="px-6 py-3">
<a
href="#"
hx-get={ sortLink(data.SortBy, data.SortDir, "processed_time", basePath, data.Filter, data.Limit) }
hx-target={ targetContainerID }
hx-swap="innerHTML"
class="flex items-center hover:text-blue-600 dark:hover:text-blue-400"
>
Processed time <i class={ sortIconClass(data.SortBy, data.SortDir, "processed_time") }></i>
</a>
</th>
<th scope="col" class="px-6 py-3">
<a
href="#"
hx-get={ sortLink(data.SortBy, data.SortDir, "status", basePath, data.Filter, data.Limit) }
hx-target={ targetContainerID }
hx-swap="innerHTML"
class="flex items-center hover:text-blue-600 dark:hover:text-blue-400"
>
Status <i class={ sortIconClass(data.SortBy, data.SortDir, "status") }></i>
</a>
</th>
if data.Job == nil {
<th scope="col" class="px-6 py-3">Job</th> <th scope="col" class="px-6 py-3">Job</th>
} }
<th scope="col" class="px-6 py-3">Actions</th> <th scope="col" class="px-6 py-3">Actions</th>
@@ -51,39 +144,48 @@ templ FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetada
</thead> </thead>
<tbody> <tbody>
for _, file := range data.Files { for _, file := range data.Files {
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"> <tr id={ fmt.Sprintf("file-row-%d", file.ID) } class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
<td class="px-6 py-4 font-medium text-gray-900 dark:text-white"> <td class="px-6 py-4 font-medium text-gray-900 dark:text-white">
{ strconv.FormatInt(int64(file.ID), 10) } { strconv.FormatUint(uint64(file.ID), 10) }
</td> </td>
<td class="px-6 py-4"> <td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
<a href={ templ.SafeURL(fmt.Sprintf("/files/%d", file.ID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline"> <a href={ templ.SafeURL(fmt.Sprintf("/files/%d", file.ID)) } class="hover:underline">
{ file.FileName } { file.FileName }
</a> </a>
</td> </td>
<td class="px-6 py-4"> <td class="px-6 py-4">
{ strconv.FormatInt(file.FileSize, 10) } bytes { utils.FormatFileSize(file.FileSize) }
</td> </td>
<td class="px-6 py-4"> <td class="px-6 py-4">
{ file.ProcessedTime.Format("2006-01-02 15:04:05") } { file.ProcessedTime.Format("2006-01-02 15:04:05") }
</td> </td>
<td class="px-6 py-4"> <td class="px-6 py-4">
<span class={ fmt.Sprintf("px-2 py-1 text-xs font-medium rounded-full %s", getStatusClass(file.Status)) }> <span class={ "text-xs font-medium px-2.5 py-0.5 rounded", utils.GetStatusBadgeClass(file.Status) }>
{ file.Status } { file.Status }
</span> </span>
</td> </td>
if data.Job != nil { if data.Job == nil {
<td class="px-6 py-4"> <td class="px-6 py-4">
<a href={ templ.SafeURL(fmt.Sprintf("/jobs/%d", data.Job.ID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline"> if file.Job.ID > 0 {
{ data.Job.Name } <a href={ templ.SafeURL(fmt.Sprintf("/files/job/%d", file.Job.ID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
</a> { file.Job.Name }
</a>
} else {
<span class="text-gray-400 dark:text-gray-500 italic">N/A</span>
}
</td> </td>
} }
<td class="px-6 py-4"> <td class="px-6 py-4">
<div class="flex items-center space-x-3"> <div class="flex space-x-3">
<a href={ templ.SafeURL(fmt.Sprintf("/files/%d", file.ID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline"> <a href={ templ.SafeURL(fmt.Sprintf("/files/%d", file.ID)) } class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
</a> </a>
<button onclick="showModal('delete-dialog')" data-file-id={ strconv.FormatInt(int64(file.ID), 10) } class="font-medium text-red-600 dark:text-red-500 hover:underline"> <button
type="button"
onclick={ templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", file.ID)} }
data-file-id={ strconv.FormatUint(uint64(file.ID), 10) }
data-file-name={ file.FileName }
class="font-medium text-red-600 dark:text-red-500 hover:underline">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
@@ -94,44 +196,74 @@ templ FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetada
</table> </table>
</div> </div>
<!-- Pagination --> <!-- Pagination with HTMX (Update links to include sorting and targetContainerID) -->
if data.TotalPages > 1 { if data.TotalPages > 1 {
<div class="flex items-center justify-between pt-4"> <nav class="flex items-center flex-column flex-wrap md:flex-row justify-between p-4" aria-label="Table navigation">
<div class="flex items-center"> <span class="text-sm font-normal text-gray-500 dark:text-gray-400 mb-4 md:mb-0">
<span class="text-sm text-gray-700 dark:text-gray-400"> Showing <span class="font-semibold text-gray-900 dark:text-white">{ strconv.Itoa((data.Page-1)*data.Limit+1) }-{ strconv.Itoa(func() int {
Showing { strconv.FormatInt(int64((data.Page - 1) * data.Limit + 1), 10) } to { strconv.FormatInt(int64(min(data.Page * data.Limit, int(data.TotalCount))), 10) } of { strconv.FormatInt(data.TotalCount, 10) } files end := data.Page*data.Limit
</span> if int64(end) > data.TotalCount {
</div> return int(data.TotalCount)
<div class="flex items-center space-x-2"> }
if data.Page > 1 { return end
<a href={ templ.SafeURL(fmt.Sprintf("/files?page=%d", data.Page - 1)) } class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700"> }()) }</span> of <span class="font-semibold text-gray-900 dark:text-white">{ strconv.FormatInt(data.TotalCount, 10) }</span>
Previous </span>
</a> <ul class="inline-flex -space-x-px rtl:space-x-reverse text-sm h-8">
} <li>
if data.Page < data.TotalPages { if data.Page == 1 {
<a href={ templ.SafeURL(fmt.Sprintf("/files?page=%d", data.Page + 1)) } class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700"> <span class="flex items-center justify-center px-3 h-8 ms-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-s-lg cursor-not-allowed dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400">
Next Previous
</a> </span>
} } else {
</div> <a hx-get={ fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, data.Page - 1, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir) }
</div> hx-target={ targetContainerID }
} hx-swap="innerHTML"
} class="flex items-center justify-center px-3 h-8 ms-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
Previous
</a>
}
</li>
// getStatusClass returns the appropriate Tailwind CSS class for the given status for i := 1; i <= data.TotalPages; i++ {
func getStatusClass(status string) string { if i == 1 || i == data.TotalPages || (i >= data.Page-2 && i <= data.Page+2) {
switch status { <li>
case "processed": if i == data.Page {
return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300" <span aria-current="page" class="flex items-center justify-center px-3 h-8 text-blue-600 border border-gray-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white">
case "archived": { strconv.Itoa(i) }
return "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300" </span>
case "deleted": } else {
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300" <a hx-get={ fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, i, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir) }
case "archived_and_deleted": hx-target={ targetContainerID }
return "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300" hx-swap="innerHTML"
case "error": class="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300" { strconv.Itoa(i) }
default: </a>
return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300" }
</li>
} else if (i == 2 && data.Page > 4) || (i == data.TotalPages-1 && data.Page < data.TotalPages-3) {
<li>
<span class="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400">
...
</span>
</li>
}
}
<li>
if data.Page == data.TotalPages {
<span class="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg cursor-not-allowed dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400">
Next
</span>
} else {
<a hx-get={ fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, data.Page + 1, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir) }
hx-target={ targetContainerID }
hx-swap="innerHTML"
class="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
Next
</a>
}
</li>
</ul>
</nav>
} }
} }
@@ -12,11 +12,60 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/dialog"
"github.com/starfleetcptn/gomft/components/file_metadata/utils"
"net/url"
"strconv" "strconv"
) )
// Helper function to generate sorting links
func sortLink(currentSortBy, currentSortDir, targetSortBy, basePath string, filter file_metadata.FileMetadataFilter, limit int) string {
nextSortDir := "asc"
if currentSortBy == targetSortBy && currentSortDir == "asc" {
nextSortDir = "desc"
}
q := url.Values{}
q.Set("page", "1")
q.Set("limit", strconv.Itoa(limit))
q.Set("sort_by", targetSortBy)
q.Set("sort_dir", nextSortDir)
if filter.Status != "" {
q.Set("status", filter.Status)
}
if filter.FileName != "" {
q.Set("filename", filter.FileName)
}
if filter.JobID != "" {
q.Set("job_id", filter.JobID)
}
if filter.Hash != "" {
q.Set("hash", filter.Hash)
}
if filter.StartDate != "" {
q.Set("start_date", filter.StartDate)
}
if filter.EndDate != "" {
q.Set("end_date", filter.EndDate)
}
return fmt.Sprintf("%s?%s", basePath, q.Encode())
}
// Helper function to get sort icon class
func sortIconClass(currentSortBy, currentSortDir, targetSortBy string) string {
if currentSortBy == targetSortBy {
if currentSortDir == "asc" {
return "fas fa-sort-up ml-1"
}
return "fas fa-sort-down ml-1"
}
return "fas fa-sort text-gray-400 ml-1"
}
// FileMetadataListPartial renders the list of file metadata in a table format // FileMetadataListPartial renders the list of file metadata in a table format
func FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadataListData) templ.Component { // Added basePath and targetContainerID parameters
func FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadataListData, basePath string, targetContainerID string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -37,255 +86,711 @@ func FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadat
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Delete Dialog --><div id=\"delete-dialog\" class=\"hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50\"><div class=\"relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800\"><div class=\"mt-3 text-center\"><h3 class=\"text-lg leading-6 font-medium text-gray-900 dark:text-white\">Delete File</h3><div class=\"mt-2 px-7 py-3\"><p class=\"text-sm text-gray-500 dark:text-gray-400\">Are you sure you want to delete this file? This action cannot be undone.</p></div><div class=\"items-center px-4 py-3\"><button id=\"confirm-delete\" class=\"px-4 py-2 bg-red-600 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500\">Delete</button></div><div class=\"items-center px-4 py-3\"><button onclick=\"closeModal(&#39;delete-dialog&#39;)\" class=\"px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500\">Cancel</button></div></div></div></div><!-- File List Table --><div class=\"relative overflow-x-auto shadow-md sm:rounded-lg\"><table class=\"w-full text-sm text-left text-gray-500 dark:text-gray-400\"><thead class=\"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400\"><tr><th scope=\"col\" class=\"px-6 py-3\">ID</th><th scope=\"col\" class=\"px-6 py-3\">Filename</th><th scope=\"col\" class=\"px-6 py-3\">Size</th><th scope=\"col\" class=\"px-6 py-3\">Processed time</th><th scope=\"col\" class=\"px-6 py-3\">Status</th>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Container for dynamically generated dialogs --><div id=\"dialog-container\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Job != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<th scope=\"col\" class=\"px-6 py-3\">Job</th>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<th scope=\"col\" class=\"px-6 py-3\">Actions</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
for _, file := range data.Files { for _, file := range data.Files {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<tr class=\"bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600\"><td class=\"px-6 py-4 font-medium text-gray-900 dark:text-white\">") templ_7745c5c3_Err = dialog.FileMetadataDialog(
if templ_7745c5c3_Err != nil { fmt.Sprintf("delete-file-dialog-%d", file.ID),
return templ_7745c5c3_Err "Delete File Metadata",
} fmt.Sprintf("Are you sure you want to delete the metadata for '%s'? This cannot be undone.", file.FileName),
var templ_7745c5c3_Var2 string "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",
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(file.ID), 10)) "Delete",
if templ_7745c5c3_Err != nil { "delete",
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 56, Col: 46} file.ID,
} file.FileName,
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) "list",
if templ_7745c5c3_Err != nil { ).Render(ctx, templ_7745c5c3_Buffer)
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</td><td class=\"px-6 py-4\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/%d", file.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(file.FileName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 60, Col: 23}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</a></td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(file.FileSize, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 64, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " bytes</td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(file.ProcessedTime.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 67, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 = []any{fmt.Sprintf("px-2 py-1 text-xs font-medium rounded-full %s", getStatusClass(file.Status))}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<span class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(file.Status)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 71, Col: 21}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</span></td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Job != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<td class=\"px-6 py-4\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/jobs/%d", data.Job.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var10)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(data.Job.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 77, Col: 24}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</a></td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<td class=\"px-6 py-4\"><div class=\"flex items-center space-x-3\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/%d", file.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var12)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\"><i class=\"fas fa-eye\"></i></a> <button onclick=\"showModal(&#39;delete-dialog&#39;)\" data-file-id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(file.ID), 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 86, Col: 105}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"font-medium text-red-600 dark:text-red-500 hover:underline\"><i class=\"fas fa-trash\"></i></button></div></td></tr>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</tbody></table></div><!-- Pagination -->") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div><!-- File List Table --><div class=\"relative overflow-x-auto shadow-md sm:rounded-lg\"><table class=\"w-full text-sm text-left text-gray-500 dark:text-gray-400\"><thead class=\"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400\"><tr><th scope=\"col\" class=\"px-6 py-3\"><a href=\"#\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(sortLink(data.SortBy, data.SortDir, "id", basePath, data.Filter, data.Limit))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 87, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 88, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" hx-swap=\"innerHTML\" class=\"flex items-center hover:text-blue-600 dark:hover:text-blue-400\">ID ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 = []any{sortIconClass(data.SortBy, data.SortDir, "id")}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<i class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var4).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"></i></a></th><th scope=\"col\" class=\"px-6 py-3\"><a href=\"#\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(sortLink(data.SortBy, data.SortDir, "filename", basePath, data.Filter, data.Limit))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 98, Col: 98}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 99, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" hx-swap=\"innerHTML\" class=\"flex items-center hover:text-blue-600 dark:hover:text-blue-400\">Filename ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 = []any{sortIconClass(data.SortBy, data.SortDir, "filename")}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<i class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var8).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"></i></a></th><th scope=\"col\" class=\"px-6 py-3\"><a href=\"#\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(sortLink(data.SortBy, data.SortDir, "size", basePath, data.Filter, data.Limit))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 109, Col: 94}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 110, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" hx-swap=\"innerHTML\" class=\"flex items-center hover:text-blue-600 dark:hover:text-blue-400\">Size ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 = []any{sortIconClass(data.SortBy, data.SortDir, "size")}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<i class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"></i></a></th><th scope=\"col\" class=\"px-6 py-3\"><a href=\"#\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(sortLink(data.SortBy, data.SortDir, "processed_time", basePath, data.Filter, data.Limit))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 120, Col: 104}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 121, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" hx-swap=\"innerHTML\" class=\"flex items-center hover:text-blue-600 dark:hover:text-blue-400\">Processed time ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 = []any{sortIconClass(data.SortBy, data.SortDir, "processed_time")}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<i class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var16).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"></i></a></th><th scope=\"col\" class=\"px-6 py-3\"><a href=\"#\" hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(sortLink(data.SortBy, data.SortDir, "status", basePath, data.Filter, data.Limit))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 131, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 132, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" hx-swap=\"innerHTML\" class=\"flex items-center hover:text-blue-600 dark:hover:text-blue-400\">Status ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 = []any{sortIconClass(data.SortBy, data.SortDir, "status")}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<i class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var20).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"></i></a></th>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Job == nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<th scope=\"col\" class=\"px-6 py-3\">Job</th>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<th scope=\"col\" class=\"px-6 py-3\">Actions</th></tr></thead> <tbody>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, file := range data.Files {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<tr id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("file-row-%d", file.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 147, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" class=\"bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600\"><td class=\"px-6 py-4 font-medium text-gray-900 dark:text-white\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatUint(uint64(file.ID), 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 149, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</td><td class=\"px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/%d", file.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var24)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" class=\"hover:underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(file.FileName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 153, Col: 23}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</a></td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(utils.FormatFileSize(file.FileSize))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 157, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var27 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(file.ProcessedTime.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 160, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</td><td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 = []any{"text-xs font-medium px-2.5 py-0.5 rounded", utils.GetStatusBadgeClass(file.Status)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var28...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<span class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var29 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var28).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var30 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(file.Status)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 164, Col: 21}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</span></td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Job == nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<td class=\"px-6 py-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if file.Job.ID > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/job/%d", file.Job.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var31)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(file.Job.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 171, Col: 25}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<span class=\"text-gray-400 dark:text-gray-500 italic\">N/A</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</td>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<td class=\"px-6 py-4\"><div class=\"flex space-x-3\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files/%d", file.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var33)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\" class=\"font-medium text-blue-600 dark:text-blue-500 hover:underline\"><i class=\"fas fa-eye\"></i></a> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", file.ID)})
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<button type=\"button\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-file-dialog-%d')", file.ID)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\" data-file-id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var35 string
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatUint(uint64(file.ID), 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 186, Col: 63}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\" data-file-name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var36 string
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(file.FileName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 187, Col: 39}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\" class=\"font-medium text-red-600 dark:text-red-500 hover:underline\"><i class=\"fas fa-trash\"></i></button></div></td></tr>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</tbody></table></div><!-- Pagination with HTMX (Update links to include sorting and targetContainerID) -->")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if data.TotalPages > 1 { if data.TotalPages > 1 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div class=\"flex items-center justify-between pt-4\"><div class=\"flex items-center\"><span class=\"text-sm text-gray-700 dark:text-gray-400\">Showing ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<nav class=\"flex items-center flex-column flex-wrap md:flex-row justify-between p-4\" aria-label=\"Table navigation\"><span class=\"text-sm font-normal text-gray-500 dark:text-gray-400 mb-4 md:mb-0\">Showing <span class=\"font-semibold text-gray-900 dark:text-white\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var14 string var templ_7745c5c3_Var37 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64((data.Page-1)*data.Limit+1), 10)) templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa((data.Page-1)*data.Limit + 1))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 102, Col: 77} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 203, Col: 112}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " to ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "-")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var15 string var templ_7745c5c3_Var38 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(min(data.Page*data.Limit, int(data.TotalCount))), 10)) templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(func() int {
end := data.Page * data.Limit
if int64(end) > data.TotalCount {
return int(data.TotalCount)
}
return end
}()))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 102, Col: 164} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 209, Col: 8}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, " of ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</span> of <span class=\"font-semibold text-gray-900 dark:text-white\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var16 string var templ_7745c5c3_Var39 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.TotalCount, 10)) templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.TotalCount, 10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 102, Col: 210} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 209, Col: 119}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " files</span></div><div class=\"flex items-center space-x-2\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</span></span><ul class=\"inline-flex -space-x-px rtl:space-x-reverse text-sm h-8\"><li>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if data.Page > 1 { if data.Page == 1 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<a href=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<span class=\"flex items-center justify-center px-3 h-8 ms-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-s-lg cursor-not-allowed dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400\">Previous</span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var17 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files?page=%d", data.Page-1)) } else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var17))) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<a hx-get=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" class=\"px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700\">Previous</a> ") var templ_7745c5c3_Var40 string
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, data.Page-1, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 218, Col: 232}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var41 string
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 219, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" hx-swap=\"innerHTML\" class=\"flex items-center justify-center px-3 h-8 ms-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white\">Previous</a>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
if data.Page < data.TotalPages { templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</li>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a href=\"") if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for i := 1; i <= data.TotalPages; i++ {
if i == 1 || i == data.TotalPages || (i >= data.Page-2 && i <= data.Page+2) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if i == data.Page {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<span aria-current=\"page\" class=\"flex items-center justify-center px-3 h-8 text-blue-600 border border-gray-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var42 string
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 232, Col: 26}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<a hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var43 string
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, i, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 235, Col: 222}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var44 string
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 236, Col: 40}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" hx-swap=\"innerHTML\" class=\"flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var45 string
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(i))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 239, Col: 26}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if (i == 2 && data.Page > 4) || (i == data.TotalPages-1 && data.Page < data.TotalPages-3) {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<li><span class=\"flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400\">...</span></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Page == data.TotalPages {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<span class=\"flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg cursor-not-allowed dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400\">Next</span>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var18 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/files?page=%d", data.Page+1)) } else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var18))) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<a hx-get=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700\">Next</a>") var templ_7745c5c3_Var46 string
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s?page=%d&limit=%d&status=%s&filename=%s&job_id=%s&sort_by=%s&sort_dir=%s", basePath, data.Page+1, data.Limit, data.Filter.Status, data.Filter.FileName, data.Filter.JobID, data.SortBy, data.SortDir))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 258, Col: 232}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var47 string
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(targetContainerID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list_partial.templ`, Line: 259, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\" hx-swap=\"innerHTML\" class=\"flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white\">Next</a>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</li></ul></nav>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -294,22 +799,4 @@ func FileMetadataListPartial(ctx context.Context, data file_metadata.FileMetadat
}) })
} }
// getStatusClass returns the appropriate Tailwind CSS class for the given status
func getStatusClass(status string) string {
switch status {
case "processed":
return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300"
case "archived":
return "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"
case "deleted":
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"
case "archived_and_deleted":
return "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300"
case "error":
return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300"
default:
return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300"
}
}
var _ = templruntime.GeneratedTemplate var _ = templruntime.GeneratedTemplate
@@ -10,9 +10,9 @@ import templruntime "github.com/a-h/templ/runtime"
import ( import (
"context" "context"
"github.com/starfleetcptn/gomft/components" // Import the main components package
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/utils"
"github.com/starfleetcptn/gomft/components/layout"
"strconv" "strconv"
) )
@@ -50,68 +50,154 @@ func FileMetadataList(ctx context.Context, data file_metadata.FileMetadataListDa
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Status and Error Messages --> <div id=\"toast-container\" class=\"fixed top-5 right-5 z-50 flex flex-col gap-2\"></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <!-- Status and Error Messages --> <div id=\"toast-container\" class=\"fixed top-5 right-5 z-50 flex flex-col gap-2\"></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = utils.fileMetadataJS().Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = utils.FileMetadataJS().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"list-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-file-alt w-6 h-6 mr-2 text-blue-500\"></i> Files</h1><div class=\"flex gap-3\"><a href=\"/files/search\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-search mr-2\"></i> Search Files</a></div></div><div class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><!-- Card header --><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">File List</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Showing ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"list-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-file-alt w-6 h-6 mr-2 text-blue-500\"></i> Files</h1><div class=\"flex gap-3\"><a href=\"/files/search\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-search mr-2\"></i> Advanced Search</a></div></div><!-- Filter Form --><div class=\"p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><h5 class=\"mb-4 text-lg font-semibold text-gray-900 dark:text-white\">Filter Files</h5><form hx-get=\"/files/partial\" hx-target=\"#file-list-container\" hx-swap=\"innerHTML\" hx-indicator=\"#filter-loading\" hx-headers=\"{&#34;X-HX-Request&#34;: &#34;true&#34;}\" class=\"grid grid-cols-1 md:grid-cols-3 gap-4\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var3 string if data.Job == nil {
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64((data.Page-1)*data.Limit+1), 10)) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div><label for=\"job_id\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Job</label> <input type=\"text\" id=\"job_id\" name=\"job_id\" value=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 40, Col: 79} return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.JobID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 46, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" placeholder=\"Job ID\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div><label for=\"status\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Status</label> <select id=\"status\" name=\"status\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"\">All Statuses</option> <option value=\"processed\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " to ") if data.Filter.Status == "processed" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">Processed</option> <option value=\"archived\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "archived" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, ">Archived</option> <option value=\"deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">Deleted</option> <option value=\"archived_and_deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "archived_and_deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ">Archived & Deleted</option> <option value=\"error\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "error" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, ">Error</option></select></div><div><label for=\"filename\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Filename</label> <input type=\"text\" id=\"filename\" name=\"filename\" value=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(min(data.Page*data.Limit, int(data.TotalCount))), 10)) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.FileName)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 40, Col: 166} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 62, Col: 84}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " of ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" placeholder=\"Filename or partial match\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div class=\"md:col-span-3 flex justify-end items-center\"><button type=\"submit\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-filter mr-2\"></i> Apply Filters</button><div id=\"filter-loading\" class=\"htmx-indicator ml-2 flex items-center\"><i class=\"fas fa-circle-notch fa-spin text-blue-600\"></i></div></div></form></div><div class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><!-- Card header --><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">File List</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Showing ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.TotalCount, 10)) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64((data.Page-1)*data.Limit+1), 10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 40, Col: 212} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 84, Col: 79}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " files</p></div><!-- Card content --><div class=\"p-4 md:p-5\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " to ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = FileMetadataListPartial(ctx, data).Render(ctx, templ_7745c5c3_Buffer) var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(int64(min(data.Page*data.Limit, int(data.TotalCount))), 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 84, Col: 166}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('list-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('list-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " of ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.TotalCount, 10))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/list/file_metadata_list.templ`, Line: 84, Col: 212}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " files</p></div><!-- Card content --><div id=\"file-list-container\" class=\"p-4 md:p-5\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = FileMetadataListPartial(ctx, data, "/files/partial", "#file-list-container").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('list-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('list-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
return nil return nil
}) })
templ_7745c5c3_Err = layout.LayoutWithContext("Files", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) templ_7745c5c3_Err = components.LayoutWithContext("Files", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -2,20 +2,18 @@ package search
import ( import (
"context" "context"
"strconv"
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/list"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/utils"
"github.com/starfleetcptn/gomft/components/layout" "github.com/starfleetcptn/gomft/components" // Import the main components package
) )
// FileMetadataSearch renders the search interface for file metadata // FileMetadataSearch renders the search interface for file metadata
templ FileMetadataSearch(ctx context.Context, data file_metadata.FileMetadataSearchData) { templ FileMetadataSearch(ctx context.Context, data file_metadata.FileMetadataSearchData) {
@layout.LayoutWithContext("Search Files", ctx) { @components.LayoutWithContext("Search Files", ctx) {
<!-- Status and Error Messages --> <!-- Status and Error Messages -->
<div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div> <div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div>
@utils.fileMetadataJS() @utils.FileMetadataJS() // Include JS for toasts, etc.
<div id="search-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900"> <div id="search-container" style="min-height: 100vh;" class="bg-gray-50 dark:bg-gray-900">
<div class="pb-8 w-full"> <div class="pb-8 w-full">
@@ -31,28 +29,82 @@ templ FileMetadataSearch(ctx context.Context, data file_metadata.FileMetadataSea
</div> </div>
</div> </div>
<div class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full"> <!-- Advanced Search Form -->
<!-- Card header --> <div class="p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full">
<h5 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Advanced File Search</h5>
<form
hx-get="/files/search/partial"
hx-target="#search-results-container"
hx-swap="innerHTML"
hx-indicator="#search-form-loading"
hx-headers='{"X-HX-Request": "true"}'
hx-boost="false"
class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="job_id" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Job ID</label>
<input type="text" id="job_id" name="job_id" value={ data.Filter.JobID } placeholder="Job ID"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="status" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
<select id="status" name="status" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="">All Statuses</option>
<option value="processed" selected?={ data.Filter.Status == "processed" }>Processed</option>
<option value="archived" selected?={ data.Filter.Status == "archived" }>Archived</option>
<option value="deleted" selected?={ data.Filter.Status == "deleted" }>Deleted</option>
<option value="archived_and_deleted" selected?={ data.Filter.Status == "archived_and_deleted" }>Archived & Deleted</option>
<option value="error" selected?={ data.Filter.Status == "error" }>Error</option>
</select>
</div>
<div>
<label for="filename" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Filename</label>
<input type="text" id="filename" name="filename" value={ data.Filter.FileName } placeholder="Filename or partial match"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="hash" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">File Hash</label>
<input type="text" id="hash" name="hash" value={ data.Filter.Hash } placeholder="MD5 hash"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="start_date" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Processed After</label>
<input type="date" id="start_date" name="start_date" value={ data.Filter.StartDate }
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="end_date" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Processed Before</label>
<input type="date" id="end_date" name="end_date" value={ data.Filter.EndDate }
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div class="md:col-span-2 flex justify-end">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<i class="fas fa-search mr-2"></i> Search Files
</button>
<div id="search-form-loading" class="htmx-indicator ml-2 flex items-center">
<i class="fas fa-circle-notch fa-spin text-blue-600"></i>
</div>
</div>
</form>
</div>
<!-- Results Container (Initially empty, populated by HTMX) -->
<div id="search-results-container" class="bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full mt-6">
<div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700"> <div class="p-4 md:p-5 border-b border-gray-200 dark:border-gray-700">
<h5 class="text-xl font-bold leading-none text-gray-900 dark:text-white"> <h5 class="text-xl font-bold leading-none text-gray-900 dark:text-white">
Search Results Search Results
</h5> </h5>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400"> <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
Found { strconv.FormatInt(data.TotalCount, 10) } files matching your criteria Enter search criteria above and click "Search Files".
</p> </p>
</div> </div>
<!-- Card content -->
<div class="p-4 md:p-5"> <div class="p-4 md:p-5">
@list.FileMetadataListPartial(ctx, file_metadata.FileMetadataListData{ <!-- Content will be loaded here by HTMX -->
Files: data.Files, <div class="text-center text-gray-500 dark:text-gray-400 py-8">
Page: data.Page, No results yet.
Limit: data.Limit, </div>
TotalCount: data.TotalCount,
TotalPages: data.TotalPages,
})
</div> </div>
</div> </div>
</div> </div>
<script> <script>
@@ -6,75 +6,21 @@ import (
"github.com/starfleetcptn/gomft/components/file_metadata/list" "github.com/starfleetcptn/gomft/components/file_metadata/list"
) )
// FileMetadataSearchContent renders the search form and results // FileMetadataSearchContent renders only the search results table and pagination
templ FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetadataSearchData) { templ FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetadataSearchData) {
<!-- Search Form -->
<div class="p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full">
<h5 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Search Files</h5>
<form
hx-get="/files/search/partial"
hx-target="#search-results"
hx-swap="innerHTML"
hx-indicator="#search-loading"
hx-headers='{"X-HX-Request": "true"}'
class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="job_id" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Job</label>
<input type="text" id="job_id" name="job_id" value={ data.Filter.JobID } placeholder="Job ID"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="status" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
<select id="status" name="status" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="">All Statuses</option>
<option value="processed" selected?={ data.Filter.Status == "processed" }>Processed</option>
<option value="archived" selected?={ data.Filter.Status == "archived" }>Archived</option>
<option value="deleted" selected?={ data.Filter.Status == "deleted" }>Deleted</option>
<option value="archived_and_deleted" selected?={ data.Filter.Status == "archived_and_deleted" }>Archived & Deleted</option>
<option value="error" selected?={ data.Filter.Status == "error" }>Error</option>
</select>
</div>
<div>
<label for="filename" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Filename</label>
<input type="text" id="filename" name="filename" value={ data.Filter.FileName } placeholder="Filename or partial match"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="hash" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Hash</label>
<input type="text" id="hash" name="hash" value={ data.Filter.Hash } placeholder="File hash"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="start_date" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Start Date</label>
<input type="datetime-local" id="start_date" name="start_date" value={ data.Filter.StartDate }
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div>
<label for="end_date" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">End Date</label>
<input type="datetime-local" id="end_date" name="end_date" value={ data.Filter.EndDate }
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
<div class="md:col-span-3 flex justify-end items-center">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<i class="fas fa-search mr-2"></i> Search
</button>
<div id="search-loading" class="htmx-indicator ml-2 flex items-center">
<i class="fas fa-circle-notch fa-spin text-blue-600"></i>
</div>
</div>
</form>
</div>
<!-- Search Results --> <!-- Search Results -->
<div id="search-results"> <div id="search-results">
if len(data.Files) > 0 { if len(data.Files) > 0 {
@list.FileMetadataListPartial(ctx, file_metadata.FileMetadataListData{ @list.FileMetadataListPartial(ctx, file_metadata.FileMetadataListData{
Files: data.Files, Files: data.Files,
Page: data.Page, Page: data.Page,
Limit: data.Limit, Limit: data.Limit,
TotalCount: data.TotalCount, TotalCount: data.TotalCount,
TotalPages: data.TotalPages, TotalPages: data.TotalPages,
}) Filter: data.Filter, // Pass filter data for pagination links
SortBy: data.SortBy,
SortDir: data.SortDir,
}, "/files/search/partial", "#search-results-container") // Pass correct base path and target ID
} else { } else {
<div class="p-6 text-center text-gray-500 dark:text-gray-400"> <div class="p-6 text-center text-gray-500 dark:text-gray-400">
<svg class="mx-auto mb-4 w-12 h-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <svg class="mx-auto mb-4 w-12 h-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
@@ -14,7 +14,7 @@ import (
"github.com/starfleetcptn/gomft/components/file_metadata/list" "github.com/starfleetcptn/gomft/components/file_metadata/list"
) )
// FileMetadataSearchContent renders the search form and results // FileMetadataSearchContent renders only the search results table and pagination
func FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetadataSearchData) templ.Component { func FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetadataSearchData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@@ -36,122 +36,7 @@ func FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetad
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Search Form --><div class=\"p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><h5 class=\"mb-4 text-lg font-semibold text-gray-900 dark:text-white\">Search Files</h5><form hx-get=\"/files/search/partial\" hx-target=\"#search-results\" hx-swap=\"innerHTML\" hx-indicator=\"#search-loading\" hx-headers=\"{&#34;X-HX-Request&#34;: &#34;true&#34;}\" class=\"grid grid-cols-1 md:grid-cols-3 gap-4\"><div><label for=\"job_id\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Job</label> <input type=\"text\" id=\"job_id\" name=\"job_id\" value=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Search Results --><div id=\"search-results\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.JobID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search_content.templ`, Line: 23, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" placeholder=\"Job ID\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"status\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Status</label> <select id=\"status\" name=\"status\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"\">All Statuses</option> <option value=\"processed\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "processed" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, ">Processed</option> <option value=\"archived\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "archived" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, ">Archived</option> <option value=\"deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, ">Deleted</option> <option value=\"archived_and_deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "archived_and_deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, ">Archived & Deleted</option> <option value=\"error\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "error" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, ">Error</option></select></div><div><label for=\"filename\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Filename</label> <input type=\"text\" id=\"filename\" name=\"filename\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.FileName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search_content.templ`, Line: 39, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" placeholder=\"Filename or partial match\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"hash\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Hash</label> <input type=\"text\" id=\"hash\" name=\"hash\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.Hash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search_content.templ`, Line: 44, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" placeholder=\"File hash\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"start_date\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Start Date</label> <input type=\"datetime-local\" id=\"start_date\" name=\"start_date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.StartDate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search_content.templ`, Line: 49, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"end_date\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">End Date</label> <input type=\"datetime-local\" id=\"end_date\" name=\"end_date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.EndDate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search_content.templ`, Line: 54, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div class=\"md:col-span-3 flex justify-end items-center\"><button type=\"submit\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-search mr-2\"></i> Search</button><div id=\"search-loading\" class=\"htmx-indicator ml-2 flex items-center\"><i class=\"fas fa-circle-notch fa-spin text-blue-600\"></i></div></div></form></div><!-- Search Results --><div id=\"search-results\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -162,17 +47,24 @@ func FileMetadataSearchContent(ctx context.Context, data file_metadata.FileMetad
Limit: data.Limit, Limit: data.Limit,
TotalCount: data.TotalCount, TotalCount: data.TotalCount,
TotalPages: data.TotalPages, TotalPages: data.TotalPages,
}).Render(ctx, templ_7745c5c3_Buffer) Filter: data.Filter, // Pass filter data for pagination links
SortBy: data.SortBy,
SortDir: data.SortDir,
}, "/files/search/partial", "#search-results-container").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} else { } else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"p-6 text-center text-gray-500 dark:text-gray-400\"><svg class=\"mx-auto mb-4 w-12 h-12 text-gray-400\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\"></path></svg><p>No files found matching your search criteria.</p></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"p-6 text-center text-gray-500 dark:text-gray-400\"><svg class=\"mx-auto mb-4 w-12 h-12 text-gray-400\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\"></path></svg><p>No files found matching your search criteria.</p></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -10,11 +10,9 @@ import templruntime "github.com/a-h/templ/runtime"
import ( import (
"context" "context"
"github.com/starfleetcptn/gomft/components" // Import the main components package
"github.com/starfleetcptn/gomft/components/file_metadata" "github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/list"
"github.com/starfleetcptn/gomft/components/file_metadata/utils" "github.com/starfleetcptn/gomft/components/file_metadata/utils"
"github.com/starfleetcptn/gomft/components/layout"
"strconv"
) )
// FileMetadataSearch renders the search interface for file metadata // FileMetadataSearch renders the search interface for file metadata
@@ -55,44 +53,132 @@ func FileMetadataSearch(ctx context.Context, data file_metadata.FileMetadataSear
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = utils.fileMetadataJS().Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = utils.FileMetadataJS().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"search-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-search w-6 h-6 mr-2 text-blue-500\"></i> Search Files</h1><div class=\"flex gap-3\"><a href=\"/files\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-arrow-left mr-2\"></i> Back to Files</a></div></div><div class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><!-- Card header --><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">Search Results</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Found ") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " <div id=\"search-container\" style=\"min-height: 100vh;\" class=\"bg-gray-50 dark:bg-gray-900\"><div class=\"pb-8 w-full\"><div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-search w-6 h-6 mr-2 text-blue-500\"></i> Search Files</h1><div class=\"flex gap-3\"><a href=\"/files\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-arrow-left mr-2\"></i> Back to Files</a></div></div><!-- Advanced Search Form --><div class=\"p-4 mb-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full\"><h5 class=\"mb-4 text-lg font-semibold text-gray-900 dark:text-white\">Advanced File Search</h5><form hx-get=\"/files/search/partial\" hx-target=\"#search-results-container\" hx-swap=\"innerHTML\" hx-indicator=\"#search-form-loading\" hx-headers=\"{&#34;X-HX-Request&#34;: &#34;true&#34;}\" hx-boost=\"false\" class=\"grid grid-cols-1 md:grid-cols-2 gap-4\"><div><label for=\"job_id\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Job ID</label> <input type=\"text\" id=\"job_id\" name=\"job_id\" value=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.FormatInt(data.TotalCount, 10)) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.JobID)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 41, Col: 53} return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 45, Col: 77}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, " files matching your criteria</p></div><!-- Card content --><div class=\"p-4 md:p-5\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" placeholder=\"Job ID\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"status\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Status</label> <select id=\"status\" name=\"status\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"\">All Statuses</option> <option value=\"processed\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = list.FileMetadataListPartial(ctx, file_metadata.FileMetadataListData{ if data.Filter.Status == "processed" {
Files: data.Files, templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " selected")
Page: data.Page, if templ_7745c5c3_Err != nil {
Limit: data.Limit, return templ_7745c5c3_Err
TotalCount: data.TotalCount, }
TotalPages: data.TotalPages, }
}).Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, ">Processed</option> <option value=\"archived\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('search-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('search-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>") if data.Filter.Status == "archived" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ">Archived</option> <option value=\"deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, ">Deleted</option> <option value=\"archived_and_deleted\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "archived_and_deleted" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">Archived & Deleted</option> <option value=\"error\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Filter.Status == "error" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ">Error</option></select></div><div><label for=\"filename\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Filename</label> <input type=\"text\" id=\"filename\" name=\"filename\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.FileName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 61, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" placeholder=\"Filename or partial match\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"hash\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">File Hash</label> <input type=\"text\" id=\"hash\" name=\"hash\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.Hash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 66, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" placeholder=\"MD5 hash\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"start_date\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Processed After</label> <input type=\"date\" id=\"start_date\" name=\"start_date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.StartDate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 71, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div><label for=\"end_date\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Processed Before</label> <input type=\"date\" id=\"end_date\" name=\"end_date\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.Filter.EndDate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/file_metadata/search/file_metadata_search.templ`, Line: 76, Col: 83}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div class=\"md:col-span-2 flex justify-end\"><button type=\"submit\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-search mr-2\"></i> Search Files</button><div id=\"search-form-loading\" class=\"htmx-indicator ml-2 flex items-center\"><i class=\"fas fa-circle-notch fa-spin text-blue-600\"></i></div></div></form></div><!-- Results Container (Initially empty, populated by HTMX) --><div id=\"search-results-container\" class=\"bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700 w-full mt-6\"><div class=\"p-4 md:p-5 border-b border-gray-200 dark:border-gray-700\"><h5 class=\"text-xl font-bold leading-none text-gray-900 dark:text-white\">Search Results</h5><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Enter search criteria above and click \"Search Files\".</p></div><div class=\"p-4 md:p-5\"><!-- Content will be loaded here by HTMX --><div class=\"text-center text-gray-500 dark:text-gray-400 py-8\">No results yet.</div></div></div></div><script>\n\t\t\t\t// Set dark background color if in dark mode\n\t\t\t\tif (document.documentElement.classList.contains('dark')) {\n\t\t\t\t\tdocument.getElementById('search-container').style.backgroundColor = '#111827';\n\t\t\t\t}\n\n\t\t\t\t// Add event listener for theme changes\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconst themeToggle = document.getElementById('theme-toggle');\n\t\t\t\t\tif (themeToggle) {\n\t\t\t\t\t\tthemeToggle.addEventListener('click', function() {\n\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\t\t\t\t\tdocument.getElementById('search-container').style.backgroundColor = isDark ? '#111827' : 'rgb(249, 250, 251)';\n\t\t\t\t\t\t\t}, 50);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
return nil return nil
}) })
templ_7745c5c3_Err = layout.LayoutWithContext("Search Files", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) templ_7745c5c3_Err = components.LayoutWithContext("Search Files", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
+4
View File
@@ -21,6 +21,8 @@ type FileMetadataListData struct {
TotalPages int TotalPages int
Job *db.Job // Optional: if viewing files for a specific job Job *db.Job // Optional: if viewing files for a specific job
Filter FileMetadataFilter Filter FileMetadataFilter
SortBy string // Added for sorting
SortDir string // Added for sorting ("asc" or "desc")
} }
// FileMetadataDetailsData contains data for the file metadata details template // FileMetadataDetailsData contains data for the file metadata details template
@@ -36,4 +38,6 @@ type FileMetadataSearchData struct {
Limit int Limit int
TotalPages int TotalPages int
Filter FileMetadataFilter Filter FileMetadataFilter
SortBy string // Added for sorting
SortDir string // Added for sorting ("asc" or "desc")
} }
@@ -1,91 +1,114 @@
package utils package utils
// fileMetadataJS provides common JavaScript functions for file metadata components // FileMetadataJS provides common JavaScript functions for file metadata components,
templ fileMetadataJS() { // including HTMX event listeners for delete toasts.
templ FileMetadataJS() {
<script> <script>
// Show a modal by ID // Function to create and show a toast (Defined locally for guaranteed availability)
function showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('hidden');
modal.classList.add('flex');
}
}
// Close a modal by ID
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
}
// Show a toast notification
function showToast(message, type = 'info') { function showToast(message, type = 'info') {
const container = document.getElementById('toast-container'); const toastContainer = document.getElementById('toast-container');
if (!container) return; if (!toastContainer) {
console.error("Toast container not found!"); // Keep this error log
return;
}
// Create toast element
const toast = document.createElement('div'); const toast = document.createElement('div');
toast.className = `p-4 rounded-lg shadow-lg ${ toast.id = 'toast-' + type + '-' + Date.now();
type === 'success' ? 'bg-green-500' : // Use classes similar to the original file_metadata.templ for consistency
type === 'error' ? 'bg-red-500' : toast.className = 'flex items-center w-full max-w-xs p-4 mb-4 rounded-lg shadow text-gray-500 bg-white dark:text-gray-400 dark:bg-gray-800 transform translate-y-16 opacity-0 transition-all duration-300 ease-out';
type === 'warning' ? 'bg-yellow-500' : toast.role = 'alert';
'bg-blue-500'
} text-white`;
toast.textContent = message;
container.appendChild(toast); // Set toast content based on type
let iconClass;
if (type === 'success') {
iconClass = 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200';
} else if (type === 'error') {
iconClass = 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200';
} else { // Default to info
iconClass = 'text-blue-500 bg-blue-100 dark:bg-blue-800 dark:text-blue-200';
}
// Remove the toast after 5 seconds // Set inner HTML with appropriate icon and message
toast.innerHTML = `
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 rounded-lg ${iconClass}">
${type === 'success'
? '<i class="fas fa-check"></i>'
: type === 'error'
? '<i class="fas fa-exclamation-circle"></i>'
: '<i class="fas fa-info-circle"></i>'}
</div>
<div class="ml-3 text-sm font-normal">${message}</div>
<button type="button" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" data-dismiss-target="#${toast.id}" aria-label="Close">
<span class="sr-only">Close</span>
<i class="fas fa-times"></i>
</button>
`;
// Add toast to container
toastContainer.appendChild(toast);
// Trigger animation after a small delay
setTimeout(() => { setTimeout(() => {
toast.remove(); toast.classList.remove('translate-y-16', 'opacity-0');
toast.classList.add('translate-y-0', 'opacity-100');
}, 10);
// Add event listener to close button
const closeButton = toast.querySelector('button[data-dismiss-target]');
closeButton.addEventListener('click', function() {
toast.classList.add('opacity-0', 'translate-y-4');
setTimeout(() => { toast.remove(); }, 300);
});
// Auto-remove toast after 5 seconds
setTimeout(() => {
toast.classList.add('opacity-0', 'translate-y-4');
setTimeout(() => { toast.remove(); }, 300);
}, 5000); }, 5000);
} }
// Close modal when clicking outside // --- HTMX Event Listener for Delete Toasts ---
document.addEventListener('click', function(event) {
if (event.target.classList.contains('fixed')) {
event.target.classList.add('hidden');
event.target.classList.remove('flex');
}
});
// Handle delete confirmation // Ensure listener is attached only once using a flag
document.addEventListener('click', function(event) { if (!window._gomft_fileMetadataListenerAttached) {
if (event.target.id === 'confirm-delete') { document.body.addEventListener('htmx:afterRequest', function(event) {
const fileId = event.target.dataset.fileId; const triggerElement = event.detail.elt;
if (!fileId) return;
fetch(`/files/${fileId}`, { // Check if the element that triggered this request was the file delete button from the dialog
method: 'DELETE', if (triggerElement && triggerElement.id && triggerElement.id.startsWith('delete-file-btn-')) {
headers: { // Get the path directly from the element's hx-delete attribute
'Content-Type': 'application/json', const path = triggerElement.getAttribute('hx-delete');
}, // Ensure requestConfig exists before accessing verb (robustness)
}) const method = event.detail.requestConfig ? event.detail.requestConfig.verb : null;
.then(response => {
if (response.ok) { // Check if the method was delete and the path from the attribute matches the expected pattern
showToast('File deleted successfully', 'success'); if (method === 'delete' && path && path.match(/^\/files\/\d+$/)) {
// Reload the page after a short delay const fileName = triggerElement.getAttribute('data-file-name') || "Unknown"; // Get filename from the button
setTimeout(() => {
window.location.reload(); // Call the locally defined showToast function
}, 1000); if (event.detail.successful) {
} else { showToast(`File "${fileName}" metadata deleted successfully`, 'success');
throw new Error('Failed to delete file'); } else {
let errorMsg = `Failed to delete file "${fileName}" metadata`;
if (event.detail.xhr && event.detail.xhr.responseText) {
try {
const responseJson = JSON.parse(event.detail.xhr.responseText);
errorMsg = responseJson.error ? `Error: ${responseJson.error}` : `Error: ${event.detail.xhr.responseText}`;
} catch (e) {
errorMsg = `Error: ${event.detail.xhr.responseText}`;
}
}
showToast(errorMsg, 'error');
}
} }
}) }
.catch(error => { });
showToast(error.message, 'error');
}) // Set the flag to true after attaching the listener
.finally(() => { window._gomft_fileMetadataListenerAttached = true;
closeModal('delete-dialog'); console.log("[FileMetadataJS] HTMX afterRequest listener attached."); // Log attachment once
}); }
}
});
// Handle HTMX request errors
document.body.addEventListener('htmx:responseError', function(evt) {
showToast('An error occurred while processing your request', 'error');
});
</script> </script>
} }
File diff suppressed because one or more lines are too long
+5
View File
@@ -6,6 +6,7 @@ import (
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"time" "time"
"github.com/starfleetcptn/gomft/components/shared/toast"
) )
// AppVersion will be set at build time using ldflags // AppVersion will be set at build time using ldflags
@@ -444,6 +445,8 @@ templ LayoutWithContext(title string, ctx context.Context) {
</div> </div>
</div> </div>
</header> </header>
<!-- Toast Container -->
@toast.Container()
<!-- Page Content --> <!-- Page Content -->
<main class="flex-1 bg-gray-50 dark:bg-gray-900"> <main class="flex-1 bg-gray-50 dark:bg-gray-900">
<div class="py-6 bg-gray-50 dark:bg-gray-900"> <div class="py-6 bg-gray-50 dark:bg-gray-900">
@@ -498,6 +501,8 @@ templ LayoutWithContext(title string, ctx context.Context) {
<!-- Scripts --> <!-- Scripts -->
<!-- Alpine.js and dependencies --> <!-- Alpine.js and dependencies -->
<script defer src="/static/dist/vendor.js"></script> <script defer src="/static/dist/vendor.js"></script>
<!-- Shared Scripts -->
@toast.ShowToastJS()
<!-- Application scripts --> <!-- Application scripts -->
<script defer src="/static/dist/app.js"></script> <script defer src="/static/dist/app.js"></script>
<script defer src="/static/dist/init.js"></script> <script defer src="/static/dist/init.js"></script>
@@ -0,0 +1,43 @@
package dialog
import (
"fmt"
// "strconv" // No longer needed here
)
// NotificationDialog 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) {
<div id={ id } tabindex="-1" aria-hidden="true" class="hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<!-- Backdrop -->
<div id={ fmt.Sprintf("%s-backdrop", id) } class="fixed inset-0 bg-gray-900/50 dark:bg-gray-900/80 backdrop-blur-sm"></div>
<!-- Modal content -->
<div class="relative p-4 w-full max-w-md max-h-full mx-auto">
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<div class="p-6 text-center">
if action == "delete" {
<i class="fas fa-trash-alt text-red-400 text-3xl mb-4"></i>
} else {
<i class="fas fa-exclamation-triangle text-yellow-400 text-3xl mb-4"></i>
}
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">{ message }</h3>
<button
type="button"
class={ confirmClass }
hx-delete={ fmt.Sprintf("/admin/settings/notifications/%d", serviceID) }
hx-target="body"
data-service-name={ serviceName }
data-service-id={ fmt.Sprint(serviceID) }
id={ fmt.Sprintf("delete-btn-%d", serviceID) }
onclick={ templ.ComponentScript{Call: fmt.Sprintf("triggerServiceDelete('%s', %d, '%s')", id, serviceID, serviceName)} }>
{ confirmText }
</button>
<button type="button" onclick={ templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)} } class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">
Cancel
</button>
</div>
</div>
</div>
</div>
}
// Scripts (triggerServiceDelete, closeModal, showModal) are now expected to be defined globally or in the calling template (e.g., list.templ).
@@ -0,0 +1,20 @@
package dialog
// DialogScripts provides the JavaScript function specific to the notification delete confirmation dialog.
templ DialogScripts() {
<script type="text/javascript">
// Called when the delete confirmation button is clicked.
// Primarily closes the modal; the actual delete is handled by hx-delete.
function triggerServiceDelete(dialogId, serviceId, serviceName) {
console.log(`Confirmed delete for service: ${serviceName} (ID: ${serviceId}). Closing modal: ${dialogId}`);
// Call the global closeModal function defined elsewhere (e.g., app.js)
if (typeof closeModal === 'function') {
closeModal(dialogId);
} else {
console.error('Global closeModal function not found.');
}
// Optional: Show a "Deleting..." toast here if desired.
// The hx-delete attribute on the button will trigger the actual backend request.
}
</script>
}
@@ -0,0 +1,41 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package dialog
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
// DialogScripts provides the JavaScript function specific to the notification delete confirmation dialog.
func DialogScripts() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script type=\"text/javascript\">\n\t\t// Called when the delete confirmation button is clicked.\n\t\t// Primarily closes the modal; the actual delete is handled by hx-delete.\n\t\tfunction triggerServiceDelete(dialogId, serviceId, serviceName) {\n\t\t\tconsole.log(`Confirmed delete for service: ${serviceName} (ID: ${serviceId}). Closing modal: ${dialogId}`);\n\t\t\t// Call the global closeModal function defined elsewhere (e.g., app.js)\n\t\t\tif (typeof closeModal === 'function') {\n\t\t\t\tcloseModal(dialogId);\n\t\t\t} else {\n\t\t\t\tconsole.error('Global closeModal function not found.');\n\t\t\t}\n\t\t\t// Optional: Show a \"Deleting...\" toast here if desired.\n\t\t\t// The hx-delete attribute on the button will trigger the actual backend request.\n\t\t}\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,218 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package dialog
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"fmt"
// "strconv" // No longer needed here
)
// NotificationDialog component for confirmation dialogs using Flowbite modal
func NotificationDialog(id string, title string, message string, confirmClass string, confirmText string, action string, serviceID uint, serviceName string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(id)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 10, Col: 13}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" tabindex=\"-1\" aria-hidden=\"true\" class=\"hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full\"><!-- Backdrop --><div id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s-backdrop", id))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 12, Col: 42}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" class=\"fixed inset-0 bg-gray-900/50 dark:bg-gray-900/80 backdrop-blur-sm\"></div><!-- Modal content --><div class=\"relative p-4 w-full max-w-md max-h-full mx-auto\"><div class=\"relative bg-white rounded-lg shadow dark:bg-gray-700\"><div class=\"p-6 text-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if action == "delete" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<i class=\"fas fa-trash-alt text-red-400 text-3xl mb-4\"></i>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<i class=\"fas fa-exclamation-triangle text-yellow-400 text-3xl mb-4\"></i>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<h3 class=\"mb-5 text-lg font-normal text-gray-500 dark:text-gray-400\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(message)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 22, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</h3>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 = []any{confirmClass}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("triggerServiceDelete('%s', %d, '%s')", id, serviceID, serviceName)})
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<button type=\"button\" class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var5).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" hx-delete=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/admin/settings/notifications/%d", serviceID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 26, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" hx-target=\"body\" data-service-name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(serviceName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 28, Col: 37}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" data-service-id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(serviceID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 29, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("delete-btn-%d", serviceID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 30, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("triggerServiceDelete('%s', %d, '%s')", id, serviceID, serviceName)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(confirmText)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/dialog/dialog.templ`, Line: 32, Col: 19}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</button> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)})
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<button type=\"button\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("closeModal('%s')", id)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600\">Cancel</button></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// Scripts (triggerServiceDelete, closeModal, showModal) are now expected to be defined globally or in the calling template (e.g., list.templ).
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,32 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
// No utils needed for this specific template yet
)
templ EmailFields(data types.NotificationFormData) {
<!-- TODO: Populate value attributes if editing an email service -->
<div id="email_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="smtp_host" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">SMTP Host</label>
<input type="text" id="smtp_host" name="smtp_host" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="smtp.example.com"/>
</div>
<div class="mb-6">
<label for="smtp_port" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">SMTP Port</label>
<input type="number" id="smtp_port" name="smtp_port" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="587"/>
</div>
<div class="mb-6">
<label for="smtp_username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">SMTP Username</label>
<input type="text" id="smtp_username" name="smtp_username" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="user@example.com"/>
</div>
<div class="mb-6">
<label for="smtp_password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">SMTP Password</label>
<input type="password" id="smtp_password" name="smtp_password" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"/>
</div>
<div class="mb-6">
<label for="from_email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">From Email</label>
<input type="email" id="from_email" name="from_email" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="notifications@example.com"/>
</div>
</div>
}
@@ -0,0 +1,45 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
// No utils needed for this specific template yet
)
func EmailFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- TODO: Populate value attributes if editing an email service --><div id=\"email_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"smtp_host\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">SMTP Host</label> <input type=\"text\" id=\"smtp_host\" name=\"smtp_host\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"smtp.example.com\"></div><div class=\"mb-6\"><label for=\"smtp_port\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">SMTP Port</label> <input type=\"number\" id=\"smtp_port\" name=\"smtp_port\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"587\"></div><div class=\"mb-6\"><label for=\"smtp_username\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">SMTP Username</label> <input type=\"text\" id=\"smtp_username\" name=\"smtp_username\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"user@example.com\"></div><div class=\"mb-6\"><label for=\"smtp_password\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">SMTP Password</label> <input type=\"password\" id=\"smtp_password\" name=\"smtp_password\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"></div><div class=\"mb-6\"><label for=\"from_email\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">From Email</label> <input type=\"email\" id=\"from_email\" name=\"from_email\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"notifications@example.com\"></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,116 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
)
templ GotifyFields(data types.NotificationFormData) {
<div id="gotify_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="gotify_url" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Gotify Server URL</label>
if data.NotificationService.GotifyURL != "" {
<input type="url" id="gotify_url" name="gotify_url" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://gotify.example.com" value={ data.NotificationService.GotifyURL }/>
} else {
<input type="url" id="gotify_url" name="gotify_url" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://gotify.example.com" value=""/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">URL of your Gotify server</p>
</div>
<div class="mb-6">
<label for="gotify_token" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Application Token</label>
if data.NotificationService.GotifyToken != "" {
<input type="text" id="gotify_token" name="gotify_token" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="A-M-XiEQj.zX5d" value={ data.NotificationService.GotifyToken }/>
} else {
<input type="text" id="gotify_token" name="gotify_token" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="A-M-XiEQj.zX5d" value=""/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Find this in your Gotify application settings</p>
</div>
<div class="mb-6">
<label for="gotify_priority" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Default Priority</label>
<select id="gotify_priority" name="gotify_priority" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="0">Low (0)</option>
if data.NotificationService.GotifyPriority != "" && data.NotificationService.GotifyPriority == "5" {
<option value="5" selected="selected">Normal (5)</option>
} else {
<option value="5">Normal (5)</option>
}
<option value="8">High (8)</option>
</select>
</div>
<div class="mb-6">
<label for="gotify_title_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Title Template</label>
if data.NotificationService.GotifyTitleTemplate != "" {
<input type="text" id="gotify_title_template" name="gotify_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value={ data.NotificationService.GotifyTitleTemplate }/>
} else {
<input type="text" id="gotify_title_template" name="gotify_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="{{job.name}} {{job.status}}" value=""/>
}
</div>
<div class="mb-6">
<label for="gotify_message_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Body Template</label>
<textarea
id="gotify_message_template"
name="gotify_message_template"
rows="4"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Job '{{job.name}}' {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes)."
>
if data.NotificationService.GotifyMessageTemplate != "" {
data.NotificationService.GotifyMessageTemplate
}</textarea>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p>
</div>
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Event Triggers</label>
<div class="space-y-2">
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="gotify_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")) }/>
} else {
<input id="gotify_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="gotify_trigger_job_start" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Start</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="gotify_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")) }/>
} else {
<input id="gotify_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="gotify_trigger_job_complete" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Complete</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="gotify_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")) }/>
} else {
<input id="gotify_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="gotify_trigger_job_error" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Error</label>
</div>
</div>
</div>
<!-- Test notification button for Gotify -->
<div class="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
<div class="flex items-center justify-between mb-2">
<h4 class="text-base font-medium text-gray-900 dark:text-white">Test Configuration</h4>
<button
type="button"
id="test-gotify-btn"
hx-post="/admin/settings/notifications/test"
hx-trigger="click"
hx-target="#test-notification-result"
hx-swap="outerHTML"
class="px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
<i class="fas fa-paper-plane mr-1"></i>
Send Test Notification
</button>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
Send a test notification to verify your Gotify configuration works correctly before saving.
</p>
<div id="test-notification-result" class="mt-3 hidden">
<!-- Result will be shown here -->
</div>
</div>
</div>
}
@@ -0,0 +1,238 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func GotifyFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"gotify_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"gotify_url\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Gotify Server URL</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.GotifyURL != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<input type=\"url\" id=\"gotify_url\" name=\"gotify_url\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://gotify.example.com\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.GotifyURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 13, Col: 407}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<input type=\"url\" id=\"gotify_url\" name=\"gotify_url\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://gotify.example.com\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">URL of your Gotify server</p></div><div class=\"mb-6\"><label for=\"gotify_token\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Application Token</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.GotifyToken != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<input type=\"text\" id=\"gotify_token\" name=\"gotify_token\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"A-M-XiEQj.zX5d\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.GotifyToken)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 22, Col: 402}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<input type=\"text\" id=\"gotify_token\" name=\"gotify_token\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"A-M-XiEQj.zX5d\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Find this in your Gotify application settings</p></div><div class=\"mb-6\"><label for=\"gotify_priority\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Default Priority</label> <select id=\"gotify_priority\" name=\"gotify_priority\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"0\">Low (0)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.GotifyPriority != "" && data.NotificationService.GotifyPriority == "5" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<option value=\"5\" selected=\"selected\">Normal (5)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<option value=\"5\">Normal (5)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<option value=\"8\">High (8)</option></select></div><div class=\"mb-6\"><label for=\"gotify_title_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Title Template</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.GotifyTitleTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<input type=\"text\" id=\"gotify_title_template\" name=\"gotify_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.GotifyTitleTemplate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 43, Col: 399}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<input type=\"text\" id=\"gotify_title_template\" name=\"gotify_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{{job.name}} {{job.status}}\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div><div class=\"mb-6\"><label for=\"gotify_message_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Body Template</label> <textarea id=\"gotify_message_template\" name=\"gotify_message_template\" rows=\"4\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Job &#39;{{job.name}}&#39; {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes).\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.GotifyMessageTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "data.NotificationService.GotifyMessageTemplate")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</textarea><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p></div><div class=\"mb-6\"><label class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Event Triggers</label><div class=\"space-y-2\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<input id=\"gotify_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 67, Col: 361}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<input id=\"gotify_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<label for=\"gotify_trigger_job_start\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Start</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input id=\"gotify_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 75, Col: 370}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<input id=\"gotify_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<label for=\"gotify_trigger_job_complete\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Complete</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<input id=\"gotify_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/gotify.templ`, Line: 83, Col: 361}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input id=\"gotify_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<label for=\"gotify_trigger_job_error\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Error</label></div></div></div><!-- Test notification button for Gotify --><div class=\"mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600\"><div class=\"flex items-center justify-between mb-2\"><h4 class=\"text-base font-medium text-gray-900 dark:text-white\">Test Configuration</h4><button type=\"button\" id=\"test-gotify-btn\" hx-post=\"/admin/settings/notifications/test\" hx-trigger=\"click\" hx-target=\"#test-notification-result\" hx-swap=\"outerHTML\" class=\"px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-paper-plane mr-1\"></i> Send Test Notification</button></div><p class=\"text-sm text-gray-500 dark:text-gray-400\">Send a test notification to verify your Gotify configuration works correctly before saving.</p><div id=\"test-notification-result\" class=\"mt-3 hidden\"><!-- Result will be shown here --></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,133 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
)
templ NtfyFields(data types.NotificationFormData) {
<div id="ntfy_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="ntfy_server" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Ntfy Server</label>
if data.NotificationService.NtfyServer != "" {
<input type="url" id="ntfy_server" name="ntfy_server" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://ntfy.sh" value={ data.NotificationService.NtfyServer }/>
} else {
<input type="url" id="ntfy_server" name="ntfy_server" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://ntfy.sh" value="https://ntfy.sh"/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">The Ntfy server URL (default: ntfy.sh)</p>
</div>
<div class="mb-6">
<label for="ntfy_topic" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Topic</label>
if data.NotificationService.NtfyTopic != "" {
<input type="text" id="ntfy_topic" name="ntfy_topic" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="your-unique-topic" value={ data.NotificationService.NtfyTopic }/>
} else {
<input type="text" id="ntfy_topic" name="ntfy_topic" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="your-unique-topic" value="gomft"/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Choose a unique, unguessable topic name</p>
</div>
<div class="mb-6">
<label for="ntfy_priority" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Default Priority</label>
<select id="ntfy_priority" name="ntfy_priority" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="1">Low (1)</option>
if data.NotificationService.NtfyPriority == "3" {
<option value="3" selected="selected">Default (3)</option>
} else {
<option value="3">Default (3)</option>
}
<option value="4">High (4)</option>
<option value="5">Urgent (5)</option>
</select>
</div>
<div class="mb-6">
<label for="ntfy_username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Username (Optional)</label>
if data.NotificationService.NtfyUsername != "" {
<input type="text" id="ntfy_username" name="ntfy_username" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Username for protected topics" value={ data.NotificationService.NtfyUsername }/>
} else {
<input type="text" id="ntfy_username" name="ntfy_username" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Username for protected topics" value=""/>
}
</div>
<div class="mb-6">
<label for="ntfy_password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password (Optional)</label>
if data.NotificationService.NtfyPassword != "" {
<input type="password" id="ntfy_password" name="ntfy_password" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Password for protected topics" value={ data.NotificationService.NtfyPassword }/>
} else {
<input type="password" id="ntfy_password" name="ntfy_password" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Password for protected topics" value=""/>
}
</div>
<div class="mb-6">
<label for="ntfy_title_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Notification Title Template</label>
if data.NotificationService.NtfyTitleTemplate != "" {
<input type="text" id="ntfy_title_template" name="ntfy_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value={ data.NotificationService.NtfyTitleTemplate }/>
} else {
<input type="text" id="ntfy_title_template" name="ntfy_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="{{job.name}} {{job.status}}" value=""/>
}
</div>
<div class="mb-6">
<label for="ntfy_message_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Body Template</label>
<textarea
id="ntfy_message_template"
name="ntfy_message_template"
rows="4"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Job '{{job.name}}' {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes)."
>
if data.NotificationService.NtfyMessageTemplate != "" {
data.NotificationService.NtfyMessageTemplate
} </textarea>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p>
</div>
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Event Triggers</label>
<div class="space-y-2">
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="ntfy_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")) }/>
} else {
<input id="ntfy_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="ntfy_trigger_job_start" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Start</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="ntfy_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")) }/>
} else {
<input id="ntfy_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="ntfy_trigger_job_complete" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Complete</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="ntfy_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")) }/>
} else {
<input id="ntfy_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="ntfy_trigger_job_error" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Error</label>
</div>
</div>
</div>
<!-- Test notification button for Ntfy -->
<div class="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
<div class="flex items-center justify-between mb-2">
<h4 class="text-base font-medium text-gray-900 dark:text-white">Test Configuration</h4>
<button
type="button"
id="test-ntfy-btn"
hx-post="/admin/settings/notifications/test"
hx-trigger="click"
hx-target="#test-notification-result"
hx-swap="outerHTML"
class="px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
<i class="fas fa-paper-plane mr-1"></i>
Send Test Notification
</button>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
Send a test notification to verify your Ntfy configuration works correctly before saving.
</p>
<div id="test-notification-result" class="mt-3 hidden">
<!-- Result will be shown here -->
</div>
</div>
</div>
}
@@ -0,0 +1,294 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func NtfyFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"ntfy_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"ntfy_server\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Ntfy Server</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyServer != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<input type=\"url\" id=\"ntfy_server\" name=\"ntfy_server\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://ntfy.sh\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.NtfyServer)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 13, Col: 399}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<input type=\"url\" id=\"ntfy_server\" name=\"ntfy_server\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://ntfy.sh\" value=\"https://ntfy.sh\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">The Ntfy server URL (default: ntfy.sh)</p></div><div class=\"mb-6\"><label for=\"ntfy_topic\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Topic</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyTopic != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<input type=\"text\" id=\"ntfy_topic\" name=\"ntfy_topic\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"your-unique-topic\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.NtfyTopic)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 22, Col: 399}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<input type=\"text\" id=\"ntfy_topic\" name=\"ntfy_topic\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"your-unique-topic\" value=\"gomft\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Choose a unique, unguessable topic name</p></div><div class=\"mb-6\"><label for=\"ntfy_priority\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Default Priority</label> <select id=\"ntfy_priority\" name=\"ntfy_priority\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"1\">Low (1)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyPriority == "3" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<option value=\"3\" selected=\"selected\">Default (3)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<option value=\"3\">Default (3)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<option value=\"4\">High (4)</option> <option value=\"5\">Urgent (5)</option></select></div><div class=\"mb-6\"><label for=\"ntfy_username\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Username (Optional)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyUsername != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<input type=\"text\" id=\"ntfy_username\" name=\"ntfy_username\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Username for protected topics\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.NtfyUsername)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 44, Col: 420}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<input type=\"text\" id=\"ntfy_username\" name=\"ntfy_username\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Username for protected topics\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div><div class=\"mb-6\"><label for=\"ntfy_password\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Password (Optional)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyPassword != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<input type=\"password\" id=\"ntfy_password\" name=\"ntfy_password\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Password for protected topics\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.NtfyPassword)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 52, Col: 424}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<input type=\"password\" id=\"ntfy_password\" name=\"ntfy_password\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Password for protected topics\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div><div class=\"mb-6\"><label for=\"ntfy_title_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Notification Title Template</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyTitleTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<input type=\"text\" id=\"ntfy_title_template\" name=\"ntfy_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.NtfyTitleTemplate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 60, Col: 393}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" id=\"ntfy_title_template\" name=\"ntfy_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{{job.name}} {{job.status}}\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div><div class=\"mb-6\"><label for=\"ntfy_message_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Body Template</label> <textarea id=\"ntfy_message_template\" name=\"ntfy_message_template\" rows=\"4\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Job &#39;{{job.name}}&#39; {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes).\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.NtfyMessageTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "data.NotificationService.NtfyMessageTemplate")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</textarea><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p></div><div class=\"mb-6\"><label class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Event Triggers</label><div class=\"space-y-2\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<input id=\"ntfy_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 84, Col: 359}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input id=\"ntfy_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<label for=\"ntfy_trigger_job_start\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Start</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<input id=\"ntfy_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 92, Col: 368}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<input id=\"ntfy_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<label for=\"ntfy_trigger_job_complete\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Complete</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<input id=\"ntfy_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/ntfy.templ`, Line: 100, Col: 359}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<input id=\"ntfy_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<label for=\"ntfy_trigger_job_error\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Error</label></div></div></div><!-- Test notification button for Ntfy --><div class=\"mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600\"><div class=\"flex items-center justify-between mb-2\"><h4 class=\"text-base font-medium text-gray-900 dark:text-white\">Test Configuration</h4><button type=\"button\" id=\"test-ntfy-btn\" hx-post=\"/admin/settings/notifications/test\" hx-trigger=\"click\" hx-target=\"#test-notification-result\" hx-swap=\"outerHTML\" class=\"px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-paper-plane mr-1\"></i> Send Test Notification</button></div><p class=\"text-sm text-gray-500 dark:text-gray-400\">Send a test notification to verify your Ntfy configuration works correctly before saving.</p><div id=\"test-notification-result\" class=\"mt-3 hidden\"><!-- Result will be shown here --></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,103 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
)
templ PushbulletFields(data types.NotificationFormData) {
<div id="pushbullet_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="pushbullet_api_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">API Key</label>
if data.NotificationService.PushbulletAPIKey != "" {
<input type="text" id="pushbullet_api_key" name="pushbullet_api_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="o.XyzAbCdEfGhIjKlMnOpQrSt" value={ data.NotificationService.PushbulletAPIKey }/>
} else {
<input type="text" id="pushbullet_api_key" name="pushbullet_api_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="o.XyzAbCdEfGhIjKlMnOpQrSt" value=""/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Get your API key from <a href="https://www.pushbullet.com/#settings/account" target="_blank" class="text-blue-500 hover:underline">Pushbullet Account Settings</a></p>
</div>
<div class="mb-6">
<label for="pushbullet_device_iden" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Device Identifier (Optional)</label>
if data.NotificationService.PushbulletDeviceID != "" {
<input type="text" id="pushbullet_device_iden" name="pushbullet_device_iden" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Leave empty to send to all devices" value={ data.NotificationService.PushbulletDeviceID }/>
} else {
<input type="text" id="pushbullet_device_iden" name="pushbullet_device_iden" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Leave empty to send to all devices" value=""/>
}
</div>
<div class="mb-6">
<label for="pushbullet_title_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Notification Title Template</label>
if data.NotificationService.PushbulletTitleTemplate != "" {
<input type="text" id="pushbullet_title_template" name="pushbullet_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value={ data.NotificationService.PushbulletTitleTemplate }/>
} else {
<input type="text" id="pushbullet_title_template" name="pushbullet_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="{{job.name}} {{job.status}}" value=""/>
}
</div>
<div class="mb-6">
<label for="pushbullet_body_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Body Template</label>
<textarea
id="pushbullet_body_template"
name="pushbullet_body_template"
rows="4"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Job '{{job.name}}' {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes)."
>
if data.NotificationService.PushbulletBodyTemplate != "" {
data.NotificationService.PushbulletBodyTemplate
} </textarea>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p>
</div>
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Event Triggers</label>
<div class="space-y-2">
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pb_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")) }/>
} else {
<input id="pb_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pb_trigger_job_start" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Start</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pb_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")) }/>
} else {
<input id="pb_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pb_trigger_job_complete" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Complete</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pb_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")) }/>
} else {
<input id="pb_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pb_trigger_job_error" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Error</label>
</div>
</div>
</div>
<!-- Test notification button for Pushbullet -->
<div class="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
<div class="flex items-center justify-between mb-2">
<h4 class="text-base font-medium text-gray-900 dark:text-white">Test Configuration</h4>
<button
type="button"
id="test-pushbullet-btn"
hx-post="/admin/settings/notifications/test"
hx-trigger="click"
hx-target="#test-notification-result"
hx-swap="outerHTML"
class="px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
<i class="fas fa-paper-plane mr-1"></i>
Send Test Notification
</button>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
Send a test notification to verify your Pushbullet configuration works correctly before saving.
</p>
<div id="test-notification-result" class="mt-3 hidden">
<!-- Result will be shown here -->
</div>
</div>
</div>
}
@@ -0,0 +1,223 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func PushbulletFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"pushbullet_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"pushbullet_api_key\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">API Key</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushbulletAPIKey != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<input type=\"text\" id=\"pushbullet_api_key\" name=\"pushbullet_api_key\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"o.XyzAbCdEfGhIjKlMnOpQrSt\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushbulletAPIKey)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 13, Col: 430}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<input type=\"text\" id=\"pushbullet_api_key\" name=\"pushbullet_api_key\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"o.XyzAbCdEfGhIjKlMnOpQrSt\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Get your API key from <a href=\"https://www.pushbullet.com/#settings/account\" target=\"_blank\" class=\"text-blue-500 hover:underline\">Pushbullet Account Settings</a></p></div><div class=\"mb-6\"><label for=\"pushbullet_device_iden\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Device Identifier (Optional)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushbulletDeviceID != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<input type=\"text\" id=\"pushbullet_device_iden\" name=\"pushbullet_device_iden\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Leave empty to send to all devices\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushbulletDeviceID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 22, Col: 449}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<input type=\"text\" id=\"pushbullet_device_iden\" name=\"pushbullet_device_iden\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Leave empty to send to all devices\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</div><div class=\"mb-6\"><label for=\"pushbullet_title_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Notification Title Template</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushbulletTitleTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<input type=\"text\" id=\"pushbullet_title_template\" name=\"pushbullet_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushbulletTitleTemplate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 30, Col: 411}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<input type=\"text\" id=\"pushbullet_title_template\" name=\"pushbullet_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{{job.name}} {{job.status}}\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><div class=\"mb-6\"><label for=\"pushbullet_body_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Body Template</label> <textarea id=\"pushbullet_body_template\" name=\"pushbullet_body_template\" rows=\"4\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Job &#39;{{job.name}}&#39; {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes).\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushbulletBodyTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "data.NotificationService.PushbulletBodyTemplate")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</textarea><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p></div><div class=\"mb-6\"><label class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Event Triggers</label><div class=\"space-y-2\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<input id=\"pb_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 54, Col: 357}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<input id=\"pb_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<label for=\"pb_trigger_job_start\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Start</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<input id=\"pb_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 62, Col: 366}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<input id=\"pb_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<label for=\"pb_trigger_job_complete\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Complete</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<input id=\"pb_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushbullet.templ`, Line: 70, Col: 357}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<input id=\"pb_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<label for=\"pb_trigger_job_error\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Error</label></div></div></div><!-- Test notification button for Pushbullet --><div class=\"mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600\"><div class=\"flex items-center justify-between mb-2\"><h4 class=\"text-base font-medium text-gray-900 dark:text-white\">Test Configuration</h4><button type=\"button\" id=\"test-pushbullet-btn\" hx-post=\"/admin/settings/notifications/test\" hx-trigger=\"click\" hx-target=\"#test-notification-result\" hx-swap=\"outerHTML\" class=\"px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-paper-plane mr-1\"></i> Send Test Notification</button></div><p class=\"text-sm text-gray-500 dark:text-gray-400\">Send a test notification to verify your Pushbullet configuration works correctly before saving.</p><div id=\"test-notification-result\" class=\"mt-3 hidden\"><!-- Result will be shown here --></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,154 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
)
templ PushoverFields(data types.NotificationFormData) {
<div id="pushover_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="pushover_app_token" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">API Token/Key</label>
if data.NotificationService.PushoverAPIToken != "" {
<input type="text" id="pushover_app_token" name="pushover_app_token" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="azGDORePK8gMaC0QOYAMyEEuzJnyUi" value={ data.NotificationService.PushoverAPIToken }/>
} else {
<input type="text" id="pushover_app_token" name="pushover_app_token" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="azGDORePK8gMaC0QOYAMyEEuzJnyUi" value=""/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Your application's API token/key from <a href="https://pushover.net/apps" target="_blank" class="text-blue-500 hover:underline">Pushover Dashboard</a></p>
</div>
<div class="mb-6">
<label for="pushover_user_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">User Key</label>
if data.NotificationService.PushoverUserKey != "" {
<input type="text" id="pushover_user_key" name="pushover_user_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="uQiRzpo4DXghDmr9QzzfQu27cmVRsG" value={ data.NotificationService.PushoverUserKey }/>
} else {
<input type="text" id="pushover_user_key" name="pushover_user_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="uQiRzpo4DXghDmr9QzzfQu27cmVRsG" value=""/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Your user key from <a href="https://pushover.net/" target="_blank" class="text-blue-500 hover:underline">Pushover Dashboard</a></p>
</div>
<div class="mb-6">
<label for="pushover_device" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Device Name (Optional)</label>
if data.NotificationService.PushoverDevice != "" {
<input type="text" id="pushover_device" name="pushover_device" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Leave empty to send to all devices" value={ data.NotificationService.PushoverDevice }/>
} else {
<input type="text" id="pushover_device" name="pushover_device" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Leave empty to send to all devices" value=""/>
}
</div>
<div class="mb-6">
<label for="pushover_priority" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Default Priority</label>
<select id="pushover_priority" name="pushover_priority" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="-2">Lowest (-2)</option>
<option value="-1">Low (-1)</option>
if data.NotificationService.PushoverPriority != "" && data.NotificationService.PushoverPriority == "0" {
<option value="0" selected="selected">Normal (0)</option>
} else {
<option value="0">Normal (0)</option>
}
<option value="1">High (1)</option>
<option value="2">Emergency (2)</option>
</select>
</div>
<div class="mb-6">
<label for="pushover_sound" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Sound</label>
<select id="pushover_sound" name="pushover_sound" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="pushover">Pushover (default)</option>
<option value="bike">Bike</option>
<option value="bugle">Bugle</option>
<option value="cashregister">Cash Register</option>
<option value="classical">Classical</option>
<option value="cosmic">Cosmic</option>
<option value="falling">Falling</option>
<option value="gamelan">Gamelan</option>
<option value="incoming">Incoming</option>
<option value="intermission">Intermission</option>
<option value="magic">Magic</option>
<option value="mechanical">Mechanical</option>
<option value="pianobar">Piano Bar</option>
<option value="siren">Siren</option>
<option value="spacealarm">Space Alarm</option>
<option value="tugboat">Tug Boat</option>
<option value="alien">Alien Alarm (long)</option>
<option value="climb">Climb (long)</option>
<option value="persistent">Persistent (long)</option>
<option value="echo">Echo (long)</option>
<option value="updown">Up Down (long)</option>
<option value="vibrate">Vibrate Only</option>
<option value="none">None (silent)</option>
</select>
</div>
<div class="mb-6">
<label for="pushover_title_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Title Template</label>
if data.NotificationService.PushoverTitleTemplate != "" {
<input type="text" id="pushover_title_template" name="pushover_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value={ data.NotificationService.PushoverTitleTemplate }/>
} else {
<input type="text" id="pushover_title_template" name="pushover_title_template" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="'{{job.name}}' {{job.status}}" value=""/>
}
</div>
<div class="mb-6">
<label for="pushover_message_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Message Body Template</label>
<textarea
id="pushover_message_template"
name="pushover_message_template"
rows="4"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Job '{{job.name}}' {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes)."
>
if data.NotificationService.PushoverMessageTemplate != "" {
data.NotificationService.PushoverMessageTemplate
} </textarea>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p>
</div>
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Event Triggers</label>
<div class="space-y-2">
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pushover_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")) }/>
} else {
<input id="pushover_trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pushover_trigger_job_start" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Start</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pushover_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")) }/>
} else {
<input id="pushover_trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pushover_trigger_job_complete" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Complete</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="pushover_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")) }/>
} else {
<input id="pushover_trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800"/>
}
<label for="pushover_trigger_job_error" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Error</label>
</div>
</div>
</div>
<!-- Test notification button for Pushover -->
<div class="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
<div class="flex items-center justify-between mb-2">
<h4 class="text-base font-medium text-gray-900 dark:text-white">Test Configuration</h4>
<button
type="button"
id="test-pushover-btn"
hx-post="/admin/settings/notifications/test"
hx-trigger="click"
hx-target="#test-notification-result"
hx-swap="outerHTML"
class="px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
<i class="fas fa-paper-plane mr-1"></i>
Send Test Notification
</button>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
Send a test notification to verify your Pushover configuration works correctly before saving.
</p>
<div id="test-notification-result" class="mt-3 hidden">
<!-- Result will be shown here -->
</div>
</div>
</div>
}
@@ -0,0 +1,266 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func PushoverFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"pushover_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"pushover_app_token\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">API Token/Key</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverAPIToken != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<input type=\"text\" id=\"pushover_app_token\" name=\"pushover_app_token\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"azGDORePK8gMaC0QOYAMyEEuzJnyUi\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushoverAPIToken)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 13, Col: 435}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<input type=\"text\" id=\"pushover_app_token\" name=\"pushover_app_token\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"azGDORePK8gMaC0QOYAMyEEuzJnyUi\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Your application's API token/key from <a href=\"https://pushover.net/apps\" target=\"_blank\" class=\"text-blue-500 hover:underline\">Pushover Dashboard</a></p></div><div class=\"mb-6\"><label for=\"pushover_user_key\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">User Key</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverUserKey != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<input type=\"text\" id=\"pushover_user_key\" name=\"pushover_user_key\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"uQiRzpo4DXghDmr9QzzfQu27cmVRsG\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushoverUserKey)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 22, Col: 432}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<input type=\"text\" id=\"pushover_user_key\" name=\"pushover_user_key\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"uQiRzpo4DXghDmr9QzzfQu27cmVRsG\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Your user key from <a href=\"https://pushover.net/\" target=\"_blank\" class=\"text-blue-500 hover:underline\">Pushover Dashboard</a></p></div><div class=\"mb-6\"><label for=\"pushover_device\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Device Name (Optional)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverDevice != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<input type=\"text\" id=\"pushover_device\" name=\"pushover_device\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Leave empty to send to all devices\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushoverDevice)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 31, Col: 431}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<input type=\"text\" id=\"pushover_device\" name=\"pushover_device\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Leave empty to send to all devices\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div><div class=\"mb-6\"><label for=\"pushover_priority\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Default Priority</label> <select id=\"pushover_priority\" name=\"pushover_priority\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"-2\">Lowest (-2)</option> <option value=\"-1\">Low (-1)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverPriority != "" && data.NotificationService.PushoverPriority == "0" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<option value=\"0\" selected=\"selected\">Normal (0)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<option value=\"0\">Normal (0)</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<option value=\"1\">High (1)</option> <option value=\"2\">Emergency (2)</option></select></div><div class=\"mb-6\"><label for=\"pushover_sound\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Sound</label> <select id=\"pushover_sound\" name=\"pushover_sound\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"pushover\">Pushover (default)</option> <option value=\"bike\">Bike</option> <option value=\"bugle\">Bugle</option> <option value=\"cashregister\">Cash Register</option> <option value=\"classical\">Classical</option> <option value=\"cosmic\">Cosmic</option> <option value=\"falling\">Falling</option> <option value=\"gamelan\">Gamelan</option> <option value=\"incoming\">Incoming</option> <option value=\"intermission\">Intermission</option> <option value=\"magic\">Magic</option> <option value=\"mechanical\">Mechanical</option> <option value=\"pianobar\">Piano Bar</option> <option value=\"siren\">Siren</option> <option value=\"spacealarm\">Space Alarm</option> <option value=\"tugboat\">Tug Boat</option> <option value=\"alien\">Alien Alarm (long)</option> <option value=\"climb\">Climb (long)</option> <option value=\"persistent\">Persistent (long)</option> <option value=\"echo\">Echo (long)</option> <option value=\"updown\">Up Down (long)</option> <option value=\"vibrate\">Vibrate Only</option> <option value=\"none\">None (silent)</option></select></div><div class=\"mb-6\"><label for=\"pushover_title_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Title Template</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverTitleTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<input type=\"text\" id=\"pushover_title_template\" name=\"pushover_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.PushoverTitleTemplate)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 81, Col: 405}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<input type=\"text\" id=\"pushover_title_template\" name=\"pushover_title_template\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"&#39;{{job.name}}&#39; {{job.status}}\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div><div class=\"mb-6\"><label for=\"pushover_message_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Message Body Template</label> <textarea id=\"pushover_message_template\" name=\"pushover_message_template\" rows=\"4\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Job &#39;{{job.name}}&#39; {{job.status}} at {{job.completed_at}}. {{job.file_count}} files transferred ({{job.transfer_bytes}} bytes).\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PushoverMessageTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "data.NotificationService.PushoverMessageTemplate")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</textarea><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p></div><div class=\"mb-6\"><label class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Event Triggers</label><div class=\"space-y-2\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input id=\"pushover_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 105, Col: 363}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<input id=\"pushover_trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<label for=\"pushover_trigger_job_start\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Start</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<input id=\"pushover_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 113, Col: 372}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<input id=\"pushover_trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<label for=\"pushover_trigger_job_complete\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Complete</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<input id=\"pushover_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/pushover.templ`, Line: 121, Col: 363}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<input id=\"pushover_trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<label for=\"pushover_trigger_job_error\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Error</label></div></div></div><!-- Test notification button for Pushover --><div class=\"mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600\"><div class=\"flex items-center justify-between mb-2\"><h4 class=\"text-base font-medium text-gray-900 dark:text-white\">Test Configuration</h4><button type=\"button\" id=\"test-pushover-btn\" hx-post=\"/admin/settings/notifications/test\" hx-trigger=\"click\" hx-target=\"#test-notification-result\" hx-swap=\"outerHTML\" class=\"px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-paper-plane mr-1\"></i> Send Test Notification</button></div><p class=\"text-sm text-gray-500 dark:text-gray-400\">Send a test notification to verify your Pushover configuration works correctly before saving.</p><div id=\"test-notification-result\" class=\"mt-3 hidden\"><!-- Result will be shown here --></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,164 @@
package fields
import (
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
)
templ WebhookFields(data types.NotificationFormData) {
<div id="webhook_fields" class="hidden notification-fields">
<div class="mb-6">
<label for="webhook_url" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Webhook URL</label>
if data.NotificationService.WebhookURL != "" {
<input type="url" id="webhook_url" name="webhook_url" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://api.example.com/webhook" value={ data.NotificationService.WebhookURL }/>
} else {
<input type="url" id="webhook_url" name="webhook_url" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="https://api.example.com/webhook" value=""/>
}
</div>
<div class="mb-6">
<label for="method" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">HTTP Method</label>
<select id="method" name="method" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
if data.NotificationService.Method != "" {
if data.NotificationService.Method == "POST" {
<option value="POST" selected="selected">POST</option>
} else {
<option value="POST">POST</option>
}
if data.NotificationService.Method == "PUT" {
<option value="PUT" selected="selected">PUT</option>
} else {
<option value="PUT">PUT</option>
}
} else {
<option value="POST">POST</option>
<option value="PUT">PUT</option>
}
</select>
</div>
<div class="mb-6">
<label for="headers" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Headers (JSON)</label>
if data.NotificationService.Headers != "" {
<textarea id="headers" name="headers" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder='{"Content-Type": "application/json", "Authorization": "Bearer token"}'>{ data.NotificationService.Headers }</textarea>
} else {
<textarea id="headers" name="headers" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder='{"Content-Type": "application/json", "Authorization": "Bearer token"}'></textarea>
}
</div>
<div class="mb-6">
<label for="payload_template" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Payload Template (JSON)</label>
<textarea
id="payload_template"
name="payload_template"
rows="5"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder='{
"event": "{{job.event}}",
"job": {
"id": "{{job.id}}",
"name": "{{job.name}}",
"status": "{{job.status}}",
"message": "{{job.message}}",
"started_at": "{{job.started_at}}",
"completed_at": "{{job.completed_at}}",
"duration_seconds": {{job.duration_seconds}},
"config_id": "{{job.config_id}}",
"config_name": "{{job.config_name}}",
"transfer_bytes": {{job.transfer_bytes}},
"file_count": {{job.file_count}}
},
"instance": {
"id": "{{instance.id}}",
"name": "{{instance.name}}",
"version": "{{instance.version}}",
"environment": "{{instance.environment}}"
},
"timestamp": "{{timestamp}}",
"notification_id": "{{notification.id}}"
}'
>
if data.NotificationService.PayloadTemplate != "" {
data.NotificationService.PayloadTemplate
} </textarea>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p>
</div>
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Event Triggers</label>
<div class="space-y-2">
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="trigger_job_start" name="trigger_job_start" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")) }/>
}
<label for="trigger_job_start" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Start</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="trigger_job_complete" name="trigger_job_complete" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")) }/>
}
<label for="trigger_job_complete" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Complete</label>
</div>
<div class="flex items-center">
if data.NotificationService.EventTriggers != nil {
<input id="trigger_job_error" name="trigger_job_error" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")) }/>
}
<label for="trigger_job_error" class="ml-2 text-sm font-medium text-gray-900 dark:text-white">Job Error</label>
</div>
</div>
</div>
<div class="mb-6">
<label for="secret_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Secret Key (for signature verification)</label>
if data.NotificationService.SecretKey != "" {
<input type="text" id="secret_key" name="secret_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Optional signature verification key" value={ data.NotificationService.SecretKey }/>
}
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">If provided, all webhooks will include an X-GoMFT-Signature header</p>
</div>
<div class="mb-6">
<label for="retry_policy" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Retry Policy</label>
<select id="retry_policy" name="retry_policy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
if data.NotificationService.RetryPolicy != "" {
if data.NotificationService.RetryPolicy == "none" {
<option value="none" selected="selected">No retries</option>
} else {
<option value="none">No retries</option>
}
if data.NotificationService.RetryPolicy == "simple" {
<option value="simple" selected="selected">Simple (3 retries)</option>
} else {
<option value="simple">Simple (3 retries)</option>
}
if data.NotificationService.RetryPolicy == "exponential" {
<option value="exponential" selected="selected">Exponential backoff</option>
} else {
<option value="exponential">Exponential backoff</option>
}
} else {
<option value="none">No retries</option>
<option value="simple">Simple (3 retries)</option>
<option value="exponential">Exponential backoff</option>
}
</select>
</div>
<!-- Test notification button -->
<div class="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600">
<div class="flex items-center justify-between mb-2">
<h4 class="text-base font-medium text-gray-900 dark:text-white">Test Configuration</h4>
<button
type="button"
id="test-webhook-btn"
hx-post="/admin/settings/notifications/test"
hx-trigger="click"
hx-target="#test-notification-result"
hx-swap="outerHTML"
class="px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
>
<i class="fas fa-paper-plane mr-1"></i>
Send Test Notification
</button>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">
Send a test notification to verify your configuration works correctly before saving.
</p>
<div id="test-notification-result" class="mt-3 hidden">
<!-- Result will be shown here -->
</div>
</div>
</div>
}
@@ -0,0 +1,292 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package fields
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func WebhookFields(data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"webhook_fields\" class=\"hidden notification-fields\"><div class=\"mb-6\"><label for=\"webhook_url\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Webhook URL</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.WebhookURL != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<input type=\"url\" id=\"webhook_url\" name=\"webhook_url\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://api.example.com/webhook\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.WebhookURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 13, Col: 415}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<input type=\"url\" id=\"webhook_url\" name=\"webhook_url\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"https://api.example.com/webhook\" value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div><div class=\"mb-6\"><label for=\"method\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">HTTP Method</label> <select id=\"method\" name=\"method\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.Method != "" {
if data.NotificationService.Method == "POST" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<option value=\"POST\" selected=\"selected\">POST</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<option value=\"POST\">POST</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.Method == "PUT" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<option value=\"PUT\" selected=\"selected\">PUT</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<option value=\"PUT\">PUT</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<option value=\"POST\">POST</option> <option value=\"PUT\">PUT</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</select></div><div class=\"mb-6\"><label for=\"headers\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Headers (JSON)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.Headers != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<textarea id=\"headers\" name=\"headers\" rows=\"3\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{&#34;Content-Type&#34;: &#34;application/json&#34;, &#34;Authorization&#34;: &#34;Bearer token&#34;}\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.Headers)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 41, Col: 437}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</textarea>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<textarea id=\"headers\" name=\"headers\" rows=\"3\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{&#34;Content-Type&#34;: &#34;application/json&#34;, &#34;Authorization&#34;: &#34;Bearer token&#34;}\"></textarea>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div><div class=\"mb-6\"><label for=\"payload_template\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Payload Template (JSON)</label> <textarea id=\"payload_template\" name=\"payload_template\" rows=\"5\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"{\n\t&#34;event&#34;: &#34;{{job.event}}&#34;,\n\t&#34;job&#34;: {\n\t\t\t&#34;id&#34;: &#34;{{job.id}}&#34;,\n\t\t\t&#34;name&#34;: &#34;{{job.name}}&#34;,\n\t\t\t&#34;status&#34;: &#34;{{job.status}}&#34;,\n\t\t\t&#34;message&#34;: &#34;{{job.message}}&#34;,\n\t\t\t&#34;started_at&#34;: &#34;{{job.started_at}}&#34;,\n\t\t\t&#34;completed_at&#34;: &#34;{{job.completed_at}}&#34;,\n\t\t\t&#34;duration_seconds&#34;: {{job.duration_seconds}},\n\t\t\t&#34;config_id&#34;: &#34;{{job.config_id}}&#34;,\n\t\t\t&#34;config_name&#34;: &#34;{{job.config_name}}&#34;,\n\t\t\t&#34;transfer_bytes&#34;: {{job.transfer_bytes}},\n\t\t\t&#34;file_count&#34;: {{job.file_count}}\n\t},\n\t&#34;instance&#34;: {\n\t\t\t&#34;id&#34;: &#34;{{instance.id}}&#34;,\n\t\t\t&#34;name&#34;: &#34;{{instance.name}}&#34;,\n\t\t\t&#34;version&#34;: &#34;{{instance.version}}&#34;,\n\t\t\t&#34;environment&#34;: &#34;{{instance.environment}}&#34;\n\t},\n\t&#34;timestamp&#34;: &#34;{{timestamp}}&#34;,\n\t&#34;notification_id&#34;: &#34;{{notification.id}}&#34;\n}\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.PayloadTemplate != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "data.NotificationService.PayloadTemplate")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</textarea><p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">Use placeholders for dynamic values. Available variables: job.*, instance.*, timestamp, notification.*</p></div><div class=\"mb-6\"><label class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Event Triggers</label><div class=\"space-y-2\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<input id=\"trigger_job_start\" name=\"trigger_job_start\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_start")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 88, Col: 354}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<label for=\"trigger_job_start\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Start</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<input id=\"trigger_job_complete\" name=\"trigger_job_complete\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_complete")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 94, Col: 363}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<label for=\"trigger_job_complete\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Complete</label></div><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.EventTriggers != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<input id=\"trigger_job_error\" name=\"trigger_job_error\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(utils.Contains(data.NotificationService.EventTriggers, "job_error")))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 100, Col: 354}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<label for=\"trigger_job_error\" class=\"ml-2 text-sm font-medium text-gray-900 dark:text-white\">Job Error</label></div></div></div><div class=\"mb-6\"><label for=\"secret_key\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Secret Key (for signature verification)</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.SecretKey != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<input type=\"text\" id=\"secret_key\" name=\"secret_key\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Optional signature verification key\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.SecretKey)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/fields/webhook.templ`, Line: 109, Col: 417}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p class=\"mt-1 text-sm text-gray-500 dark:text-gray-400\">If provided, all webhooks will include an X-GoMFT-Signature header</p></div><div class=\"mb-6\"><label for=\"retry_policy\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Retry Policy</label> <select id=\"retry_policy\" name=\"retry_policy\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.RetryPolicy != "" {
if data.NotificationService.RetryPolicy == "none" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<option value=\"none\" selected=\"selected\">No retries</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<option value=\"none\">No retries</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.RetryPolicy == "simple" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<option value=\"simple\" selected=\"selected\">Simple (3 retries)</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<option value=\"simple\">Simple (3 retries)</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.RetryPolicy == "exponential" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<option value=\"exponential\" selected=\"selected\">Exponential backoff</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<option value=\"exponential\">Exponential backoff</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<option value=\"none\">No retries</option> <option value=\"simple\">Simple (3 retries)</option> <option value=\"exponential\">Exponential backoff</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</select></div><!-- Test notification button --><div class=\"mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600\"><div class=\"flex items-center justify-between mb-2\"><h4 class=\"text-base font-medium text-gray-900 dark:text-white\">Test Configuration</h4><button type=\"button\" id=\"test-webhook-btn\" hx-post=\"/admin/settings/notifications/test\" hx-trigger=\"click\" hx-target=\"#test-notification-result\" hx-swap=\"outerHTML\" class=\"px-3 py-2 text-xs font-medium text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-paper-plane mr-1\"></i> Send Test Notification</button></div><p class=\"text-sm text-gray-500 dark:text-gray-400\">Send a test notification to verify your configuration works correctly before saving.</p><div id=\"test-notification-result\" class=\"mt-3 hidden\"><!-- Result will be shown here --></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
+148
View File
@@ -0,0 +1,148 @@
package form
import (
"context"
"fmt"
"github.com/starfleetcptn/gomft/components" // For LayoutWithContext
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/form/fields" // Import fields
)
templ NotificationForm(ctx context.Context, data types.NotificationFormData) {
@FormScripts() // Include the form-specific scripts
@components.LayoutWithContext(utils.GetNotificationFormTitle(data.IsNew), ctx) {
<!-- Status and Error Messages (Handled by shared toast component in layout) -->
<div id="notification-form-container" class="notifications-page bg-gray-50 dark:bg-gray-900 min-h-screen">
<div class="pb-8 w-full max-w-4xl mx-auto">
<!-- Success Message (hidden, used for HTMX responses) -->
if data.SuccessMessage != "" {
<div class="hidden success-message">{ data.SuccessMessage }</div>
}
<!-- Error Message (hidden, used for HTMX responses) -->
if data.ErrorMessage != "" {
<div class="hidden error-message">{ data.ErrorMessage }</div>
}
<div class="mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white flex items-center">
<i class="fas fa-bell w-6 h-6 mr-2 text-blue-500 dark:text-blue-400"></i>
{ utils.GetNotificationFormTitle(data.IsNew) }
</h1>
<a href="/admin/settings/notifications" class="flex items-center justify-center text-gray-700 bg-gray-100 hover:bg-gray-200 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg px-5 py-2.5 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 focus:outline-none dark:focus:ring-gray-700">
<i class="fas fa-arrow-left w-4 h-4 mr-2"></i>
Back to Notification Services
</a>
</div>
<!-- Add Notification Service Form -->
<div class="mb-6 p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 dark:bg-gray-800">
<form id="notification-form"
if data.IsNew {
hx-post="/admin/settings/notifications"
} else {
hx-put={ fmt.Sprintf("/admin/settings/notifications/%d", data.NotificationService.ID) }
}
hx-target="body">
<div class="mb-6">
<label for="notification_type" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Notification Type</label>
<select id="notification_type" name="type" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="">Select a type</option>
if data.NotificationService != nil && data.NotificationService.Type == "webhook" {
<option value="webhook" selected="selected">Webhook</option>
} else {
<option value="webhook">Webhook</option>
}
if data.NotificationService != nil && data.NotificationService.Type == "pushbullet" {
<option value="pushbullet" selected="selected">Pushbullet</option>
} else {
<option value="pushbullet">Pushbullet</option>
}
if data.NotificationService != nil && data.NotificationService.Type == "ntfy" {
<option value="ntfy" selected="selected">Ntfy</option>
} else {
<option value="ntfy">Ntfy</option>
}
if data.NotificationService != nil && data.NotificationService.Type == "gotify" {
<option value="gotify" selected="selected">Gotify</option>
} else {
<option value="gotify">Gotify</option>
}
if data.NotificationService != nil && data.NotificationService.Type == "pushover" {
<option value="pushover" selected="selected">Pushover</option>
} else {
<option value="pushover">Pushover</option>
}
<option value="email" disabled>Email (Coming Soon)</option>
</select>
</div>
<div class="mb-6 hidden common-fields">
<label for="notification_name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Name</label>
if data.NotificationService.Name != "" {
<input type="text" id="notification_name" name="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="My Notification Service" required value={ data.NotificationService.Name }/>
} else {
<input type="text" id="notification_name" name="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="My Notification Service" required value=""/>
}
</div>
<div class="mb-6 hidden common-fields">
<label for="notification_description" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Description</label>
if data.NotificationService.Description != "" {
<textarea id="notification_description" name="description" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Description for this notification service">{ data.NotificationService.Description }</textarea>
} else {
<textarea id="notification_description" name="description" rows="3" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Description for this notification service"></textarea>
}
</div>
<!-- Dynamic fields based on notification type -->
@fields.EmailFields(data)
@fields.WebhookFields(data)
@fields.PushbulletFields(data)
@fields.NtfyFields(data)
@fields.GotifyFields(data)
@fields.PushoverFields(data)
<div class="flex items-start mb-6 hidden common-fields">
<div class="flex items-center h-5">
if data.NotificationService.IsEnabled != false {
<input id="is_enabled" name="is_enabled" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked={ utils.BoolToString(data.NotificationService.IsEnabled) }/>
} else {
// Default to checked=true if IsEnabled is explicitly false (or not set) when creating new
<input id="is_enabled" name="is_enabled" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" checked="true"/>
}
</div>
<div class="ml-3 text-sm">
<label for="is_enabled" class="font-medium text-gray-900 dark:text-white">Enable this notification service</label>
</div>
</div>
<div class="hidden common-fields">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
if data.IsNew {
Add Service
} else {
Save Changes
}
</button>
</div>
</form>
</div>
<!-- Help Notice -->
<div class="mt-8 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-info-circle text-blue-400 dark:text-blue-400"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700 dark:text-blue-400">
Configure your notification service to receive alerts for job events. Different notification types have different configuration options.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Theme-specific background handled by Tailwind classes -->
}
}
@@ -0,0 +1,45 @@
package form
// FormScripts contains JavaScript specific to the notification form page.
templ FormScripts() {
<script>
// Toggle notification fields based on selection
document.addEventListener('DOMContentLoaded', function() {
const typeSelector = document.getElementById('notification_type');
// Ensure typeSelector exists before adding listener
if (!typeSelector) {
console.warn("Notification type selector not found.");
return;
}
const allFields = document.querySelectorAll('.notification-fields');
const commonFields = document.querySelectorAll('.common-fields');
function toggleFields() {
// Hide all specific fields first
allFields.forEach(field => field.classList.add('hidden'));
// Show/hide common fields based on selection
const selectedType = typeSelector.value;
if (selectedType) {
// Show common fields (name, description, is_enabled, submit)
commonFields.forEach(field => field.classList.remove('hidden'));
// Show the selected type's specific fields
const fieldsToShow = document.getElementById(`${selectedType}_fields`);
if (fieldsToShow) {
fieldsToShow.classList.remove('hidden');
}
} else {
// Hide common fields if no type selected
commonFields.forEach(field => field.classList.add('hidden'));
}
}
typeSelector.addEventListener('change', toggleFields);
// Initialize form state on load (if editing or if a type is pre-selected)
toggleFields();
});
</script>
}
@@ -0,0 +1,41 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package form
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
// FormScripts contains JavaScript specific to the notification form page.
func FormScripts() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script>\n\t\t// Toggle notification fields based on selection\n\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\tconst typeSelector = document.getElementById('notification_type');\n\t\t\t// Ensure typeSelector exists before adding listener\n\t\t\tif (!typeSelector) {\n\t\t\t\tconsole.warn(\"Notification type selector not found.\");\n\t\t\t\treturn; \n\t\t\t}\n\n\t\t\tconst allFields = document.querySelectorAll('.notification-fields');\n\t\t\tconst commonFields = document.querySelectorAll('.common-fields');\n\n\t\t\tfunction toggleFields() {\n\t\t\t\t// Hide all specific fields first\n\t\t\t\tallFields.forEach(field => field.classList.add('hidden'));\n\n\t\t\t\t// Show/hide common fields based on selection\n\t\t\t\tconst selectedType = typeSelector.value;\n\t\t\t\tif (selectedType) {\n\t\t\t\t\t// Show common fields (name, description, is_enabled, submit)\n\t\t\t\t\tcommonFields.forEach(field => field.classList.remove('hidden'));\n\n\t\t\t\t\t// Show the selected type's specific fields\n\t\t\t\t\tconst fieldsToShow = document.getElementById(`${selectedType}_fields`);\n\t\t\t\t\tif (fieldsToShow) {\n\t\t\t\t\t\tfieldsToShow.classList.remove('hidden');\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Hide common fields if no type selected\n\t\t\t\t\tcommonFields.forEach(field => field.classList.add('hidden'));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttypeSelector.addEventListener('change', toggleFields);\n\n\t\t\t// Initialize form state on load (if editing or if a type is pre-selected)\n\t\t\ttoggleFields(); \n\t\t});\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
+344
View File
@@ -0,0 +1,344 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package form
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"context"
"fmt"
"github.com/starfleetcptn/gomft/components" // For LayoutWithContext
"github.com/starfleetcptn/gomft/components/notifications/form/fields" // Import fields
"github.com/starfleetcptn/gomft/components/notifications/form/utils"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
func NotificationForm(ctx context.Context, data types.NotificationFormData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = FormScripts().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Status and Error Messages (Handled by shared toast component in layout) --> <div id=\"notification-form-container\" class=\"notifications-page bg-gray-50 dark:bg-gray-900 min-h-screen\"><div class=\"pb-8 w-full max-w-4xl mx-auto\"><!-- Success Message (hidden, used for HTMX responses) -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.SuccessMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"hidden success-message\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.SuccessMessage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 21, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<!-- Error Message (hidden, used for HTMX responses) -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.ErrorMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"hidden error-message\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.ErrorMessage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 25, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-bell w-6 h-6 mr-2 text-blue-500 dark:text-blue-400\"></i> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(utils.GetNotificationFormTitle(data.IsNew))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 31, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</h1><a href=\"/admin/settings/notifications\" class=\"flex items-center justify-center text-gray-700 bg-gray-100 hover:bg-gray-200 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg px-5 py-2.5 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 focus:outline-none dark:focus:ring-gray-700\"><i class=\"fas fa-arrow-left w-4 h-4 mr-2\"></i> Back to Notification Services</a></div><!-- Add Notification Service Form --><div class=\"mb-6 p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 dark:bg-gray-800\"><form id=\"notification-form\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.IsNew {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " hx-post=\"/admin/settings/notifications\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " hx-put=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/admin/settings/notifications/%d", data.NotificationService.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 45, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " hx-target=\"body\"><div class=\"mb-6\"><label for=\"notification_type\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Notification Type</label> <select id=\"notification_type\" name=\"type\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"><option value=\"\">Select a type</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService != nil && data.NotificationService.Type == "webhook" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<option value=\"webhook\" selected=\"selected\">Webhook</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<option value=\"webhook\">Webhook</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.NotificationService != nil && data.NotificationService.Type == "pushbullet" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<option value=\"pushbullet\" selected=\"selected\">Pushbullet</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<option value=\"pushbullet\">Pushbullet</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.NotificationService != nil && data.NotificationService.Type == "ntfy" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<option value=\"ntfy\" selected=\"selected\">Ntfy</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<option value=\"ntfy\">Ntfy</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.NotificationService != nil && data.NotificationService.Type == "gotify" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<option value=\"gotify\" selected=\"selected\">Gotify</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<option value=\"gotify\">Gotify</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.NotificationService != nil && data.NotificationService.Type == "pushover" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<option value=\"pushover\" selected=\"selected\">Pushover</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<option value=\"pushover\">Pushover</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<option value=\"email\" disabled>Email (Coming Soon)</option></select></div><div class=\"mb-6 hidden common-fields\"><label for=\"notification_name\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Name</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.Name != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<input type=\"text\" id=\"notification_name\" name=\"name\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"My Notification Service\" required value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 83, Col: 414}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<input type=\"text\" id=\"notification_name\" name=\"name\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"My Notification Service\" required value=\"\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div><div class=\"mb-6 hidden common-fields\"><label for=\"notification_description\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Description</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.Description != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<textarea id=\"notification_description\" name=\"description\" rows=\"3\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Description for this notification service\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.NotificationService.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 91, Col: 438}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</textarea>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<textarea id=\"notification_description\" name=\"description\" rows=\"3\" class=\"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" placeholder=\"Description for this notification service\"></textarea>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</div><!-- Dynamic fields based on notification type -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.EmailFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.WebhookFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.PushbulletFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.NtfyFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.GotifyFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = fields.PushoverFields(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div class=\"flex items-start mb-6 hidden common-fields\"><div class=\"flex items-center h-5\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.NotificationService.IsEnabled != false {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<input id=\"is_enabled\" name=\"is_enabled\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(utils.BoolToString(data.NotificationService.IsEnabled))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/form/form.templ`, Line: 108, Col: 310}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " <input id=\"is_enabled\" name=\"is_enabled\" type=\"checkbox\" class=\"w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800\" checked=\"true\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div><div class=\"ml-3 text-sm\"><label for=\"is_enabled\" class=\"font-medium text-gray-900 dark:text-white\">Enable this notification service</label></div></div><div class=\"hidden common-fields\"><button type=\"submit\" class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.IsNew {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "Add Service")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "Save Changes")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</button></div></form></div><!-- Help Notice --><div class=\"mt-8 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700\"><div class=\"flex\"><div class=\"flex-shrink-0\"><i class=\"fas fa-info-circle text-blue-400 dark:text-blue-400\"></i></div><div class=\"ml-3\"><p class=\"text-sm text-blue-700 dark:text-blue-400\">Configure your notification service to receive alerts for job events. Different notification types have different configuration options.</p></div></div></div></div></div><!-- Theme-specific background handled by Tailwind classes -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = components.LayoutWithContext(utils.GetNotificationFormTitle(data.IsNew), ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
@@ -0,0 +1,28 @@
package utils
// Contains checks if a string is in a slice.
func Contains(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}
// BoolToString converts bool to string for HTML attributes.
// Returns "true" for true, "" for false (useful for checked attribute).
func BoolToString(b bool) string {
if b {
return "true"
}
return ""
}
// GetNotificationFormTitle returns the appropriate title for the form.
func GetNotificationFormTitle(isNew bool) string {
if isNew {
return "Add Notification Service"
}
return "Edit Notification Service"
}
+207
View File
@@ -0,0 +1,207 @@
package list
import (
"context"
"fmt"
"github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/notifications/dialog"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
// List renders the notification services list page.
templ List(ctx context.Context, data types.SettingsNotificationsData) {
@components.LayoutWithContext("Notification Services", ctx) {
<!-- Status and Error Messages (Handled by shared toast component in layout) -->
<div id="notifications-container" class="notifications-page bg-gray-50 dark:bg-gray-900 min-h-screen">
<div class="pb-8 w-full">
<!-- Success Message (hidden, used for HTMX responses/toast trigger) -->
if data.SuccessMessage != "" {
<div class="hidden success-message">{ data.SuccessMessage }</div>
}
<!-- Error Message (hidden, used for HTMX responses/toast trigger) -->
if data.ErrorMessage != "" {
<div class="hidden error-message">{ data.ErrorMessage }</div>
}
<div class="mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white flex items-center">
<i class="fas fa-bell w-6 h-6 mr-2 text-blue-500 dark:text-blue-400"></i>
Notification Services
</h1>
<a href="/admin/settings/notifications/new" class="flex items-center justify-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
<i class="fas fa-plus w-4 h-4 mr-2"></i>
Add Notification Service
</a>
</div>
<!-- List of Notification Services -->
if len(data.NotificationServices) == 0 {
<div class="text-center py-8 bg-white dark:bg-gray-800 shadow-md rounded-lg">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 mb-4">
<i class="fas fa-bell text-2xl text-blue-600 dark:text-blue-400"></i>
</div>
<h3 class="mb-2 text-lg font-semibold text-gray-900 dark:text-white">No notification services configured</h3>
<p class="text-gray-500 dark:text-gray-400 mb-4">Add a notification service to receive alerts for job events.</p>
<a href="/admin/settings/notifications/new" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
<i class="fas fa-plus w-4 h-4 mr-2"></i>
Add First Notification Service
</a>
</div>
} else {
<div class="bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 dark:bg-gray-800 overflow-hidden">
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
for _, service := range data.NotificationServices {
<li>
<div class="block hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<div class="flex items-center">
if service.Type == "email" {
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 dark:bg-blue-900 dark:text-blue-400 mr-3">
<i class="fas fa-envelope"></i>
</div>
} else if service.Type == "webhook" {
<div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center text-green-600 dark:bg-green-900 dark:text-green-400 mr-3">
<i class="fas fa-code"></i>
</div>
} else { // Default icon
<div class="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 dark:bg-gray-700 dark:text-gray-400 mr-3">
<i class="fas fa-bell"></i>
</div>
}
<div>
<p class="text-sm font-medium text-blue-600 dark:text-blue-400 truncate">
{ service.Name }
</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
{ service.Description }
</p>
</div>
</div>
<div class="ml-2 flex-shrink-0 flex space-x-2">
<a
href={ templ.SafeURL(fmt.Sprintf("/admin/settings/notifications/%d/edit", service.ID)) }
class="text-gray-500 bg-white focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 rounded-lg text-sm p-2 mr-1 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
>
<i class="fas fa-edit"></i>
</a>
<!-- Add notification delete dialog -->
@dialog.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,
)
<button
type="button"
onclick={ templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-notification-dialog-%d')", service.ID)} }
class="text-red-500 bg-white focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 rounded-lg text-sm p-2 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
>
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
<div class="mt-3 sm:flex sm:justify-between">
<div class="sm:flex flex-col md:flex-row gap-2 md:gap-6">
<div class="flex items-center">
<span
class={ "px-2 py-1 text-xs font-medium rounded-full",
templ.KV("bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300", service.IsEnabled),
templ.KV("bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300", !service.IsEnabled) }
>
if service.IsEnabled {
Active
} else {
Disabled
}
</span>
<span class="ml-2 px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300 rounded-full">
{ service.Type }
</span>
if len(service.EventTriggers) > 0 && service.Type == "webhook" {
<span class="ml-2 px-2 py-1 text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300 rounded-full">
{ fmt.Sprintf("%d triggers", len(service.EventTriggers)) }
</span>
}
if service.SuccessCount > 0 || service.FailureCount > 0 {
<span class="ml-2 px-2 py-1 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300 rounded-full">
{ fmt.Sprintf("%d/%d", service.SuccessCount, service.SuccessCount + service.FailureCount) }
</span>
}
</div>
</div>
if service.Type == "webhook" {
<div class="mt-2 md:mt-0 flex items-center space-x-4">
<div class="text-xs">
<span class="text-gray-500 dark:text-gray-400">Events:</span>
<span class="ml-1 text-gray-900 dark:text-gray-300">
if len(service.EventTriggers) == 0 {
None
} else {
for i, trigger := range service.EventTriggers {
if i > 0 {
<span>, </span>
}
{ trigger }
}
}
</span>
</div>
<div class="text-xs">
<span class="text-gray-500 dark:text-gray-400">Retry:</span>
<span class="ml-1 text-gray-900 dark:text-gray-300">
if service.RetryPolicy == "" {
Default
} else {
{ service.RetryPolicy }
}
</span>
</div>
</div>
} else {
<div class="mt-2 md:mt-0 flex items-center text-sm text-gray-500 dark:text-gray-400">
<i class="far fa-clock w-4 h-4 mr-1.5 text-gray-400 dark:text-gray-500"></i>
<p>
Last sent:
if service.SuccessCount > 0 {
"Recently"
} else {
"Never"
}
</p>
</div>
}
</div>
</div>
</div>
</li>
}
</ul>
</div>
}
<!-- Help Notice Placeholder -->
<div class="mt-8 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-info-circle text-blue-400 dark:text-blue-400"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700 dark:text-blue-400">
Notification services allow the system to send alerts for job events such as completion, errors, or when jobs start.
</p>
</div>
</div>
</div>
</div>
</div>
@dialog.DialogScripts()
}
// Script call removed for now
}
+221
View File
@@ -0,0 +1,221 @@
package list
// ListScripts contains JavaScript specific to the notification list page.
templ ListScripts() {
<script>
// Notification system (showToast function is now in shared/toast/toast_js.templ)
// Track all HTMX events for debugging
document.addEventListener('htmx:beforeRequest', function(event) {
// 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) {
// 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");
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}`);
// Ensure showToast is globally available
if (typeof showToast === 'function') {
showToast(`Notification service "${serviceName}" deleted successfully`, 'success');
} else {
console.error("showToast function not found!");
}
// 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;
}
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 specificErrorMsg = `Failed to delete notification service "${serviceName}"`;
if (event.detail.xhr && event.detail.xhr.responseText) {
// Try to provide a more specific error from the response
specificErrorMsg = `Error deleting "${serviceName}": ${event.detail.xhr.responseText}`;
}
console.log(`Showing error notification: ${specificErrorMsg}`);
if (typeof showToast === 'function') {
showToast(specificErrorMsg, 'error');
} else {
console.error("showToast function not found!");
}
// Clear flags
window.currentlyDeletingService = false;
window.isServiceDeleteRequest = false;
window.lastDeletedService = null;
} else {
// General error toast
if (typeof showToast === 'function') {
showToast(errorMsg, 'error');
} else {
console.error("showToast function not found!");
}
}
});
// Handle modal hide buttons (This might be better placed globally or in layout if modals are used elsewhere)
document.addEventListener('DOMContentLoaded', function() {
// This listener handles closing modals via data-modal-hide attribute
// It might conflict or be redundant if Flowbite's JS handles this already.
// Consider removing if Flowbite is initialized globally.
const hideButtons = document.querySelectorAll('[data-modal-hide]');
hideButtons.forEach(button => {
button.addEventListener('click', function() {
const modalId = this.getAttribute('data-modal-hide');
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
const backdrop = document.getElementById(modalId + "-backdrop");
if (backdrop) {
backdrop.classList.add("hidden");
}
});
});
// Show any success or error messages passed via data struct as toasts
const successDiv = document.querySelector('.success-message');
if (successDiv) {
const successMsg = successDiv.textContent.trim();
if (successMsg && typeof showToast === 'function') {
showToast(successMsg, 'success');
} else if (successMsg) {
console.error("showToast function not found, cannot display success message:", successMsg);
}
}
const errorDiv = document.querySelector('.error-message');
if (errorDiv) {
const errorMsg = errorDiv.textContent.trim();
if (errorMsg && typeof showToast === 'function') {
showToast(errorMsg, 'error');
} else if (errorMsg) {
console.error("showToast function not found, cannot display error message:", errorMsg);
}
}
});
// Script for handling the service deletion trigger
function triggerServiceDelete(dialogId, serviceID, serviceName) {
// Hide the dialog first
closeModal(dialogId); // Reuse closeModal logic
// 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;
}
// Script for closing the modal
function closeModal(id) {
const dialog = document.getElementById(id);
if (dialog) {
dialog.classList.add("hidden");
dialog.classList.remove("flex");
}
const backdrop = document.getElementById(id + "-backdrop");
if (backdrop) {
// Instead of removing, hide it to potentially reuse
backdrop.classList.add("hidden");
}
}
// Script for showing the modal
function showModal(id) {
const dialog = document.getElementById(id);
if (dialog) {
dialog.classList.remove("hidden");
dialog.classList.add("flex"); // Use flex to center content
}
const backdrop = document.getElementById(id + "-backdrop");
if (backdrop) {
backdrop.classList.remove("hidden");
}
}
</script>
}
File diff suppressed because one or more lines are too long
+403
View File
@@ -0,0 +1,403 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package list
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"context"
"fmt"
"github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/notifications/dialog"
"github.com/starfleetcptn/gomft/components/notifications/types"
)
// List renders the notification services list page.
func List(ctx context.Context, data types.SettingsNotificationsData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!-- Status and Error Messages (Handled by shared toast component in layout) --> <div id=\"notifications-container\" class=\"notifications-page bg-gray-50 dark:bg-gray-900 min-h-screen\"><div class=\"pb-8 w-full\"><!-- Success Message (hidden, used for HTMX responses/toast trigger) -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.SuccessMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"hidden success-message\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.SuccessMessage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 20, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<!-- Error Message (hidden, used for HTMX responses/toast trigger) -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.ErrorMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"hidden error-message\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.ErrorMessage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 24, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"mb-6 flex flex-col md:flex-row md:items-center md:justify-between gap-4\"><h1 class=\"text-2xl font-bold text-gray-900 dark:text-white flex items-center\"><i class=\"fas fa-bell w-6 h-6 mr-2 text-blue-500 dark:text-blue-400\"></i> Notification Services</h1><a href=\"/admin/settings/notifications/new\" class=\"flex items-center justify-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800\"><i class=\"fas fa-plus w-4 h-4 mr-2\"></i> Add Notification Service</a></div><!-- List of Notification Services -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(data.NotificationServices) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div class=\"text-center py-8 bg-white dark:bg-gray-800 shadow-md rounded-lg\"><div class=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900 mb-4\"><i class=\"fas fa-bell text-2xl text-blue-600 dark:text-blue-400\"></i></div><h3 class=\"mb-2 text-lg font-semibold text-gray-900 dark:text-white\">No notification services configured</h3><p class=\"text-gray-500 dark:text-gray-400 mb-4\">Add a notification service to receive alerts for job events.</p><a href=\"/admin/settings/notifications/new\" class=\"inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800\"><i class=\"fas fa-plus w-4 h-4 mr-2\"></i> Add First Notification Service</a></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 dark:bg-gray-800 overflow-hidden\"><ul class=\"divide-y divide-gray-200 dark:divide-gray-700\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, service := range data.NotificationServices {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<li><div class=\"block hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors\"><div class=\"px-4 py-4 sm:px-6\"><div class=\"flex items-center justify-between\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if service.Type == "email" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 dark:bg-blue-900 dark:text-blue-400 mr-3\"><i class=\"fas fa-envelope\"></i></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if service.Type == "webhook" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"w-10 h-10 rounded-full bg-green-100 flex items-center justify-center text-green-600 dark:bg-green-900 dark:text-green-400 mr-3\"><i class=\"fas fa-code\"></i></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " <div class=\"w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center text-gray-600 dark:bg-gray-700 dark:text-gray-400 mr-3\"><i class=\"fas fa-bell\"></i></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div><p class=\"text-sm font-medium text-blue-600 dark:text-blue-400 truncate\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(service.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 75, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</p><p class=\"text-sm text-gray-500 dark:text-gray-400 mt-1\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(service.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 78, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</p></div></div><div class=\"ml-2 flex-shrink-0 flex space-x-2\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL(fmt.Sprintf("/admin/settings/notifications/%d/edit", service.ID))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"text-gray-500 bg-white focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 rounded-lg text-sm p-2 mr-1 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700\"><i class=\"fas fa-edit\"></i></a><!-- Add notification delete dialog -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dialog.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,
).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-notification-dialog-%d')", service.ID)})
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<button type=\"button\" onclick=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.ComponentScript = templ.ComponentScript{Call: fmt.Sprintf("showModal('delete-notification-dialog-%d')", service.ID)}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8.Call)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" class=\"text-red-500 bg-white focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 rounded-lg text-sm p-2 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700\"><i class=\"fas fa-trash-alt\"></i></button></div></div><div class=\"mt-3 sm:flex sm:justify-between\"><div class=\"sm:flex flex-col md:flex-row gap-2 md:gap-6\"><div class=\"flex items-center\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 = []any{"px-2 py-1 text-xs font-medium rounded-full",
templ.KV("bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300", service.IsEnabled),
templ.KV("bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300", !service.IsEnabled)}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<span class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if service.IsEnabled {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "Active")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "Disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</span> <span class=\"ml-2 px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300 rounded-full\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(service.Type)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 124, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(service.EventTriggers) > 0 && service.Type == "webhook" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span class=\"ml-2 px-2 py-1 text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300 rounded-full\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d triggers", len(service.EventTriggers)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 128, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</span> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if service.SuccessCount > 0 || service.FailureCount > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<span class=\"ml-2 px-2 py-1 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300 rounded-full\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d/%d", service.SuccessCount, service.SuccessCount+service.FailureCount))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 133, Col: 105}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if service.Type == "webhook" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<div class=\"mt-2 md:mt-0 flex items-center space-x-4\"><div class=\"text-xs\"><span class=\"text-gray-500 dark:text-gray-400\">Events:</span> <span class=\"ml-1 text-gray-900 dark:text-gray-300\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(service.EventTriggers) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "None")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
for i, trigger := range service.EventTriggers {
if i > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<span>, </span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(trigger)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 150, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</span></div><div class=\"text-xs\"><span class=\"text-gray-500 dark:text-gray-400\">Retry:</span> <span class=\"ml-1 text-gray-900 dark:text-gray-300\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if service.RetryPolicy == "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "Default")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(service.RetryPolicy)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/notifications/list/list.templ`, Line: 161, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</span></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div class=\"mt-2 md:mt-0 flex items-center text-sm text-gray-500 dark:text-gray-400\"><i class=\"far fa-clock w-4 h-4 mr-1.5 text-gray-400 dark:text-gray-500\"></i><p>Last sent: ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if service.SuccessCount > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\"Recently\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\"Never\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</div></div></div></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</ul></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<!-- Help Notice Placeholder --><div class=\"mt-8 p-4 bg-gray-50 border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700\"><div class=\"flex\"><div class=\"flex-shrink-0\"><i class=\"fas fa-info-circle text-blue-400 dark:text-blue-400\"></i></div><div class=\"ml-3\"><p class=\"text-sm text-blue-700 dark:text-blue-400\">Notification services allow the system to send alerts for job events such as completion, errors, or when jobs start.</p></div></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = dialog.DialogScripts().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = components.LayoutWithContext("Notification Services", ctx).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
+69
View File
@@ -0,0 +1,69 @@
package types
// SettingsNotificationsData defines the data needed for the notifications list page
type SettingsNotificationsData struct {
NotificationServices []NotificationServiceData
SuccessMessage string
ErrorMessage string
}
// NotificationServiceData defines the data for a single service in the list
type NotificationServiceData struct {
ID uint
Name string
Type string
IsEnabled bool
Config map[string]string // Keep for now, might refine later
Description string
EventTriggers []string
PayloadTemplate string
SecretKey string
RetryPolicy string
SuccessCount int
FailureCount int
}
// NotificationFormData defines the data needed for the notification add/edit form
// TODO: This will be moved from notification_form.templ later
type NotificationFormData struct {
NotificationService *struct {
ID uint
Name string
Description string
Type string
IsEnabled bool
EventTriggers []string
RetryPolicy string
WebhookURL string
Method string
Headers string
PayloadTemplate string
SecretKey string
PushbulletAPIKey string
PushbulletDeviceID string
PushbulletTitleTemplate string
PushbulletBodyTemplate string
NtfyServer string
NtfyTopic string
NtfyPriority string
NtfyUsername string
NtfyPassword string
NtfyTitleTemplate string
NtfyMessageTemplate string
GotifyURL string
GotifyToken string
GotifyPriority string
GotifyTitleTemplate string
GotifyMessageTemplate string
PushoverAPIToken string
PushoverUserKey string
PushoverDevice string
PushoverPriority string
PushoverSound string
PushoverTitleTemplate string
PushoverMessageTemplate string
}
IsNew bool
SuccessMessage string
ErrorMessage string
}
+5
View File
@@ -0,0 +1,5 @@
package toast
templ Container() {
<div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col gap-2"></div>
}
+80
View File
@@ -0,0 +1,80 @@
package toast
templ ShowToastJS() {
<script>
// Notification system
function showToast(message, type) {
const toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
console.error("Toast container not found!");
return;
}
// Create toast element
const toast = document.createElement('div');
toast.id = 'toast-' + type + '-' + Date.now();
toast.className = 'flex items-center w-full max-w-xs p-4 mb-4 rounded-lg shadow text-gray-500 bg-white dark:text-gray-400 dark:bg-gray-800 transform translate-y-16 opacity-0 transition-all duration-300 ease-out';
toast.role = 'alert';
// Set toast content based on type
let iconClass, bgColorClass, textColorClass;
if (type === 'success') {
iconClass = 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200';
bgColorClass = 'text-green-500 dark:text-green-200';
textColorClass = 'text-green-500 dark:text-green-200';
} else if (type === 'error') {
iconClass = 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200';
bgColorClass = 'text-red-500 dark:text-red-200';
textColorClass = 'text-red-500 dark:text-red-200';
} else { // Default to info
iconClass = 'text-blue-500 bg-blue-100 dark:bg-blue-800 dark:text-blue-200';
bgColorClass = 'text-blue-500 dark:text-blue-200';
textColorClass = 'text-blue-500 dark:text-blue-200';
}
// Set inner HTML with appropriate icon and message
toast.innerHTML = `
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 rounded-lg ${iconClass}">
${type === 'success'
? '<i class="fas fa-check"></i>'
: type === 'error'
? '<i class="fas fa-exclamation-circle"></i>'
: '<i class="fas fa-info-circle"></i>'}
</div>
<div class="ml-3 text-sm font-normal">${message}</div>
<button type="button" class="ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" data-dismiss-target="#${toast.id}" aria-label="Close">
<span class="sr-only">Close</span>
<i class="fas fa-times"></i>
</button>
`;
// Add toast to container
toastContainer.appendChild(toast);
// Trigger animation after a small delay
setTimeout(() => {
toast.classList.remove('translate-y-16', 'opacity-0');
toast.classList.add('translate-y-0', 'opacity-100');
}, 10);
// Add event listener to close button
const closeButton = toast.querySelector('button[data-dismiss-target]');
closeButton.addEventListener('click', function() {
// Animate out before removing
toast.classList.add('opacity-0', 'translate-y-4');
setTimeout(() => {
toast.remove();
}, 300);
});
// Auto-remove toast after 5 seconds
setTimeout(() => {
toast.classList.add('opacity-0', 'translate-y-4');
setTimeout(() => {
toast.remove();
}, 300);
}, 5000);
}
</script>
}
+40
View File
@@ -0,0 +1,40 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package toast
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func ShowToastJS() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script>\n\t\t// Notification system\n\t\tfunction showToast(message, type) {\n\t\t\tconst toastContainer = document.getElementById('toast-container');\n\t\t\tif (!toastContainer) {\n\t\t\t\tconsole.error(\"Toast container not found!\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Create toast element\n\t\t\tconst toast = document.createElement('div');\n\t\t\ttoast.id = 'toast-' + type + '-' + Date.now();\n\t\t\ttoast.className = 'flex items-center w-full max-w-xs p-4 mb-4 rounded-lg shadow text-gray-500 bg-white dark:text-gray-400 dark:bg-gray-800 transform translate-y-16 opacity-0 transition-all duration-300 ease-out';\n\t\t\ttoast.role = 'alert';\n\n\t\t\t// Set toast content based on type\n\t\t\tlet iconClass, bgColorClass, textColorClass;\n\n\t\t\tif (type === 'success') {\n\t\t\t\ticonClass = 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200';\n\t\t\t\tbgColorClass = 'text-green-500 dark:text-green-200';\n\t\t\t\ttextColorClass = 'text-green-500 dark:text-green-200';\n\t\t\t} else if (type === 'error') {\n\t\t\t\ticonClass = 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200';\n\t\t\t\tbgColorClass = 'text-red-500 dark:text-red-200';\n\t\t\t\ttextColorClass = 'text-red-500 dark:text-red-200';\n\t\t\t} else { // Default to info\n\t\t\t\ticonClass = 'text-blue-500 bg-blue-100 dark:bg-blue-800 dark:text-blue-200';\n\t\t\t\tbgColorClass = 'text-blue-500 dark:text-blue-200';\n\t\t\t\ttextColorClass = 'text-blue-500 dark:text-blue-200';\n\t\t\t}\n\n\t\t\t// Set inner HTML with appropriate icon and message\n\t\t\ttoast.innerHTML = `\n\t\t\t\t<div class=\"inline-flex items-center justify-center flex-shrink-0 w-8 h-8 rounded-lg ${iconClass}\">\n\t\t\t\t\t${type === 'success'\n\t\t\t\t\t\t? '<i class=\"fas fa-check\"></i>'\n\t\t\t\t\t\t: type === 'error'\n\t\t\t\t\t\t? '<i class=\"fas fa-exclamation-circle\"></i>'\n\t\t\t\t\t\t: '<i class=\"fas fa-info-circle\"></i>'}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"ml-3 text-sm font-normal\">${message}</div>\n\t\t\t\t<button type=\"button\" class=\"ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700\" data-dismiss-target=\"#${toast.id}\" aria-label=\"Close\">\n\t\t\t\t\t<span class=\"sr-only\">Close</span>\n\t\t\t\t\t<i class=\"fas fa-times\"></i>\n\t\t\t\t</button>\n\t\t\t`;\n\n\t\t\t// Add toast to container\n\t\t\ttoastContainer.appendChild(toast);\n\n\t\t\t// Trigger animation after a small delay\n\t\t\tsetTimeout(() => {\n\t\t\t\ttoast.classList.remove('translate-y-16', 'opacity-0');\n\t\t\t\ttoast.classList.add('translate-y-0', 'opacity-100');\n\t\t\t}, 10);\n\n\t\t\t// Add event listener to close button\n\t\t\tconst closeButton = toast.querySelector('button[data-dismiss-target]');\n\t\t\tcloseButton.addEventListener('click', function() {\n\t\t\t\t// Animate out before removing\n\t\t\t\ttoast.classList.add('opacity-0', 'translate-y-4');\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\ttoast.remove();\n\t\t\t\t}, 300);\n\t\t\t});\n\n\t\t\t// Auto-remove toast after 5 seconds\n\t\t\tsetTimeout(() => {\n\t\t\t\ttoast.classList.add('opacity-0', 'translate-y-4');\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\ttoast.remove();\n\t\t\t\t}, 300);\n\t\t\t}, 5000);\n\t\t}\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
+40
View File
@@ -0,0 +1,40 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package toast
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Container() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"toast-container\" class=\"fixed top-5 right-5 z-50 flex flex-col gap-2\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate
+139 -25
View File
@@ -7,6 +7,10 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/starfleetcptn/gomft/components" "github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/file_metadata"
"github.com/starfleetcptn/gomft/components/file_metadata/details"
"github.com/starfleetcptn/gomft/components/file_metadata/list"
"github.com/starfleetcptn/gomft/components/file_metadata/search"
"github.com/starfleetcptn/gomft/internal/db" "github.com/starfleetcptn/gomft/internal/db"
) )
@@ -37,6 +41,26 @@ func (h *FileMetadataHandler) ListFileMetadata(c *gin.Context) {
status := c.Query("status") status := c.Query("status")
jobIDStr := c.Query("job_id") jobIDStr := c.Query("job_id")
fileName := c.Query("filename") fileName := c.Query("filename")
sortBy := c.DefaultQuery("sort_by", "processed_time")
sortDir := c.DefaultQuery("sort_dir", "desc")
// Validate sort parameters
allowedSortColumns := map[string]string{
"id": "file_metadata.id",
"filename": "file_metadata.file_name",
"size": "file_metadata.file_size",
"processed_time": "file_metadata.processed_time",
"status": "file_metadata.status",
}
dbColumn, ok := allowedSortColumns[sortBy]
if !ok {
sortBy = "processed_time" // Default sort column
dbColumn = allowedSortColumns[sortBy]
}
if sortDir != "asc" && sortDir != "desc" {
sortDir = "desc" // Default sort direction
}
orderClause := fmt.Sprintf("%s %s", dbColumn, sortDir)
// Base query // Base query
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id") query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
@@ -66,7 +90,7 @@ func (h *FileMetadataHandler) ListFileMetadata(c *gin.Context) {
var fileMetadata []db.FileMetadata var fileMetadata []db.FileMetadata
offset := (page - 1) * limit offset := (page - 1) * limit
err := query.Preload("Job").Preload("Job.Config"). err := query.Preload("Job").Preload("Job.Config").
Order("file_metadata.processed_time DESC"). Order(orderClause). // Use dynamic order clause
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
Find(&fileMetadata).Error Find(&fileMetadata).Error
@@ -79,17 +103,19 @@ func (h *FileMetadataHandler) ListFileMetadata(c *gin.Context) {
ctx := components.CreateTemplateContext(c) ctx := components.CreateTemplateContext(c)
// Render the file metadata list template // Render the file metadata list template
data := components.FileMetadataListData{ data := file_metadata.FileMetadataListData{
Files: fileMetadata, Files: fileMetadata,
TotalCount: totalCount, TotalCount: totalCount,
Page: page, Page: page,
Limit: limit, Limit: limit,
TotalPages: int(totalCount) / limit, TotalPages: int(totalCount) / limit,
Filter: components.FileMetadataFilter{ Filter: file_metadata.FileMetadataFilter{
Status: status, Status: status,
JobID: jobIDStr, JobID: jobIDStr,
FileName: fileName, FileName: fileName,
}, },
SortBy: sortBy, // Pass sorting info
SortDir: sortDir, // Pass sorting info
} }
// If total count is not exactly divisible by limit, add one more page // If total count is not exactly divisible by limit, add one more page
@@ -104,10 +130,10 @@ func (h *FileMetadataHandler) ListFileMetadata(c *gin.Context) {
if isHtmxRequest { if isHtmxRequest {
// For HTMX requests, render just the partial template // For HTMX requests, render just the partial template
components.FileMetadataListPartial(data).Render(ctx, c.Writer) list.FileMetadataListPartial(ctx, data, "/files/partial", "#file-list-container").Render(ctx, c.Writer)
} else { } else {
// For full page requests, render the complete template // For full page requests, render the complete template
components.FileMetadataList(ctx, data).Render(ctx, c.Writer) list.FileMetadataList(ctx, data).Render(ctx, c.Writer)
} }
} }
@@ -142,12 +168,12 @@ func (h *FileMetadataHandler) GetFileMetadataDetails(c *gin.Context) {
ctx := components.CreateTemplateContext(c) ctx := components.CreateTemplateContext(c)
// Render the file metadata details template // Render the file metadata details template
data := components.FileMetadataDetailsData{ data := file_metadata.FileMetadataDetailsData{
File: fileMetadata, File: fileMetadata,
} }
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")
components.FileMetadataDetails(ctx, data).Render(ctx, c.Writer) details.FileMetadataDetails(ctx, data).Render(ctx, c.Writer)
} }
// GetFileMetadataForJob displays file metadata for a specific job // GetFileMetadataForJob displays file metadata for a specific job
@@ -187,6 +213,26 @@ func (h *FileMetadataHandler) GetFileMetadataForJob(c *gin.Context) {
status := c.Query("status") status := c.Query("status")
fileName := c.Query("filename") fileName := c.Query("filename")
sortBy := c.DefaultQuery("sort_by", "processed_time")
sortDir := c.DefaultQuery("sort_dir", "desc")
// Validate sort parameters
allowedSortColumns := map[string]string{
"id": "file_metadata.id",
"filename": "file_metadata.file_name",
"size": "file_metadata.file_size",
"processed_time": "file_metadata.processed_time",
"status": "file_metadata.status",
}
dbColumn, ok := allowedSortColumns[sortBy]
if !ok {
sortBy = "processed_time" // Default sort column
dbColumn = allowedSortColumns[sortBy]
}
if sortDir != "asc" && sortDir != "desc" {
sortDir = "desc" // Default sort direction
}
orderClause := fmt.Sprintf("%s %s", dbColumn, sortDir)
// Base query // Base query
query := h.DB.DB.Model(&db.FileMetadata{}).Where("job_id = ?", jobID) query := h.DB.DB.Model(&db.FileMetadata{}).Where("job_id = ?", jobID)
@@ -208,7 +254,7 @@ func (h *FileMetadataHandler) GetFileMetadataForJob(c *gin.Context) {
var fileMetadata []db.FileMetadata var fileMetadata []db.FileMetadata
offset := (page - 1) * limit offset := (page - 1) * limit
err = query.Preload("Job").Preload("Job.Config"). err = query.Preload("Job").Preload("Job.Config").
Order("processed_time DESC"). Order(orderClause). // Use dynamic order clause
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
Find(&fileMetadata).Error Find(&fileMetadata).Error
@@ -221,18 +267,20 @@ func (h *FileMetadataHandler) GetFileMetadataForJob(c *gin.Context) {
ctx := components.CreateTemplateContext(c) ctx := components.CreateTemplateContext(c)
// Render the file metadata list template // Render the file metadata list template
data := components.FileMetadataListData{ data := file_metadata.FileMetadataListData{
Files: fileMetadata, Files: fileMetadata,
TotalCount: totalCount, TotalCount: totalCount,
Page: page, Page: page,
Limit: limit, Limit: limit,
TotalPages: int(totalCount) / limit, TotalPages: int(totalCount) / limit,
Job: &job, Job: &job,
Filter: components.FileMetadataFilter{ Filter: file_metadata.FileMetadataFilter{
Status: status, Status: status,
JobID: strconv.FormatUint(uint64(job.ID), 10), JobID: strconv.FormatUint(uint64(job.ID), 10),
FileName: fileName, FileName: fileName,
}, },
SortBy: sortBy, // Pass sorting info
SortDir: sortDir, // Pass sorting info
} }
// If total count is not exactly divisible by limit, add one more page // If total count is not exactly divisible by limit, add one more page
@@ -247,10 +295,10 @@ func (h *FileMetadataHandler) GetFileMetadataForJob(c *gin.Context) {
if isHtmxRequest { if isHtmxRequest {
// For HTMX requests, render just the partial template // For HTMX requests, render just the partial template
components.FileMetadataListPartial(data).Render(ctx, c.Writer) list.FileMetadataListPartial(ctx, data, "/files/partial", "#file-list-container").Render(ctx, c.Writer)
} else { } else {
// For full page requests, render the complete template // For full page requests, render the complete template
components.FileMetadataList(ctx, data).Render(ctx, c.Writer) list.FileMetadataList(ctx, data).Render(ctx, c.Writer)
} }
} }
@@ -275,6 +323,26 @@ func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
hash := c.Query("hash") hash := c.Query("hash")
startDate := c.Query("start_date") startDate := c.Query("start_date")
endDate := c.Query("end_date") endDate := c.Query("end_date")
sortBy := c.DefaultQuery("sort_by", "processed_time")
sortDir := c.DefaultQuery("sort_dir", "desc")
// Validate sort parameters
allowedSortColumns := map[string]string{
"id": "file_metadata.id",
"filename": "file_metadata.file_name",
"size": "file_metadata.file_size",
"processed_time": "file_metadata.processed_time",
"status": "file_metadata.status",
}
dbColumn, ok := allowedSortColumns[sortBy]
if !ok {
sortBy = "processed_time" // Default sort column
dbColumn = allowedSortColumns[sortBy]
}
if sortDir != "asc" && sortDir != "desc" {
sortDir = "desc" // Default sort direction
}
orderClause := fmt.Sprintf("%s %s", dbColumn, sortDir)
// Base query // Base query
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id") query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
@@ -316,7 +384,7 @@ func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
var fileMetadata []db.FileMetadata var fileMetadata []db.FileMetadata
offset := (page - 1) * limit offset := (page - 1) * limit
err := query.Preload("Job").Preload("Job.Config"). err := query.Preload("Job").Preload("Job.Config").
Order("file_metadata.processed_time DESC"). Order(orderClause). // Use dynamic order clause
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
Find(&fileMetadata).Error Find(&fileMetadata).Error
@@ -326,13 +394,13 @@ func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
} }
// Render the file metadata search template // Render the file metadata search template
data := components.FileMetadataSearchData{ data := file_metadata.FileMetadataSearchData{
Files: fileMetadata, Files: fileMetadata,
TotalCount: totalCount, TotalCount: totalCount,
Page: page, Page: page,
Limit: limit, Limit: limit,
TotalPages: int(totalCount) / limit, TotalPages: int(totalCount) / limit,
Filter: components.FileMetadataFilter{ Filter: file_metadata.FileMetadataFilter{
Status: status, Status: status,
JobID: jobIDStr, JobID: jobIDStr,
FileName: fileName, FileName: fileName,
@@ -340,6 +408,8 @@ func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
StartDate: startDate, StartDate: startDate,
EndDate: endDate, EndDate: endDate,
}, },
SortBy: sortBy, // Pass sorting info
SortDir: sortDir, // Pass sorting info
} }
// If total count is not exactly divisible by limit, add one more page // If total count is not exactly divisible by limit, add one more page
@@ -357,10 +427,10 @@ func (h *FileMetadataHandler) SearchFileMetadata(c *gin.Context) {
if isHtmxRequest { if isHtmxRequest {
// For HTMX requests, render just the partial template // For HTMX requests, render just the partial template
components.FileMetadataSearchContent(data).Render(ctx, c.Writer) search.FileMetadataSearchContent(ctx, data).Render(ctx, c.Writer)
} else { } else {
// For full page requests, render the complete template // For full page requests, render the complete template
components.FileMetadataSearch(ctx, data).Render(ctx, c.Writer) search.FileMetadataSearch(ctx, data).Render(ctx, c.Writer)
} }
} }
@@ -444,6 +514,26 @@ func (h *FileMetadataHandler) HandleFileMetadataPartial(c *gin.Context) {
status := c.Query("status") status := c.Query("status")
jobIDStr := c.Query("job_id") jobIDStr := c.Query("job_id")
fileName := c.Query("filename") fileName := c.Query("filename")
sortBy := c.DefaultQuery("sort_by", "processed_time")
sortDir := c.DefaultQuery("sort_dir", "desc")
// Validate sort parameters
allowedSortColumns := map[string]string{
"id": "file_metadata.id",
"filename": "file_metadata.file_name",
"size": "file_metadata.file_size",
"processed_time": "file_metadata.processed_time",
"status": "file_metadata.status",
}
dbColumn, ok := allowedSortColumns[sortBy]
if !ok {
sortBy = "processed_time" // Default sort column
dbColumn = allowedSortColumns[sortBy]
}
if sortDir != "asc" && sortDir != "desc" {
sortDir = "desc" // Default sort direction
}
orderClause := fmt.Sprintf("%s %s", dbColumn, sortDir)
// Base query // Base query
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id") query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
@@ -473,7 +563,7 @@ func (h *FileMetadataHandler) HandleFileMetadataPartial(c *gin.Context) {
var fileMetadata []db.FileMetadata var fileMetadata []db.FileMetadata
offset := (page - 1) * limit offset := (page - 1) * limit
err := query.Preload("Job").Preload("Job.Config"). err := query.Preload("Job").Preload("Job.Config").
Order("file_metadata.processed_time DESC"). Order(orderClause). // Use dynamic order clause
Offset(offset).Limit(limit). Offset(offset).Limit(limit).
Find(&fileMetadata).Error Find(&fileMetadata).Error
@@ -496,18 +586,20 @@ func (h *FileMetadataHandler) HandleFileMetadataPartial(c *gin.Context) {
} }
// Render the file metadata list template // Render the file metadata list template
data := components.FileMetadataListData{ data := file_metadata.FileMetadataListData{
Files: fileMetadata, Files: fileMetadata,
TotalCount: totalCount, TotalCount: totalCount,
Page: page, Page: page,
Limit: limit, Limit: limit,
TotalPages: int(totalCount) / limit, TotalPages: int(totalCount) / limit,
Job: job, Job: job,
Filter: components.FileMetadataFilter{ Filter: file_metadata.FileMetadataFilter{
Status: status, Status: status,
JobID: jobIDStr, JobID: jobIDStr,
FileName: fileName, FileName: fileName,
}, },
SortBy: sortBy, // Pass sorting info
SortDir: sortDir, // Pass sorting info
} }
// If total count is not exactly divisible by limit, add one more page // If total count is not exactly divisible by limit, add one more page
@@ -516,7 +608,7 @@ func (h *FileMetadataHandler) HandleFileMetadataPartial(c *gin.Context) {
} }
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")
components.FileMetadataListPartial(data).Render(ctx, c.Writer) list.FileMetadataListPartial(ctx, data, "/files/partial", "#file-list-container").Render(ctx, c.Writer)
} }
// HandleFileMetadataSearchPartial handles partial updates for search results // HandleFileMetadataSearchPartial handles partial updates for search results
@@ -553,6 +645,26 @@ func (h *FileMetadataHandler) HandleFileMetadataSearchPartial(c *gin.Context) {
hash := c.Query("hash") hash := c.Query("hash")
startDate := c.Query("start_date") startDate := c.Query("start_date")
endDate := c.Query("end_date") endDate := c.Query("end_date")
sortBy := c.DefaultQuery("sort_by", "processed_time")
sortDir := c.DefaultQuery("sort_dir", "desc")
// Validate sort parameters
allowedSortColumns := map[string]string{
"id": "file_metadata.id",
"filename": "file_metadata.file_name",
"size": "file_metadata.file_size",
"processed_time": "file_metadata.processed_time",
"status": "file_metadata.status",
}
dbColumn, ok := allowedSortColumns[sortBy]
if !ok {
sortBy = "processed_time" // Default sort column
dbColumn = allowedSortColumns[sortBy]
}
if sortDir != "asc" && sortDir != "desc" {
sortDir = "desc" // Default sort direction
}
orderClause := fmt.Sprintf("%s %s", dbColumn, sortDir)
// Execute the search query // Execute the search query
query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id") query := h.DB.DB.Model(&db.FileMetadata{}).Joins("JOIN jobs ON file_metadata.job_id = jobs.id")
@@ -597,7 +709,7 @@ func (h *FileMetadataHandler) HandleFileMetadataSearchPartial(c *gin.Context) {
var files []db.FileMetadata var files []db.FileMetadata
if err := query. if err := query.
Preload("Job"). Preload("Job").
Order("file_metadata.processed_time DESC"). Order(orderClause). // Use dynamic order clause
Limit(limit). Limit(limit).
Offset((page - 1) * limit). Offset((page - 1) * limit).
Find(&files).Error; err != nil { Find(&files).Error; err != nil {
@@ -606,13 +718,13 @@ func (h *FileMetadataHandler) HandleFileMetadataSearchPartial(c *gin.Context) {
} }
// Build the template data // Build the template data
data := components.FileMetadataSearchData{ data := file_metadata.FileMetadataSearchData{
Files: files, Files: files,
TotalCount: totalCount, TotalCount: totalCount,
Page: page, Page: page,
Limit: limit, Limit: limit,
TotalPages: int(totalCount) / limit, TotalPages: int(totalCount) / limit,
Filter: components.FileMetadataFilter{ Filter: file_metadata.FileMetadataFilter{
Status: status, Status: status,
JobID: jobIDStr, JobID: jobIDStr,
FileName: fileName, FileName: fileName,
@@ -620,6 +732,8 @@ func (h *FileMetadataHandler) HandleFileMetadataSearchPartial(c *gin.Context) {
StartDate: startDate, StartDate: startDate,
EndDate: endDate, EndDate: endDate,
}, },
SortBy: sortBy, // Pass sorting info
SortDir: sortDir, // Pass sorting info
} }
if int(totalCount)%limit > 0 { if int(totalCount)%limit > 0 {
@@ -630,5 +744,5 @@ func (h *FileMetadataHandler) HandleFileMetadataSearchPartial(c *gin.Context) {
ctx := components.CreateTemplateContext(c) ctx := components.CreateTemplateContext(c)
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")
components.FileMetadataSearchContent(data).Render(ctx, c.Writer) search.FileMetadataSearchContent(ctx, data).Render(ctx, c.Writer)
} }
+19 -16
View File
@@ -17,6 +17,9 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/starfleetcptn/gomft/components" "github.com/starfleetcptn/gomft/components"
"github.com/starfleetcptn/gomft/components/notifications/form"
"github.com/starfleetcptn/gomft/components/notifications/list"
"github.com/starfleetcptn/gomft/components/notifications/types"
"github.com/starfleetcptn/gomft/internal/db" "github.com/starfleetcptn/gomft/internal/db"
) )
@@ -1133,9 +1136,9 @@ func (h *Handlers) HandleNotificationsPage(c *gin.Context) {
} }
// Convert to components.NotificationService // Convert to components.NotificationService
var componentServices []components.NotificationService var componentServices []types.NotificationServiceData // Use types.NotificationServiceData
for _, service := range notificationServices { for _, service := range notificationServices {
componentServices = append(componentServices, components.NotificationService{ componentServices = append(componentServices, types.NotificationServiceData{ // Use types.NotificationServiceData
ID: service.ID, ID: service.ID,
Name: service.Name, Name: service.Name,
Type: service.Type, Type: service.Type,
@@ -1151,12 +1154,12 @@ func (h *Handlers) HandleNotificationsPage(c *gin.Context) {
}) })
} }
data := components.SettingsNotificationsData{ data := types.SettingsNotificationsData{ // Use types.SettingsNotificationsData
NotificationServices: componentServices, NotificationServices: componentServices,
} }
ctx := h.CreateTemplateContext(c) ctx := h.CreateTemplateContext(c)
components.Notifications(ctx, data).Render(ctx, c.Writer) list.List(ctx, data).Render(ctx, c.Writer) // Use list.List
} }
// HandleNewNotificationPage handles GET /admin/settings/notifications/new // HandleNewNotificationPage handles GET /admin/settings/notifications/new
@@ -1167,7 +1170,7 @@ func (h *Handlers) HandleNewNotificationPage(c *gin.Context) {
return return
} }
data := components.NotificationFormData{ data := types.NotificationFormData{ // Use types.NotificationFormData
IsNew: true, IsNew: true,
NotificationService: &struct { NotificationService: &struct {
ID uint ID uint
@@ -1209,7 +1212,7 @@ func (h *Handlers) HandleNewNotificationPage(c *gin.Context) {
} }
ctx := h.CreateTemplateContext(c) ctx := h.CreateTemplateContext(c)
components.NotificationForm(ctx, data).Render(ctx, c.Writer) form.NotificationForm(ctx, data).Render(ctx, c.Writer) // Use form.NotificationForm
} }
// HandleEditNotificationPage handles GET /admin/settings/notifications/:id/edit // HandleEditNotificationPage handles GET /admin/settings/notifications/:id/edit
@@ -1235,7 +1238,7 @@ func (h *Handlers) HandleEditNotificationPage(c *gin.Context) {
} }
// Convert to NotificationFormData // Convert to NotificationFormData
data := components.NotificationFormData{ data := types.NotificationFormData{ // Use types.NotificationFormData
IsNew: false, IsNew: false,
NotificationService: &struct { NotificationService: &struct {
ID uint ID uint
@@ -1320,7 +1323,7 @@ func (h *Handlers) HandleEditNotificationPage(c *gin.Context) {
} }
ctx := h.CreateTemplateContext(c) ctx := h.CreateTemplateContext(c)
components.NotificationForm(ctx, data).Render(ctx, c.Writer) form.NotificationForm(ctx, data).Render(ctx, c.Writer) // Use form.NotificationForm
} }
// HandleUpdateNotificationService handles PUT /admin/settings/notifications/:id // HandleUpdateNotificationService handles PUT /admin/settings/notifications/:id
@@ -1515,9 +1518,9 @@ func (h *Handlers) handleNotificationsWithError(c *gin.Context, errorMessage str
} }
// Convert to components.NotificationService // Convert to components.NotificationService
var componentServices []components.NotificationService var componentServices []types.NotificationServiceData // Use types.NotificationServiceData
for _, service := range notificationServices { for _, service := range notificationServices {
componentServices = append(componentServices, components.NotificationService{ componentServices = append(componentServices, types.NotificationServiceData{ // Use types.NotificationServiceData
ID: service.ID, ID: service.ID,
Name: service.Name, Name: service.Name,
Type: service.Type, Type: service.Type,
@@ -1533,13 +1536,13 @@ func (h *Handlers) handleNotificationsWithError(c *gin.Context, errorMessage str
}) })
} }
data := components.SettingsNotificationsData{ data := types.SettingsNotificationsData{ // Use types.SettingsNotificationsData
NotificationServices: componentServices, NotificationServices: componentServices,
ErrorMessage: errorMessage, ErrorMessage: errorMessage,
} }
ctx := h.CreateTemplateContext(c) ctx := h.CreateTemplateContext(c)
components.Notifications(ctx, data).Render(ctx, c.Writer) list.List(ctx, data).Render(ctx, c.Writer) // Use list.List
} }
// handleNotificationsWithSuccess renders the notifications page with a success message // handleNotificationsWithSuccess renders the notifications page with a success message
@@ -1550,9 +1553,9 @@ func (h *Handlers) handleNotificationsWithSuccess(c *gin.Context, successMessage
} }
// Convert to components.NotificationService // Convert to components.NotificationService
var componentServices []components.NotificationService var componentServices []types.NotificationServiceData // Use types.NotificationServiceData
for _, service := range notificationServices { for _, service := range notificationServices {
componentServices = append(componentServices, components.NotificationService{ componentServices = append(componentServices, types.NotificationServiceData{ // Use types.NotificationServiceData
ID: service.ID, ID: service.ID,
Name: service.Name, Name: service.Name,
Type: service.Type, Type: service.Type,
@@ -1568,11 +1571,11 @@ func (h *Handlers) handleNotificationsWithSuccess(c *gin.Context, successMessage
}) })
} }
data := components.SettingsNotificationsData{ data := types.SettingsNotificationsData{ // Use types.SettingsNotificationsData
NotificationServices: componentServices, NotificationServices: componentServices,
SuccessMessage: successMessage, SuccessMessage: successMessage,
} }
ctx := h.CreateTemplateContext(c) ctx := h.CreateTemplateContext(c)
components.Notifications(ctx, data).Render(ctx, c.Writer) list.List(ctx, data).Render(ctx, c.Writer) // Use list.List
} }
+93 -1
View File
@@ -54,7 +54,8 @@ function initializeTheme() {
} }
// Toggle between light and dark theme // Toggle between light and dark theme
function toggleTheme() { // Make toggleTheme global for onclick
window.toggleTheme = function() {
const currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; const currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
if (currentTheme === 'dark') { if (currentTheme === 'dark') {
applyLightTheme(); applyLightTheme();
@@ -237,6 +238,97 @@ function setCookie(name, value, days) {
document.cookie = name + '=' + (value || '') + expires + '; path=/; SameSite=Strict'; document.cookie = name + '=' + (value || '') + expires + '; path=/; SameSite=Strict';
} }
// Closes the Flowbite modal with the given ID.
window.closeModal = function(modalId) {
// Get the modal element
const modalEl = document.getElementById(modalId);
if (!modalEl) {
console.error('Modal element not found:', modalId);
return;
}
// Assuming Flowbite's Modal class is available globally
// and initialized elsewhere (e.g., via data attributes or init script)
// Use try-catch in case FlowbiteInstances is not defined
try {
const modalInstance = FlowbiteInstances.getInstance('Modal', modalId);
if (modalInstance) {
modalInstance.hide();
} else {
console.warn('Flowbite Modal instance not found for ID:', modalId, '. Attempting manual hide or ensure Flowbite is initialized.');
// Basic fallback: Directly manipulate classes if Flowbite JS fails
modalEl.classList.add('hidden');
modalEl.classList.remove('flex'); // Assuming 'flex' is used to show
}
} catch (e) {
console.error('Error interacting with Flowbite:', e);
// Basic fallback: Directly manipulate classes if Flowbite JS fails
modalEl.classList.add('hidden');
modalEl.classList.remove('flex'); // Assuming 'flex' is used to show
}
}
// Function to show the modal (might be useful elsewhere)
window.showModal = function(modalId) {
const modalEl = document.getElementById(modalId);
if (!modalEl) {
console.error('Modal element not found:', modalId);
return;
}
// Use try-catch in case FlowbiteInstances is not defined
try {
const modalInstance = FlowbiteInstances.getInstance('Modal', modalId);
if (modalInstance) {
modalInstance.show();
} else {
// If instance doesn't exist, try creating one (requires Flowbite JS loaded)
// This assumes the modal element has the necessary data-modal attributes
console.warn('Flowbite Modal instance not found for ID:', modalId, '. Attempting to create instance or ensure Flowbite is initialized.');
try {
// Requires Flowbite constructor to be available
const newModal = new Modal(modalEl);
newModal.show();
} catch (createError) {
console.error('Failed to create Flowbite modal instance:', createError);
// Basic fallback: Directly manipulate classes if Flowbite JS fails
modalEl.classList.remove('hidden');
modalEl.classList.add('flex'); // Assuming 'flex' is used to show
}
}
} catch (e) {
console.error('Error interacting with Flowbite:', e);
// Basic fallback: Directly manipulate classes if Flowbite JS fails
modalEl.classList.remove('hidden');
modalEl.classList.add('flex'); // Assuming 'flex' is used to show
}
}
// Called when the file metadata delete confirmation button is clicked.
// Primarily closes the modal and sets flags; the actual delete is handled by hx-delete.
window.triggerFileDelete = function(dialogId, fileID, fileName) {
console.log(`Confirmed delete for file: ${fileName} (ID: ${fileID}). Closing modal: ${dialogId}`);
// Close the modal using the global function
if (typeof window.closeModal === 'function') {
window.closeModal(dialogId);
} else {
console.error('Global closeModal function not found.');
}
// Store data in a way that might be accessible to other event handlers (e.g., HTMX)
window.lastDeletedFile = {
id: fileID,
name: fileName
};
// Add custom marker to track this deletion (if needed by other scripts)
window.currentlyDeletingFile = true;
// Optional: Show a "Deleting..." toast here if desired.
// The hx-delete attribute on the button will trigger the actual backend request.
}
// Add CSS for theme transition animations // Add CSS for theme transition animations
const style = document.createElement('style'); const style = document.createElement('style');
style.textContent = ` style.textContent = `