diff --git a/admin/accounthttp.go b/admin/accounthttp.go index 1e4742b..b2c3b0d 100644 --- a/admin/accounthttp.go +++ b/admin/accounthttp.go @@ -45,7 +45,7 @@ func accountIndexHandler(app *model.AppState) http.Handler { } accountResponse struct { - adminPageData + Session *model.Session TOTPs []TOTP } ) @@ -66,7 +66,7 @@ func accountIndexHandler(app *model.AppState) http.Handler { session.Error = sessionError err = templates.AccountTemplate.Execute(w, accountResponse{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, TOTPs: totps, }) if err != nil { @@ -170,7 +170,7 @@ func deleteAccountHandler(app *model.AppState) http.Handler { } type totpConfirmData struct { - adminPageData + Session *model.Session TOTP *model.TOTP NameEscaped string QRBase64Image string @@ -179,9 +179,13 @@ type totpConfirmData struct { func totpSetupHandler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { + type totpSetupData struct { + Session *model.Session + } + session := r.Context().Value("session").(*model.Session) - err := templates.TOTPSetupTemplate.Execute(w, adminPageData{ Path: "/account", Session: session }) + err := templates.TOTPSetupTemplate.Execute(w, totpSetupData{ Session: session }) if err != nil { fmt.Printf("WARN: Failed to render TOTP setup page: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -218,9 +222,7 @@ func totpSetupHandler(app *model.AppState) http.Handler { if err != nil { fmt.Printf("WARN: Failed to create TOTP method: %s\n", err) controller.SetSessionError(app.DB, session, "Something went wrong. Please try again.") - err := templates.TOTPSetupTemplate.Execute(w, totpConfirmData{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, - }) + err := templates.TOTPSetupTemplate.Execute(w, totpConfirmData{ Session: session }) if err != nil { fmt.Printf("WARN: Failed to render TOTP setup page: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -235,7 +237,7 @@ func totpSetupHandler(app *model.AppState) http.Handler { } err = templates.TOTPConfirmTemplate.Execute(w, totpConfirmData{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, TOTP: &totp, NameEscaped: url.PathEscape(totp.Name), QRBase64Image: qrBase64Image, @@ -296,7 +298,7 @@ func totpConfirmHandler(app *model.AppState) http.Handler { if code != confirmCodeOffset { session.Error = sql.NullString{ Valid: true, String: "Incorrect TOTP code. Please try again." } err = templates.TOTPConfirmTemplate.Execute(w, totpConfirmData{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, TOTP: totp, NameEscaped: url.PathEscape(totp.Name), QRBase64Image: qrBase64Image, diff --git a/admin/artisthttp.go b/admin/artisthttp.go index 8c33050..67ea7d2 100644 --- a/admin/artisthttp.go +++ b/admin/artisthttp.go @@ -33,7 +33,7 @@ func serveArtist(app *model.AppState) http.Handler { } type ArtistResponse struct { - adminPageData + Session *model.Session Artist *model.Artist Credits []*model.Credit } @@ -41,7 +41,7 @@ func serveArtist(app *model.AppState) http.Handler { session := r.Context().Value("session").(*model.Session) err = templates.EditArtistTemplate.Execute(w, ArtistResponse{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, Artist: artist, Credits: credits, }) diff --git a/admin/http.go b/admin/http.go index 136309e..05e181d 100644 --- a/admin/http.go +++ b/admin/http.go @@ -21,11 +21,6 @@ import ( "golang.org/x/crypto/bcrypt" ) -type adminPageData struct { - Path string - Session *model.Session -} - func Handler(app *model.AppState) http.Handler { mux := http.NewServeMux() @@ -72,18 +67,12 @@ func AdminIndexHandler(app *model.AppState) http.Handler { session := r.Context().Value("session").(*model.Session) - releases, err := controller.GetAllReleases(app.DB, false, 3, true) + releases, err := controller.GetAllReleases(app.DB, false, 0, true) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } - releaseCount, err := controller.GetReleasesCount(app.DB, false) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases count: %s\n", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } artists, err := controller.GetAllArtists(app.DB) if err != nil { @@ -100,17 +89,15 @@ func AdminIndexHandler(app *model.AppState) http.Handler { } type IndexData struct { - adminPageData - Releases []*model.Release - ReleaseCount int - Artists []*model.Artist - Tracks []*model.Track + Session *model.Session + Releases []*model.Release + Artists []*model.Artist + Tracks []*model.Track } err = templates.IndexTemplate.Execute(w, IndexData{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, Releases: releases, - ReleaseCount: releaseCount, Artists: artists, Tracks: tracks, }) @@ -132,8 +119,12 @@ func registerAccountHandler(app *model.AppState) http.Handler { return } + type registerData struct { + Session *model.Session + } + render := func() { - err := templates.RegisterTemplate.Execute(w, adminPageData{ Path: r.URL.Path, Session: session }) + err := templates.RegisterTemplate.Execute(w, registerData{ Session: session }) if err != nil { fmt.Printf("WARN: Error rendering create account page: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -238,8 +229,12 @@ func loginHandler(app *model.AppState) http.Handler { session := r.Context().Value("session").(*model.Session) + type loginData struct { + Session *model.Session + } + render := func() { - err := templates.LoginTemplate.Execute(w, adminPageData{ Path: r.URL.Path, Session: session }) + err := templates.LoginTemplate.Execute(w, loginData{ Session: session }) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Error rendering admin login page: %s\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -350,8 +345,12 @@ func loginTOTPHandler(app *model.AppState) http.Handler { return } + type loginTOTPData struct { + Session *model.Session + } + render := func() { - err := templates.LoginTOTPTemplate.Execute(w, adminPageData{ Path: r.URL.Path, Session: session }) + err := templates.LoginTOTPTemplate.Execute(w, loginTOTPData{ Session: session }) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to render login TOTP page: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/admin/logshttp.go b/admin/logshttp.go index a6d8e40..99f0ef1 100644 --- a/admin/logshttp.go +++ b/admin/logshttp.go @@ -51,12 +51,12 @@ func logsHandler(app *model.AppState) http.Handler { } type LogsResponse struct { - adminPageData + Session *model.Session Logs []*log.Log } err = templates.LogsTemplate.Execute(w, LogsResponse{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, Logs: logs, }) if err != nil { diff --git a/admin/releasehttp.go b/admin/releasehttp.go index 991845f..30c967b 100644 --- a/admin/releasehttp.go +++ b/admin/releasehttp.go @@ -57,12 +57,12 @@ func serveRelease(app *model.AppState) http.Handler { } type ReleaseResponse struct { - adminPageData + Session *model.Session Release *model.Release } err = templates.EditReleaseTemplate.Execute(w, ReleaseResponse{ - adminPageData: adminPageData{ Path: r.URL.Path, Session: session }, + Session: session, Release: release, }) if err != nil { diff --git a/admin/static/admin.css b/admin/static/admin.css index 7dad27f..1f5a1fb 100644 --- a/admin/static/admin.css +++ b/admin/static/admin.css @@ -3,9 +3,9 @@ :root { --bg-0: #101010; - --bg-1: #181818; - --bg-2: #282828; - --bg-3: #404040; + --bg-1: #141414; + --bg-2: #181818; + --bg-3: #202020; --fg-0: #b0b0b0; --fg-1: #c0c0c0; @@ -67,111 +67,74 @@ } body { - width: calc(100% - 180px); + width: 100%; height: calc(100vh - 1em); - margin: 0 0 0 180px; + margin: 0; padding: 0; - display: flex; - flex-direction: row; font-family: "Inter", sans-serif; font-size: 16px; + color: var(--fg-0); background: var(--bg-0); - - transition: background .1s ease-out, color .1s ease-out; } h1, h2, h3, h4, h5, h6 { color: var(--fg-3); } -header { - position: fixed; - left: 0; - height: 100vh; - display: flex; - flex-direction: column; - width: 180px; - background-color: var(--bg-1); - box-shadow: var(--shadow-md); - - transition: background .1s ease-out, color .1s ease-out; -} nav { - height: 100%; - margin: 1em 0; + width: min(720px, calc(100% - 2em)); + height: 2em; + margin: 1em auto; display: flex; - flex-direction: column; + flex-direction: row; justify-content: left; + gap: .5em; user-select: none; } nav .icon { - width: fit-content; - height: fit-content; - padding: 0; - margin: 0 auto 1em auto; - display: flex; - + height: 100%; border-radius: 100%; box-shadow: var(--shadow-sm); - overflow: clip; + overflow: hidden; } nav .icon img { - width: 3em; - height: 3em; + width: 100%; + height: 100%; } .nav-item { + width: auto; + height: 100%; + padding: 0 1em; display: flex; + color: var(--fg-2); + background: var(--bg-2); + border-radius: 10em; + box-shadow: var(--shadow-sm); + line-height: 2em; font-weight: 500; - transition: color .1s, background-color .1s; } .nav-item:hover { - background: var(--bg-2); + background: var(--bg-1); text-decoration: none; } -.nav-item.active { - border-left: 4px solid var(--fg-2); -} -.nav-item.active a { - padding-left: calc(1em - 4px); -} nav a { - padding: .2em 1em; text-decoration: none; color: inherit; - width: 100%; } -nav a.active { - border-left: 5px solid var(--fg-0); - padding-left: calc(1em - 5px); -} -nav hr { - width: calc(100% - 2em); - margin: .5em auto; - border: none; - border-bottom: 1px solid var(--fg-0); -} -nav .section-label { - margin: 8px 0 2px 15px; - font-size: 10px; - text-transform: uppercase; - font-weight: 600; +nav #logout { + /* margin-left: auto; */ } main { - width: min(calc(100% - 16px), 720px); - height: fit-content; - min-height: calc(100vh - 2em); + width: min(720px, calc(100% - 2em)); margin: 0 auto; padding: 1em; } -main.dashboard { - width: 100%; -} a { color: inherit; @@ -197,24 +160,7 @@ code { -.cards { - width: 100%; - height: fit-content; - display: flex; - gap: 2em; - flex-wrap: wrap; -} - .card { - flex-basis: 40em; - padding: 1em; - background: var(--bg-1); - border-radius: 16px; - box-shadow: var(--shadow-lg); - - transition: background .1s ease-out, color .1s ease-out; -} -main:not(.dashboard) .card { margin-bottom: 1em; } @@ -222,7 +168,7 @@ main:not(.dashboard) .card { margin: 0 0 .5em 0; } -.card-header { +.card-title { margin-bottom: 1em; display: flex; gap: 1em; @@ -230,31 +176,17 @@ main:not(.dashboard) .card { align-items: center; justify-content: space-between; } -.card-header h1, -.card-header h2, -.card-header h3 { + +.card-title h1, +.card-title h2, +.card-title h3 { margin: 0; } -.card-header a:hover { - text-decoration: underline; -} -.card-header small { - display: inline-block; - font-size: 15px; - transform: translateY(-2px); - color: var(--fg-0); -} .flex-fill { flex-grow: 1; } -.artists-group { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 1em; -} - @media screen and (max-width: 520px) { body { font-size: 12px; diff --git a/admin/static/edit-account.css b/admin/static/edit-account.css index 9db3773..7a4d34a 100644 --- a/admin/static/edit-account.css +++ b/admin/static/edit-account.css @@ -25,14 +25,12 @@ input { .mfa-device { padding: .75em; + background: #f8f8f8f8; + border: 1px solid #808080; + border-radius: 8px; margin-bottom: .5em; display: flex; justify-content: space-between; - - color: var(--fg-3); - background: var(--bg-2); - box-shadow: var(--shadow-md); - border-radius: 16px; } .mfa-device div { diff --git a/admin/static/edit-artist.css b/admin/static/edit-artist.css index b171a6e..1bab082 100644 --- a/admin/static/edit-artist.css +++ b/admin/static/edit-artist.css @@ -28,7 +28,7 @@ h1 { } .artist-avatar #remove-avatar { margin-top: .5em; - padding: .3em .6em; + padding: .3em .4em; } .artist-info { @@ -75,7 +75,7 @@ input[type="text"]:focus { justify-content: right; } -.card-header a.button { +.card-title a.button { text-decoration: none; } @@ -90,13 +90,6 @@ input[type="text"]:focus { border-radius: 16px; background: var(--bg-2); box-shadow: var(--shadow-md); - - cursor: pointer; - transition: background .1s; -} - -.credit:hover { - background: var(--bg-1); } .release-artwork { diff --git a/admin/static/edit-artist.js b/admin/static/edit-artist.js index 2ca4c0d..8f1bb2b 100644 --- a/admin/static/edit-artist.js +++ b/admin/static/edit-artist.js @@ -1,5 +1,3 @@ -import { hijackClickEvent } from "./admin.js"; - const artistID = document.getElementById("artist").dataset.id; const nameInput = document.getElementById("name"); const avatarImg = document.getElementById("avatar"); @@ -79,9 +77,3 @@ removeAvatarBtn.addEventListener("click", () => { avatarImg.src = "/img/default-avatar.png" saveBtn.disabled = false; }); - -document.addEventListener('readystatechange', () => { - document.querySelectorAll('.card#releases .credit').forEach(el => { - hijackClickEvent(el, el.querySelector('.credit-name a')); - }); -}); diff --git a/admin/static/edit-release.css b/admin/static/edit-release.css index b4fe17b..d30db84 100644 --- a/admin/static/edit-release.css +++ b/admin/static/edit-release.css @@ -155,7 +155,7 @@ dialog div.dialog-actions { gap: .5em; } -.card-header a.button { +.card-title a.button { text-decoration: none; } @@ -163,7 +163,7 @@ dialog div.dialog-actions { * RELEASE CREDITS */ -.card#credits .credit { +.card.credits .credit { margin-bottom: .5em; padding: .5em; display: flex; @@ -172,30 +172,24 @@ dialog div.dialog-actions { gap: 1em; border-radius: 16px; - background-color: var(--bg-2); + background: var(--bg-2); box-shadow: var(--shadow-md); - - cursor: pointer; - transition: background .1s ease-out; -} -.card#credits .credit:hover { - background-color: var(--bg-1); } -.card#credits .credit p { +.card.credits .credit p { margin: 0; } -.card#credits .credit .artist-avatar { +.card.credits .credit .artist-avatar { border-radius: 12px; } -.card#credits .credit .artist-name { +.card.credits .credit .artist-name { color: var(--fg-3); font-weight: bold; } -.card#credits .credit .artist-role small { +.card.credits .credit .artist-role small { font-size: inherit; opacity: .66; } @@ -314,34 +308,24 @@ dialog div.dialog-actions { * RELEASE LINKS */ -.card#links ul { - padding: 0; +.card.links { display: flex; gap: .2em; } -.card#links a.button:hover { - color: var(--bg-3) !important; - background-color: var(--fg-3) !important; -} - -.card#links a.button[data-name="spotify"] { - color: #101010; +.card.links a.button[data-name="spotify"] { background-color: #8cff83 } -.card#links a.button[data-name="apple music"] { - color: #101010; +.card.links a.button[data-name="apple music"] { background-color: #8cd9ff } -.card#links a.button[data-name="soundcloud"] { - color: #101010; +.card.links a.button[data-name="soundcloud"] { background-color: #fdaa6d } -.card#links a.button[data-name="youtube"] { - color: #101010; +.card.links a.button[data-name="youtube"] { background-color: #ff6e6e } @@ -428,7 +412,7 @@ dialog div.dialog-actions { * RELEASE TRACKS */ -.card#tracks .track { +.card.tracks .track { margin-bottom: 1em; padding: 1em; display: flex; @@ -440,47 +424,43 @@ dialog div.dialog-actions { box-shadow: var(--shadow-md); } -.card#tracks .track h3, -.card#tracks .track p { +.card.tracks .track h3, +.card.tracks .track p { margin: 0; } -.card#tracks h2.track-title { +.card.tracks h2.track-title { margin: 0; display: flex; gap: .5em; } -.card#tracks h2.track-title .track-number { +.card.tracks h2.track-title .track-number { opacity: .5; } -.card#tracks a:hover { - text-decoration: underline; -} - -.card#tracks .track-album { +.card.tracks .track-album { margin-left: auto; font-style: italic; font-size: .75em; opacity: .5; } -.card#tracks .track-album.empty { +.card.tracks .track-album.empty { color: #ff2020; opacity: 1; } -.card#tracks .track-description { +.card.tracks .track-description { font-style: italic; } -.card#tracks .track-lyrics { +.card.tracks .track-lyrics { max-height: 10em; overflow-y: scroll; } -.card#tracks .track .empty { +.card.tracks .track .empty { opacity: 0.75; } diff --git a/admin/static/edit-release.js b/admin/static/edit-release.js index 5db4bbf..11d21f0 100644 --- a/admin/static/edit-release.js +++ b/admin/static/edit-release.js @@ -1,5 +1,3 @@ -import { hijackClickEvent } from "./admin.js"; - const releaseID = document.getElementById("release").dataset.id; const titleInput = document.getElementById("title"); const artworkImg = document.getElementById("artwork"); @@ -98,9 +96,3 @@ removeArtworkBtn.addEventListener("click", () => { artworkData = ""; saveBtn.disabled = false; }); - -document.addEventListener("readystatechange", () => { - document.querySelectorAll(".card#credits .credit").forEach(el => { - hijackClickEvent(el, el.querySelector(".artist-name a")); - }); -}); diff --git a/admin/static/index.css b/admin/static/index.css index 5f64a2f..278224e 100644 --- a/admin/static/index.css +++ b/admin/static/index.css @@ -1,16 +1,20 @@ @import url("/admin/static/release-list-item.css"); .artist { + margin-bottom: .5em; padding: .5em; - + display: flex; + flex-direction: row; + align-items: center; + gap: .5em; color: var(--fg-3); + + border-radius: 16px; background: var(--bg-2); box-shadow: var(--shadow-md); - border-radius: 16px; - text-align: center; + transition: background .1s ease-out; cursor: pointer; - transition: background .1s ease-out, color .1s ease-out; } .artist:hover { @@ -19,9 +23,10 @@ } .artist-avatar { - width: 100%; + width: 32px; + height: 32px; object-fit: cover; - border-radius: 8px; + border-radius: 100%; } .track { @@ -34,8 +39,6 @@ border-radius: 8px; background: #f8f8f8f8; border: 1px solid #808080; - - transition: background .1s ease-out, color .1s ease-out; } .track p { diff --git a/admin/static/index.js b/admin/static/index.js index c35b596..0091fa4 100644 --- a/admin/static/index.js +++ b/admin/static/index.js @@ -76,7 +76,7 @@ newTrackBtn.addEventListener("click", event => { }); document.addEventListener("readystatechange", () => { - document.querySelectorAll("#artists .artist").forEach(el => { + document.querySelectorAll(".card.artists .artist").forEach(el => { hijackClickEvent(el, el.querySelector("a.artist-name")) }); }); diff --git a/admin/static/release-list-item.css b/admin/static/release-list-item.css index 3481257..fb5d2d4 100644 --- a/admin/static/release-list-item.css +++ b/admin/static/release-list-item.css @@ -8,8 +8,6 @@ border-radius: 16px; background: var(--bg-2); box-shadow: var(--shadow-md); - - transition: background .1s ease-out, color .1s ease-out; } .release h3, diff --git a/admin/templates/html/edit-account.html b/admin/templates/html/edit-account.html index a081995..6c17088 100644 --- a/admin/templates/html/edit-account.html +++ b/admin/templates/html/edit-account.html @@ -14,10 +14,10 @@ {{end}}

Account Settings ({{.Session.Account.Username}})

+
+

Change Password

+
-
-

Change Password

-
@@ -32,10 +32,10 @@
+
+

MFA Devices

+
-
-

MFA Devices

-
{{if .TOTPs}} {{range .TOTPs}}
@@ -58,10 +58,10 @@
+
+

Danger Zone

+
-
-

Danger Zone

-

Clicking the button below will delete your account. This action is irreversible. diff --git a/admin/templates/html/edit-artist.html b/admin/templates/html/edit-artist.html index 6b95de8..b0cfb41 100644 --- a/admin/templates/html/edit-artist.html +++ b/admin/templates/html/edit-artist.html @@ -29,10 +29,10 @@

-
-
-

Featured in

-
+
+

Featured in

+
+
{{if .Credits}} {{range .Credits}}
@@ -54,10 +54,10 @@ {{end}}
-
-
-

Danger Zone

-
+
+

Danger Zone

+
+

Clicking the button below will delete this artist. This action is irreversible. diff --git a/admin/templates/html/edit-release.html b/admin/templates/html/edit-release.html index 309decf..02447e1 100644 --- a/admin/templates/html/edit-release.html +++ b/admin/templates/html/edit-release.html @@ -97,16 +97,16 @@

-
-
-

Credits ({{len .Release.Credits}})

- Edit -
+
+

Credits ({{len .Release.Credits}})

+ Edit +
+
{{range .Release.Credits}}
@@ -126,33 +126,31 @@ {{end}}
-