Add vendor pre-population for bulk code and individual edit forms

- Add vendor-changed HTMX handlers for both bulk code and individual edit
- Pre-populate default account at 100% when vendor is selected and no accounts exist
- Fix render-accounts-section to render from step-params correctly
- Change bulk code vendor-changed from hx-get to hx-post to include form data
- Add routes for vendor-changed endpoints
- Update e2e tests to cover vendor pre-population
- Run lein cljfmt fix across codebase
This commit is contained in:
2026-05-21 14:45:19 -07:00
parent 8bd0cee1b1
commit ba87805d4c
210 changed files with 8694 additions and 9627 deletions

View File

@@ -55,8 +55,8 @@
":style" (format "selected == '%s' ? 'max-height: ' + $el.scrollHeight + 'px' : ''" (:selector params))))
(for [c children]
[:li
(update-in c [1 1 :class ] (fn [c]
(hh/add-class (or c "") " flex items-center p-2 pl-11 w-full text-base font-normal rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")))])])
(update-in c [1 1 :class] (fn [c]
(hh/add-class (or c "") " flex items-center p-2 pl-11 w-full text-base font-normal rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")))])])
(defn left-aside- [{:keys [nav page-specific]} & _]
[:aside {:id "left-nav",
@@ -83,7 +83,6 @@
[:div {:class "overflow-y-auto py-5 px-3 h-full bg-gray-50 border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700"}
nav
(when page-specific
[:div {:class " pt-5 mt-5 space-y-2 border-t border-gray-200 dark:border-gray-700"}
page-specific])]])
@@ -94,7 +93,7 @@
"invoices"
(#{:pos-sales :pos-expected-deposits :pos-tenders :pos-refunds :pos-cash-drawer-shifts ::ss-routes/page} (:matched-route request))
"sales"
"sales"
(#{::payment-routes/all-page ::payment-routes/pending-page ::payment-routes/cleared-page ::payment-routes/voided-page} (:matched-route request))
"payments"
(#{::transaction-routes/page ::transaction-routes/approved-page ::transaction-routes/unapproved-page ::transaction-routes/requires-feedback-page :transaction-insights} (:matched-route request))
@@ -108,7 +107,7 @@
[:li
(menu-button- {:icon svg/pie
:href (bidi/path-for ssr-routes/only-routes
:href (bidi/path-for ssr-routes/only-routes
::dashboard/page)}
"Dashboard")]
@@ -147,7 +146,6 @@
:hx-boost "true"}
"Voided")
(when (can? (:identity request)
{:subject :invoice
:activity :import})
@@ -156,7 +154,6 @@
:active? (= ::invoice-route/import-page (:matched-route request))
:hx-boost "true"} "Import"))
#_(when (can? (:identity request)
{:subject :invoice
:activity :import})
@@ -168,7 +165,6 @@
"Glimpse"
(tags/pill- {:color :secondary} "Beta")]))
(when (can? (:identity request)
{:subject :ar-invoice
:activity :read})
@@ -213,12 +209,12 @@
:hx-boost "true"}
"Refunds")
(menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
:pos-cash-drawer-shifts)
"?date-range=week")
:active? (= :pos-cash-drawer-shifts (:matched-route request))
:hx-boost "true"}
"Cash drawer shifts")
(menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
:pos-cash-drawer-shifts)
"?date-range=week")
:active? (= :pos-cash-drawer-shifts (:matched-route request))
:hx-boost "true"}
"Cash drawer shifts")
(menu-button- {:href (str (bidi/path-for ssr-routes/only-routes
::ss-routes/page)
"?date-range=week")
@@ -288,7 +284,6 @@
(menu-button- {:href (bidi/path-for ssr-routes/only-routes
:transaction-insights)} "Insights")))]
(when (can? (:identity request)
{:subject :ledger-page})
(list
@@ -314,7 +309,7 @@
[:div.flex.gap-2
"External Register"
(tags/pill- {:color :secondary} "WIP")]))
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/profit-and-loss))
:active? (= ::ledger-routes/profit-and-loss (:matched-route request))
@@ -322,7 +317,7 @@
[:div.flex.gap-2
"Profit and loss"
(tags/pill- {:color :secondary} "WIP")])
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/cash-flows))
:active? (= ::ledger-routes/cash-flows (:matched-route request))
@@ -330,7 +325,7 @@
[:div.flex.gap-2
"Cash flows"
(tags/pill- {:color :secondary} "WIP")])
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/balance-sheet))
:active? (= ::ledger-routes/balance-sheet (:matched-route request))
@@ -338,8 +333,7 @@
[:div.flex.gap-2
"Balance Sheet"
(tags/pill- {:color :secondary} "WIP")])
(menu-button- {:href (hu/url (bidi/path-for ssr-routes/only-routes
::ledger-routes/external-import-page)
{:date-range "month"})
@@ -349,7 +343,6 @@
"External Import"
(tags/pill- {:color :secondary} "WIP")]))))]))
(defn company-aside-nav- [request]
[:ul {:class "space-y-2" :hx-boost "true"}
[:li
@@ -465,7 +458,6 @@
:hx-boost true}
"Background Jobs")]
(menu-button- {:icon svg/arrow-in
"@click.prevent" "if (selected == 'import') {selected = null } else { selected = 'import'} "}
"Import")

