From 63122eb4283d7259be112facb7ff5944944575be Mon Sep 17 00:00:00 2001 From: ari melody Date: Sat, 31 Aug 2024 15:25:44 +0100 Subject: [PATCH] tracks can be edited! + major template overhaul --- .air.toml | 2 +- .../components/release/release-list-item.html | 23 ++ admin/http.go | 107 +++------ admin/releasehttp.go | 62 +++-- admin/static/admin.css | 2 + admin/static/edit-release.css | 12 +- admin/static/edit-release.js | 157 +++++++------ admin/static/edit-track.css | 219 ++++++++++++++++++ admin/static/edit-track.js | 58 +++++ admin/static/index.css | 9 + admin/templates.go | 50 ++++ admin/trackhttp.go | 28 +++ admin/views/edit-release.html | 16 +- admin/views/edit-track.html | 68 ++++++ admin/views/index.html | 29 +-- admin/views/layout.html | 3 +- admin/views/login.html | 17 +- admin/views/logout.html | 2 +- api/release.go | 9 +- global/data.go | 7 +- views/music-gateway.html | 15 +- 21 files changed, 674 insertions(+), 221 deletions(-) create mode 100644 admin/components/release/release-list-item.html create mode 100644 admin/static/edit-track.css create mode 100644 admin/static/edit-track.js create mode 100644 admin/templates.go create mode 100644 admin/trackhttp.go create mode 100644 admin/views/edit-track.html diff --git a/.air.toml b/.air.toml index 3f45b18..4900634 100644 --- a/.air.toml +++ b/.air.toml @@ -14,7 +14,7 @@ tmp_dir = "tmp" follow_symlink = false full_bin = "" include_dir = [] - include_ext = ["go", "tpl", "tmpl"] + include_ext = ["go", "tpl", "tmpl", "html"] include_file = [] kill_delay = "0s" log = "build-errors.log" diff --git a/admin/components/release/release-list-item.html b/admin/components/release/release-list-item.html new file mode 100644 index 0000000..44511d8 --- /dev/null +++ b/admin/components/release/release-list-item.html @@ -0,0 +1,23 @@ +{{define "release"}} +
+
+ +
+
+

+ {{.Title}} + + {{.GetReleaseYear}} + {{if not .Visible}}(hidden){{end}} + +

+

{{.PrintArtists true true}}

+

{{.ReleaseType}} + ({{len .Tracks}} track{{if not (eq (len .Tracks) 1)}}s{{end}})

