Files
rose-ash/events/sexp/calendar.sexpr
giles f9d9697c67
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m20s
Externalize sexp to .sexpr files + render() API
Replace all 676 inline sexp() string calls across 7 services with
render(component_name, **kwargs) calls backed by 46 external .sexpr
component definition files (587 defcomps total).

- Add render() function to shared/sexp/jinja_bridge.py
- Add load_service_components() helper and update load_sexp_dir() for *.sexpr
- Update parser keyword regex to support HTMX hx-on::event syntax
- Convert remaining inline HTML in route files to render() calls
- Add shared/sexp/templates/misc.sexp for cross-service utility components

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 16:14:58 +00:00

103 lines
5.5 KiB
Plaintext

;; Events calendar components
(defcomp ~events-calendar-nav-arrow (&key pill-cls href label)
(a :class (str pill-cls " text-xl") :href href
:hx-get href :hx-target "#main-panel" :hx-select "#main-panel" :hx-swap "outerHTML" :hx-push-url "true" label))
(defcomp ~events-calendar-month-label (&key month-name year)
(div :class "px-3 font-medium" (str month-name " " year)))
(defcomp ~events-calendar-weekday (&key name)
(div :class "py-1" name))
(defcomp ~events-calendar-day-short (&key day-str)
(span :class "sm:hidden text-[16px] text-stone-500" day-str))
(defcomp ~events-calendar-day-num (&key pill-cls href num)
(a :class pill-cls :href href :hx-get href :hx-target "#main-panel" :hx-select "#main-panel"
:hx-swap "outerHTML" :hx-push-url "true" num))
(defcomp ~events-calendar-entry-badge (&key bg-cls name state-label)
(div :class (str "flex items-center justify-between gap-1 text-[11px] rounded px-1 py-0.5 " bg-cls)
(span :class "truncate" name)
(span :class "shrink-0 text-[10px] font-semibold uppercase tracking-tight" state-label)))
(defcomp ~events-calendar-cell (&key cell-cls day-short-html day-num-html badges-html)
(div :class cell-cls
(div :class "flex justify-between items-center"
(div :class "flex flex-col" (raw! day-short-html) (raw! day-num-html)))
(div :class "mt-1 space-y-0.5" (raw! badges-html))))
(defcomp ~events-calendar-grid (&key arrows-html weekdays-html cells-html)
(section :class "bg-orange-100"
(header :class "flex items-center justify-center mt-2"
(nav :class "flex items-center gap-2 text-2xl" (raw! arrows-html)))
(div :class "rounded-2xl border border-stone-200 bg-white/80 p-4"
(div :class "hidden sm:grid grid-cols-7 text-center text-md font-semibold text-stone-700 mb-2" (raw! weekdays-html))
(div :class "grid grid-cols-1 sm:grid-cols-7 gap-px bg-stone-200 rounded-xl overflow-hidden" (raw! cells-html)))))
(defcomp ~events-calendars-create-form (&key create-url csrf)
(<>
(div :id "cal-create-errors" :class "mt-2 text-sm text-red-600")
(form :class "mt-4 flex gap-2 items-end" :hx-post create-url
:hx-target "#calendars-list" :hx-select "#calendars-list" :hx-swap "outerHTML"
:hx-on::before-request "document.querySelector('#cal-create-errors').textContent='';"
:hx-on::response-error "document.querySelector('#cal-create-errors').innerHTML = event.detail.xhr.responseText;"
(input :type "hidden" :name "csrf_token" :value csrf)
(div :class "flex-1"
(label :class "block text-sm text-gray-600" "Name")
(input :name "name" :type "text" :required true :class "w-full border rounded px-3 py-2"
:placeholder "e.g. Events, Gigs, Meetings"))
(button :type "submit" :class "border rounded px-3 py-2" "Add calendar"))))
(defcomp ~events-calendars-panel (&key form-html list-html)
(section :class "p-4"
(raw! form-html)
(div :id "calendars-list" :class "mt-6" (raw! list-html))))
(defcomp ~events-calendars-empty ()
(p :class "text-gray-500 mt-4" "No calendars yet. Create one above."))
(defcomp ~events-calendars-item (&key href cal-name cal-slug del-url csrf-hdr)
(div :class "mt-6 border rounded-lg p-4"
(div :class "flex items-center justify-between gap-3"
(a :class "flex items-baseline gap-3" :href href
:hx-get href :hx-target "#main-panel" :hx-select "#main-panel" :hx-swap "outerHTML" :hx-push-url "true"
(h3 :class "font-semibold" cal-name)
(h4 :class "text-gray-500" (str "/" cal-slug "/")))
(button :class "text-sm border rounded px-3 py-1 hover:bg-red-50 hover:border-red-400"
:data-confirm true :data-confirm-title "Delete calendar?"
:data-confirm-text "Entries will be hidden (soft delete)"
:data-confirm-icon "warning" :data-confirm-confirm-text "Yes, delete it"
:data-confirm-cancel-text "Cancel" :data-confirm-event "confirmed"
:hx-delete del-url :hx-trigger "confirmed"
:hx-target "#calendars-list" :hx-select "#calendars-list" :hx-swap "outerHTML"
:hx-headers csrf-hdr
(i :class "fa-solid fa-trash")))))
(defcomp ~events-calendar-description-display (&key description edit-url)
(div :id "calendar-description"
(if description
(p :class "text-stone-700 whitespace-pre-line break-all" description)
(p :class "text-stone-400 italic" "No description yet."))
(button :type "button" :class "mt-2 text-xs underline"
:hx-get edit-url :hx-target "#calendar-description" :hx-swap "outerHTML"
(i :class "fas fa-edit"))))
(defcomp ~events-calendar-description-title-oob (&key description)
(div :id "calendar-description-title" :hx-swap-oob "outerHTML"
:class "text-base font-normal break-words whitespace-normal min-w-0 break-all w-full text-center block"
description))
(defcomp ~events-calendar-description-edit-form (&key save-url cancel-url csrf description)
(div :id "calendar-description"
(form :hx-post save-url :hx-target "#calendar-description" :hx-swap "outerHTML"
(input :type "hidden" :name "csrf_token" :value csrf)
(textarea :name "description" :autocomplete "off" :rows "4"
:class "w-full p-2 border rounded" description)
(div :class "mt-2 flex gap-2 text-xs"
(button :type "submit" :class "px-3 py-1 rounded bg-stone-800 text-white" "Save")
(button :type "button" :class "px-3 py-1 rounded border"
:hx-get cancel-url :hx-target "#calendar-description" :hx-swap "outerHTML"
"Cancel")))))