add latest blog post to admin dashboard view

This commit is contained in:
ari melody 2025-11-07 18:16:14 +00:00
parent 21912d4ec2
commit fec3325503
Signed by: ari
GPG key ID: CF99829C92678188
5 changed files with 93 additions and 7 deletions

View file

@ -62,39 +62,69 @@ func adminIndexHandler(app *model.AppState) http.Handler {
releases, err := controller.GetAllReleases(app.DB, false, 3, true) releases, err := controller.GetAllReleases(app.DB, false, 3, true)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
releaseCount, err := controller.GetReleaseCount(app.DB, false) releaseCount, err := controller.GetReleaseCount(app.DB, false)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases count: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases count: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
artists, err := controller.GetAllArtists(app.DB) artists, err := controller.GetAllArtists(app.DB)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
artistCount, err := controller.GetArtistCount(app.DB) artistCount, err := controller.GetArtistCount(app.DB)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull artist count: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull artist count: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
tracks, err := controller.GetOrphanTracks(app.DB) tracks, err := controller.GetOrphanTracks(app.DB)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
trackCount, err := controller.GetTrackCount(app.DB) trackCount, err := controller.GetTrackCount(app.DB)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull track count: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to pull track count: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
type BlogPost struct {
*model.BlogPost
Author *model.Account
}
blogPosts, err := controller.GetBlogPosts(app.DB, false, 1, 0)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull blog posts: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
var latestBlogPost *BlogPost = nil
if len(blogPosts) > 0 {
author, err := controller.GetAccountByID(app.DB, blogPosts[0].AuthorID)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull latest blog post author %s: %v\n", blogPosts[0].AuthorID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
latestBlogPost = &BlogPost{
BlogPost: blogPosts[0],
Author: author,
}
}
blogCount, err := controller.GetBlogPostCount(app.DB, false)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to pull blog post count: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
@ -107,6 +137,8 @@ func adminIndexHandler(app *model.AppState) http.Handler {
ArtistCount int ArtistCount int
Tracks []*model.Track Tracks []*model.Track
TrackCount int TrackCount int
BlogPost *BlogPost
BlogCount int
} }
err = templates.IndexTemplate.Execute(w, IndexData{ err = templates.IndexTemplate.Execute(w, IndexData{
@ -117,9 +149,11 @@ func adminIndexHandler(app *model.AppState) http.Handler {
ArtistCount: artistCount, ArtistCount: artistCount,
Tracks: tracks, Tracks: tracks,
TrackCount: trackCount, TrackCount: trackCount,
BlogPost: latestBlogPost,
BlogCount: blogCount,
}) })
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "WARN: Failed to render admin index: %s\n", err) fmt.Fprintf(os.Stderr, "WARN: Failed to render admin index: %v\n", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }

View file

@ -0,0 +1,25 @@
document.addEventListener('readystatechange', () => {
const newBlogBtn = document.getElementById("create-post");
if (newBlogBtn) newBlogBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this blog post:");
if (id == null || id == "") return;
fetch("/api/v1/blog", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
if (res.ok) location = "/admin/blogs/" + id;
else {
res.text().then(err => {
alert(err);
console.error(err);
});
}
}).catch(err => {
alert("Failed to create release. Check the console for details.");
console.error(err);
});
});
});

View file

@ -4,6 +4,7 @@
<link rel="stylesheet" href="/admin/static/releases.css"> <link rel="stylesheet" href="/admin/static/releases.css">
<link rel="stylesheet" href="/admin/static/artists.css"> <link rel="stylesheet" href="/admin/static/artists.css">
<link rel="stylesheet" href="/admin/static/tracks.css"> <link rel="stylesheet" href="/admin/static/tracks.css">
<link rel="stylesheet" href="/admin/static/blog.css">
{{end}} {{end}}
{{define "content"}} {{define "content"}}
@ -52,6 +53,18 @@
{{block "track" .}}{{end}} {{block "track" .}}{{end}}
{{end}} {{end}}
</div> </div>
<div class="card" id="blogs">
<div class="card-header">
<h2><a href="/admin/blogs/">Latest Blog Post</a> <small>({{.BlogCount}} total)</small></h2>
<a class="button new" id="create-post">Create New</a>
</div>
{{if .BlogPost}}
{{block "blogpost" .BlogPost}}{{end}}
{{else}}
<p>There are no blog posts.</p>
{{end}}
</div>
</div> </div>
</main> </main>
@ -59,4 +72,5 @@
<script type="module" src="/admin/static/releases.js"></script> <script type="module" src="/admin/static/releases.js"></script>
<script type="module" src="/admin/static/artists.js"></script> <script type="module" src="/admin/static/artists.js"></script>
<script type="module" src="/admin/static/tracks.js"></script> <script type="module" src="/admin/static/tracks.js"></script>
<script type="module" src="/admin/static/blog.js"></script>
{{end}} {{end}}

View file

@ -43,6 +43,7 @@ var IndexTemplate = template.Must(template.Must(BaseTemplate.Clone()).Parse(
componentReleaseHTML, componentReleaseHTML,
componentArtistHTML, componentArtistHTML,
componentTrackHTML, componentTrackHTML,
componentBlogPostHTML,
}, "\n"), }, "\n"),
)) ))

View file

@ -44,6 +44,18 @@ func GetBlogPosts(db *sqlx.DB, onlyVisible bool, limit int, offset int) ([]*mode
return blogs, nil return blogs, nil
} }
func GetBlogPostCount(db *sqlx.DB, onlyVisible bool) (int, error) {
query := "SELECT count(*) FROM blogpost"
if onlyVisible {
query += " WHERE visible=true"
}
var count int
err := db.Get(&count, query)
return count, err
}
func CreateBlogPost(db *sqlx.DB, post *model.BlogPost) error { func CreateBlogPost(db *sqlx.DB, post *model.BlogPost) error {
_, err := db.Exec( _, err := db.Exec(
"INSERT INTO blogpost (id,title,description,visible,author,markdown,html,bluesky_actor,bluesky_post) " + "INSERT INTO blogpost (id,title,description,visible,author,markdown,html,bluesky_actor,bluesky_post) " +