+
+ Edit + Gateway +
+
+
+{{end}} diff --git a/admin/http.go b/admin/http.go index 6c24167..b69d8dd 100644 --- a/admin/http.go +++ b/admin/http.go @@ -12,6 +12,7 @@ import ( "arimelody.me/arimelody.me/discord" "arimelody.me/arimelody.me/global" + musicController "arimelody.me/arimelody.me/music/controller" musicModel "arimelody.me/arimelody.me/music/model" ) @@ -27,6 +28,19 @@ func Handler() http.Handler { mux.Handle("/logout", MustAuthorise(LogoutHandler())) mux.Handle("/static/", http.StripPrefix("/static", staticHandler())) mux.Handle("/release/", MustAuthorise(http.StripPrefix("/release", serveRelease()))) + mux.Handle("/track/", MustAuthorise(http.StripPrefix("/track", serveTrack()))) + mux.Handle("/createtrack", MustAuthorise(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + track := musicModel.Track{ Title: "Untitled Track" } + trackID, err := musicController.CreateTrackDB(global.DB, &track) + if err != nil { + fmt.Printf("Failed to create track: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + track.ID = trackID + global.Tracks = append(global.Tracks, &track) + http.Redirect(w, r, fmt.Sprintf("/admin/track/%s", trackID), http.StatusTemporaryRedirect) + }))) mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) @@ -61,11 +75,16 @@ func Handler() http.Handler { }) } - serveTemplate("index.html", IndexData{ + err := pages["index"].Execute(w, IndexData{ Releases: global.Releases, Artists: global.Artists, Tracks: tracks, - }).ServeHTTP(w, r) + }) + if err != nil { + fmt.Printf("Error executing template: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } })) return mux @@ -144,7 +163,7 @@ func LoginHandler() http.Handler { code := r.URL.Query().Get("code") if code == "" { - serveTemplate("login.html", loginData{DiscordURI: discord.REDIRECT_URI}).ServeHTTP(w, r) + pages["login"].Execute(w, loginData{DiscordURI: discord.REDIRECT_URI}) return } @@ -183,7 +202,12 @@ func LoginHandler() http.Handler { cookie.Path = "/" http.SetCookie(w, &cookie) - serveTemplate("login.html", loginData{Token: session.Token}).ServeHTTP(w, r) + err = pages["login"].Execute(w, loginData{Token: session.Token}) + if err != nil { + fmt.Printf("Error rendering admin login page: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } }) } @@ -207,78 +231,15 @@ func LogoutHandler() http.Handler { return new_sessions }(session.Token) - serveTemplate("logout.html", nil).ServeHTTP(w, r) + err := pages["logout"].Execute(w, nil) + if err != nil { + fmt.Printf("Error rendering admin logout page: %s\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } }) } -func serveTemplate(page string, data any) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - lp_layout := filepath.Join("admin", "views", "layout.html") - lp_prideflag := filepath.Join("views", "prideflag.html") - fp := filepath.Join("admin", "views", filepath.Clean(page)) - - info, err := os.Stat(fp) - if err != nil { - if os.IsNotExist(err) { - http.NotFound(w, r) - return - } - } - - if info.IsDir() { - http.NotFound(w, r) - return - } - - template, err := template.ParseFiles(lp_layout, lp_prideflag, fp) - if err != nil { - fmt.Printf("Error parsing template files: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = template.ExecuteTemplate(w, "layout.html", data) - if err != nil { - fmt.Printf("Error executing template: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - }) -} - -func serveComponent(page string, data any) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fp := filepath.Join("admin", "components", filepath.Clean(page)) - - info, err := os.Stat(fp) - if err != nil { - if os.IsNotExist(err) { - http.NotFound(w, r) - return - } - } - - if info.IsDir() { - http.NotFound(w, r) - return - } - - template, err := template.ParseFiles(fp) - if err != nil { - fmt.Printf("Error parsing template files: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - err = template.Execute(w, data); - if err != nil { - fmt.Printf("Error executing template: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - }) -} - func staticHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { info, err := os.Stat(filepath.Join("admin", "static", filepath.Clean(r.URL.Path))) diff --git a/admin/releasehttp.go b/admin/releasehttp.go index 8ca55b2..2932c26 100644 --- a/admin/releasehttp.go +++ b/admin/releasehttp.go @@ -4,7 +4,6 @@ import ( "fmt" "html/template" "net/http" - "path" "strings" "arimelody.me/arimelody.me/global" @@ -71,13 +70,10 @@ func serveRelease() http.Handler { }) } - lrw := global.LoggingResponseWriter{ResponseWriter: w, Code: http.StatusOK} - - serveTemplate("edit-release.html", gatewayRelease{release, tracks}).ServeHTTP(&lrw, r) - - if lrw.Code != http.StatusOK { - fmt.Printf("Error rendering admin release page for %s\n", id) - return + err := pages["release"].Execute(w, gatewayRelease{release, tracks}) + if err != nil { + fmt.Printf("Error rendering admin release page for %s: %s\n", id, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } }) } @@ -85,8 +81,11 @@ func serveRelease() http.Handler { func serveEditCredits(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("credits", "editcredits.html"), release).ServeHTTP(w, r) - return + err := components["editcredits"].Execute(w, release) + if err != nil { + fmt.Printf("Error rendering edit credits component for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } }) } @@ -112,11 +111,14 @@ func serveAddCredit(release *model.Release) http.Handler { } w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("credits", "addcredit.html"), response{ + err := components["addcredit"].Execute(w, response{ ReleaseID: release.ID, Artists: artists, - }).ServeHTTP(w, r) - return + }) + if err != nil { + fmt.Printf("Error rendering add credits component for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } }) } @@ -129,7 +131,11 @@ func serveNewCredit() http.Handler { } w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("credits", "newcredit.html"), artist).ServeHTTP(w, r) + err := components["newcredit"].Execute(w, artist) + if err != nil { + fmt.Printf("Error rendering new credit component for %s: %s\n", artist.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } return }) } @@ -137,7 +143,11 @@ func serveNewCredit() http.Handler { func serveEditLinks(release *model.Release) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("links", "editlinks.html"), release).ServeHTTP(w, r) + err := components["editlinks"].Execute(w, release) + if err != nil { + fmt.Printf("Error rendering edit links component for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } return }) } @@ -158,7 +168,11 @@ func serveEditTracks(release *model.Release) http.Handler { data.Tracks = append(data.Tracks, Track{track, i + 1}) } - serveComponent(path.Join("tracks", "edittracks.html"), data).ServeHTTP(w, r) + err := components["edittracks"].Execute(w, data) + if err != nil { + fmt.Printf("Error rendering edit tracks component for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } return }) } @@ -185,10 +199,14 @@ func serveAddTrack(release *model.Release) http.Handler { } w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("tracks", "addtrack.html"), response{ + err := components["addtrack"].Execute(w, response{ ReleaseID: release.ID, Tracks: tracks, - }).ServeHTTP(w, r) + }) + if err != nil { + fmt.Printf("Error rendering add tracks component for %s: %s\n", release.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } return }) } @@ -207,10 +225,14 @@ func serveNewTrack(release *model.Release) http.Handler { } w.Header().Set("Content-Type", "text/html") - serveComponent(path.Join("tracks", "newtrack.html"), Track{ + err := components["newtrack"].Execute(w, Track{ track, len(release.Tracks) + 1, - }).ServeHTTP(w, r) + }) + if err != nil { + fmt.Printf("Error rendering new track component for %s: %s\n", track.ID, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } return }) } diff --git a/admin/static/admin.css b/admin/static/admin.css index 9073d27..9092980 100644 --- a/admin/static/admin.css +++ b/admin/static/admin.css @@ -93,10 +93,12 @@ a img { margin: 0 0 .5em 0; } +/* .card h3, .card p { margin: 0; } +*/ .card-title { margin-bottom: 1em; diff --git a/admin/static/edit-release.css b/admin/static/edit-release.css index 30c96cb..7321424 100644 --- a/admin/static/edit-release.css +++ b/admin/static/edit-release.css @@ -87,9 +87,8 @@ input[type="text"] { .release-info table td select, .release-info table td input, .release-info table td textarea { - padding: .2em; - resize: none; width: 100%; + padding: .2em; font-family: inherit; font-size: inherit; color: inherit; @@ -212,6 +211,10 @@ dialog div.dialog-actions { border: 1px solid #808080; } +.card.credits .credit p { + margin: 0; +} + .card.credits .credit .artist-avatar { border-radius: .5em; } @@ -441,6 +444,11 @@ dialog div.dialog-actions { border: 1px solid #808080; } +.card.tracks .track h3, +.card.tracks .track p { + margin: 0; +} + .card.tracks h2.track-title { margin: 0; display: flex; diff --git a/admin/static/edit-release.js b/admin/static/edit-release.js index 1e9fa29..4273d97 100644 --- a/admin/static/edit-release.js +++ b/admin/static/edit-release.js @@ -1,112 +1,127 @@ import Stateful from "/script/silver.min.js" const releaseID = document.getElementById("release").dataset.id; -const title_input = document.getElementById("title"); -const artwork_img = document.getElementById("artwork"); -const artwork_input = document.getElementById("artwork-file"); -const type_input = document.getElementById("type"); -const desc_input = document.getElementById("description"); -const date_input = document.getElementById("release-date"); -const buyname_input = document.getElementById("buyname"); -const buylink_input = document.getElementById("buylink"); -const vis_input = document.getElementById("visibility"); -const save_btn = document.getElementById("save"); - -var artwork_data = artwork_img.attributes.src.value; - -var token = atob(localStorage.getItem("arime-token")); +const titleInput = document.getElementById("title"); +const artworkImg = document.getElementById("artwork"); +const artworkInput = document.getElementById("artwork-file"); +const typeInput = document.getElementById("type"); +const descInput = document.getElementById("description"); +const dateInput = document.getElementById("release-date"); +const buynameInput = document.getElementById("buyname"); +const buylinkInput = document.getElementById("buylink"); +const visInput = document.getElementById("visibility"); +const saveBtn = document.getElementById("save"); +const deleteBtn = document.getElementById("delete"); +var artworkData = artworkImg.attributes.src.value; var edited = new Stateful(false); +var releaseData = updateData(undefined); -var release_data = update_data(undefined); - -function update_data(old) { - var release_data = { - visible: vis_input.value === "true", - title: title_input.value, - description: desc_input.value, - type: type_input.value, - releaseDate: date_input.value, - artwork: artwork_data, - buyname: buyname_input.value, - buylink: buylink_input.value, +function updateData(old) { + var releaseData = { + visible: visInput.value === "true", + title: titleInput.value, + description: descInput.value, + type: typeInput.value, + releaseDate: dateInput.value, + artwork: artworkData, + buyname: buynameInput.value, + buylink: buylinkInput.value, }; - if (release_data && release_data != old) { + if (releaseData && releaseData != old) { edited.set(true); } - return release_data; + return releaseData; } -function save_release() { - console.table(release_data); - - (async () => { - const res = await fetch( - "/api/v1/music/" + releaseID, { - method: "PUT", - body: JSON.stringify(release_data), - headers: { - "Content-Type": "application/json", - "Authorisation": "Bearer " + token, - }, - }); +function saveRelease() { + console.table(releaseData); + fetch("/api/v1/music/" + releaseID, { + method: "PUT", + body: JSON.stringify(releaseData), + headers: { "Content-Type": "application/json" } + }).then(res => { if (!res.ok) { - const text = await res.text(); - console.error(text); - alert(text); + res.text().then(error => { + console.error(error); + alert("Failed to update release: " + error); + }); return; } location = location; - })(); + }); +} + +function deleteRelease() { + fetch("/api/v1/music/" + releaseID, { + method: "DELETE", + }).then(res => { + if (!res.ok) { + res.text().then(error => { + console.error(error); + alert("Failed to delete release: " + error); + }); + return; + } + + location = "/admin"; + }); } edited.onUpdate(edited => { - save_btn.disabled = !edited; + saveBtn.disabled = !edited; }) -title_input.addEventListener("change", () => { - release_data = update_data(release_data); +titleInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -artwork_img.addEventListener("click", () => { - artwork_input.addEventListener("change", () => { - if (artwork_input.files.length > 0) { +artworkImg.addEventListener("click", () => { + artworkInput.addEventListener("change", () => { + if (artworkInput.files.length > 0) { const reader = new FileReader(); reader.onload = e => { const data = e.target.result; - artwork_img.src = data; - artwork_data = data; - release_data = update_data(release_data); + artworkImg.src = data; + artworkData = data; + releaseData = updateData(releaseData); }; - reader.readAsDataURL(artwork_input.files[0]); + reader.readAsDataURL(artworkInput.files[0]); } }); - artwork_input.click(); + artworkInput.click(); }); -type_input.addEventListener("change", () => { - release_data = update_data(release_data); +typeInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -desc_input.addEventListener("change", () => { - release_data = update_data(release_data); +descInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -date_input.addEventListener("change", () => { - release_data = update_data(release_data); +dateInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -buyname_input.addEventListener("change", () => { - release_data = update_data(release_data); +buynameInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -buylink_input.addEventListener("change", () => { - release_data = update_data(release_data); +buylinkInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -vis_input.addEventListener("change", () => { - release_data = update_data(release_data); +visInput.addEventListener("change", () => { + releaseData = updateData(releaseData); }); -save_btn.addEventListener("click", () => { +saveBtn.addEventListener("click", () => { if (!edited.get()) return; + saveRelease(); +}); - save_release(); -}) +deleteBtn.addEventListener("click", () => { + if (releaseID != prompt( + "You are about to permanently delete " + releaseID + ". " + + "This action is irreversible. " + + "Please enter \"" + releaseID + "\" to continue.")) return; + deleteRelease(); +}); diff --git a/admin/static/edit-track.css b/admin/static/edit-track.css new file mode 100644 index 0000000..9d60619 --- /dev/null +++ b/admin/static/edit-track.css @@ -0,0 +1,219 @@ +h1 { + margin: 0 0 .5em 0; +} + +#track { + margin-bottom: 1em; + padding: 1.5em; + display: flex; + flex-direction: row; + gap: 1.2em; + + border-radius: .5em; + background: #f8f8f8f8; + border: 1px solid #808080; +} + +.track-info { + width: 100%; + margin: 0; + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.track-title-header { + margin: 0; + opacity: .5; +} + +.track-title { + margin: 0; +} + +#title { + width: 100%; + margin: -.1em -.2em; + padding: .1em .2em; + font-weight: bold; + font-size: inherit; + border-radius: 4px; + border: 1px solid transparent; + background: transparent; + outline: none; +} + +#title:hover { + background: #ffffff; + border-color: #80808080; +} + +#title:active, +#title:focus { + background: #ffffff; + border-color: #808080; +} + +.track-title small { + opacity: .75; +} + +.track-info h2 { + margin-bottom: .4em; +} + +.track-info textarea { + width: 100%; + padding: .5em; + font-family: inherit; + font-size: inherit; + color: inherit; + border: none; + outline: none; + resize: vertical; + border-radius: 4px; +} + +button, .button { + padding: .5em .8em; + font-family: inherit; + font-size: inherit; + border-radius: .5em; + border: 1px solid #a0a0a0; + background: #f0f0f0; + color: inherit; +} +button:hover, .button:hover { + background: #fff; + border-color: #d0d0d0; +} +button:active, .button:active { + background: #d0d0d0; + border-color: #808080; +} + +button { + color: inherit; +} +button.save { + background: #6fd7ff; + border-color: #6f9eb0; +} +button.delete { + background: #ff7171; + border-color: #7d3535; +} +button:hover { + background: #fff; + border-color: #d0d0d0; +} +button:active { + background: #d0d0d0; + border-color: #808080; +} +button[disabled] { + background: #d0d0d0 !important; + border-color: #808080 !important; + opacity: .5; + cursor: not-allowed !important; +} + +a.delete { + color: #d22828; +} + +.track-actions { + margin-top: 1em; + display: flex; + gap: .5em; + flex-direction: row; + justify-content: right; +} + +.release { + margin-bottom: 1em; + padding: 1em; + display: flex; + flex-direction: row; + gap: 1em; + + border-radius: .5em; + background: #f8f8f8f8; + border: 1px solid #808080; +} + +.release h3, +.release p { + margin: 0; +} + +.release-artwork { + width: 96px; + + display: flex; + justify-content: center; + align-items: center; +} + +.release-artwork img { + width: 100%; + aspect-ratio: 1; +} + +.release-title small { + opacity: .75; +} + +.release-links { + margin: .5em 0; + padding: 0; + display: flex; + flex-direction: row; + list-style: none; + flex-wrap: wrap; + gap: .5em; +} + +.release-links li { + flex-grow: 1; +} + +.release-links a { + padding: .5em; + display: block; + + border-radius: .5em; + text-decoration: none; + color: #f0f0f0; + background: #303030; + text-align: center; + + transition: color .1s, background .1s; +} + +.release-links a:hover { + color: #303030; + background: #f0f0f0; +} + +.release-actions { + margin-top: .5em; +} + +.release-actions a { + margin-right: .3em; + padding: .3em .5em; + display: inline-block; + + border-radius: .3em; + background: #e0e0e0; + + transition: color .1s, background .1s; +} + +.release-actions a:hover { + color: #303030; + background: #f0f0f0; + + text-decoration: none; +} diff --git a/admin/static/edit-track.js b/admin/static/edit-track.js new file mode 100644 index 0000000..49fc25e --- /dev/null +++ b/admin/static/edit-track.js @@ -0,0 +1,58 @@ +const trackID = document.getElementById("track").dataset.id; +const titleInput = document.getElementById("title"); +const descInput = document.getElementById("description"); +const lyricsInput = document.getElementById("lyrics"); +const saveBtn = document.getElementById("save"); +const deleteBtn = document.getElementById("delete"); + +saveBtn.addEventListener("click", () => { + fetch("/api/v1/track/" + trackID, { + method: "PUT", + body: JSON.stringify({ + title: titleInput.value, + description: descInput.value, + lyrics: lyricsInput.value, + }), + headers: { "Content-Type": "application/json" } + }).then(res => { + if (!res.ok) { + res.text().then(error => { + console.error(error); + alert("Failed to update track: " + error); + }); + return; + } + + location = location; + }); +}); + +deleteBtn.addEventListener("click", () => { + if (!confirm( + "You are about to permanently delete \"" + titleInput.value + "\".\n" + + "This action is irreversible. Do you wish to continue?")) return; + + fetch("/api/v1/track/" + trackID, { + method: "DELETE", + }).then(res => { + if (!res.ok) { + res.text().then(error => { + console.error(error); + alert("Failed to delete track: " + error); + }); + return; + } + + location = "/admin"; + }); +}); + +[titleInput, descInput, lyricsInput].forEach(input => { + input.addEventListener("change", () => { + saveBtn.disabled = false; + }); + input.addEventListener("keypress", () => { + saveBtn.disabled = false; + }); +}); + diff --git a/admin/static/index.css b/admin/static/index.css index 37bde08..fd603a3 100644 --- a/admin/static/index.css +++ b/admin/static/index.css @@ -28,6 +28,11 @@ border: 1px solid #808080; } +.release h3, +.release p { + margin: 0; +} + .release-artwork { width: 96px; @@ -140,6 +145,10 @@ border: 1px solid #808080; } +.track p { + margin: 0; +} + .card h2.track-title { margin: 0; display: flex; diff --git a/admin/templates.go b/admin/templates.go new file mode 100644 index 0000000..ea7aaaa --- /dev/null +++ b/admin/templates.go @@ -0,0 +1,50 @@ +package admin + +import ( + "html/template" + "path/filepath" +) + +var pages = map[string]*template.Template{ + "index": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "components", "release", "release-list-item.html"), + filepath.Join("admin", "views", "index.html"), + )), + + "login": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "views", "login.html"), + )), + "logout": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "views", "logout.html"), + )), + + "release": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "views", "edit-release.html"), + )), + "track": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "components", "release", "release-list-item.html"), + filepath.Join("admin", "views", "edit-track.html"), + )), +} + +var components = map[string]*template.Template{ + "editcredits": template.Must(template.ParseFiles(filepath.Join("admin", "components", "credits", "editcredits.html"))), + "addcredit": template.Must(template.ParseFiles(filepath.Join("admin", "components", "credits", "addcredit.html"))), + "newcredit": template.Must(template.ParseFiles(filepath.Join("admin", "components", "credits", "newcredit.html"))), + + "editlinks": template.Must(template.ParseFiles(filepath.Join("admin", "components", "links", "editlinks.html"))), + + "edittracks": template.Must(template.ParseFiles(filepath.Join("admin", "components", "tracks", "addtrack.html"))), + "addtrack": template.Must(template.ParseFiles(filepath.Join("admin", "components", "tracks", "addtrack.html"))), + "newtrack": template.Must(template.ParseFiles(filepath.Join("admin", "components", "tracks", "addtrack.html"))), +} diff --git a/admin/trackhttp.go b/admin/trackhttp.go new file mode 100644 index 0000000..3af672d --- /dev/null +++ b/admin/trackhttp.go @@ -0,0 +1,28 @@ +package admin + +import ( + "fmt" + "net/http" + "strings" + + "arimelody.me/arimelody.me/global" +) + +func serveTrack() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + slices := strings.Split(r.URL.Path[1:], "/") + id := slices[0] + track := global.GetTrack(id) + if track == nil { + http.NotFound(w, r) + return + } + + err := pages["track"].Execute(w, track) + if err != nil { + fmt.Printf("Error rendering admin track page for %s: %s\n", id, err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + } + }) +} + diff --git a/admin/views/edit-release.html b/admin/views/edit-release.html index d33c8e6..3f7c12a 100644 --- a/admin/views/edit-release.html +++ b/admin/views/edit-release.html @@ -1,5 +1,5 @@ {{define "head"}} -editing {{.Title}} - ari melody 💫 +Editing {{.Title}} - ari melody 💫 @@ -129,7 +129,7 @@ {{end}} -
+
- {{range $Release := .Releases}} -
-
- -
-
-

- {{$Release.Title}} - - {{$Release.GetReleaseYear}} - {{if not $Release.Visible}}(hidden){{end}} - -

-

{{$Release.PrintArtists true true}}

-

{{$Release.ReleaseType}} - ({{len $Release.Tracks}} track{{if not (eq (len $Release.Tracks) 1)}}s{{end}})

-
- Edit - Gateway -
-
-
+ {{range .Releases}} + {{block "release" .}}{{end}} {{end}} {{if not .Releases}}

There are no releases.

@@ -66,14 +46,13 @@ {{range $Track := .Tracks}}

- {{$Track.Title}} + {{$Track.Title}} {{if $Track.Release}} {{$Track.Release.Title}} {{else}} (no release) {{end}}

-

{{$Track.ID}}

{{if $Track.Description}}

{{$Track.Description}}

{{else}} diff --git a/admin/views/layout.html b/admin/views/layout.html index babc698..ca9ce1f 100644 --- a/admin/views/layout.html +++ b/admin/views/layout.html @@ -23,8 +23,7 @@ - {{block "content" .}} - {{end}} + {{block "content" .}}{{end}} {{template "prideflag"}} diff --git a/admin/views/login.html b/admin/views/login.html index 0edb29e..f615746 100644 --- a/admin/views/login.html +++ b/admin/views/login.html @@ -1,5 +1,5 @@ {{define "head"}} -login - ari melody 💫 +Login - ari melody 💫