View File

@@ -1,4 +1,4 @@
(ns auto-ap.ssr.components.bank-account-icon
(ns auto-ap.ssr.components.bank-account-icon
(:require [auto-ap.ssr.hiccup-helper :as hh]
[auto-ap.ssr.svg :as svg]))

View File

@@ -8,15 +8,15 @@
[:a {:href "#", :class "inline-flex w-4 h-4 mr-2 items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white"}
[:div.w-4.h-4 svg/home]]]
(for [p steps]
[:li
[:li
[:div {:class "flex items-center"}
[:div {:class "w-6 h-6 text-gray-400",}
[:div {:class "w-6 h-6 text-gray-400"}
svg/breadcrumb-component]
(update-in p [1 :class] str " ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2 dark:text-gray-400 dark:hover:text-white")]])
#_[:li {:aria-current "page"}
[:div {:class "flex items-center"}
[:svg {:aria-hidden "true", :class "w-6 h-6 text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
[:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]]
[:span {:class "ml-1 text-sm font-medium text-gray-500 md:ml-2 dark:text-gray-400"} "Flowbite"]]]]])
[:div {:class "flex items-center"}
[:svg {:aria-hidden "true", :class "w-6 h-6 text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
[:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]]
[:span {:class "ml-1 text-sm font-medium text-gray-500 md:ml-2 dark:text-gray-400"} "Flowbite"]]]]])

View File

