Files
mono/events/sxc/pages/events.sx
giles 1d59023571 Move events composition from Python to .sx defcomps (Phase 9)
Convert all 14 events page helpers from returning sx_call() strings
to returning data dicts. Defpage expressions compose SX components
with data bindings using map/fn/if/when.

Complex sub-panels (entry tickets config, buy form, posts panel,
options buttons, entry nav menu) returned as SxExpr from existing
render functions which remain for HTMX handler use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 02:30:46 +00:00

236 lines
8.9 KiB
Plaintext

;; Events pages — auto-mounted with absolute paths
;; All helpers return data dicts — markup composition in SX.
;; Calendar admin
(defpage calendar-admin
:path "/<slug>/<calendar_slug>/admin/"
:auth :admin
:layout :events-calendar-admin
:data (calendar-admin-data calendar-slug)
:content (~events-calendar-admin-panel
:description-content (~events-calendar-description-display
:description cal-description :edit-url desc-edit-url)
:csrf csrf :description cal-description))
;; Day admin
(defpage day-admin
:path "/<slug>/<calendar_slug>/day/<int:year>/<int:month>/<int:day>/admin/"
:auth :admin
:layout :events-day-admin
:data (day-admin-data calendar-slug year month day)
:content (~events-day-admin-panel))
;; Slots listing
(defpage slots-listing
:path "/<slug>/<calendar_slug>/slots/"
:auth :public
:layout :events-slots
:data (slots-data calendar-slug)
:content (~events-slots-table
:list-container list-container
:rows (if has-slots
(<> (map (fn (s)
(~events-slots-row
:tr-cls tr-cls :slot-href (get s "slot-href")
:pill-cls pill-cls :hx-select hx-select
:slot-name (get s "name") :description (get s "description")
:flexible (get s "flexible")
:days (if (get s "has-days")
(~events-slot-days-pills :days-inner
(<> (map (fn (d) (~events-slot-day-pill :day d)) (get s "day-list"))))
(~events-slot-no-days))
:time-str (get s "time-str")
:cost-str (get s "cost-str") :action-btn action-btn
:del-url (get s "del-url")
:csrf-hdr csrf-hdr))
slots-list))
(~events-slots-empty-row))
:pre-action pre-action :add-url add-url))
;; Slot detail
(defpage slot-detail
:path "/<slug>/<calendar_slug>/slots/<int:slot_id>/"
:auth :admin
:layout :events-slot
:data (slot-data calendar-slug slot-id)
:content (~events-slot-panel
:slot-id slot-id-str
:list-container list-container
:days (if has-days
(~events-slot-days-pills :days-inner
(<> (map (fn (d) (~events-slot-day-pill :day d)) day-list)))
(~events-slot-no-days))
:flexible flexible
:time-str time-str :cost-str cost-str
:pre-action pre-action :edit-url edit-url))
;; Entry detail
(defpage entry-detail
:path "/<slug>/<calendar_slug>/day/<int:year>/<int:month>/<int:day>/entries/<int:entry_id>/"
:auth :admin
:layout :events-entry
:data (entry-data calendar-slug entry-id)
:content (~events-entry-panel
:entry-id entry-id-str :list-container list-container
:name (~events-entry-field :label "Name"
:content (~events-entry-name-field :name entry-name))
:slot (~events-entry-field :label "Slot"
:content (if has-slot
(~events-entry-slot-assigned :slot-name slot-name :flex-label flex-label)
(~events-entry-slot-none)))
:time (~events-entry-field :label "Time Period"
:content (~events-entry-time-field :time-str time-str))
:state (~events-entry-field :label "State"
:content (~events-entry-state-field :entry-id entry-id-str
:badge (~badge :cls state-badge-cls :label state-badge-label)))
:cost (~events-entry-field :label "Cost"
:content (~events-entry-cost-field :cost cost-str))
:tickets (~events-entry-field :label "Tickets"
:content (~events-entry-tickets-field :entry-id entry-id-str
:tickets-config tickets-config))
:buy buy-form
:date (~events-entry-field :label "Date"
:content (~events-entry-date-field :date-str date-str))
:posts (~events-entry-field :label "Associated Posts"
:content (~events-entry-posts-field :entry-id entry-id-str
:posts-panel posts-panel))
:options options-html
:pre-action pre-action :edit-url edit-url)
:menu entry-menu)
;; Entry admin
(defpage entry-admin
:path "/<slug>/<calendar_slug>/day/<int:year>/<int:month>/<int:day>/entries/<int:entry_id>/admin/"
:auth :admin
:layout :events-entry-admin
:data (entry-admin-data calendar-slug entry-id year month day)
:content (~nav-link :href ticket-types-href :label "ticket_types"
:select-colours select-colours :aclass nav-btn :is-selected false)
:menu (~events-admin-placeholder-nav))
;; Ticket types listing
(defpage ticket-types-listing
:path "/<slug>/<calendar_slug>/day/<int:year>/<int:month>/<int:day>/entries/<int:entry_id>/ticket-types/"
:auth :public
:layout :events-ticket-types
:data (ticket-types-data calendar-slug entry-id year month day)
:content (~events-ticket-types-table
:list-container list-container
:rows (if has-types
(<> (map (fn (tt)
(~events-ticket-types-row
:tr-cls tr-cls :tt-href (get tt "tt-href")
:pill-cls pill-cls :hx-select hx-select
:tt-name (get tt "tt-name") :cost-str (get tt "cost-str")
:count (get tt "count") :action-btn action-btn
:del-url (get tt "del-url")
:csrf-hdr csrf-hdr))
types-list))
(~events-ticket-types-empty-row))
:action-btn action-btn :add-url add-url)
:menu (~events-admin-placeholder-nav))
;; Ticket type detail
(defpage ticket-type-detail
:path "/<slug>/<calendar_slug>/day/<int:year>/<int:month>/<int:day>/entries/<int:entry_id>/ticket-types/<int:ticket_type_id>/"
:auth :admin
:layout :events-ticket-type
:data (ticket-type-data calendar-slug entry-id ticket-type-id year month day)
:content (~events-ticket-type-panel
:ticket-id ticket-id :list-container list-container
:c1 (~events-ticket-type-col :label "Name" :value tt-name)
:c2 (~events-ticket-type-col :label "Cost" :value cost-str)
:c3 (~events-ticket-type-col :label "Count" :value count-str)
:pre-action pre-action :edit-url edit-url)
:menu (~events-admin-placeholder-nav))
;; My tickets
(defpage my-tickets
:path "/tickets/"
:auth :public
:layout :root
:data (tickets-data)
:content (~events-tickets-panel
:list-container list-container
:has-tickets has-tickets
:cards (when has-tickets
(<> (map (fn (t)
(~events-ticket-card
:href (get t "href") :entry-name (get t "entry-name")
:type-name (get t "type-name") :time-str (get t "time-str")
:cal-name (get t "cal-name")
:badge (~badge :cls (get t "badge-cls") :label (get t "badge-label"))
:code-prefix (get t "code-prefix")))
tickets-list)))))
;; Ticket detail
(defpage ticket-detail
:path "/tickets/<code>/"
:auth :public
:layout :root
:data (ticket-detail-data code)
:content (~events-ticket-detail
:list-container list-container :back-href back-href
:header-bg header-bg :entry-name entry-name
:badge (span :class (str "inline-flex items-center rounded-full px-3 py-1 text-sm font-medium " badge-cls)
badge-label)
:type-name type-name :code ticket-code
:time-date time-date :time-range time-range
:cal-name cal-name :type-desc type-desc :checkin-str checkin-str
:qr-script qr-script))
;; Ticket admin dashboard
(defpage ticket-admin
:path "/admin/tickets/"
:auth :admin
:layout :root
:data (ticket-admin-data)
:content (~events-ticket-admin-panel
:list-container list-container
:stats (<> (map (fn (s)
(~events-ticket-admin-stat
:border (get s "border") :bg (get s "bg")
:text-cls (get s "text-cls") :label-cls (get s "label-cls")
:value (get s "value") :label (get s "label")))
admin-stats))
:lookup-url lookup-url :has-tickets has-tickets
:rows (when has-tickets
(<> (map (fn (t)
(~events-ticket-admin-row
:code (get t "code") :code-short (get t "code-short")
:entry-name (get t "entry-name")
:date (when (get t "date-str")
(~events-ticket-admin-date :date-str (get t "date-str")))
:type-name (get t "type-name")
:badge (~badge :cls (get t "badge-cls") :label (get t "badge-label"))
:action (if (get t "can-checkin")
(~events-ticket-admin-checkin-form
:checkin-url (get t "checkin-url") :code (get t "code") :csrf csrf)
(when (get t "is-checked-in")
(~events-ticket-admin-checked-in :time-str (get t "checkin-time"))))))
admin-tickets)))))
;; Markets
(defpage events-markets
:path "/<slug>/markets/"
:auth :public
:layout :events-markets
:data (markets-data)
:content (~crud-panel
:list-id "markets-list"
:form (when can-create
(~crud-create-form :create-url create-url :csrf csrf
:errors-id "market-create-errors" :list-id "markets-list"
:placeholder "e.g. Farm Shop, Bakery" :btn-label "Add market"))
:list (if markets-list
(<> (map (fn (m)
(~crud-item :href (get m "href") :name (get m "name")
:slug (get m "slug") :del-url (get m "del-url")
:csrf-hdr (get m "csrf-hdr")
:list-id "markets-list"
:confirm-title "Delete market?"
:confirm-text "Products will be hidden (soft delete)"))
markets-list))
(~empty-state :message "No markets yet. Create one above."
:cls "text-gray-500 mt-4"))))