aboutsummaryrefslogtreecommitdiff
path: root/weed/admin/view/app/topics_templ.go
blob: 6920a2e539516bf4fed64ce284306ca4749d634f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Code generated by templ - DO NOT EDIT.

// templ: version: v0.3.960
package app

//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"
import "strings"
import "github.com/seaweedfs/seaweedfs/weed/admin/dash"

func Topics(data dash.TopicsData) 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 class=\"container-fluid\"><div class=\"row\"><div class=\"col-12\"><div class=\"d-flex justify-content-between align-items-center mb-4\"><h1 class=\"h3 mb-0\">Message Queue Topics</h1><small class=\"text-muted\">Last updated: ")
		if templ_7745c5c3_Err != nil {
			return templ_7745c5c3_Err
		}
		var templ_7745c5c3_Var2 string
		templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastUpdated.Format("2006-01-02 15:04:05"))
		if templ_7745c5c3_Err != nil {
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 13, Col: 107}
		}
		_, 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, "</small></div><!-- Summary Cards --><div class=\"row mb-4\"><div class=\"col-md-6\"><div class=\"card text-center\"><div class=\"card-body\"><h5 class=\"card-title\">Total Topics</h5><h3 class=\"text-primary\">")
		if templ_7745c5c3_Err != nil {
			return templ_7745c5c3_Err
		}
		var templ_7745c5c3_Var3 string
		templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalTopics))
		if templ_7745c5c3_Err != nil {
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 22, Col: 93}
		}
		_, 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, "</h3></div></div></div><div class=\"col-md-6\"><div class=\"card text-center\"><div class=\"card-body\"><h5 class=\"card-title\">Available Topics</h5><h3 class=\"text-info\">")
		if templ_7745c5c3_Err != nil {
			return templ_7745c5c3_Err
		}
		var templ_7745c5c3_Var4 string
		templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(data.Topics)))
		if templ_7745c5c3_Err != nil {
			return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 30, Col: 90}
		}
		_, 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, 4, "</h3></div></div></div></div><!-- Topics Table --><div class=\"card\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\">Topics</h5><div><button class=\"btn btn-sm btn-primary me-2\" onclick=\"showCreateTopicModal()\"><i class=\"fas fa-plus me-1\"></i>Create Topic</button> <button class=\"btn btn-sm btn-outline-secondary\" onclick=\"exportTopicsCSV()\"><i class=\"fas fa-download me-1\"></i>Export CSV</button></div></div><div class=\"card-body\">")
		if templ_7745c5c3_Err != nil {
			return templ_7745c5c3_Err
		}
		if len(data.Topics) == 0 {
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"text-center py-4\"><i class=\"fas fa-list-alt fa-3x text-muted mb-3\"></i><h5>No Topics Found</h5><p class=\"text-muted\">No message queue topics are currently configured.</p></div>")
			if templ_7745c5c3_Err != nil {
				return templ_7745c5c3_Err
			}
		} else {
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div class=\"table-responsive\"><table class=\"table table-striped\" id=\"topicsTable\"><thead><tr><th>Namespace</th><th>Topic Name</th><th>Partitions</th><th>Retention</th><th>Actions</th></tr></thead> <tbody>")
			if templ_7745c5c3_Err != nil {
				return templ_7745c5c3_Err
			}
			for _, topic := range data.Topics {
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<tr class=\"topic-row\" data-topic-name=\"")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var5 string
				templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(topic.Name)
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 70, Col: 93}
				}
				_, 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, "\" style=\"cursor: pointer;\"><td><span class=\"badge bg-secondary\">")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var6 string
				templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(func() string {
					idx := strings.LastIndex(topic.Name, ".")
					if idx == -1 {
						return "default"
					}
					return topic.Name[:idx]
				}())
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 78, Col: 55}
				}
				_, 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, "</span></td><td><strong>")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var7 string
				templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(func() string {
					idx := strings.LastIndex(topic.Name, ".")
					if idx == -1 {
						return topic.Name
					}
					return topic.Name[idx+1:]
				}())
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 87, Col: 55}
				}
				_, 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, "</strong></td><td><span class=\"badge bg-info\">")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var8 string
				templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", topic.Partitions))
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 90, Col: 116}
				}
				_, 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, "</span></td><td>")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				if topic.Retention.Enabled {
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<span class=\"badge bg-success\"><i class=\"fas fa-clock me-1\"></i> ")
					if templ_7745c5c3_Err != nil {
						return templ_7745c5c3_Err
					}
					var templ_7745c5c3_Var9 string
					templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d %s", topic.Retention.DisplayValue, topic.Retention.DisplayUnit))
					if templ_7745c5c3_Err != nil {
						return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 96, Col: 140}
					}
					_, 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, 13, "</span>")
					if templ_7745c5c3_Err != nil {
						return templ_7745c5c3_Err
					}
				} else {
					templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<span class=\"badge bg-secondary\"><i class=\"fas fa-times me-1\"></i>Disabled</span>")
					if templ_7745c5c3_Err != nil {
						return templ_7745c5c3_Err
					}
				}
				templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</td><td><button class=\"btn btn-sm btn-outline-primary\" data-action=\"view-topic-details\" data-topic-name=\"")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var10 string
				templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(topic.Name)
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 105, Col: 160}
				}
				_, 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, 16, "\"><i class=\"fas fa-eye\"></i></button></td></tr><tr class=\"topic-details-row\" id=\"")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
				var templ_7745c5c3_Var11 string
				templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("details-%s", strings.ReplaceAll(topic.Name, ".", "_")))
				if templ_7745c5c3_Err != nil {
					return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/topics.templ`, Line: 110, Col: 146}
				}
				_, 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, 17, "\" style=\"display: none;\"><td colspan=\"5\"><div class=\"topic-details-content\"><div class=\"text-center py-3\"><i class=\"fas fa-spinner fa-spin\"></i> Loading topic details...</div></div></td></tr>")
				if templ_7745c5c3_Err != nil {
					return templ_7745c5c3_Err
				}
			}
			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</tbody></table></div>")
			if templ_7745c5c3_Err != nil {
				return templ_7745c5c3_Err
			}
		}
		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></div></div></div></div><!-- Create Topic Modal --><div class=\"modal fade\" id=\"createTopicModal\" tabindex=\"-1\" role=\"dialog\"><div class=\"modal-dialog modal-lg\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\"><i class=\"fas fa-plus me-2\"></i>Create New Topic</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button></div><div class=\"modal-body\"><form id=\"createTopicForm\"><div class=\"row\"><div class=\"col-md-6\"><div class=\"mb-3\"><label for=\"topicNamespace\" class=\"form-label\">Namespace *</label> <input type=\"text\" class=\"form-control\" id=\"topicNamespace\" name=\"namespace\" required placeholder=\"e.g., default\"></div></div><div class=\"col-md-6\"><div class=\"mb-3\"><label for=\"topicName\" class=\"form-label\">Topic Name *</label> <input type=\"text\" class=\"form-control\" id=\"topicName\" name=\"name\" required placeholder=\"e.g., user-events\"></div></div></div><div class=\"row\"><div class=\"col-md-6\"><div class=\"mb-3\"><label for=\"partitionCount\" class=\"form-label\">Partition Count *</label> <input type=\"number\" class=\"form-control\" id=\"partitionCount\" name=\"partitionCount\" required min=\"1\" max=\"100\" value=\"6\"></div></div></div><!-- Retention Configuration --><div class=\"card mt-3\"><div class=\"card-header\"><h6 class=\"mb-0\"><i class=\"fas fa-clock me-2\"></i>Retention Policy</h6></div><div class=\"card-body\"><div class=\"form-check mb-3\"><input class=\"form-check-input\" type=\"checkbox\" id=\"enableRetention\" name=\"enableRetention\" onchange=\"toggleRetentionFields()\"> <label class=\"form-check-label\" for=\"enableRetention\">Enable data retention</label></div><div id=\"retentionFields\" style=\"display: none;\"><div class=\"row\"><div class=\"col-md-6\"><div class=\"mb-3\"><label for=\"retentionValue\" class=\"form-label\">Retention Duration</label> <input type=\"number\" class=\"form-control\" id=\"retentionValue\" name=\"retentionValue\" min=\"1\" value=\"7\"></div></div><div class=\"col-md-6\"><div class=\"mb-3\"><label for=\"retentionUnit\" class=\"form-label\">Unit</label> <select class=\"form-control\" id=\"retentionUnit\" name=\"retentionUnit\"><option value=\"hours\">Hours</option> <option value=\"days\" selected>Days</option></select></div></div></div><div class=\"alert alert-info\"><i class=\"fas fa-info-circle me-2\"></i> Data older than this duration will be automatically purged to save storage space.</div></div></div></div></form></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"button\" class=\"btn btn-primary\" onclick=\"createTopic()\"><i class=\"fas fa-plus me-1\"></i>Create Topic</button></div></div></div></div><script type=\"text/javascript\">\n        // Topic management functions\n        function showCreateTopicModal() {\n            var modal = new bootstrap.Modal(document.getElementById('createTopicModal'));\n            modal.show();\n        }\n\n        function toggleRetentionFields() {\n            var enableRetention = document.getElementById('enableRetention').checked;\n            var retentionFields = document.getElementById('retentionFields');\n            \n            if (enableRetention) {\n                retentionFields.style.display = 'block';\n            } else {\n                retentionFields.style.display = 'none';\n            }\n        }\n\n        function createTopic() {\n            var form = document.getElementById('createTopicForm');\n            var formData = new FormData(form);\n            \n            if (!form.checkValidity()) {\n                form.classList.add('was-validated');\n                return;\n            }\n            \n            var namespace = formData.get('namespace');\n            var name = formData.get('name');\n            var partitionCount = formData.get('partitionCount');\n            var enableRetention = formData.get('enableRetention');\n            var retentionValue = enableRetention === 'on' ? parseInt(formData.get('retentionValue')) : 0;\n            var retentionUnit = enableRetention === 'on' ? formData.get('retentionUnit') : 'hours';\n            \n            // Convert retention to seconds\n            var retentionSeconds = 0;\n            if (enableRetention === 'on' && retentionValue > 0) {\n                if (retentionUnit === 'hours') {\n                    retentionSeconds = retentionValue * 3600;\n                } else if (retentionUnit === 'days') {\n                    retentionSeconds = retentionValue * 86400;\n                }\n            }\n            \n            var topicData = {\n                namespace: namespace,\n                name: name,\n                partition_count: parseInt(partitionCount),\n                retention: {\n                    enabled: enableRetention === 'on',\n                    retention_seconds: retentionSeconds\n                }\n            };\n            \n            // Create the topic\n            fetch('/api/mq/topics/create', {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json'\n                },\n                body: JSON.stringify(topicData)\n            })\n            .then(response => {\n                if (response.ok) {\n                    return response.json();\n                }\n                throw new Error('Failed to create topic');\n            })\n            .then(data => {\n                // Hide modal and refresh page\n                var modal = bootstrap.Modal.getInstance(document.getElementById('createTopicModal'));\n                modal.hide();\n                location.reload();\n            })\n            .catch(error => {\n                console.error('Error:', error);\n                alert('Error creating topic: ' + error.message);\n            });\n        }\n\n        function exportTopicsCSV() {\n            var csvContent = 'Namespace,Topic Name,Partitions,Retention Enabled,Retention Value,Retention Unit\\n';\n            \n            var rows = document.querySelectorAll('#topicsTable tbody tr.topic-row');\n            rows.forEach(function(row) {\n                var cells = row.querySelectorAll('td');\n                var namespace = cells[0].textContent.trim();\n                var topicName = cells[1].textContent.trim();\n                var partitions = cells[2].textContent.trim();\n                var retention = cells[3].textContent.trim();\n                \n                var retentionEnabled = retention !== 'Disabled';\n                var retentionValue = retentionEnabled ? retention.split(' ')[0] : '';\n                var retentionUnit = retentionEnabled ? retention.split(' ')[1] : '';\n                \n                csvContent += namespace + ',' + topicName + ',' + partitions + ',' + retentionEnabled + ',' + retentionValue + ',' + retentionUnit + '\\n';\n            });\n            \n            var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n            var link = document.createElement('a');\n            var url = URL.createObjectURL(blob);\n            link.setAttribute('href', url);\n            link.setAttribute('download', 'topics_export.csv');\n            link.style.visibility = 'hidden';\n            document.body.appendChild(link);\n            link.click();\n            document.body.removeChild(link);\n        }\n\n        // Topic details functionality\n        document.addEventListener('DOMContentLoaded', function() {\n            // Handle view topic details buttons\n            document.querySelectorAll('[data-action=\"view-topic-details\"]').forEach(function(button) {\n                button.addEventListener('click', function(e) {\n                    e.stopPropagation();\n                    var topicName = this.getAttribute('data-topic-name');\n                    var detailsRow = document.getElementById('details-' + topicName.replace(/\\./g, '_'));\n                    \n                    if (detailsRow.style.display === 'none') {\n                        detailsRow.style.display = 'table-row';\n                        this.innerHTML = '<i class=\"fas fa-eye-slash\"></i>';\n                        \n                        // Load topic details\n                        loadTopicDetails(topicName);\n                    } else {\n                        detailsRow.style.display = 'none';\n                        this.innerHTML = '<i class=\"fas fa-eye\"></i>';\n                    }\n                });\n            });\n        });\n\n        function loadTopicDetails(topicName) {\n            var detailsRow = document.getElementById('details-' + topicName.replace(/\\./g, '_'));\n            var contentDiv = detailsRow.querySelector('.topic-details-content');\n            \n            fetch('/admin/topics/' + encodeURIComponent(topicName) + '/details')\n                .then(response => response.json())\n                .then(data => {\n                    var html = '<div class=\"row\">';\n                    html += '<div class=\"col-md-6\">';\n                    html += '<h6>Topic Configuration</h6>';\n                    html += '<ul class=\"list-unstyled\">';\n                    html += '<li><strong>Full Name:</strong> ' + data.name + '</li>';\n                    html += '<li><strong>Partitions:</strong> ' + data.partitions + '</li>';\n                    html += '<li><strong>Created:</strong> ' + (data.created || 'N/A') + '</li>';\n                    html += '</ul>';\n                    html += '</div>';\n                    html += '<div class=\"col-md-6\">';\n                    html += '<h6>Retention Policy</h6>';\n                    if (data.retention && data.retention.enabled) {\n                        html += '<p><i class=\"fas fa-check-circle text-success\"></i> Enabled</p>';\n                        html += '<p><strong>Duration:</strong> ' + data.retention.value + ' ' + data.retention.unit + '</p>';\n                    } else {\n                        html += '<p><i class=\"fas fa-times-circle text-danger\"></i> Disabled</p>';\n                    }\n                    html += '</div>';\n                    html += '</div>';\n                    \n                    contentDiv.innerHTML = html;\n                })\n                .catch(error => {\n                    console.error('Error loading topic details:', error);\n                    contentDiv.innerHTML = '<div class=\"alert alert-danger\">Failed to load topic details</div>';\n                });\n        }\n    </script>")
		if templ_7745c5c3_Err != nil {
			return templ_7745c5c3_Err
		}
		return nil
	})
}

var _ = templruntime.GeneratedTemplate