From 658f3735bf3f044741636dd629d8ec87e49b932a Mon Sep 17 00:00:00 2001 From: Vovko Date: Thu, 18 Jul 2024 12:47:44 -0400 Subject: [PATCH] finished bulk of widget UI --- cmd/frontend/components/search.templ | 81 +++-- cmd/frontend/components/search_templ.go | 99 ++--- cmd/frontend/components/widget/card.templ | 97 +++-- cmd/frontend/components/widget/card_templ.go | 338 +++++++++++++----- cmd/frontend/components/widget/options.templ | 15 +- .../components/widget/options_templ.go | 143 ++++---- cmd/frontend/handler/context.go | 33 +- cmd/frontend/handlers.go | 14 +- cmd/frontend/routes/api/widget/custom.go | 239 +++++++++++++ cmd/frontend/routes/api/widget/personal.go | 9 - cmd/frontend/routes/app/index.templ | 7 +- cmd/frontend/routes/app/index_templ.go | 11 +- cmd/frontend/routes/app/widgets/edit.templ | 35 +- cmd/frontend/routes/app/widgets/edit_templ.go | 55 ++- cmd/frontend/routes/app/widgets/new.templ | 12 +- cmd/frontend/routes/app/widgets/new_templ.go | 12 +- cmd/frontend/routes/widget/live.templ | 2 +- cmd/frontend/routes/widget/live_templ.go | 2 +- cmd/frontend/routes/widget/preview.templ | 2 +- cmd/frontend/routes/widget/preview_templ.go | 8 +- internal/constants/widget.go | 1 + internal/database/client.go | 4 +- internal/database/widget_settings.go | 22 +- tests/static_database.go | 10 +- 24 files changed, 887 insertions(+), 364 deletions(-) create mode 100644 cmd/frontend/routes/api/widget/custom.go delete mode 100644 cmd/frontend/routes/api/widget/personal.go diff --git a/cmd/frontend/components/search.templ b/cmd/frontend/components/search.templ index 9760546f..8b5edb79 100644 --- a/cmd/frontend/components/search.templ +++ b/cmd/frontend/components/search.templ @@ -30,64 +30,67 @@ templ (p PlayerSearch) RealmOptions(selected string) { } script searchOnRealmSelect(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID string) { - const accountId = document.querySelector(accountIDInputID); - const results = document.querySelector(searchResultsID); - const select = document.querySelector(realmSelectID); - const realm = select.value; + const accountIdError = () => document.querySelector(accountIDInputID+"_error"); + const accountId = () => document.querySelector(accountIDInputID); + const results = () => document.querySelector(searchResultsID); + const select = () => document.querySelector(realmSelectID); + const realm = () => select().value; // enable/disable field - const nickname = document.querySelector(nicknameInputID); - if (realm) { - if (nickname.value.length >= 5) { + const nickname = () => document.querySelector(nicknameInputID); + if (realm()) { + if (nickname().value.length >= 5) { // if the input already exists - send a request - results.innerHTML = '
  • '; - const event = new CustomEvent("player-search", { detail: {query: nickname.value, realm} }); + results().innerHTML = '
  • '; + const event = new CustomEvent("player-search", { detail: {query: nickname().value, realm: realm()} }); document.dispatchEvent(event); return } - nickname.disabled = false; + nickname().disabled = false; + accountIdError()?.classList.add("hidden") } else { - nickname.disabled = true; + nickname().disabled = true; } // clear results - results.innerHTML = 'Start typing to search'; + results().innerHTML = 'Start typing to search'; } script searchOnNicknameInput(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID string) { - const accountId = document.querySelector(accountIDInputID); - const nickname = document.querySelector(nicknameInputID); - const results = document.querySelector(searchResultsID); - const select = document.querySelector(realmSelectID); - const realm = select.value; - if (!realm) { - results.innerHTML = 'Start typing to search'; - nickname.disabled = true; - nickname.value = ''; + const accountIdError = () => document.querySelector(accountIDInputID+"_error"); + const nickname = () => document.querySelector(nicknameInputID); + const results = () => document.querySelector(searchResultsID); + const select = () => document.querySelector(realmSelectID); + const realm = () => select().value; + if (!realm()) { + results().innerHTML = 'Start typing to search'; + nickname().disabled = true; + nickname().value = ''; return; } - if (!nickname.value) { - results.innerHTML = 'Start typing to search'; + if (!nickname().value) { + results().innerHTML = 'Start typing to search'; return; } - if (nickname.value.length < 5) { - results.innerHTML = 'Continue typing to search'; + if (nickname().value.length < 5) { + results().innerHTML = 'Continue typing to search'; return; } - const event = new CustomEvent("player-search", { detail: {query: nickname.value, realm} }); + accountIdError()?.classList.add("hidden") + const event = new CustomEvent("player-search", { detail: {query: nickname().value, realm: realm()} }); document.dispatchEvent(event); } script searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID string, appId string) { - const results = document.querySelector(searchResultsID); - const nickname = document.querySelector(nicknameInputID); - const accountId = document.querySelector(accountIDInputID); + const results = () => document.querySelector(searchResultsID); + const nickname = () => document.querySelector(nicknameInputID); + const accountId = () => document.querySelector(accountIDInputID); const setResultsLoading = () => { - results.innerHTML = '
  • '; + results().innerHTML = '
  • '; } const selectAccount = (name, id) => { - nickname.value = name - accountId.value = id + nickname().value = name + accountId().value = id } window.amthSelectAccount = selectAccount @@ -126,8 +129,8 @@ script searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID str fetch(url).then(res => res.json()).then(data => { if (data.status != "ok") { - results.innerHTML = `${data?.error?.message.toLocaleLowerCase().replaceAll("_", " ") || "Failed to get accounts"}`; - nickname.disabled = false; + results().innerHTML = `${data?.error?.message.toLocaleLowerCase().replaceAll("_", " ") || "Failed to get accounts"}`; + nickname().disabled = false; return; } const elements = [] @@ -136,16 +139,16 @@ script searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID str elements.push(`
  • `); } if (elements.length == 0) { - results.innerHTML = 'No players found'; - nickname.disabled = false; + results().innerHTML = 'No players found'; + nickname().disabled = false; return; } - results.innerHTML = elements.join(""); + results().innerHTML = elements.join(""); return; }).catch(e => { console.log("failed to search for accounts",e); - results.innerHTML = 'No players found'; - nickname.disabled = false; + results().innerHTML = 'No players found'; + nickname().disabled = false; }); }, debounceDelay); }, diff --git a/cmd/frontend/components/search_templ.go b/cmd/frontend/components/search_templ.go index 6b4d2e65..a379ab90 100644 --- a/cmd/frontend/components/search_templ.go +++ b/cmd/frontend/components/search_templ.go @@ -98,77 +98,80 @@ func (p PlayerSearch) RealmOptions(selected string) templ.Component { func searchOnRealmSelect(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID string) templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_searchOnRealmSelect_c62e`, - Function: `function __templ_searchOnRealmSelect_c62e(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID){const accountId = document.querySelector(accountIDInputID); - const results = document.querySelector(searchResultsID); - const select = document.querySelector(realmSelectID); - const realm = select.value; + Name: `__templ_searchOnRealmSelect_3536`, + Function: `function __templ_searchOnRealmSelect_3536(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID){const accountIdError = () => document.querySelector(accountIDInputID+"_error"); + const accountId = () => document.querySelector(accountIDInputID); + const results = () => document.querySelector(searchResultsID); + const select = () => document.querySelector(realmSelectID); + const realm = () => select().value; // enable/disable field - const nickname = document.querySelector(nicknameInputID); - if (realm) { - if (nickname.value.length >= 5) { + const nickname = () => document.querySelector(nicknameInputID); + if (realm()) { + if (nickname().value.length >= 5) { // if the input already exists - send a request - results.innerHTML = '
  • '; - const event = new CustomEvent("player-search", { detail: {query: nickname.value, realm} }); + results().innerHTML = '
  • '; + const event = new CustomEvent("player-search", { detail: {query: nickname().value, realm: realm()} }); document.dispatchEvent(event); return } - nickname.disabled = false; + nickname().disabled = false; + accountIdError()?.classList.add("hidden") } else { - nickname.disabled = true; + nickname().disabled = true; } // clear results - results.innerHTML = 'Start typing to search'; + results().innerHTML = 'Start typing to search'; }`, - Call: templ.SafeScript(`__templ_searchOnRealmSelect_c62e`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), - CallInline: templ.SafeScriptInline(`__templ_searchOnRealmSelect_c62e`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), + Call: templ.SafeScript(`__templ_searchOnRealmSelect_3536`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), + CallInline: templ.SafeScriptInline(`__templ_searchOnRealmSelect_3536`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), } } func searchOnNicknameInput(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID string) templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_searchOnNicknameInput_5399`, - Function: `function __templ_searchOnNicknameInput_5399(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID){const accountId = document.querySelector(accountIDInputID); - const nickname = document.querySelector(nicknameInputID); - const results = document.querySelector(searchResultsID); - const select = document.querySelector(realmSelectID); - const realm = select.value; - if (!realm) { - results.innerHTML = 'Start typing to search'; - nickname.disabled = true; - nickname.value = ''; + Name: `__templ_searchOnNicknameInput_76ea`, + Function: `function __templ_searchOnNicknameInput_76ea(searchResultsID, realmSelectID, nicknameInputID, accountIDInputID){const accountIdError = () => document.querySelector(accountIDInputID+"_error"); + const nickname = () => document.querySelector(nicknameInputID); + const results = () => document.querySelector(searchResultsID); + const select = () => document.querySelector(realmSelectID); + const realm = () => select().value; + if (!realm()) { + results().innerHTML = 'Start typing to search'; + nickname().disabled = true; + nickname().value = ''; return; } - if (!nickname.value) { - results.innerHTML = 'Start typing to search'; + if (!nickname().value) { + results().innerHTML = 'Start typing to search'; return; } - if (nickname.value.length < 5) { - results.innerHTML = 'Continue typing to search'; + if (nickname().value.length < 5) { + results().innerHTML = 'Continue typing to search'; return; } - const event = new CustomEvent("player-search", { detail: {query: nickname.value, realm} }); + accountIdError()?.classList.add("hidden") + const event = new CustomEvent("player-search", { detail: {query: nickname().value, realm: realm()} }); document.dispatchEvent(event); }`, - Call: templ.SafeScript(`__templ_searchOnNicknameInput_5399`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), - CallInline: templ.SafeScriptInline(`__templ_searchOnNicknameInput_5399`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), + Call: templ.SafeScript(`__templ_searchOnNicknameInput_76ea`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), + CallInline: templ.SafeScriptInline(`__templ_searchOnNicknameInput_76ea`, searchResultsID, realmSelectID, nicknameInputID, accountIDInputID), } } func searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID string, appId string) templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_searchEventHandler_3728`, - Function: `function __templ_searchEventHandler_3728(searchResultsID, nicknameInputID, accountIDInputID, appId){const results = document.querySelector(searchResultsID); - const nickname = document.querySelector(nicknameInputID); - const accountId = document.querySelector(accountIDInputID); + Name: `__templ_searchEventHandler_b9c0`, + Function: `function __templ_searchEventHandler_b9c0(searchResultsID, nicknameInputID, accountIDInputID, appId){const results = () => document.querySelector(searchResultsID); + const nickname = () => document.querySelector(nicknameInputID); + const accountId = () => document.querySelector(accountIDInputID); const setResultsLoading = () => { - results.innerHTML = '
  • '; + results().innerHTML = '
  • '; } const selectAccount = (name, id) => { - nickname.value = name - accountId.value = id + nickname().value = name + accountId().value = id } window.amthSelectAccount = selectAccount @@ -207,8 +210,8 @@ func searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID strin fetch(url).then(res => res.json()).then(data => { if (data.status != "ok") { - results.innerHTML = ` + "`" + `${data?.error?.message.toLocaleLowerCase().replaceAll("_", " ") || "Failed to get accounts"}` + "`" + `; - nickname.disabled = false; + results().innerHTML = ` + "`" + `${data?.error?.message.toLocaleLowerCase().replaceAll("_", " ") || "Failed to get accounts"}` + "`" + `; + nickname().disabled = false; return; } const elements = [] @@ -217,23 +220,23 @@ func searchEventHandler(searchResultsID, nicknameInputID, accountIDInputID strin elements.push(` + "`" + `
  • ` + "`" + `); } if (elements.length == 0) { - results.innerHTML = 'No players found'; - nickname.disabled = false; + results().innerHTML = 'No players found'; + nickname().disabled = false; return; } - results.innerHTML = elements.join(""); + results().innerHTML = elements.join(""); return; }).catch(e => { console.log("failed to search for accounts",e); - results.innerHTML = 'No players found'; - nickname.disabled = false; + results().innerHTML = 'No players found'; + nickname().disabled = false; }); }, debounceDelay); }, false, ); }`, - Call: templ.SafeScript(`__templ_searchEventHandler_3728`, searchResultsID, nicknameInputID, accountIDInputID, appId), - CallInline: templ.SafeScriptInline(`__templ_searchEventHandler_3728`, searchResultsID, nicknameInputID, accountIDInputID, appId), + Call: templ.SafeScript(`__templ_searchEventHandler_b9c0`, searchResultsID, nicknameInputID, accountIDInputID, appId), + CallInline: templ.SafeScriptInline(`__templ_searchEventHandler_b9c0`, searchResultsID, nicknameInputID, accountIDInputID, appId), } } diff --git a/cmd/frontend/components/widget/card.templ b/cmd/frontend/components/widget/card.templ index 4dd0219e..ef334592 100644 --- a/cmd/frontend/components/widget/card.templ +++ b/cmd/frontend/components/widget/card.templ @@ -40,48 +40,71 @@ templ WidgetCard(widget WidgetWithAccount) { -
    - @QuickSettingButton(widget.Style.RatingOverview.Visible) { - Rating Battles - } - @QuickSettingButton(widget.Style.UnratedOverview.Visible) { - Regular Battles - } - @QuickSettingButton(widget.Style.Vehicles.Visible) { - Vehicles - if widget.Style.Vehicles.Visible { - - ({ fmt.Sprint(widget.Style.Vehicles.Limit) }) +
    + @QuickSettingButton("rating_overview", widget.WidgetOptions) + @QuickSettingButton("unrated_overview", widget.WidgetOptions) + @QuickSettingButton("unrated_vehicles", widget.WidgetOptions) +
    + - @logic.EmbedMinifiedScript(resetTimeoutTicker("widget-reset-button-"+widget.ID, (constants.WidgetSessionResetTimeout-time.Since(widget.SessionFrom)).Seconds()), "widget-reset-button-"+widget.ID, (constants.WidgetSessionResetTimeout - time.Since(widget.SessionFrom)).Seconds()) + + @logic.EmbedMinifiedScript(resetTimeoutTicker("widget-reset-button-"+widget.ID, (constants.WidgetSessionResetTimeout-time.Since(widget.SessionFrom)).Seconds()), "widget-reset-button-"+widget.ID, (constants.WidgetSessionResetTimeout - time.Since(widget.SessionFrom)).Seconds()) +
    } -templ QuickSettingButton(enabled bool) { - +templ QuickSettingButton(id string, widget models.WidgetOptions) { + switch id { + case "rating_overview": + @quickSettingButton(widget.ID, id, widget.Style.RatingOverview.Visible) { + Rating Battles + } + case "unrated_overview": + @quickSettingButton(widget.ID, id, widget.Style.UnratedOverview.Visible) { + Regular Battles + } + case "unrated_vehicles": + @quickSettingButton(widget.ID, id, widget.Style.Vehicles.Visible) { + Vehicles + if widget.Style.Vehicles.Visible { + 0) }> + ({ fmt.Sprint(widget.Style.Vehicles.Limit) }) + + } + } + } +} + +templ quickSettingButton(widgetID, fieldID string, enabled bool) { +
    + + +
    } script resetTimeoutTicker(elementID string, seconds float64) { @@ -112,7 +135,7 @@ script resetTimeoutTicker(elementID string, seconds float64) { } script copyButtonAction(elementID, widgetID string) { - const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + widgetID + const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + widgetID + "/live/" navigator.clipboard.writeText(url); const btn = document.getElementById(elementID) diff --git a/cmd/frontend/components/widget/card_templ.go b/cmd/frontend/components/widget/card_templ.go index 71b27bd3..f5986b60 100644 --- a/cmd/frontend/components/widget/card_templ.go +++ b/cmd/frontend/components/widget/card_templ.go @@ -105,109 +105,36 @@ func WidgetCard(widget WidgetWithAccount) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">
    ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Var7 := 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 = templ_7745c5c3_Buffer.WriteString("Rating Battles") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = QuickSettingButton(widget.Style.RatingOverview.Visible).Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = QuickSettingButton("rating_overview", widget.WidgetOptions).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Var8 := 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 = templ_7745c5c3_Buffer.WriteString("Regular Battles") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = QuickSettingButton(widget.Style.UnratedOverview.Visible).Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = QuickSettingButton("unrated_overview", widget.WidgetOptions).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Var9 := 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 = templ_7745c5c3_Buffer.WriteString("Vehicles ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if widget.Style.Vehicles.Visible { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("(") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(widget.Style.Vehicles.Limit)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/frontend/components/widget/card.templ`, Line: 54, Col: 49} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(")") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = QuickSettingButton(widget.Style.Vehicles.Visible).Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = QuickSettingButton("unrated_vehicles", widget.WidgetOptions).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"group btn hover:bg-base-200 p-1 flex justify-center items-center gap-2 w-full bg-base-100 rounded-sm flex-nowrap px-2\"> Reset Session") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -215,7 +142,7 @@ func WidgetCard(widget WidgetWithAccount) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -223,7 +150,7 @@ func WidgetCard(widget WidgetWithAccount) templ.Component { }) } -func QuickSettingButton(enabled bool) templ.Component { +func QuickSettingButton(id string, widget models.WidgetOptions) 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 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -236,12 +163,233 @@ func QuickSettingButton(enabled bool) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var12 := templ.GetChildren(ctx) - if templ_7745c5c3_Var12 == nil { - templ_7745c5c3_Var12 = templ.NopComponent + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -323,8 +471,8 @@ func resetTimeoutTicker(elementID string, seconds float64) templ.ComponentScript func copyButtonAction(elementID, widgetID string) templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_copyButtonAction_dba2`, - Function: `function __templ_copyButtonAction_dba2(elementID, widgetID){const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + widgetID + Name: `__templ_copyButtonAction_2e09`, + Function: `function __templ_copyButtonAction_2e09(elementID, widgetID){const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + widgetID + "/live/" navigator.clipboard.writeText(url); const btn = document.getElementById(elementID) @@ -340,7 +488,7 @@ func copyButtonAction(elementID, widgetID string) templ.ComponentScript { btn.innerHTML = oldContent; }, 2000) }`, - Call: templ.SafeScript(`__templ_copyButtonAction_dba2`, elementID, widgetID), - CallInline: templ.SafeScriptInline(`__templ_copyButtonAction_dba2`, elementID, widgetID), + Call: templ.SafeScript(`__templ_copyButtonAction_2e09`, elementID, widgetID), + CallInline: templ.SafeScriptInline(`__templ_copyButtonAction_2e09`, elementID, widgetID), } } diff --git a/cmd/frontend/components/widget/options.templ b/cmd/frontend/components/widget/options.templ index cae9225b..67080e78 100644 --- a/cmd/frontend/components/widget/options.templ +++ b/cmd/frontend/components/widget/options.templ @@ -37,12 +37,12 @@ templ Settings(onChange templ.ComponentScript, or, ou bool, vl int) { } -func CustomOptionsForm(options WidgetWithAccount, submit templ.Component, attrs templ.Attributes) templ.Component { +func CustomOptionsForm(options WidgetWithAccount, submit templ.Component, attrs templ.Attributes, errors map[string]string) templ.Component { search := components.PlayerSearch{RealmSelect: "#account_realm", SearchResults: "#search_results", NicknameInput: "#account_nickname", AccountIDInput: "#account_id"} - return customOptionsForm(search, options, submit, attrs) + return customOptionsForm(search, options, submit, attrs, errors) } -templ customOptionsForm(search components.PlayerSearch, options WidgetWithAccount, submit templ.Component, attrs templ.Attributes) { +templ customOptionsForm(search components.PlayerSearch, options WidgetWithAccount, submit templ.Component, attrs templ.Attributes, errors map[string]string) {
    + if errors["account_id"] != "" { +
    + { errors["account_id"] } +
    + }
    Rating Battles @@ -108,8 +113,8 @@ templ customOptionsForm(search components.PlayerSearch, options WidgetWithAccoun @submit
    - @search.Scripts() @initFormScripts.Once() { + @search.Scripts() ") + templ_7745c5c3_Err = search.Scripts().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = initFormScripts.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var28), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = initFormScripts.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var29), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/cmd/frontend/handler/context.go b/cmd/frontend/handler/context.go index 6d619b5c..7821178b 100644 --- a/cmd/frontend/handler/context.go +++ b/cmd/frontend/handler/context.go @@ -35,8 +35,9 @@ type Context struct { userOptions database.UserGetOptions session *models.Session - w http.ResponseWriter - r *http.Request + formParsed bool + w http.ResponseWriter + r *http.Request } type Layout func(ctx *Context, children ...templ.Component) (templ.Component, error) @@ -59,8 +60,25 @@ func (ctx *Context) SetCookie(cookie *http.Cookie) { func (ctx *Context) Query(key string) string { return ctx.r.URL.Query().Get(key) } -func (ctx *Context) Form(key string) string { - return ctx.r.Form.Get(key) +func (ctx *Context) Form(key string) (string, error) { + if ctx.formParsed { + return ctx.r.Form.Get(key), nil + } + if err := ctx.r.ParseForm(); err != nil { + return "", err + } + ctx.formParsed = true + return ctx.r.Form.Get(key), nil +} +func (ctx *Context) FormValues() (url.Values, error) { + if ctx.formParsed { + return ctx.r.Form, nil + } + if err := ctx.r.ParseForm(); err != nil { + return nil, err + } + ctx.formParsed = true + return ctx.r.Form, nil } func (ctx *Context) Path(key string) string { return ctx.r.PathValue(key) @@ -69,7 +87,7 @@ func (ctx *Context) URL() *url.URL { return ctx.r.URL } func (ctx *Context) SetHeader(key, value string) { - ctx.r.Header.Set(key, value) + ctx.w.Header().Set(key, value) } func (ctx *Context) GetHeader(key string) string { return ctx.r.Header.Get(key) @@ -173,8 +191,11 @@ func (ctx *Context) Error(err error, context ...string) error { } func (ctx *Context) Redirect(path string, code int) error { + if ctx.r.Header.Get("HX-Request") == "true" { + ctx.w.Header().Set("HX-Redirect", path) + return nil // this would never cause an error + } http.Redirect(ctx.w, ctx.r, path, code) - ctx.r.Header.Set("HX-Redirect", path) return nil // this would never cause an error } diff --git a/cmd/frontend/handlers.go b/cmd/frontend/handlers.go index 39ec9d1c..48355658 100644 --- a/cmd/frontend/handlers.go +++ b/cmd/frontend/handlers.go @@ -93,7 +93,7 @@ func Handlers(core core.Client) ([]server.Handler, error) { Func: redirect("/app/widget"), }, { - Path: get("/widget/custom/{widgetId}"), + Path: get("/widget/custom/{widgetId}/live"), Func: handler.Chain(core, widget.CustomLiveWidget), }, // app routes @@ -134,6 +134,18 @@ func Handlers(core core.Client) ([]server.Handler, error) { Path: get("/api/widget/{accountId}"), Func: handler.Chain(core, aWidget.AccountWidget), }, + { + Path: "PATCH /api/widget/custom/{widgetId}/{$}", + Func: handler.Chain(core, aWidget.UpdateCustomWidget), + }, + { + Path: "PATCH /api/widget/custom/{widgetId}/action/{$}", + Func: handler.Chain(core, aWidget.QuickAction), + }, + { + Path: "POST /api/widget/custom/{$}", + Func: handler.Chain(core, aWidget.CreateCustomWidget), + }, { Path: "DELETE /api/connections/{connectionId}/{$}", Func: handler.Chain(core, api.RemoveConnection), diff --git a/cmd/frontend/routes/api/widget/custom.go b/cmd/frontend/routes/api/widget/custom.go new file mode 100644 index 00000000..513a5a70 --- /dev/null +++ b/cmd/frontend/routes/api/widget/custom.go @@ -0,0 +1,239 @@ +package widget + +import ( + "errors" + "net/http" + "net/url" + "reflect" + "strconv" + + "github.com/a-h/templ" + "github.com/cufee/aftermath/cmd/frontend/components/widget" + "github.com/cufee/aftermath/cmd/frontend/handler" + "github.com/cufee/aftermath/cmd/frontend/routes/app/widgets" + "github.com/cufee/aftermath/internal/constants" + "github.com/cufee/aftermath/internal/database" + "github.com/cufee/aftermath/internal/database/models" +) + +type widgetSettingsPayload struct { + Title *string `form:"widget_title"` + AccountID *string `form:"account_id"` + Realm *string `form:"account_realm"` + Nickname *string `form:"account_nickname"` + ShowRatingOverview *bool `form:"rating_overview"` + ShowUnratedOverview *bool `form:"unrated_overview"` + ShowVehicles *bool `form:"unrated_vehicles"` + VehicleLimit *int `form:"vehicle_limit"` +} + +func (p *widgetSettingsPayload) updateOptions(s *models.WidgetOptions) { + if s == nil { + return + } + if v := p.Title; v != nil { + s.Title = *v + } + if v := p.AccountID; v != nil { + s.AccountID = *v + } + if v := p.ShowRatingOverview; v != nil { + s.Style.RatingOverview.Visible = *v + } + if v := p.ShowUnratedOverview; v != nil { + s.Style.UnratedOverview.Visible = *v + } + if v := p.ShowVehicles; v != nil { + s.Style.Vehicles.Visible = *v + } + if v := p.VehicleLimit; v != nil && *v >= 0 && *v <= 10 { + s.Style.Vehicles.Limit = *v + } +} + +func (p *widgetSettingsPayload) parse(values url.Values) { + v := reflect.ValueOf(p).Elem() // Get the value of the struct + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := v.Type().Field(i) + formTag := fieldType.Tag.Get("form") + + if formTag == "" { + continue + } + + if formValues, ok := values[formTag]; ok && len(formValues) > 0 { + switch field.Kind() { + case reflect.Ptr: + switch fieldType.Type.Elem().Kind() { + case reflect.String: + strVal := formValues[0] + field.Set(reflect.ValueOf(&strVal)) + case reflect.Bool: + boolVal, err := strconv.ParseBool(formValues[0]) + if err == nil { + field.Set(reflect.ValueOf(&boolVal)) + } + case reflect.Int: + intVal, err := strconv.Atoi(formValues[0]) + if err == nil { + field.Set(reflect.ValueOf(&intVal)) + } + } + } + } + } +} + +var CreateCustomWidget handler.Partial = func(ctx *handler.Context) (templ.Component, error) { + user, err := ctx.SessionUser() + if err != nil { + return nil, ctx.Redirect("/login", http.StatusTemporaryRedirect) + } + + existing, err := ctx.Database().GetUserWidgetSettings(ctx.Context, user.ID, nil) + if err != nil && !database.IsNotFound(err) { + return nil, ctx.Error(err, "failed to get user widgets") + } + if len(existing) >= constants.WidgetAccountLimit { + return nil, ctx.Error(errors.New("widget limit reached")) + } + + form, err := ctx.FormValues() + if err != nil { + return nil, ctx.Error(err, "invalid form data") + } + + var data widgetSettingsPayload + data.parse(form) + + var settings models.WidgetOptions + settings.Style = models.DefaultWidgetStyle + settings.UserID = user.ID + data.updateOptions(&settings) + + if settings.AccountID == "" { + var account models.Account + if data.Realm != nil { + account.Realm = *data.Realm + } + if data.Nickname != nil { + account.Nickname = *data.Nickname + } + if data.AccountID != nil { + account.ID = *data.AccountID + } + return widgets.NewWidgetPage(widget.WidgetWithAccount{WidgetOptions: settings, Account: account}, map[string]string{"account_id": "You need to select a user from the list of search results."}), nil + } + + created, err := ctx.Database().CreateWidgetSettings(ctx.Context, user.ID, settings) + if err != nil { + return nil, ctx.Error(err, "failed to create widget settings") + } + return nil, ctx.Redirect("/app/widgets/"+created.ID, http.StatusTemporaryRedirect) +} + +var UpdateCustomWidget handler.Partial = func(ctx *handler.Context) (templ.Component, error) { + user, err := ctx.SessionUser() + if err != nil { + return nil, ctx.Redirect("/login", http.StatusTemporaryRedirect) + } + + widgetID := ctx.Path("widgetId") + if widgetID == "" { + return nil, ctx.Error(errors.New("invalid widget id")) + } + + form, err := ctx.FormValues() + if err != nil { + return nil, ctx.Error(err, "invalid form data") + } + + var data widgetSettingsPayload + data.parse(form) + + settings, err := ctx.Database().GetWidgetSettings(ctx.Context, widgetID) + if err != nil { + if database.IsNotFound(err) { + return nil, ctx.Error(errors.New("widget not found")) + } + return nil, ctx.Error(err, "failed to get widget settings") + } + if settings.UserID != user.ID { + return nil, ctx.Redirect("/login", http.StatusTemporaryRedirect) + } + data.updateOptions(&settings) + + if settings.AccountID == "" { + var account models.Account + if data.Realm != nil { + account.Realm = *data.Realm + } + if data.Nickname != nil { + account.Nickname = *data.Nickname + } + if data.AccountID != nil { + account.ID = *data.AccountID + } + return widgets.WidgetConfiguratorPage(widget.WidgetWithAccount{WidgetOptions: settings, Account: account}, map[string]string{"account_id": "You need to select a user from the list of search results."}), nil + } + + updated, err := ctx.Database().UpdateWidgetSettings(ctx.Context, settings.ID, settings) + if err != nil { + return nil, ctx.Error(err, "failed to update widget settings") + } + + account, err := ctx.Database().GetAccountByID(ctx.Context, updated.AccountID) + if database.IsNotFound(err) { + account, err = ctx.Fetch().Account(ctx.Context, updated.AccountID) + } + if err != nil { + return nil, ctx.Error(err, "failed to get updated widget account") + } + + return widgets.WidgetConfiguratorPage(widget.WidgetWithAccount{WidgetOptions: updated, Account: account}, nil), nil +} + +var QuickAction handler.Partial = func(ctx *handler.Context) (templ.Component, error) { + user, err := ctx.SessionUser() + if err != nil { + return nil, ctx.Redirect("/login", http.StatusTemporaryRedirect) + } + + widgetID := ctx.Path("widgetId") + if widgetID == "" { + return nil, ctx.Error(errors.New("invalid widget id")) + } + + form, err := ctx.FormValues() + if err != nil { + return nil, ctx.Error(err, "invalid form data") + } + + var data widgetSettingsPayload + data.parse(form) + + settings, err := ctx.Database().GetWidgetSettings(ctx.Context, widgetID) + if err != nil { + if database.IsNotFound(err) { + return nil, ctx.Error(errors.New("widget not found")) + } + return nil, ctx.Error(err, "failed to get widget settings") + } + if settings.UserID != user.ID { + return nil, ctx.Redirect("/login", http.StatusTemporaryRedirect) + } + data.updateOptions(&settings) + + if settings.AccountID == "" { + return nil, ctx.Error(errors.New("widget has no account id set")) + } + + updated, err := ctx.Database().UpdateWidgetSettings(ctx.Context, settings.ID, settings) + if err != nil { + return nil, ctx.Error(err, "failed to update widget settings") + } + + return widget.QuickSettingButton(ctx.Query("field"), updated), nil +} diff --git a/cmd/frontend/routes/api/widget/personal.go b/cmd/frontend/routes/api/widget/personal.go deleted file mode 100644 index e9a1ee04..00000000 --- a/cmd/frontend/routes/api/widget/personal.go +++ /dev/null @@ -1,9 +0,0 @@ -package widget - -import ( - "github.com/cufee/aftermath/cmd/frontend/handler" -) - -var UpdateStyle handler.Endpoint = func(ctx *handler.Context) error { - return nil -} diff --git a/cmd/frontend/routes/app/index.templ b/cmd/frontend/routes/app/index.templ index 256c98cb..1e9f31cd 100644 --- a/cmd/frontend/routes/app/index.templ +++ b/cmd/frontend/routes/app/index.templ @@ -6,11 +6,11 @@ import ( wc "github.com/cufee/aftermath/cmd/frontend/components/widget" "github.com/cufee/aftermath/cmd/frontend/handler" "github.com/cufee/aftermath/cmd/frontend/layouts" + "github.com/cufee/aftermath/internal/constants" "github.com/cufee/aftermath/internal/database" "github.com/cufee/aftermath/internal/database/models" "net/http" "slices" - "time" ) var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Component, error) { @@ -26,7 +26,6 @@ var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Compo return nil, nil, ctx.Error(err, "failed to get widgets") } for _, widget := range settings { - widget.SessionFrom = time.Now() widgets = append(widgets, wc.WidgetWithAccount{WidgetOptions: widget}) if !slices.Contains(ids, widget.AccountID) { ids = append(ids, widget.AccountID) @@ -63,7 +62,7 @@ var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Compo } defaultConn, _ := user.Connection(models.ConnectionTypeWargaming, map[string]any{"default": true}) - return layouts.Main, index(user, connections, defaultConn.ID, 3, widgets, 10), nil + return layouts.Main, index(user, connections, defaultConn.ID, 3, widgets, constants.WidgetAccountLimit), nil } templ index(user *models.User, connections []components.ConnectionWithAccount, defaultConnID string, linkLimit int, widgets []wc.WidgetWithAccount, widgetsLimit int) { @@ -114,7 +113,7 @@ templ index(user *models.User, connections []components.ConnectionWithAccount, d
    BETA - + diff --git a/cmd/frontend/routes/app/index_templ.go b/cmd/frontend/routes/app/index_templ.go index bded986d..47827ca5 100644 --- a/cmd/frontend/routes/app/index_templ.go +++ b/cmd/frontend/routes/app/index_templ.go @@ -14,11 +14,11 @@ import ( wc "github.com/cufee/aftermath/cmd/frontend/components/widget" "github.com/cufee/aftermath/cmd/frontend/handler" "github.com/cufee/aftermath/cmd/frontend/layouts" + "github.com/cufee/aftermath/internal/constants" "github.com/cufee/aftermath/internal/database" "github.com/cufee/aftermath/internal/database/models" "net/http" "slices" - "time" ) var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Component, error) { @@ -34,7 +34,6 @@ var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Compo return nil, nil, ctx.Error(err, "failed to get widgets") } for _, widget := range settings { - widget.SessionFrom = time.Now() widgets = append(widgets, wc.WidgetWithAccount{WidgetOptions: widget}) if !slices.Contains(ids, widget.AccountID) { ids = append(ids, widget.AccountID) @@ -71,7 +70,7 @@ var Index handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Compo } defaultConn, _ := user.Connection(models.ConnectionTypeWargaming, map[string]any{"default": true}) - return layouts.Main, index(user, connections, defaultConn.ID, 3, widgets, 10), nil + return layouts.Main, index(user, connections, defaultConn.ID, 3, widgets, constants.WidgetAccountLimit), nil } func index(user *models.User, connections []components.ConnectionWithAccount, defaultConnID string, linkLimit int, widgets []wc.WidgetWithAccount, widgetsLimit int) templ.Component { @@ -99,7 +98,7 @@ func index(user *models.User, connections []components.ConnectionWithAccount, de var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d/%d", len(connections), linkLimit)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/frontend/routes/app/index.templ`, Line: 72, Col: 69} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/frontend/routes/app/index.templ`, Line: 71, Col: 69} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -128,13 +127,13 @@ func index(user *models.User, connections []components.ConnectionWithAccount, de var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d/%d", len(widgets), widgetsLimit)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/frontend/routes/app/index.templ`, Line: 112, Col: 68} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `cmd/frontend/routes/app/index.templ`, Line: 111, Col: 68} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    BETA
    ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    BETA
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/cmd/frontend/routes/app/widgets/edit.templ b/cmd/frontend/routes/app/widgets/edit.templ index 67aae305..67f9d0d1 100644 --- a/cmd/frontend/routes/app/widgets/edit.templ +++ b/cmd/frontend/routes/app/widgets/edit.templ @@ -42,10 +42,10 @@ var EditSettings handler.Page = func(ctx *handler.Context) (handler.Layout, temp return nil, nil, errors.New("invalid account id") } - return layouts.Main, widgetConfiguratorPage(widget.WidgetWithAccount{options, account}), nil + return layouts.Main, WidgetConfiguratorPage(widget.WidgetWithAccount{WidgetOptions: options, Account: account}, nil), nil } -templ widgetConfiguratorPage(options widget.WidgetWithAccount) { +templ WidgetConfiguratorPage(options widget.WidgetWithAccount, errors map[string]string) {
    - @widget.CustomOptionsForm(options, widgetOptionsSave(), templ.Attributes{ + @widget.CustomOptionsForm(options, widgetOptionsSave(options.ID), templ.Attributes{ "hx-swap": "outerHTML", - "hx-target": "#form-content", - "hx-select": "#form-content", - "hx-patch": "/api/widget/custom/" + options.ID, - }) + "hx-target": "#widget-style-settings", + "hx-select": "#widget-style-settings", + "hx-patch": "/api/widget/custom/" + options.ID + "/", + }, errors)
    } -templ widgetOptionsSave() { - +templ widgetOptionsSave(id string) { +
    + + +
    +} + +script copyButtonAction(id string) { + const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + id + "/live/" + navigator.clipboard.writeText(url); + + const btn = document.getElementById("copy-widget-link") + const oldText = btn.textContent + btn.textContent = "Copied!"; + btn.classList.add("btn-success"); + setTimeout(()=> { + btn.textContent = oldText; + btn.classList.remove("btn-success"); + }, 2000) } diff --git a/cmd/frontend/routes/app/widgets/edit_templ.go b/cmd/frontend/routes/app/widgets/edit_templ.go index 63ba1abd..c3dc1247 100644 --- a/cmd/frontend/routes/app/widgets/edit_templ.go +++ b/cmd/frontend/routes/app/widgets/edit_templ.go @@ -50,10 +50,10 @@ var EditSettings handler.Page = func(ctx *handler.Context) (handler.Layout, temp return nil, nil, errors.New("invalid account id") } - return layouts.Main, widgetConfiguratorPage(widget.WidgetWithAccount{options, account}), nil + return layouts.Main, WidgetConfiguratorPage(widget.WidgetWithAccount{WidgetOptions: options, Account: account}, nil), nil } -func widgetConfiguratorPage(options widget.WidgetWithAccount) templ.Component { +func WidgetConfiguratorPage(options widget.WidgetWithAccount, errors map[string]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 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -75,12 +75,12 @@ func widgetConfiguratorPage(options widget.WidgetWithAccount) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = widget.CustomOptionsForm(options, widgetOptionsSave(), templ.Attributes{ + templ_7745c5c3_Err = widget.CustomOptionsForm(options, widgetOptionsSave(options.ID), templ.Attributes{ "hx-swap": "outerHTML", - "hx-target": "#form-content", - "hx-select": "#form-content", - "hx-patch": "/api/widget/custom/" + options.ID, - }).Render(ctx, templ_7745c5c3_Buffer) + "hx-target": "#widget-style-settings", + "hx-select": "#widget-style-settings", + "hx-patch": "/api/widget/custom/" + options.ID + "/", + }, errors).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -92,7 +92,7 @@ func widgetConfiguratorPage(options widget.WidgetWithAccount) templ.Component { }) } -func widgetOptionsSave() templ.Component { +func widgetOptionsSave(id 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 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -110,10 +110,47 @@ func widgetOptionsSave() templ.Component { templ_7745c5c3_Var2 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, copyButtonAction(id)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return templ_7745c5c3_Err }) } + +func copyButtonAction(id string) templ.ComponentScript { + return templ.ComponentScript{ + Name: `__templ_copyButtonAction_48ed`, + Function: `function __templ_copyButtonAction_48ed(id){const url = window.location.protocol + "//" + window.location.host + "/widget/custom/" + id + "/live/" + navigator.clipboard.writeText(url); + + const btn = document.getElementById("copy-widget-link") + const oldText = btn.textContent + btn.textContent = "Copied!"; + btn.classList.add("btn-success"); + setTimeout(()=> { + btn.textContent = oldText; + btn.classList.remove("btn-success"); + }, 2000) +}`, + Call: templ.SafeScript(`__templ_copyButtonAction_48ed`, id), + CallInline: templ.SafeScriptInline(`__templ_copyButtonAction_48ed`, id), + } +} diff --git a/cmd/frontend/routes/app/widgets/new.templ b/cmd/frontend/routes/app/widgets/new.templ index 1d2d7df5..afb29940 100644 --- a/cmd/frontend/routes/app/widgets/new.templ +++ b/cmd/frontend/routes/app/widgets/new.templ @@ -8,18 +8,18 @@ import ( ) var NewCustom handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Component, error) { - return layouts.Main, newWidgetPage(widget.WidgetWithAccount{WidgetOptions: models.WidgetOptions{Style: models.DefaultWidgetStyle}}), nil + return layouts.Main, NewWidgetPage(widget.WidgetWithAccount{WidgetOptions: models.WidgetOptions{Style: models.DefaultWidgetStyle}}, nil), nil } -templ newWidgetPage(options widget.WidgetWithAccount) { +templ NewWidgetPage(options widget.WidgetWithAccount, errors map[string]string) {
    @widget.CustomOptionsForm(options, newWidgetCreate(), templ.Attributes{ + "hx-post": "/api/widget/custom/", + "hx-target": "#widget-style-settings", + "hx-select": "#widget-style-settings", "hx-swap": "outerHTML", - "hx-target": "#form-content", - "hx-select": "#form-content", - "hx-post": "/api/widget/custom", - }) + }, errors)
    } diff --git a/cmd/frontend/routes/app/widgets/new_templ.go b/cmd/frontend/routes/app/widgets/new_templ.go index 7f0ec83c..62505eb8 100644 --- a/cmd/frontend/routes/app/widgets/new_templ.go +++ b/cmd/frontend/routes/app/widgets/new_templ.go @@ -16,10 +16,10 @@ import ( ) var NewCustom handler.Page = func(ctx *handler.Context) (handler.Layout, templ.Component, error) { - return layouts.Main, newWidgetPage(widget.WidgetWithAccount{WidgetOptions: models.WidgetOptions{Style: models.DefaultWidgetStyle}}), nil + return layouts.Main, NewWidgetPage(widget.WidgetWithAccount{WidgetOptions: models.WidgetOptions{Style: models.DefaultWidgetStyle}}, nil), nil } -func newWidgetPage(options widget.WidgetWithAccount) templ.Component { +func NewWidgetPage(options widget.WidgetWithAccount, errors map[string]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 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -42,11 +42,11 @@ func newWidgetPage(options widget.WidgetWithAccount) templ.Component { return templ_7745c5c3_Err } templ_7745c5c3_Err = widget.CustomOptionsForm(options, newWidgetCreate(), templ.Attributes{ + "hx-post": "/api/widget/custom/", + "hx-target": "#widget-style-settings", + "hx-select": "#widget-style-settings", "hx-swap": "outerHTML", - "hx-target": "#form-content", - "hx-select": "#form-content", - "hx-post": "/api/widget/custom", - }).Render(ctx, templ_7745c5c3_Buffer) + }, errors).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/cmd/frontend/routes/widget/live.templ b/cmd/frontend/routes/widget/live.templ index 4eda6a9f..6fd85143 100644 --- a/cmd/frontend/routes/widget/live.templ +++ b/cmd/frontend/routes/widget/live.templ @@ -47,7 +47,7 @@ var CustomLiveWidget handler.Page = func(ctx *handler.Context) (handler.Layout, return nil, nil, err } - return layouts.Main, customLiveWidget(widget.Widget(account, cards, widget.WithAutoReload(), widget.WithStyle(settings.Style))), nil + return layouts.StyleOnly, customLiveWidget(widget.Widget(account, cards, widget.WithAutoReload(), widget.WithStyle(settings.Style))), nil } templ customLiveWidget(widget templ.Component) { diff --git a/cmd/frontend/routes/widget/live_templ.go b/cmd/frontend/routes/widget/live_templ.go index 8d7e7798..4812a908 100644 --- a/cmd/frontend/routes/widget/live_templ.go +++ b/cmd/frontend/routes/widget/live_templ.go @@ -55,7 +55,7 @@ var CustomLiveWidget handler.Page = func(ctx *handler.Context) (handler.Layout, return nil, nil, err } - return layouts.Main, customLiveWidget(widget.Widget(account, cards, widget.WithAutoReload(), widget.WithStyle(settings.Style))), nil + return layouts.StyleOnly, customLiveWidget(widget.Widget(account, cards, widget.WithAutoReload(), widget.WithStyle(settings.Style))), nil } func customLiveWidget(widget templ.Component) templ.Component { diff --git a/cmd/frontend/routes/widget/preview.templ b/cmd/frontend/routes/widget/preview.templ index aa509f44..e4f8476e 100644 --- a/cmd/frontend/routes/widget/preview.templ +++ b/cmd/frontend/routes/widget/preview.templ @@ -55,7 +55,7 @@ templ widgetPreview(accountID string, widget templ.Component, or, ou bool, vl in } script copyButtonAction() { - const url = window.location.protocol + "//" + window.location.host + window.location.pathname + "/live" + window.location.search + const url = window.location.protocol + "//" + window.location.host + window.location.pathname + "live" + window.location.search navigator.clipboard.writeText(url); const btn = document.getElementById("copy-widget-link") diff --git a/cmd/frontend/routes/widget/preview_templ.go b/cmd/frontend/routes/widget/preview_templ.go index 70ecbaef..8f12fe3a 100644 --- a/cmd/frontend/routes/widget/preview_templ.go +++ b/cmd/frontend/routes/widget/preview_templ.go @@ -119,8 +119,8 @@ func widgetPreview(accountID string, widget templ.Component, or, ou bool, vl int func copyButtonAction() templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_copyButtonAction_88e1`, - Function: `function __templ_copyButtonAction_88e1(){const url = window.location.protocol + "//" + window.location.host + window.location.pathname + "/live" + window.location.search + Name: `__templ_copyButtonAction_e532`, + Function: `function __templ_copyButtonAction_e532(){const url = window.location.protocol + "//" + window.location.host + window.location.pathname + "live" + window.location.search navigator.clipboard.writeText(url); const btn = document.getElementById("copy-widget-link") @@ -132,8 +132,8 @@ func copyButtonAction() templ.ComponentScript { btn.classList.remove("btn-success"); }, 2000) }`, - Call: templ.SafeScript(`__templ_copyButtonAction_88e1`), - CallInline: templ.SafeScriptInline(`__templ_copyButtonAction_88e1`), + Call: templ.SafeScript(`__templ_copyButtonAction_e532`), + CallInline: templ.SafeScriptInline(`__templ_copyButtonAction_e532`), } } diff --git a/internal/constants/widget.go b/internal/constants/widget.go index d42a4778..bec00b95 100644 --- a/internal/constants/widget.go +++ b/internal/constants/widget.go @@ -4,4 +4,5 @@ import "time" var ( WidgetSessionResetTimeout time.Duration = time.Second * 60 + WidgetAccountLimit int = 3 ) diff --git a/internal/database/client.go b/internal/database/client.go index 835adc00..4aba817b 100644 --- a/internal/database/client.go +++ b/internal/database/client.go @@ -62,8 +62,8 @@ type UsersClient interface { GetWidgetSettings(ctx context.Context, settingsID string) (models.WidgetOptions, error) GetUserWidgetSettings(ctx context.Context, userID string, referenceID []string) ([]models.WidgetOptions, error) - UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) error - CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) error + UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) (models.WidgetOptions, error) + CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) (models.WidgetOptions, error) } type SnapshotsClient interface { diff --git a/internal/database/widget_settings.go b/internal/database/widget_settings.go index e82212a2..a61dbad4 100644 --- a/internal/database/widget_settings.go +++ b/internal/database/widget_settings.go @@ -51,38 +51,38 @@ func (c *client) GetUserWidgetSettings(ctx context.Context, userID string, refer return options, nil } -func (c *client) CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) error { +func (c *client) CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) (models.WidgetOptions, error) { user, err := c.db.User.Get(ctx, userID) if err != nil { - return err + return models.WidgetOptions{}, err } - err = c.db.WidgetSettings.Create(). + created, err := c.db.WidgetSettings.Create(). SetTitle(settings.Title). SetMetadata(settings.Meta). SetReferenceID(settings.AccountID). SetSessionFrom(settings.SessionFrom). SetStyles(settings.Style). SetUser(user). - Exec(ctx) + Save(ctx) if err != nil { - return err + return models.WidgetOptions{}, err } - return nil + return toWidgetOptions(created), nil } -func (c *client) UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) error { - err := c.db.WidgetSettings.UpdateOneID(id). +func (c *client) UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) (models.WidgetOptions, error) { + updated, err := c.db.WidgetSettings.UpdateOneID(id). SetTitle(settings.Title). SetMetadata(settings.Meta). SetReferenceID(settings.AccountID). SetSessionFrom(settings.SessionFrom). SetStyles(settings.Style). - Exec(ctx) + Save(ctx) if err != nil { - return err + return models.WidgetOptions{}, err } - return nil + return toWidgetOptions(updated), nil } diff --git a/tests/static_database.go b/tests/static_database.go index e48909de..cc560cec 100644 --- a/tests/static_database.go +++ b/tests/static_database.go @@ -263,9 +263,11 @@ func (c *staticTestingDatabase) GetUserWidgetSettings(ctx context.Context, userI s, err := c.GetWidgetSettings(ctx, userID) return []models.WidgetOptions{s}, err } -func (c *staticTestingDatabase) UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) error { - return errors.New("UpdateWidgetSettings not implemented") +func (c *staticTestingDatabase) UpdateWidgetSettings(ctx context.Context, id string, settings models.WidgetOptions) (models.WidgetOptions, error) { + return settings, nil } -func (c *staticTestingDatabase) CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) error { - return errors.New("CreateWidgetSettings not implemented") +func (c *staticTestingDatabase) CreateWidgetSettings(ctx context.Context, userID string, settings models.WidgetOptions) (models.WidgetOptions, error) { + settings.ID = fmt.Sprint(time.Now().Unix()) + settings.UserID = userID + return settings, nil }