Implemented phase 6 for the POC prototype in the Website service.

This commit is contained in:
javier committed 2026-09-21 13:53:13 +02:00
1 parent 256d22df40
commit f2f445827b
40 files changed
+1906 -37

No files matched your search

@@ -20,6 +20,9 @@ public struct TradeController<Context: RequestContext> {
/// The repository listings and shops are read through.
private let catalogue: CatalogueRepository
/// The repository the events a shop trades at are read and set through, or `nil` to leave them out.
private let collecting: CollectingRepository?
/// Told whenever the controller changes what is for sale, so pages cached elsewhere can catch up.
private let onCatalogueChange: @Sendable () async -> Void
@@ -36,6 +39,7 @@ public struct TradeController<Context: RequestContext> {
/// - trade: the repository conversations, offers, and sales are read and written through.
/// - catalogue: the repository listings and shops are read through.
/// - sessions: the store the signed-in account is read from.
/// - collecting: the repository the events a shop trades at are read and set through, or `nil` (the default) to leave them out.
/// - assetVersion: the version token appended to the pages' asset URLs, or `nil` (the default) to leave them unversioned.
/// - analytics: the analytics tracker the pages embed, or `nil` (the default) to omit it.
/// - onCatalogueChange: told whenever the controller changes what is for sale; does nothing by default.
@@ -43,6 +47,7 @@ public struct TradeController<Context: RequestContext> {
trade: TradeRepository,
catalogue: CatalogueRepository,
sessions: SessionStore,
collecting: CollectingRepository? = nil,
assetVersion: String? = nil,
analytics: Analytics? = nil,
onCatalogueChange: @escaping @Sendable () async -> Void = {}
@@ -51,6 +56,7 @@ public struct TradeController<Context: RequestContext> {
self.onCatalogueChange = onCatalogueChange
self.assetVersion = assetVersion
self.catalogue = catalogue
self.collecting = collecting
self.sessions = sessions
self.trade = trade
}
@@ -78,6 +84,7 @@ extension TradeController: RouterController {
routes.post(.Trade.sale, use: takeStep)
routes.get(.Trade.dashboard, use: dashboard)
routes.post(.Trade.answer, use: answer)
routes.post(.init("\(String.Route.sellerDashboard)/events"), use: setEvents)
return routes
}
@@ -429,6 +436,26 @@ private extension TradeController {
)
}
/// Handles the events a seller ticked on the dashboard. A form with no box ticked posts no field at all.
@Sendable
func setEvents(
request: Request,
context: Context
) async throws -> Response {
guard let (user, _) = await account(of: request) else {
return .redirect(to: .Route.signIn, type: .normal)
}
let body = try await request.body.collect(upTo: 16 * 1_024)
let slugs = SearchParameters(query: String(buffer: body)).items
.filter { $0.name == "events" }
.map(\.value)
try await collecting?.setEvents(slugs, forShopOwnedBy: user)
return .redirect(to: .Route.sellerDashboard, type: .normal)
}
/// Handles a request for the seller dashboard; an account without a shop is led to where one is opened.
@Sendable
func dashboard(
@@ -446,6 +473,7 @@ private extension TradeController {
let offers = try await trade.openOffers(for: user)
let sales = try await trade.sales(for: user).filter { $0.seller.id == user.id }
let listings = try await catalogue.listings(matching: .init(shop: shop.slug))
let events = try await collecting?.upcomingEvents() ?? []
return HTMLResponse(additionalHeaders: .perViewer) { [analytics, assetVersion] in
SellerDashboardPage(
@@ -453,6 +481,7 @@ private extension TradeController {
offers: offers,
sales: sales,
listings: listings,
events: events,
viewer: viewer,
assetVersion: assetVersion,
analytics: analytics