@@ -113,9 +113,9 @@
(= :secondary (:color params)) (str " text-white bg-blue-500 hover:bg-blue-600 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700")
(= :primary (:color params)) (str " text-white bg-green-500 hover:bg-green-600 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 ")
(= :secondary-light (:color params)) (str " text-blue-800 bg-white-200 border-gray-100 border hover:bg-blue-100 focus:ring-blue-100 dark:bg-blue-400 dark:hover:bg-blue-800 ")
(not (nil? (:color params)))
(str " text-white " (bg-colors (:color params) (:disabled params)))
(str " text-white " (bg-colors (:color params) (:disabled params)))
(nil? (:color params))
(str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 font-medium border border-gray-300 dark:border-gray-700")))
@@ -126,7 +126,7 @@
(svg/spinner {:class "inline w-4 h-4 text-white"})
[:div.ml-3 "Loading..."]])
(into [:div.inline-flex.gap-2.items-center.justify-center {:class (when (:indicator? params true)
"htmx-indicator-hidden")}]
"htmx-indicator-hidden")}]
children)])
(defn icon-button- [params & children]
@@ -162,8 +162,6 @@
[:div.ml-3 "Loading..."]]
(into [:div.htmx-indicator-hidden] children)])
(defn group-button- [{:keys [size] :or {size :normal} :as params} & children]
(into [:button (cond-> params
true (assoc :type (or (:type params) "button"))
@@ -191,7 +189,7 @@
(defn navigation-button- [{:keys [class next-arrow?] :or {next-arrow? true} :as params} & children]
[:button
(-> params
(update :class (fnil hh/add-class "")
(update :class (fnil hh/add-class "")
"p-4 text-green-700 border border-gray-300 rounded-lg bg-gray-50
dark:bg-gray-800 dark:border-green-800 dark:text-green-400
focus:ring-green-400 focus:ring-2
@@ -211,7 +209,7 @@
{:class "space-y-4 w-72"}
(for [n children]
[:li n])
#_[:li
[:div
{:class
@@ -231,7 +229,6 @@
[:span {:class "sr-only"} "Confirmation"]
[:h3 {:class "font-medium"} "5. Confirmation"]]]]])
(defn validated-save-button- [{:keys [errors class] :as params} & children]
(button- (-> {:color (or (:color params) :primary)
:type "submit" :class (cond-> (or class "")

View File

@@ -4,7 +4,7 @@
[clojure.string :as str]))
(defn card- [params & children]
(into [:div (update params :class
(into [:div (update params :class
#(cond-> (or % "")
(not (str/includes? (or % "") "bg-")) (hh/add-class "dark:bg-gray-800 bg-white ")
true (hh/add-class "shadow-md sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 overflow-hidden")))]
@@ -13,6 +13,6 @@
(defn content-card- [params & children]
[:section (merge params {:class (hh/add-class " py-3 sm:py-5" (:class params))})
[:div {:class (:max-w params "max-w-screen-2xl")}
(into
[:div {:class "relative overflow-scroll shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white"}]
children)]])
(into
[:div {:class "relative overflow-scroll shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white"}]
children)]])

View File

@@ -16,13 +16,13 @@
"@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))
:style (:style params)}]
(if (:sort-key params)
[(into [:a {:href "#"} ] rest)]
[(into [:a {:href "#"}] rest)]
rest)))
(defn sort-header- [params & rest]
[:th.px-4.py-3 {:scope "col" :class (:class params)
"@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params)) }
(into [:a {:href "#"} ] rest)])
"@click" (format "$dispatch('sorted', {key: '%s'})" (:sort-key params))}
(into [:a {:href "#"}] rest)])
(defn row- [params & rest]
(into [:tr (update params
@@ -31,11 +31,11 @@
(defn cell- [params & rest]
(into [:td.px-4.py-2 (update params
:class #(str (-> ""
(hh/add-class (or % ""))))) ]
(hh/add-class (or % "")))))]
rest))
(defn right-stack-cell- [params & rest]
(cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between
(cell- params (into [:div.flex.flex-row-reverse.items-center.justify-between
rest])))
(defn checkbox-header- [params & rest]
@@ -46,17 +46,17 @@
(defn data-grid-
[{:keys [headers thead-params id] :as params} & rest]
[:div.shrink.overflow-y-scroll
[:div.shrink.overflow-y-scroll
[:table (merge {:class "w-full text-sm text-left text-gray-500 dark:text-gray-400 shrink"}
(dissoc params :headers :thead-params))
[:thead (update thead-params :class #(-> "text-xs text-gray-800 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400 group-[.raw]:sticky group-[.raw]:z-10 group-[.raw]:top-0"
(hh/add-class (or % ""))))
(into
[:tr]
headers)]
(into
[:tbody {}]
rest)]])
(into
[:tr]
headers)]
(into
[:tbody {}]
rest)]])
;; needed for tailwind
;; lg:table-cell md:table-cell
@@ -81,35 +81,35 @@
rows] :as params} & children]
(let [card (if raw? raw-table-card content-card-)]
(card
(cond-> { :id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden")) }
root-params (merge root-params)
route (assoc
:hx-get (bidi/path-for ssr-routes/only-routes
route
:request-method :get)
:hx-trigger "clientSelected from:body, invalidated from:body"
:hx-swap "outerHTML swap:300ms"))
[:div {:class " flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
[:h1.text-2xl.mb-3.font-bold title]
[:div {:class "flex items-center flex-1 space-x-4"}
[:h5
(when subtitle
[:span subtitle])]]
(into [:div {:class "group-[.raw]:hidden flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
action-buttons)]
[:div {:class "overflow-x-auto contents"}
(data-grid- {:headers headers
:thead-params thead-params}
rows)]
(cond-> {:id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden"))}
root-params (merge root-params)
route (assoc
:hx-get (bidi/path-for ssr-routes/only-routes
route
:request-method :get)
:hx-trigger "clientSelected from:body, invalidated from:body"
:hx-swap "outerHTML swap:300ms"))
[:div {:class " flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
[:h1.text-2xl.mb-3.font-bold title]
[:div {:class "flex items-center flex-1 space-x-4"}
[:h5
(when subtitle
[:span subtitle])]]
(into [:div {:class "group-[.raw]:hidden flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
action-buttons)]
[:div {:class "overflow-x-auto contents"}
(data-grid- {:headers headers
:thead-params thead-params}
rows)]
(when (or paginate?
(nil? paginate?))
[:div {:class "contents group-[.raw]:block"}
(paginator- {:start start
:end (Math/min (+ start per-page) total)
:per-page per-page
:total total
:a-params (fn [page]
(when (or paginate?
(nil? paginate?))
[:div {:class "contents group-[.raw]:block"}
(paginator- {:start start
:end (Math/min (+ start per-page) total)
:per-page per-page
:total total
:a-params (fn [page]
;; TODO it might be good to have a more global form defined in the specific page
;; with elements that are part of item
;; that way this is not deeply coupled
@@ -117,44 +117,43 @@
;; TODO the other way to think about this is that we want the request to include
;; all of the correct parameters, not parameters to merge with the current ones.
;; think sorting, filters, pagination
{:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
route
:request-method :get)
{:start (* page per-page)})
:hx-target (str "#" id)
:hx-swap "outerHTML show:#app:top"
:hx-indicator (str "#" id)})
:per-page-params {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
{:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
route
:request-method :get))
:hx-trigger "change"
:hx-include "this"
:hx-target (str "#" id) ;
:request-method :get)
{:start (* page per-page)})
:hx-target (str "#" id)
:hx-swap "outerHTML show:#app:top"
:hx-indicator (str "#" id)}})])
children
[:div {:class "htmx-indicator absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2 overflow-hidden w-full h-full"}
[:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50" }
[:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])))
:hx-indicator (str "#" id)})
:per-page-params {:hx-get (hu/url (bidi/path-for ssr-routes/only-routes
route
:request-method :get))
:hx-trigger "change"
:hx-include "this"
:hx-target (str "#" id) ;
:hx-swap "outerHTML show:#app:top"
:hx-indicator (str "#" id)}})])
children
[:div {:class "htmx-indicator absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2 overflow-hidden w-full h-full"}
[:div {:class "flex items-center justify-center w-full h-full border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700 bg-opacity-50"}
[:div {:class "px-3 py-1 text-xs font-medium leading-none text-center text-blue-800 bg-blue-200 rounded-full animate-pulse dark:bg-blue-900 dark:text-blue-200"} "loading..."]]])))
(defn new-row- [{:keys [index colspan tr-params row-offset] :as params} & content]
(row-
(merge {:class "new-row"
"x-on:htmx:after-settle.camel" "let options=$el.parentNode.querySelectorAll('tr'); let target=options[options.length-2]; $nextTick(() => $focus.within(target).first())"
(merge {:class "new-row"
"x-on:htmx:after-settle.camel" "let options=$el.parentNode.querySelectorAll('tr'); let target=options[options.length-2]; $nextTick(() => $focus.within(target).first())"
:x-data (hx/json {:newRowIndex index
:offset (or row-offset 0)}) }
tr-params)
(cell- {:colspan colspan
:class "bg-gray-100"}
[:div.flex.justify-center
(a-button- (merge
(dissoc params :index :colspan)
{"@click.prevent" "$dispatch('newRow', {index: (newRowIndex++)})"
:color :secondary
:hx-trigger "newRow"
:hx-vals (hiccup/raw "js:{index: event.detail.index }")
:hx-target "closest .new-row"
:hx-swap "beforebegin"
})
content)])))
:x-data (hx/json {:newRowIndex index
:offset (or row-offset 0)})}
tr-params)
(cell- {:colspan colspan
:class "bg-gray-100"}
[:div.flex.justify-center
(a-button- (merge
(dissoc params :index :colspan)
{"@click.prevent" "$dispatch('newRow', {index: (newRowIndex++)})"
:color :secondary
:hx-trigger "newRow"
:hx-vals (hiccup/raw "js:{index: event.detail.index }")
:hx-target "closest .new-row"
:hx-swap "beforebegin"})
content)])))

