Create rating interface on songs view

If no ratings are present, provide a textbox to make a rating. If a rating is present, show the rating. Eventually, there will be a button that allows an additional rating to be made, and the ability to delete ratings
This commit is contained in:
mitteneer 2025-06-23 16:50:13 -04:00
parent 996022fe5f
commit 6f6aaecb8f
6 changed files with 36 additions and 7 deletions

View file

@ -3,9 +3,11 @@ const jetzig = @import("jetzig");
pub fn post(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object);
const params = try request.params();
const id = params.getT(.integer, "song_id");
const id = params.getT(.integer, "song_id").?;
const score = if (params.getT(.integer, "score")) |score| @as(i16, @truncate(score)) else null;
const review = params.getT(.string, "review");
try root.put("song_id", id);
try jetzig.database.Query(.Songrating).insert(.{ .song = id, .rating = score, .rating_text = review, .date = @divFloor(request.start_time, 1_000) }).execute(request.repo);
try root.put("score", score);
try root.put("review", review);
return request.render(.created);

View file

@ -1,2 +1 @@
{{.song_id}}
{{.review}}
<b>{{.score}}</b>: {{.review}} (Today)

View file

@ -59,5 +59,9 @@ pub fn get(id: []const u8, request: *jetzig.Request) !jetzig.View {
const timescale = try queries.entityQueryResult(request, queries.loadQuery(.song, .timescale), .{id_int});
try root.put("yearly", timescale);
const ratings = try queries.entityQueryResult(request, queries.loadQuery(.song, .get_ratings), .{id_int});
try root.put("reviews", ratings);
return request.render(.ok);
}

View file

@ -1,6 +1,7 @@
@zig {
const ColumnChoices = []const enum{song, album, artist, artistlist, scrobbles, date};
const columns: ColumnChoices = &.{.song, .artistlist, .album, .date};
const reviews = try zmpl.coerceArray(".reviews");
}
<html>
@ -25,13 +26,21 @@
</div>
<div style="display:flex;flex-direction:column;align-self:right">
<h2>Rating</h2>
<div id="review-container">
@zig {
if (reviews.len == 0) {
<form>
<input type="number" name="score" id="score" style="width:50px;height:30px">
<textarea name="review" id="review" style="width:350px;height:100px"></textarea>
<button hx-post="/ratings/songs" hx-vals='{"song_id":"{{.song.song_id}}"}' hx-target="#review-container" style="width:50px;height:30px">Post</button>
</form>
</div>
<div id="review-container">No reviews</div>
} else {
for (reviews) |review| {
<b>{{review.score}}</b>: {{review.review}} ({{review.date}})
}
}
}
</div>
</div>
</body>
</html>

View file

@ -42,6 +42,8 @@ pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args:
.artistlist = if (artist_list.getLastOrNull()) |_| try artist_list.toOwnedSlice() else null,
.scrobbles = if (entity.scrobbles) |scrobbles| scrobbles else null,
.date = if (entity.date) |date| date else null,
.score = if (entity.score) |score| score else null,
.review = if (entity.review) |review| review else null,
});
}
@ -49,7 +51,7 @@ pub fn entityQueryResult(request: *jetzig.Request, query: GeneratedQuery, args:
}
const EntityType = enum { scrobble, song, album, artist };
const QueryTypeEnum = enum { firstlast, timescale, entities, get_songs, get_albums, get_scrobbles, appears, entity_info, datestreak, entities_by_name };
const QueryTypeEnum = enum { firstlast, timescale, entities, get_songs, get_albums, get_scrobbles, appears, entity_info, datestreak, entities_by_name, get_ratings };
const GeneratedQuery = struct {
entity: EntityType,
@ -66,6 +68,8 @@ const UnifiedResult = struct {
artist_id: ?i64 = null,
scrobbles: ?i64 = null,
date: ?[]const u8 = null,
score: ?i16 = null,
review: ?[]const u8 = null,
};
const EntityInfoResult = struct {
@ -435,6 +439,15 @@ pub fn loadQuery(entity: EntityType, query_type: QueryTypeEnum) GeneratedQuery {
,
else => unreachable,
},
.get_ratings => switch (entity) {
.song =>
\\SELECT rating AS score, rating_text AS review, TO_CHAR(date, 'YYYY-MM-DD') AS date
\\FROM songratings
\\WHERE song = $1
\\ORDER BY date DESC;
,
else => unreachable,
},
},
};
}

View file

@ -129,6 +129,8 @@ pub const TableRow = struct {
artistlist: ?[]HyperlinkData = null,
scrobbles: ?i64 = null,
date: ?[]const u8 = null,
score: ?i16 = null,
review: ?[]const u8 = null,
};
pub const HyperlinkData = struct {