diff --git a/admin/accounthttp.go b/admin/accounthttp.go
index b2c3b0d..1e4742b 100644
--- a/admin/accounthttp.go
+++ b/admin/accounthttp.go
@@ -45,7 +45,7 @@ func accountIndexHandler(app *model.AppState) http.Handler {
}
accountResponse struct {
- Session *model.Session
+ adminPageData
TOTPs []TOTP
}
)
@@ -66,7 +66,7 @@ func accountIndexHandler(app *model.AppState) http.Handler {
session.Error = sessionError
err = templates.AccountTemplate.Execute(w, accountResponse{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
TOTPs: totps,
})
if err != nil {
@@ -170,7 +170,7 @@ func deleteAccountHandler(app *model.AppState) http.Handler {
}
type totpConfirmData struct {
- Session *model.Session
+ adminPageData
TOTP *model.TOTP
NameEscaped string
QRBase64Image string
@@ -179,13 +179,9 @@ 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, totpSetupData{ Session: session })
+ err := templates.TOTPSetupTemplate.Execute(w, adminPageData{ Path: "/account", 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)
@@ -222,7 +218,9 @@ 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{ Session: session })
+ err := templates.TOTPSetupTemplate.Execute(w, totpConfirmData{
+ adminPageData: adminPageData{ Path: r.URL.Path, 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)
@@ -237,7 +235,7 @@ func totpSetupHandler(app *model.AppState) http.Handler {
}
err = templates.TOTPConfirmTemplate.Execute(w, totpConfirmData{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
TOTP: &totp,
NameEscaped: url.PathEscape(totp.Name),
QRBase64Image: qrBase64Image,
@@ -298,7 +296,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{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
TOTP: totp,
NameEscaped: url.PathEscape(totp.Name),
QRBase64Image: qrBase64Image,
diff --git a/admin/artisthttp.go b/admin/artisthttp.go
index 67ea7d2..8c33050 100644
--- a/admin/artisthttp.go
+++ b/admin/artisthttp.go
@@ -33,7 +33,7 @@ func serveArtist(app *model.AppState) http.Handler {
}
type ArtistResponse struct {
- Session *model.Session
+ adminPageData
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{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
Artist: artist,
Credits: credits,
})
diff --git a/admin/http.go b/admin/http.go
index 05e181d..136309e 100644
--- a/admin/http.go
+++ b/admin/http.go
@@ -21,6 +21,11 @@ import (
"golang.org/x/crypto/bcrypt"
)
+type adminPageData struct {
+ Path string
+ Session *model.Session
+}
+
func Handler(app *model.AppState) http.Handler {
mux := http.NewServeMux()
@@ -67,12 +72,18 @@ func AdminIndexHandler(app *model.AppState) http.Handler {
session := r.Context().Value("session").(*model.Session)
- releases, err := controller.GetAllReleases(app.DB, false, 0, true)
+ releases, err := controller.GetAllReleases(app.DB, false, 3, 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 {
@@ -89,15 +100,17 @@ func AdminIndexHandler(app *model.AppState) http.Handler {
}
type IndexData struct {
- Session *model.Session
- Releases []*model.Release
- Artists []*model.Artist
- Tracks []*model.Track
+ adminPageData
+ Releases []*model.Release
+ ReleaseCount int
+ Artists []*model.Artist
+ Tracks []*model.Track
}
err = templates.IndexTemplate.Execute(w, IndexData{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
Releases: releases,
+ ReleaseCount: releaseCount,
Artists: artists,
Tracks: tracks,
})
@@ -119,12 +132,8 @@ func registerAccountHandler(app *model.AppState) http.Handler {
return
}
- type registerData struct {
- Session *model.Session
- }
-
render := func() {
- err := templates.RegisterTemplate.Execute(w, registerData{ Session: session })
+ err := templates.RegisterTemplate.Execute(w, adminPageData{ Path: r.URL.Path, 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)
@@ -229,12 +238,8 @@ 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, loginData{ Session: session })
+ err := templates.LoginTemplate.Execute(w, adminPageData{ Path: r.URL.Path, 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)
@@ -345,12 +350,8 @@ func loginTOTPHandler(app *model.AppState) http.Handler {
return
}
- type loginTOTPData struct {
- Session *model.Session
- }
-
render := func() {
- err := templates.LoginTOTPTemplate.Execute(w, loginTOTPData{ Session: session })
+ err := templates.LoginTOTPTemplate.Execute(w, adminPageData{ Path: r.URL.Path, 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 99f0ef1..a6d8e40 100644
--- a/admin/logshttp.go
+++ b/admin/logshttp.go
@@ -51,12 +51,12 @@ func logsHandler(app *model.AppState) http.Handler {
}
type LogsResponse struct {
- Session *model.Session
+ adminPageData
Logs []*log.Log
}
err = templates.LogsTemplate.Execute(w, LogsResponse{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
Logs: logs,
})
if err != nil {
diff --git a/admin/releasehttp.go b/admin/releasehttp.go
index 30c967b..991845f 100644
--- a/admin/releasehttp.go
+++ b/admin/releasehttp.go
@@ -57,12 +57,12 @@ func serveRelease(app *model.AppState) http.Handler {
}
type ReleaseResponse struct {
- Session *model.Session
+ adminPageData
Release *model.Release
}
err = templates.EditReleaseTemplate.Execute(w, ReleaseResponse{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
Release: release,
})
if err != nil {
diff --git a/admin/static/admin.css b/admin/static/admin.css
index 1f5a1fb..7dad27f 100644
--- a/admin/static/admin.css
+++ b/admin/static/admin.css
@@ -3,9 +3,9 @@
:root {
--bg-0: #101010;
- --bg-1: #141414;
- --bg-2: #181818;
- --bg-3: #202020;
+ --bg-1: #181818;
+ --bg-2: #282828;
+ --bg-3: #404040;
--fg-0: #b0b0b0;
--fg-1: #c0c0c0;
@@ -67,74 +67,111 @@
}
body {
- width: 100%;
+ width: calc(100% - 180px);
height: calc(100vh - 1em);
- margin: 0;
+ margin: 0 0 0 180px;
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);
}
-nav {
- width: min(720px, calc(100% - 2em));
- height: 2em;
- margin: 1em auto;
+header {
+ position: fixed;
+ left: 0;
+ height: 100vh;
display: flex;
- flex-direction: row;
+ 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;
+ display: flex;
+ flex-direction: column;
justify-content: left;
- gap: .5em;
user-select: none;
}
nav .icon {
- height: 100%;
- border-radius: 100%;
- box-shadow: var(--shadow-sm);
- overflow: hidden;
-}
-nav .icon img {
- width: 100%;
- height: 100%;
-}
-.nav-item {
- width: auto;
- height: 100%;
- padding: 0 1em;
+ width: fit-content;
+ height: fit-content;
+ padding: 0;
+ margin: 0 auto 1em auto;
display: flex;
- color: var(--fg-2);
- background: var(--bg-2);
- border-radius: 10em;
+ border-radius: 100%;
box-shadow: var(--shadow-sm);
-
+ overflow: clip;
+}
+nav .icon img {
+ width: 3em;
+ height: 3em;
+}
+.nav-item {
+ display: flex;
+ color: var(--fg-2);
line-height: 2em;
font-weight: 500;
+ transition: color .1s, background-color .1s;
}
.nav-item:hover {
- background: var(--bg-1);
+ background: var(--bg-2);
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 #logout {
- /* margin-left: auto; */
+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;
}
main {
- width: min(720px, calc(100% - 2em));
+ width: min(calc(100% - 16px), 720px);
+ height: fit-content;
+ min-height: calc(100vh - 2em);
margin: 0 auto;
padding: 1em;
}
+main.dashboard {
+ width: 100%;
+}
a {
color: inherit;
@@ -160,7 +197,24 @@ 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;
}
@@ -168,7 +222,7 @@ code {
margin: 0 0 .5em 0;
}
-.card-title {
+.card-header {
margin-bottom: 1em;
display: flex;
gap: 1em;
@@ -176,17 +230,31 @@ code {
align-items: center;
justify-content: space-between;
}
-
-.card-title h1,
-.card-title h2,
-.card-title h3 {
+.card-header h1,
+.card-header h2,
+.card-header 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 7a4d34a..9db3773 100644
--- a/admin/static/edit-account.css
+++ b/admin/static/edit-account.css
@@ -25,12 +25,14 @@ 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 1bab082..b171a6e 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 .4em;
+ padding: .3em .6em;
}
.artist-info {
@@ -75,7 +75,7 @@ input[type="text"]:focus {
justify-content: right;
}
-.card-title a.button {
+.card-header a.button {
text-decoration: none;
}
@@ -90,6 +90,13 @@ 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 8f1bb2b..2ca4c0d 100644
--- a/admin/static/edit-artist.js
+++ b/admin/static/edit-artist.js
@@ -1,3 +1,5 @@
+import { hijackClickEvent } from "./admin.js";
+
const artistID = document.getElementById("artist").dataset.id;
const nameInput = document.getElementById("name");
const avatarImg = document.getElementById("avatar");
@@ -77,3 +79,9 @@ 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 d30db84..b4fe17b 100644
--- a/admin/static/edit-release.css
+++ b/admin/static/edit-release.css
@@ -155,7 +155,7 @@ dialog div.dialog-actions {
gap: .5em;
}
-.card-title a.button {
+.card-header 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,24 +172,30 @@ dialog div.dialog-actions {
gap: 1em;
border-radius: 16px;
- background: var(--bg-2);
+ background-color: 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;
}
@@ -308,24 +314,34 @@ dialog div.dialog-actions {
* RELEASE LINKS
*/
-.card.links {
+.card#links ul {
+ padding: 0;
display: flex;
gap: .2em;
}
-.card.links a.button[data-name="spotify"] {
+.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;
background-color: #8cff83
}
-.card.links a.button[data-name="apple music"] {
+.card#links a.button[data-name="apple music"] {
+ color: #101010;
background-color: #8cd9ff
}
-.card.links a.button[data-name="soundcloud"] {
+.card#links a.button[data-name="soundcloud"] {
+ color: #101010;
background-color: #fdaa6d
}
-.card.links a.button[data-name="youtube"] {
+.card#links a.button[data-name="youtube"] {
+ color: #101010;
background-color: #ff6e6e
}
@@ -412,7 +428,7 @@ dialog div.dialog-actions {
* RELEASE TRACKS
*/
-.card.tracks .track {
+.card#tracks .track {
margin-bottom: 1em;
padding: 1em;
display: flex;
@@ -424,43 +440,47 @@ 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 .track-album {
+.card#tracks a:hover {
+ text-decoration: underline;
+}
+
+.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 11d21f0..5db4bbf 100644
--- a/admin/static/edit-release.js
+++ b/admin/static/edit-release.js
@@ -1,3 +1,5 @@
+import { hijackClickEvent } from "./admin.js";
+
const releaseID = document.getElementById("release").dataset.id;
const titleInput = document.getElementById("title");
const artworkImg = document.getElementById("artwork");
@@ -96,3 +98,9 @@ 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 278224e..5f64a2f 100644
--- a/admin/static/index.css
+++ b/admin/static/index.css
@@ -1,20 +1,16 @@
@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;
+ color: var(--fg-3);
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 {
@@ -23,10 +19,9 @@
}
.artist-avatar {
- width: 32px;
- height: 32px;
+ width: 100%;
object-fit: cover;
- border-radius: 100%;
+ border-radius: 8px;
}
.track {
@@ -39,6 +34,8 @@
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 0091fa4..c35b596 100644
--- a/admin/static/index.js
+++ b/admin/static/index.js
@@ -76,7 +76,7 @@ newTrackBtn.addEventListener("click", event => {
});
document.addEventListener("readystatechange", () => {
- document.querySelectorAll(".card.artists .artist").forEach(el => {
+ document.querySelectorAll("#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 fb5d2d4..3481257 100644
--- a/admin/static/release-list-item.css
+++ b/admin/static/release-list-item.css
@@ -8,6 +8,8 @@
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 6c17088..a081995 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
-
+
-
-
MFA Devices
-
+
{{if .TOTPs}}
{{range .TOTPs}}
@@ -58,10 +58,10 @@
-
-
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 b0cfb41..6b95de8 100644
--- a/admin/templates/html/edit-artist.html
+++ b/admin/templates/html/edit-artist.html
@@ -29,10 +29,10 @@
-
-
Featured in
-
-
+
+
{{if .Credits}}
{{range .Credits}}
@@ -54,10 +54,10 @@
{{end}}
-
-
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 02447e1..309decf 100644
--- a/admin/templates/html/edit-release.html
+++ b/admin/templates/html/edit-release.html
@@ -97,16 +97,16 @@
-
-
Credits ({{len .Release.Credits}})
-
Edit
-
-
+
+
{{range .Release.Credits}}
@@ -126,31 +126,33 @@
{{end}}
-
-
Links ({{len .Release.Links}})
-
Edit
-
-
- {{range .Release.Links}}
-
{{.Name}}
- {{end}}
+
+
+
+ {{range .Release.Links}}
+ {{.Name}}
+ {{end}}
+
-
-
Tracklist ({{len .Release.Tracks}})
-
Edit
-
-
+
+
{{range $i, $track := .Release.Tracks}}
@@ -175,10 +177,10 @@
{{end}}
-
-
Danger Zone
-
-
+
+
Clicking the button below will delete this release.
This action is irreversible .
diff --git a/admin/templates/html/edit-track.html b/admin/templates/html/edit-track.html
index 56e0ae4..871db6f 100644
--- a/admin/templates/html/edit-track.html
+++ b/admin/templates/html/edit-track.html
@@ -39,10 +39,10 @@
-
-
Featured in
-
+
{{if .Releases}}
{{range .Releases}}
{{block "release" .}}{{end}}
@@ -52,10 +52,10 @@
{{end}}
-
-
Danger Zone
-
+
Clicking the button below will delete this track.
This action is irreversible .
diff --git a/admin/templates/html/index.html b/admin/templates/html/index.html
index 2b9c897..dc97a3e 100644
--- a/admin/templates/html/index.html
+++ b/admin/templates/html/index.html
@@ -5,64 +5,70 @@
{{end}}
{{define "content"}}
-
+
+ Dashboard
-
-
- {{range .Releases}}
- {{block "release" .}}{{end}}
- {{end}}
- {{if not .Releases}}
-
There are no releases.
- {{end}}
-
-
-
-
- {{range $Artist := .Artists}}
-
- {{end}}
- {{if not .Artists}}
-
There are no artists.
- {{end}}
-
-
-
-
-
"Orphaned" tracks that have not yet been bound to a release.
-
- {{range $Track := .Tracks}}
-
-
- {{if $Track.Description}}
-
{{$Track.GetDescriptionHTML}}
- {{else}}
-
No description provided.
+
+
+
+ {{range .Releases}}
+ {{block "release" .}}{{end}}
{{end}}
- {{if $Track.Lyrics}}
-
{{$Track.GetLyricsHTML}}
- {{else}}
-
There are no lyrics.
+ {{if not .Releases}}
+
There are no releases.
{{end}}
-
- {{end}}
- {{if not .Artists}}
-
There are no artists.
- {{end}}
+
+
+
+
+ {{if .Artists}}
+
+ {{range $Artist := .Artists}}
+
+ {{end}}
+
+ {{else}}
+
There are no artists.
+ {{end}}
+
+
+
+
+
"Orphaned" tracks that have not yet been bound to a release.
+
+ {{range $Track := .Tracks}}
+
+
+ {{if $Track.Description}}
+
{{$Track.GetDescriptionHTML}}
+ {{else}}
+
No description provided.
+ {{end}}
+ {{if $Track.Lyrics}}
+
{{$Track.GetLyricsHTML}}
+ {{else}}
+
There are no lyrics.
+ {{end}}
+
+ {{end}}
+ {{if not .Artists}}
+
There are no artists.
+ {{end}}
+
diff --git a/admin/templates/html/layout.html b/admin/templates/html/layout.html
index 4925ce9..55f2359 100644
--- a/admin/templates/html/layout.html
+++ b/admin/templates/html/layout.html
@@ -17,28 +17,42 @@
-
+
-
+
{{if .Session.Account}}
-
+
+
+
music
+
+
+
{{end}}
{{if .Session.Account}}
-
+
{{else}}
-
+
+
{{end}}
diff --git a/admin/trackhttp.go b/admin/trackhttp.go
index e93d1bb..e4187b4 100644
--- a/admin/trackhttp.go
+++ b/admin/trackhttp.go
@@ -33,7 +33,7 @@ func serveTrack(app *model.AppState) http.Handler {
}
type TrackResponse struct {
- Session *model.Session
+ adminPageData
Track *model.Track
Releases []*model.Release
}
@@ -41,7 +41,7 @@ func serveTrack(app *model.AppState) http.Handler {
session := r.Context().Value("session").(*model.Session)
err = templates.EditTrackTemplate.Execute(w, TrackResponse{
- Session: session,
+ adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
Track: track,
Releases: releases,
})
diff --git a/controller/release.go b/controller/release.go
index afd09fb..c729c4f 100644
--- a/controller/release.go
+++ b/controller/release.go
@@ -99,6 +99,17 @@ func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*mod
return releases, nil
}
+func GetReleasesCount(db *sqlx.DB, onlyVisible bool) (int, error) {
+ query := "SELECT count(*) FROM musicrelease"
+ if onlyVisible {
+ query += " WHERE visible=true"
+ }
+
+ var count int
+ err := db.Get(&count, query)
+
+ return count, err
+}
func CreateRelease(db *sqlx.DB, release *model.Release) error {
_, err := db.Exec(