View File

@@ -5,7 +5,6 @@
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]))
(defn modal-
"This modal function is used to create a modal window with a stack that allows for transitioning between modals.
@@ -26,16 +25,16 @@
:class (fn [c] (-> c
(or "")
(hh/add-class "w-full p-4 modal-card flex max-h-[inherit]"))))
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden" }
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden"}
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header]
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full shrink"}
content]
(when footer [:div {:class "p-4"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 mr-1 bg-red-500 rounded-full"}] [:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
(when (:error params )
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex { :class "dark:bg-red-900 dark:text-red-300"}
(when (:error params)
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex {:class "dark:bg-red-900 dark:text-red-300"}
[:span {:class "w-2 h-2 mr-1 bg-red-500 rounded-full"}]
[:span.px-2.py-0.5 (:error params)]])
[:div {:class "shrink-0"}
@@ -45,7 +44,6 @@
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"}
children])
(defn modal-header-attachment- [params & children]
[:div {:class "flex items-start justify-between p-4 border-b shrink-0"}
children])
@@ -56,7 +54,7 @@
(defn modal-footer- [params & children]
[:div {:class "p-4 border-t"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex
(hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
(hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
@@ -66,7 +64,7 @@
(defn modal-card-advanced- [params & children]
[:div (merge params
{:class (hh/add-class "modal-card bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col max-h-screen max-w-screen" (:class params "")) })
{:class (hh/add-class "modal-card bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col max-h-screen max-w-screen" (:class params ""))})
children])
(defn success-modal- [{:keys [title]} & children]

View File

@@ -8,7 +8,6 @@
[clojure.string :as str]
[hiccup2.core :as hiccup]))
(def default-input-classes
["bg-gray-50" "border" "text-sm" "rounded-lg" "" "block"
"p-2.5" "border-gray-300" "text-gray-900" "focus:ring-blue-500" "focus:border-blue-500"
@@ -149,7 +148,6 @@
[:li {":style" "index == 0 && 'border: 0 !important;'"}
[:label {:class "p-3 group rounded flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 [&.implied]:text-gray-500 text-gray-800 dark:text-gray-100 cursor-pointer"
:href "#"
":class" (hx/json {"active" (hx/js-fn "active==index")
"implied" (hx/js-fn "all_selected && index != 0")})
@@ -178,7 +176,6 @@
:x-show "value.size > 0"}
svg/x]])
(defn multi-typeahead- [params]
[:div.relative {:x-data (hx/json {:baseUrl (str (:url params))
:reset_elements (js-fn "function(e) {
@@ -268,21 +265,17 @@
:x-effect "if (value.warning) { $nextTick(()=> warning_badge.update()) }"}
(tags/badge- {:class "peer"} "!")
[:div {:x-show "value.warning"
:x-ref "warning_pop"
:class "hidden peer-hover:block bg-red-50 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 p-4"
:x-text "value.warning"}]]]
(multi-typeahead-dropdown- params)])])
(defn use-size [size]
(if (= :small size)
(str " " "text-xs p-2")
(str " " "text-sm p-2.25")))
(defn text-input- [{:keys [size error?] :as params}]
[:input
(-> params
@@ -415,8 +408,6 @@
(update :class #(str % (use-size size) " w-full"))
(dissoc :size :name :x-model :x-modelable))]]))
(defn field-errors- [{:keys [source key]} & rest]
(let [errors (:errors (cond-> (meta source)
key (get key)))]
@@ -469,8 +460,6 @@
(defn hidden- [{:keys [name value] :as params}]
[:input (merge {:type "hidden" :value value :name name} params)])
(defn toggle- [params & children]
[:label {:class "inline-flex items-center cursor-pointer"}
[:input (merge {:type "checkbox", :class "sr-only peer"} params)]

View File

@@ -1,4 +1,4 @@
(ns auto-ap.ssr.components.link-dropdown
(ns auto-ap.ssr.components.link-dropdown
(:require [auto-ap.ssr.components :as com]
[auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg]))
@@ -8,8 +8,7 @@
[:div {:x-data (hx/json {})}
(com/a-icon-button {:class "relative"
"@click.prevent" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true, interactive:true, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}})"
}
"@click.prevent" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: 'light', allowHTML: true, interactive:true, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}})"}
svg/paperclip
(com/badge {:color "blue"} (count links)))
[:template {:x-ref "tooltip"}

View File

@@ -15,12 +15,11 @@
[malli.core :as mc]
[malli.core :as m]))
(def default-form-props {:hx-ext "response-targets"
:hx-swap "outerHTML"
:hx-target-400 "#form-errors .error-content"
:hx-trigger "submit"
:hx-target "this" })
:hx-target "this"})
(defprotocol ModalWizardStep
(step-key [this])
@@ -57,7 +56,6 @@
(or (get-in (:snapshot multi-form-state) edit-path)
default)))
(defn merge-multi-form-state [{:keys [snapshot edit-path step-params] :as multi-form-state}]
(let [cursor (cursor/cursor (or snapshot {}))
;; this hack makes sure that, in the event of a missing vector entry, will make sure to add it first
@@ -87,8 +85,6 @@
(fn encode-step-key [sk]
(mc/encode step-key-schema sk main-transformer))))
(defn render-timeline [linear-wizard current-step validation-route]
(let [step-names (map #(step-name (get-step linear-wizard %)) (steps linear-wizard))
active-index (.indexOf step-names (step-name current-step))]
@@ -148,9 +144,9 @@
next-button-content]}]
[:div.flex.justify-end
[:div.flex.items-baseline.gap-x-4
(let [step-errors (:step-params fc/*form-errors*)]
(com/form-errors {:errors (or (:errors step-errors)
(when (sequential? step-errors) step-errors))}))
(let [step-errors (:step-params fc/*form-errors*)]
(com/form-errors {:errors (or (:errors step-errors)
(when (sequential? step-errors) step-errors))}))
(when (not= (first (steps linear-wizard))
(step-key step))
(when validation-route
@@ -172,7 +168,7 @@
(com/modal-card-advanced
{"@keydown.enter.prevent.stop" "if ($refs.next ) {$refs.next.click()}"
:class (str
(or width-height-class " md:w-[750px] md:h-[600px] ")
(or width-height-class " md:w-[750px] md:h-[600px] ")
" w-full h-full
group-[.forward]/transition:htmx-swapping:opacity-0
group-[.forward]/transition:htmx-swapping:-translate-x-1/4
@@ -261,7 +257,7 @@
:oob (or oob []))))
(def next-handler
(-> (fn [{:keys [wizard] :as request}]
(let [current-step (get-current-step wizard)]
(if (satisfies? CustomNext current-step)
@@ -361,8 +357,6 @@
(render-wizard wizard request)])
(get query-params :replace-modal) (assoc-in [:headers "hx-trigger"] "modalswap")))
(defn wrap-init-multi-form-state [handler get-multi-form-state]
(->
(fn init-multi-form [request]

View File

@@ -6,16 +6,15 @@
[config.core :refer [env]]
[hiccup2.core :as hiccup]))
(defn page- [{:keys [nav page-specific client clients client-selection identity app-params request] :or {app-params {}} } & children]
[:div#app { "@notification.document" "notificationDetails=event.detail.value; showNotification=true"
(defn page- [{:keys [nav page-specific client clients client-selection identity app-params request] :or {app-params {}}} & children]
[:div#app {"@notification.document" "notificationDetails=event.detail.value; showNotification=true"
:x-data (hx/json {:leftNavShow true
:showError false
:errorDetails ""
:showNotification false
:notificationDetails ""})
"@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"
}
"@htmx:response-error.camel" "errorDetails = $event.detail.xhr.response; showError=true;"}
(navbar- {:client-selection client-selection
:clients clients
:client client
@@ -29,33 +28,32 @@
:page-specific page-specific})
[:div#main-content {:class "relative w-full h-full overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content lg:pl-64"
":class" "leftNavShow ? 'lg:pl-64' : ''"
:x-effect "leftNavShow ? $el.classList.add('lg:pl-64') : $el.classList.remove('lg:pl-64')"
}
:x-effect "leftNavShow ? $el.classList.add('lg:pl-64') : $el.classList.remove('lg:pl-64')"}
[:div#notification-holder
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification" }
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg {:x-show "showNotification"}
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-blue-400
{ "@click" "showNotification=false"}
{"@click" "showNotification=false"}
svg/filled-x]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-blue-800.bg-blue-50.dark:bg-gray-800.dark:text-blue-400.border-blue-300.rounded-lg.border.max-h-96
{:x-show "showNotification"
"x-transition:enter" "transition duration-300 transform ease-in-out"
"x-transition:enter-start" "opacity-0 translate-y-full"
"x-transition:enter-end" "opacity-100 translate-y-0"
"x-transition:leave" "transition duration-300 transform ease-in-out"
"x-transition:leave-start" "opacity-100 translate-y-0"
"x-transition:leave-end" "opacity-0 translate-y-full"}
"x-transition:enter" "transition duration-300 transform ease-in-out"
"x-transition:enter-start" "opacity-0 translate-y-full"
"x-transition:enter-end" "opacity-100 translate-y-0"
"x-transition:leave" "transition duration-300 transform ease-in-out"
"x-transition:leave-start" "opacity-100 translate-y-0"
"x-transition:leave-end" "opacity-0 translate-y-full"}
[:div {:class "p-4 text-lg w-full" :role "alert"}
[:div.text-sm
[:pre#notification-details.text-xs {:x-html "notificationDetails"}]]]]]]
[:div {:x-show "showError"
:x-init ""}
[:div {:x-show "showError"
:x-init ""}
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg
[:div.relative
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-red-600
{ "@click" "showError=false"}
{"@click" "showError=false"}
svg/filled-x]]
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-red-800.bg-red-50.dark:bg-gray-800.dark:text-red-400.border-red-300.rounded-lg.border.max-h-96
@@ -63,7 +61,7 @@
"x-transition:enter" "transition duration-300"
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
[:div {:class "p-4 mb-4 text-lg w-full" :role "alert"}
[:div.inline-block.w-8.h-8.mr-2 svg/alert]
[:span.font-medium "Oh, drat! An unexpected error has occurred."]
@@ -73,6 +71,4 @@
[:pre#error-details.text-xs {:x-show "expandError" :x-text "errorDetails"}]]]]]]
(into
[:div.p-4]
children)]]
])
children)]]])

View File

@@ -10,7 +10,7 @@
x
(> y z)
z
:else
:else
y))
(def elipsis-button
@@ -24,42 +24,41 @@
current-page (long (Math/floor (/ start per-page)))
first-page-button (bound 0 (- current-page buttons-before) (- total-pages max-buttons))
all-buttons (into [] (for [x (range total-pages)]
[:li
[:li
[:a (-> (a-params x)
(update
:class #(cond-> %
true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
(update
:class #(cond-> %
true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
(= current-page x)
(str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
(= current-page x)
(str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
(not= current-page x)
(str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
(not= current-page x)
(str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
(assoc :href "#"))
[:div.htmx-indicator.flex.items-center
(svg/spinner {:class "inline w-4 h-4 text-black"})]
[:div.htmx-indicator-hidden
[:div.htmx-indicator-hidden
(inc x)]]]))
last-page-button (Math/min (long total-pages) (long (+ max-buttons first-page-button)))
extended-last-page-button (when (not= last-page-button total-pages)
(list
elipsis-button
(last all-buttons)))
elipsis-button
(last all-buttons)))
extended-first-page-button (when (not= first-page-button 0)
(list
(first all-buttons)
elipsis-button))]
(first all-buttons)
elipsis-button))]
[:nav.flex.items-center.space-x-3
[:span.text-sm.text-gray-500 "Per page"]
(inputs/select- (merge per-page-params
{:options [[25 "25"]
[50 "50"]
[100 "100"]
[200 "200"]]
[50 "50"]
[100 "100"]
[200 "200"]]
:value per-page
:name "per-page"}))
[:ul {:class "inline-flex items-stretch -space-x-px"}

View File

@@ -1,4 +1,4 @@
(ns auto-ap.ssr.components.periods
(ns auto-ap.ssr.components.periods
(:require
[auto-ap.ssr.components.buttons :as buttons]
[auto-ap.ssr.components.inputs :as inputs]
@@ -19,7 +19,7 @@
(atime/unparse-local atime/normal-date))})
:x-effect "console.log('periods are', periods)"
:x-init "$watch('periods', ds => source_date= ds.length > 0 ? ds[0].end : null)" }
:x-init "$watch('periods', ds => source_date= ds.length > 0 ? ds[0].end : null)"}
[:template {:x-for "(v,n) in periods" ":key" "n"}
[:div
[:input {:type "hidden"
@@ -29,62 +29,61 @@
":name" "'periods[' + n + '][end]'"
:x-model "v.end"}]]]
(buttons/a-button- {"x-tooltip.on.click.theme.dropdown.placement.bottom.interactive" "{content: ()=> $refs.tooltip.innerHTML, allowHTML: true, appendTo: $root}"
:indicator? false}
[:template {:x-if "periods.length == 0"}
[:span.text-left.text-gray-400 "None selected"]]
[:template {:x-if "periods.length < 3 && periods.length > 0"}
[:span.inline-flex.gap-2
[:template {:x-for "p in periods"}
(tags/pill- {:color :secondary}
[:span {:x-text "p.start"}]
" - "
[:span {:x-text "p.end"}])]]]
[:template {:x-if "periods.length >= 3"}
(tags/pill- {:color :secondary}
[:span {:x-text "periods.length"}]
" periods selected")]
[:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
svg/drop-down])
:indicator? false}
[:template {:x-if "periods.length == 0"}
[:span.text-left.text-gray-400 "None selected"]]
[:template {:x-if "periods.length < 3 && periods.length > 0"}
[:span.inline-flex.gap-2
[:template {:x-for "p in periods"}
(tags/pill- {:color :secondary}
[:span {:x-text "p.start"}]
" - "
[:span {:x-text "p.end"}])]]]
[:template {:x-if "periods.length >= 3"}
(tags/pill- {:color :secondary}
[:span {:x-text "periods.length"}]
" periods selected")]
[:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
svg/drop-down])
[:template {:x-ref "tooltip"}
[:div.p-4.gap-2 {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1 p-4 w-[700px] "}
[:div.flex.flex-col.gap-2
(tabs/tabs-
{:tabs [{:name "Quick"
:content [:div.flex.flex.gap-2
(inputs/calendar-input- {:placeholder "12/21/2020" :x-model "source_date"})
[:div.flex.flex-col.gap-2
(buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending "
[:span {:x-text "source_date"}]])
(buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} [:span "Calendar year ("
[:span {:x-text "parseMMDDYYYY(source_date).getFullYear()"}]
")"])
(buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending "
[:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]])
[:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"} ]
(buttons/a-button- {"@click" "periods=getLastMonthPeriods()"} "Last Month")
(buttons/a-button- {"@click" "periods=getMonthToDatePeriods()"} "Month to date")
(buttons/a-button- {"@click" "periods=getYearToDatePeriods()"} "Year to date")
(buttons/a-button- {"@click" "periods=[]"} "Clear")]]}
{:name "Advanced"
:content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
:x-data (hx/json {:calendarTarget "0"
:calendarWhich "start"})
"@change-date.camel" "$el.querySelectorAll('.text-inputs.' + calendarWhich)[calendarTarget].focus()"}
(inputs/calendar-input- {:x-model "periods[calendarTarget][calendarWhich]"})
[:div.flex.flex-col.gap-4.p-2
[:div.overflow-y-scroll.flex.flex-col.gap-4
[:template {:x-for "(p, i) in periods" ":key" "i"}
[:div.flex.gap-4.
(inputs/text-input- { :class "text-inputs start" :x-model "periods[i].start" "@focus" "calendarTarget =i; calendarWhich='start'" })
(inputs/text-input- { :class "text-inputs end" :x-model "periods[i].end" "@focus" "calendarTarget =i; calendarWhich='end'"})
(buttons/a-icon-button- {"@click.prevent.stop" "periods=periods.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]
#_(com/pill {:color :secondary}
[:span {:x-text "p.start"}]
" - "
[:span {:x-text "p.end"}])]]
(buttons/button- {"@click.prevent.stop" "periods.push({start: '', end: ''}); calendarTarget=0" :class "w-32"} "Add new period")]
]}]
:active "Quick"}) ]]]])
{:tabs [{:name "Quick"
:content [:div.flex.flex.gap-2
(inputs/calendar-input- {:placeholder "12/21/2020" :x-model "source_date"})
[:div.flex.flex-col.gap-2
(buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} [:span "13 periods, ending "
[:span {:x-text "source_date"}]])
(buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} [:span "Calendar year ("
[:span {:x-text "parseMMDDYYYY(source_date).getFullYear()"}]
")"])
(buttons/a-button- {"@click" "periods=getTwelveCalendarMonthsPeriods(source_date)"} [:span "12 months, ending "
[:span {:x-text "parseMMDDYYYY(source_date).toLocaleString('default', { month: 'long' })"}]])
[:hr {:class "h-px my-1 bg-gray-200 border-0 dark:bg-gray-700"}]
(buttons/a-button- {"@click" "periods=getLastMonthPeriods()"} "Last Month")
(buttons/a-button- {"@click" "periods=getMonthToDatePeriods()"} "Month to date")
(buttons/a-button- {"@click" "periods=getYearToDatePeriods()"} "Year to date")
(buttons/a-button- {"@click" "periods=[]"} "Clear")]]}
{:name "Advanced"
:content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
:x-data (hx/json {:calendarTarget "0"
:calendarWhich "start"})
"@change-date.camel" "$el.querySelectorAll('.text-inputs.' + calendarWhich)[calendarTarget].focus()"}
(inputs/calendar-input- {:x-model "periods[calendarTarget][calendarWhich]"})
[:div.flex.flex-col.gap-4.p-2
[:div.overflow-y-scroll.flex.flex-col.gap-4
[:template {:x-for "(p, i) in periods" ":key" "i"}
[:div.flex.gap-4.
(inputs/text-input- {:class "text-inputs start" :x-model "periods[i].start" "@focus" "calendarTarget =i; calendarWhich='start'"})
(inputs/text-input- {:class "text-inputs end" :x-model "periods[i].end" "@focus" "calendarTarget =i; calendarWhich='end'"})
(buttons/a-icon-button- {"@click.prevent.stop" "periods=periods.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]
#_(com/pill {:color :secondary}
[:span {:x-text "p.start"}]
" - "
[:span {:x-text "p.end"}])]]
(buttons/button- {"@click.prevent.stop" "periods.push({start: '', end: ''}); calendarTarget=0" :class "w-32"} "Add new period")]]}]
:active "Quick"})]]]])
(defn dates-dropdown- [{:keys [value name]}]
[:div {:x-data (hx/json {:dates (map #(atime/unparse-local % atime/normal-date) value)})}
@@ -122,16 +121,16 @@
(buttons/a-button- {"@click" "dates=[]"} "Clear")]]}
{:name "Advanced oooo"
:content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]"
:x-data (hx/json {:calendarTarget "0" })
:x-data (hx/json {:calendarTarget "0"})
"@change-date.camel" "$el.querySelectorAll('.text-inputs')[calendarTarget].focus();"}
(inputs/calendar-input- {:x-model "dates[calendarTarget]" })
(inputs/calendar-input- {:x-model "dates[calendarTarget]"})
[:div.flex.flex-col.gap-4.p-2
[:div.overflow-y-scroll.flex.flex-col.gap-4
[:template {:x-for "(p, i) in dates" ":key" "i"}
[:div.flex.gap-4.
(inputs/text-input- {:x-model "dates[i]"
(inputs/text-input- {:x-model "dates[i]"
"@focus" "calendarTarget =i; "
:class "text-inputs"})
(buttons/a-icon-button- {"@click.prevent.stop" "dates=dates.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)] ]]
(buttons/a-icon-button- {"@click.prevent.stop" "dates=dates.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)]]]
(buttons/button- {"@click.prevent.stop" "dates.push(null); calendarTarget=0" :class "w-32"} "Add new period")]]}]
:active "Quick"})]]]])

View File

@@ -1,28 +1,26 @@
(ns auto-ap.ssr.components.tabs
(ns auto-ap.ssr.components.tabs
(:require
[auto-ap.ssr.hx :as hx]))
(defn tabs- [{:keys [tabs active]}]
[:div.flex.flex-col.gap-2 {:x-data (hx/json {:activeTab active})}
[:div {:class "text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700" }
[:div {:class "text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700"}
[:ul {:class "flex flex-wrap -mb-px"}
(for [tab tabs]
[:li {:class "me-2"}
[:a {:href "#"
:x-data (hx/json {:tabName (:name tab)})
":data-active" (format "activeTab==tabName")
"@click" (format "activeTab=tabName" )
:class "inline-block data-[active]:text-blue-600 data-[active]:border-blue-600 p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300"}
"@click" (format "activeTab=tabName")
:class "inline-block data-[active]:text-blue-600 data-[active]:border-blue-600 p-4 border-b-2 rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300"}
(:name tab)]])
#_[:li
[:a {:class "inline-block p-4 text-gray-400 rounded-t-lg cursor-not-allowed dark:text-gray-500"} "Disabled"]]]]
(for [tab tabs]
[:div {:x-data (hx/json {:tabName (:name tab)})
:x-show (format "activeTab==tabName")
:x-show (format "activeTab==tabName")
"x-transition:enter" "transition-opacity duration-300"
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
(:content tab) ])])
"x-transition:enter-start" "opacity-0"
"x-transition:enter-end" "opacity-100"}
(:content tab)])])

View File

@@ -1,7 +1,6 @@
(ns auto-ap.ssr.components.tags
(:require [auto-ap.ssr.hiccup-helper :as hh]))
(defn pill- [params & children]
(into
[:span (cond-> params
@@ -23,7 +22,6 @@
(defn badge- [params & children]
[:div (merge params {:class (-> (hh/add-class "absolute inline-flex items-center z-10 justify-center w-6 h-6 text-xs font-black text-white
border-3 border-white rounded-full -top-2 -right-2 dark:border-gray-900"
(:class params)
)
(:class params))
(hh/add-class (or (some-> (:color params) (#(str "bg-" % "-300")))
"bg-red-300")))}) children])

View File

@@ -30,14 +30,14 @@
(if active?
[:li {:class "flex items-center text-primary-600 font-medium dark:text-primary-500"}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"}]
children ]
children]
[:li {:class (cond-> "flex items-center"
(not visited?) (hh/add-class "text-gray-400"))}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}
(when visited?
[:svg {:class "w-3 h-3 text-primary-600 dark:text-primary-500", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 16 12"}
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M1 5.917 5.724 10.5 15 1.5"}]])]
children ]))
children]))
(defn vertical-timeline [params & children]
[:ol {:class (hh/add-class "flex flex-col items-start space-y-2 text-xs text-center text-gray-500 bg-gray-100 dark:text-gray-400 sm:text-base dark:bg-gray-800 sm:space-y-4 px-2"

View File

@@ -10,15 +10,14 @@
[:div {:class "flex items-center ml-3 mr-10"}
[:div
[:button#user-menu-button {:type "button", :class "flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600", :aria-expanded "false"
"@click" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: $store.darkMode.on ? 'dark' : 'light', allowHTML: true, interactive:true})"
}
"@click" "$tooltip($refs.tooltip, {content: ()=>$refs.tooltip.innerHTML, theme: $store.darkMode.on ? 'dark' : 'light', allowHTML: true, interactive:true})"}
[:span {:class "sr-only"} "Open user menu"]
[:img {:class "w-8 h-8 rounded-full", :src (pull-attr (dc/db conn) :user/profile-image-url (:db/id identity)) :alt "user photo" :referrerpolicy "no-referrer"}]]]
[:template {:class ""
:x-ref "tooltip"}
:x-ref "tooltip"}
[:div {:class "px-4 py-3", :role "none"}
[:p {:class "text-sm text-gray-900 dark:text-white", :role "none"} (:user/name identity)]
[:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} (pull-attr (dc/db conn) :user/email (:db/id identity))] ]
[:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} (pull-attr (dc/db conn) :user/email (:db/id identity))]]
[:ul {:class "py-1", :role "none"}
[:li
[:a {:href (bidi/path-for ssr-routes/only-routes :company), :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "My Company"]]
@@ -27,7 +26,7 @@
:class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Admin"])
[:li
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"
"@click.prevent" "$store.darkMode.toggle()" }
"@click.prevent" "$store.darkMode.toggle()"}
"Night Mode"]]
[:li
[:a {:href "/logout", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]] ])
[:a {:href "/logout", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]]])