import Elementary import Foundation import Infrastructure import Persistence /// Every series in the catalogue, with how many copies of each are listed. struct SeriesIndexPage { // MARK: Properties let analytics: Analytics? let assetVersion: String? let locale: Locale /// The series, by name, each with the listings of it still for sale. let entries: [(series: Series, listings: [Listing])] /// The signed-in viewer, or `nil` when signed out. let viewer: Viewer? // MARK: Initializers /// Creates the series index. /// - Parameters: /// - entries: the series, by name, each with the listings of it still for sale. /// - viewer: the signed-in viewer, or `nil` (the default) when signed out. /// - assetVersion: the version token appended to the page's asset URLs, or `nil` (the default) to leave them unversioned. /// - analytics: the analytics tracker embedded in the document head, or `nil` (the default) to omit it. init( entries: [(series: Series, listings: [Listing])], viewer: Viewer? = nil, assetVersion: String? = nil, analytics: Analytics? = nil ) { self.analytics = analytics self.assetVersion = assetVersion self.entries = entries self.locale = .init(identifier: "en") self.viewer = viewer } } // MARK: - Page extension SeriesIndexPage: Page { // MARK: Computed var content: some HTML { SiteLayout(viewer: viewer) { div(.class("page series")) { div(.class("series__intro")) { p(.class("type-eyebrow")) { "Browse by series" } h1(.class("type-h1")) { "Series" } p(.class("type-lead")) { "Every issue of a run, what is listed of each, and the gaps — one tap from an alert." } } ul(.class("series__index")) { ForEach(entries) { entry in li { a( .class("card series__entry"), .href(.Route.series(entry.series.slug)) ) { if let cover = entry.listings.first { ListingImage(cover, modifier: "cover--agreed") } span(.class("series__entry-text")) { span(.class("series__entry-name")) { entry.series.name } span(.class("type-caption")) { "\(entry.series.publisher) · \(entry.series.years) · \(entry.series.issueCount) issues" } span(.class("type-small")) { "\(entry.listings.count) \(entry.listings.count == 1 ? "copy" : "copies") listed" } } } } } } } } } var scripts: [any Asset] { [StaticFile.shared] } var stylesheets: [any Asset] { [StaticFile.shared, StaticFile.collecting] } var summary: String? { "Browse comics by series on CCN: every issue of a run, what is listed of each, and the gaps." } var title: String { "Series — CCN" } }