diff --git a/cmd/web/helpers.go b/cmd/web/helpers.go index 3060325..e1321e0 100644 --- a/cmd/web/helpers.go +++ b/cmd/web/helpers.go @@ -110,6 +110,7 @@ func (app *application) newCommonData(r *http.Request) views.CommonData { CSRFToken: nosurf.Token(r), CurrentUser: app.getCurrentUser(r), IsHtmx: r.Header.Get("Hx-Request") == "true", + RootUrl: app.rootUrl, } } diff --git a/cmd/web/main.go b/cmd/web/main.go index 7f22d07..5fe6dac 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -28,12 +28,14 @@ type application struct { formDecoder *schema.Decoder debug bool timezones []string + rootUrl string } func main() { addr := flag.String("addr", ":3000", "HTTP network address") dsn := flag.String("dsn", "guestbook.db", "data source name") debug := flag.Bool("debug", false, "enable debug mode") + root := flag.String("root", "localhost:3000", "root URL of application") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) @@ -62,6 +64,7 @@ func main() { formDecoder: formDecoder, debug: *debug, timezones: getAvailableTimezones(), + rootUrl: *root, } err = app.users.InitializeSettingsMap() diff --git a/ui/static/css/style.css b/ui/static/css/style.css index 0b1a576..1f291a6 100644 --- a/ui/static/css/style.css +++ b/ui/static/css/style.css @@ -58,10 +58,17 @@ div#dashboard { div#dashboard nav { flex: 1 1 25%; margin-top: 2rem; + min-width: 0; } div#dashboard > div { flex: 10 1 40%; + min-width: 0; +} + +div > pre { + max-width: 100%; + overflow: auto; } main nav ul { diff --git a/ui/static/js/guestbook.js b/ui/static/js/guestbook.js new file mode 100644 index 0000000..9af432e --- /dev/null +++ b/ui/static/js/guestbook.js @@ -0,0 +1,105 @@ +class CommentList extends HTMLElement { + static get observedAttributes() { + return ['src']; + } + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.comments = []; + this.loading = false; + this.error = null; + } + + connectedCallback() { + if (this.hasAttribute('src')) { + this.fetchComments(); + } + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'src' && oldValue !== newValue) { + this.fetchComments(); + } + } + + async fetchComments() { + const src = this.getAttribute('src'); + if (!src) return; + this.loading = true; + this.error = null; + this.render(); + + try { + const response = await fetch(src); + if (!response.ok) throw new Error(`HTTP error: ${response.status}`); + const data = await response.json(); + this.comments = Array.isArray(data) ? data : []; + this.loading = false; + this.render(); + } catch (err) { + this.error = err.message; + this.loading = false; + this.render(); + } + } + + formatDate(isoString) { + if (!isoString) return ''; + const date = new Date(isoString); + return date.toLocaleString(); + } + + render() { + this.shadowRoot.innerHTML = ` + +
- Stats and stuff will go here. + Use this form to allow readers of your website to comment on your guestbook!
+
+					Upload this JavaScript WebComponent to your site and include it in your { `` } tag.
+				
+						
+							{ 
+`
+    
+` }
+						
+					
+					+ Then add the custom element where you want the comments to show up +
+ {{ getUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments", data.RootUrl, shortIdToSlug(website.ShortId)) }} + // +
+						
+							{ fmt.Sprintf(` `, getUrl) }
+						
+					
+					@embedJavaScriptSnippet(data.RootUrl, website)
+				
+		
+			{ fmt.Sprintf(formStr, postUrl) }
+		
+	
+}
+
+templ embedJavaScriptSnippet(root string, website models.Website) {
+}
diff --git a/ui/views/websites_templ.go b/ui/views/websites_templ.go
index 2d6d6a6..a19e987 100644
--- a/ui/views/websites_templ.go
+++ b/ui/views/websites_templ.go
@@ -481,7 +481,70 @@ func WebsiteDashboard(title string, data CommonData, website models.Website) tem
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "Stats and stuff will go here.
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "Use this form to allow readers of your website to comment on your guestbook!
Upload this JavaScript WebComponent to your site and include it in your ")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			var templ_7745c5c3_Var26 string
+			templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(``)
+			if templ_7745c5c3_Err != nil {
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 129, Col: 140}
+			}
+			_, 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, 40, " tag.
")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			var templ_7745c5c3_Var27 string
+			templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(
+				`
+    
+`)
+			if templ_7745c5c3_Err != nil {
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 138, Col: 8}
+			}
+			_, 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, 41, "Then add the custom element where you want the comments to show up
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + getUrl := fmt.Sprintf("https://%s/websites/%s/guestbook/comments", data.RootUrl, shortIdToSlug(website.ShortId)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			var templ_7745c5c3_Var28 string
+			templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(` `, getUrl))
+			if templ_7745c5c3_Err != nil {
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 148, Col: 70}
+			}
+			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "")
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			templ_7745c5c3_Err = embedJavaScriptSnippet(data.RootUrl, website).Render(ctx, templ_7745c5c3_Buffer)
+			if templ_7745c5c3_Err != nil {
+				return templ_7745c5c3_Err
+			}
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "Coming Soon
Coming Soon
")
+		if templ_7745c5c3_Err != nil {
+			return templ_7745c5c3_Err
+		}
+		var templ_7745c5c3_Var34 string
+		templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(formStr, postUrl))
+		if templ_7745c5c3_Err != nil {
+			return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/websites.templ`, Line: 205, Col: 34}
+		}
+		_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
+		if templ_7745c5c3_Err != nil {
+			return templ_7745c5c3_Err
+		}
+		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "")
+		if templ_7745c5c3_Err != nil {
+			return templ_7745c5c3_Err
+		}
+		return nil
+	})
+}
+
+func embedJavaScriptSnippet(root string, website models.Website) 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_Var35 := templ.GetChildren(ctx)
+		if templ_7745c5c3_Var35 == nil {
+			templ_7745c5c3_Var35 = templ.NopComponent
+		}
+		ctx = templ.ClearChildren(ctx)
+		return nil
+	})
+}
+
 var _ = templruntime.GeneratedTemplate