feat(api): expose PlayoutItem row id on the playout-items list DTO (#210)
The SPA needs the item's row id to call
GET /api/playouts/items/{id}/scheduling-context. Plumbed through
PlayoutItemViewModel -> PlayoutItemResponseModel as a nullable Id
(null for synthesized UNSCHEDULED gap rows, which are PlayoutGaps,
not PlayoutItems). Additive for existing consumers (Playouts.razor
reads the VM by property). Regenerated v1.json + v1.d.ts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ internal static class Mapper
|
||||
|
||||
internal static PlayoutItemViewModel ProjectToViewModel(PlayoutItem playoutItem) =>
|
||||
new(
|
||||
playoutItem.Id,
|
||||
GetDisplayTitle(playoutItem.MediaItem, playoutItem.ChapterTitle),
|
||||
playoutItem.StartOffset,
|
||||
playoutItem.FinishOffset,
|
||||
|
||||
@@ -3,6 +3,7 @@ using ErsatzTV.Core.Domain.Filler;
|
||||
namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public record PlayoutItemViewModel(
|
||||
int? Id,
|
||||
string Title,
|
||||
DateTimeOffset Start,
|
||||
DateTimeOffset Finish,
|
||||
|
||||
@@ -106,7 +106,9 @@ public class GetFuturePlayoutItemsByIdHandler(IDbContextFactory<TvContext> dbCon
|
||||
|
||||
var gap = playoutGaps.Single(g => g.Id == c.Id);
|
||||
TimeSpan gapDuration = gap.Finish - gap.Start;
|
||||
// gaps are synthesized rows, not PlayoutItems, so they carry no row id
|
||||
return new PlayoutItemViewModel(
|
||||
null,
|
||||
"UNSCHEDULED",
|
||||
gap.StartOffset,
|
||||
gap.FinishOffset,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#nullable enable
|
||||
using ErsatzTV.Core.Domain.Filler;
|
||||
|
||||
namespace ErsatzTV.Core.Api.Playouts;
|
||||
|
||||
public record PlayoutItemResponseModel(
|
||||
int? Id,
|
||||
string Title,
|
||||
DateTimeOffset Start,
|
||||
DateTimeOffset Finish,
|
||||
|
||||
@@ -519,6 +519,7 @@ public class PlayoutControllerTests
|
||||
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
||||
|
||||
var item = new PlayoutItemViewModel(
|
||||
77,
|
||||
"Movie",
|
||||
new DateTimeOffset(2026, 7, 2, 12, 0, 0, TimeSpan.Zero),
|
||||
new DateTimeOffset(2026, 7, 2, 13, 0, 0, TimeSpan.Zero),
|
||||
@@ -526,6 +527,7 @@ public class PlayoutControllerTests
|
||||
string.Empty,
|
||||
Some(FillerKind.MidRoll));
|
||||
var gap = new PlayoutItemViewModel(
|
||||
null,
|
||||
"UNSCHEDULED",
|
||||
new DateTimeOffset(2026, 7, 2, 13, 0, 0, TimeSpan.Zero),
|
||||
new DateTimeOffset(2026, 7, 2, 13, 30, 0, TimeSpan.Zero),
|
||||
@@ -539,9 +541,11 @@ public class PlayoutControllerTests
|
||||
|
||||
var result = actionResult.ShouldBeOfType<OkObjectResult>().Value.ShouldBeOfType<PagedPlayoutItemsResponseModel>();
|
||||
result.TotalCount.ShouldBe(2);
|
||||
result.Page[0].Id.ShouldBe(77);
|
||||
result.Page[0].Title.ShouldBe("Movie");
|
||||
result.Page[0].Duration.ShouldBe("1:00:00");
|
||||
result.Page[0].FillerKind.ShouldBe(FillerKind.MidRoll);
|
||||
result.Page[1].Id.ShouldBeNull();
|
||||
result.Page[1].Title.ShouldBe("UNSCHEDULED");
|
||||
result.Page[1].FillerKind.ShouldBeNull();
|
||||
|
||||
@@ -558,6 +562,7 @@ public class PlayoutControllerTests
|
||||
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
||||
|
||||
var withContext = new PlayoutItemViewModel(
|
||||
42,
|
||||
"Movie",
|
||||
new DateTimeOffset(2026, 7, 2, 12, 0, 0, TimeSpan.Zero),
|
||||
new DateTimeOffset(2026, 7, 2, 13, 0, 0, TimeSpan.Zero),
|
||||
|
||||
@@ -743,6 +743,7 @@ public class PlayoutController(IMediator mediator) : ControllerBase
|
||||
|
||||
private static PlayoutItemResponseModel ToItemResponse(PlayoutItemViewModel vm) =>
|
||||
new(
|
||||
vm.Id,
|
||||
vm.Title,
|
||||
vm.Start,
|
||||
vm.Finish,
|
||||
|
||||
@@ -19334,6 +19334,7 @@
|
||||
},
|
||||
"PlayoutItemResponseModel": {
|
||||
"required": [
|
||||
"id",
|
||||
"title",
|
||||
"start",
|
||||
"finish",
|
||||
@@ -19343,11 +19344,15 @@
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"id": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
"integer"
|
||||
],
|
||||
"format": "int32"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"start": {
|
||||
"type": "string",
|
||||
@@ -19358,10 +19363,7 @@
|
||||
"format": "date-time"
|
||||
},
|
||||
"duration": {
|
||||
"type": [
|
||||
"null",
|
||||
"string"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"fillerKind": {
|
||||
"oneOf": [
|
||||
|
||||
Vendored
+3
-2
@@ -1044,10 +1044,11 @@ export interface components {
|
||||
"details": string;
|
||||
};
|
||||
"PlayoutItemResponseModel": {
|
||||
"title": null | string;
|
||||
"id": null | number;
|
||||
"title": string;
|
||||
"start": string;
|
||||
"finish": string;
|
||||
"duration": null | string;
|
||||
"duration": string;
|
||||
"fillerKind": null | components["schemas"]["FillerKind"];
|
||||
"hasSchedulingContext": boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user