diff --git a/routes/ui/$dev-mode_get.js b/routes/ui/$dev-mode_get.js index 5551ca1..d90df63 100644 --- a/routes/ui/$dev-mode_get.js +++ b/routes/ui/$dev-mode_get.js @@ -103,7 +103,7 @@ function debugButton() { button.onclick = () => showDebugOverlay(); button.innerHTML = "DEBUG" button.style.cssText = ` -position: absolute; +position: fixed; bottom: 0; right: 0; background-color: rgba(124,0,124,0.3); padding: 5px 10px`; diff --git a/routes/ui/forms/responses/search_get.clj b/routes/ui/forms/responses/search_get.clj index 6afc233..d7b0481 100644 --- a/routes/ui/forms/responses/search_get.clj +++ b/routes/ui/forms/responses/search_get.clj @@ -70,9 +70,10 @@ where q.resource::text ilike ?" (str "%" (:query params) "%")]) (get-in f [:resource :subject :name 0 :family])) ]]] [:div - {:class "bg-white border-b px-4 py-4 text-sm whitespace-nowrap cursor-pointer text-blue-500"} + {:data-turbo "false" + :class "bg-white border-b px-4 py-4 text-sm whitespace-nowrap cursor-pointer text-blue-500"} [:a {:class "px-3 py-2 text-gray-500 transition-colors duration-200 rounded-lg dark:text-gray-300 hover:bg-gray-100" - + :data-turbo "false" :href (str "/ui/sdc#/questionnaire-response/" (get-in f [:resource :id]))} "Open" ]]]) diff --git a/routes/ui/patients/:id_delete.clj b/routes/ui/patients/:id_delete.clj new file mode 100644 index 0000000..19440db --- /dev/null +++ b/routes/ui/patients/:id_delete.clj @@ -0,0 +1,9 @@ +(box/set-header! "Content-Type" "text/vnd.turbo-stream.html; charset=utf-8") + +(let [params (box/route-params) + result (box/delete {:id (:id params) :resourceType "Patient"})] + (when (= (:resourceType result) "Patient") + [:turbo-stream {:target (str "patient-" (:id result)) + :action "remove"}] + ) + ) diff --git a/routes/ui/patients/edit_get.clj b/routes/ui/patients/edit_get.clj new file mode 100644 index 0000000..52a13d1 --- /dev/null +++ b/routes/ui/patients/edit_get.clj @@ -0,0 +1,100 @@ +(box/set-header! "Content-Type" "text/vnd.turbo-stream.html; charset=utf-8") + +(let [params (box/url-params) + patient (when (:patient-id params) + (box/read {:id (:patient-id params) :resourceType "Patient"}))] + [:turbo-stream {:target "modal" + :action "update"} + [:template + [:section {:id "edit-patient" + :class "fixed top-0 bottom-0 right-0 left-0 z-10"} + [:script +"function removeModal(e) { +if(e.target.id == 'edit-patient-root') { +e.target.parentElement.remove(); +} +}" + ] + [:div + {:class "min-h-screen p-6 bg-gray-100 flex items-center justify-center bg-opacity-80" + :id "edit-patient-root" + :onclick "removeModal(event)" + } + [:div + {:class "container max-w-screen-lg mx-auto" + :id "edit-patient-modal"} + [:div + [:div + {:class "bg-white rounded shadow-lg p-4 px-4 md:p-8 mb-6"} + [:div + {:class "grid gap-4 gap-y-2 text-sm grid-cols-1 lg:grid-cols-3"} + [:div + {:class "text-gray-600"} + [:p {:class "font-medium text-lg"} "Personal Details"] + [:p "Please fill out all the fields."]] + [:form {:action "/ui/patients/save" :method "POST" + :class "lg:col-span-2"} + (when (:id patient) + [:input {:type "hidden" + :name "id" + :value (:id patient)}] + ) + [:div + {:class + "grid gap-4 gap-y-2 text-sm grid-cols-1 md:grid-cols-5"} + [:div + {:class "md:col-span-5"} + [:label {:for "full_name"} "First Name"] + [:input + {:type "text", + :name "given", + :id "full_name", + :required true + :class "h-10 border mt-1 rounded px-4 w-full bg-gray-50", + :value (get-in patient [:name 0 :given 0])}]] + [:div + {:class "md:col-span-5"} + [:label {:for "full_name"} "Last Name"] + [:input + {:type "text", + :name "family", + :id "full_name", + :required true + :class "h-10 border mt-1 rounded px-4 w-full bg-gray-50", + :value (get-in patient [:name 0 :family])}]] + [:div + {:class "md:col-span-3"} + [:label {:for "birth-date"} "Date of Birth"] + [:input + {:type "date", + :name "birth-date", + :required true + :id "birth-date", + :class "h-10 border mt-1 rounded px-4 w-full bg-gray-50", + :value (get-in patient [:birthDate]) + :placeholder ""}]] + [:div + {:class "md:col-span-2"} + [:label {:for "gender"} "Gender"] + [:select {:name "gender" + :required true + :class "h-10 border mt-1 rounded px-4 w-full bg-gray-50"} + [:option {:value "male" :selected (= (:gender patient) "male")} "Male"] + [:option {:value "female" :selected (= (:gender patient) "female")} "Female"]]] + + [:div + {:class "md:col-span-5 text-right"} + [:div + {:class "inline-flex items-end"} + [:button + {:class + "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"} + "Submit"]]]]]]]]]]]] + + ]) + +#_[:turbo-stream {:target "modal" + :action "update"} + [:template + [:span "hello"]] + ] diff --git a/routes/ui/patients/forms_get.clj b/routes/ui/patients/forms_get.clj new file mode 100644 index 0000000..f7cdab4 --- /dev/null +++ b/routes/ui/patients/forms_get.clj @@ -0,0 +1,15 @@ +[:html + [:title "Aidbox | Patients | Forms"] + [:script {:src "https://cdn.tailwindcss.com"}] + [:link {:href "/static/assets/img/fav.svg" :rel "shortcut icon" :type "image/x-icon"}] + [:body + [:script {:src "/ui/$dev-mode"}] + (let [forms (box/sql "select * from questionnaire") + _ (m/set :forms forms)] + [:div + (for [f forms] + [:div + [:span (get-in f [:resource :title])]])] + ) + ] + ] diff --git a/routes/ui/patients/save_post.clj b/routes/ui/patients/save_post.clj new file mode 100644 index 0000000..a616e72 --- /dev/null +++ b/routes/ui/patients/save_post.clj @@ -0,0 +1,69 @@ +(box/set-header! "Content-Type" "text/vnd.turbo-stream.html; charset=utf-8") + +(let [params (box/form-params) + patient (cond-> {:resourceType "Patient" + :name [{:given [(get-in params [:given])] + :family (get-in params [:family])}] + :gender (get-in params [:gender]) + :birthDate (get-in params [:birth-date])} + (:id params) + (assoc :id (:id params))) + saved-patient (box/create patient)] + + (if (= (:resourceType saved-patient) "Patient") + [:<> + [:turbo-stream {:target "edit-patient" + :action "remove"}] + + [:turbo-stream (if-not (:id params) + {:target "patients" + :action "append"} + {:target (str "patient-" (:id patient)) + :action "replace"}) + [:template + [:div {:class "contents" + :id (str "patient-" (:id saved-patient))} + [:div + {:class "px-4 py-4 text-sm font-medium whitespace-nowrap flex items-center border-b"} + [:h2 + {:class "font-medium text-gray-800 dark:text-white"} + (str (get-in saved-patient [:name 0 :given 0]) ", " + (get-in saved-patient [:name 0 :family]))]] + [:div + {:class "px-12 py-4 text-sm font-medium whitespace-nowrap flex items-center border-b"} + (when-let [birth-date (get-in saved-patient [:birthDate])] + [:div + {:class + "inline px-3 py-1 text-sm font-normal rounded-full text-emerald-500 gap-x-2 bg-emerald-100/60 dark:bg-gray-800"} + birth-date])] + [:div + {:class "px-4 py-4 text-sm whitespace-nowrap flex items-center border-b"} + [:div + [:h4 {:class "text-gray-700 dark:text-gray-200"} (get-in saved-patient [:gender])]]] + [:div + {:class "px-4 py-4 text-sm whitespace-nowrap flex items-center border-b"} + [:form {:action "/ui/patients/edit" + :class "mb-0"} + [:input {:type "hidden" + :name "patient-id" + :value (:id saved-patient)}] + [:button + {:class + "px-3 py-2 text-gray-500 transition-colors duration-200 rounded-lg dark:text-gray-300 hover:bg-gray-100"} + [:i.fas.fa-pencil]]] + [:a + {:class + "px-3 py-2 text-gray-500 transition-colors duration-200 rounded-lg dark:text-gray-300 hover:bg-gray-100" + :href (str "/ui/patients/" (str (:id saved-patient))) + :data-turbo-method "DELETE" + :data-turbo-confirm "Do you want to delete patient?"} + [:i.fas.fa-trash]]]]] + + ] + ] + [:turbo-stream {:target "edit-patient-modal" + :action "update"} + [:template [:div {:class "bg-white p-10" }][:code (pr-str saved-patient)]]] + ) + + ) diff --git a/routes/ui/patients/search_get.clj b/routes/ui/patients/search_get.clj new file mode 100644 index 0000000..a9297d2 --- /dev/null +++ b/routes/ui/patients/search_get.clj @@ -0,0 +1,54 @@ +(box/set-header! "Content-Type" "text/vnd.turbo-stream.html; charset=utf-8") + +(let [params (box/url-params) + patients (->> (box/sql ["select resource || jsonb_build_object('id', id) resource from Patient +where resource::text ilike ?" (str "%" (:query params) "%")]) + (mapv :resource)) + _ (m/set :patients patients) + ] + + [:turbo-stream {:target "patients" + :action "update"} + (into [:template] + (map (fn [p] + [:div {:class "contents" + :id (str "patient-" (:id p))} + [:div + {:class "px-4 py-4 text-sm font-medium whitespace-nowrap flex items-center border-b"} + [:h2 + {:class "font-medium text-gray-800 dark:text-white"} + (str (get-in p [:name 0 :given 0]) ", " + (get-in p [:name 0 :family]))]] + [:div + {:class "px-12 py-4 text-sm font-medium whitespace-nowrap flex items-center border-b"} + (when-let [birth-date (get-in p [:birthDate])] + [:div + {:class + "inline px-3 py-1 text-sm font-normal rounded-full text-emerald-500 gap-x-2 bg-emerald-100/60 dark:bg-gray-800"} + birth-date])] + [:div + {:class "px-4 py-4 text-sm whitespace-nowrap flex items-center border-b"} + [:div + [:h4 {:class "text-gray-700 dark:text-gray-200"} (get-in p [:gender])]]] + [:div + {:class "px-4 py-4 text-sm whitespace-nowrap flex items-center border-b"} + [:form {:action "/ui/patients/edit" + :class "mb-0"} + [:input {:type "hidden" + :name "patient-id" + :value (:id p)}] + [:button + {:class + "px-3 py-2 text-gray-500 transition-colors duration-200 rounded-lg dark:text-gray-300 hover:bg-gray-100"} + [:i.fas.fa-pencil]]] + [:a + {:class + "px-3 py-2 text-gray-500 transition-colors duration-200 rounded-lg dark:text-gray-300 hover:bg-gray-100" + :href (str "/ui/patients/" (str (:id p))) + :data-turbo-method "DELETE" + :data-turbo-confirm "Do you want to delete patient?"} + [:i.fas.fa-trash]]]]) patients)) + + + ] + ) diff --git a/routes/ui/patients_get.clj b/routes/ui/patients_get.clj index dc1e0b0..8cbc452 100644 --- a/routes/ui/patients_get.clj +++ b/routes/ui/patients_get.clj @@ -1,58 +1,192 @@ [:html - [:title "Aidbox | Patients"] - [:meta {:http-equiv "X-UA-Compatible" :content "IE=edge; IE=9; IE=8; IE=7"}] - [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] - [:link {:type "text/css", :href "/static/assets/css/bootstrap.min.css" :rel "stylesheet"}] - [:link {:type "text/css", :href "/static/assets/GothamPro/stylesheet.css" :rel "stylesheet"}] - [:script {:src "https://cdn.tailwindcss.com"}] - [:script {:src "https://momentjs.com/downloads/moment.js"}] - [:link {:href "/static/assets/img/fav.svg" :rel "shortcut icon" :type "image/x-icon"}] - [:script {:type "module"} "import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';"] + [:head + [:title "Aidbox | Patients"] + [:meta {:http-equiv "X-UA-Compatible" :content "IE=edge; IE=9; IE=8; IE=7"}] + [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] + [:link {:type "text/css", :href "/static/assets/css/bootstrap.min.css" :rel "stylesheet"}] + [:link {:type "text/css" :href "/static/assets/fa6/css/all.min.css" :rel "stylesheet"}] + [:link {:type "text/css", :href "/static/assets/GothamPro/stylesheet.css" :rel "stylesheet"}] + [:script {:src "https://cdn.tailwindcss.com"}] + [:script {:src "https://momentjs.com/downloads/moment.js"}] + [:link {:href "/static/assets/img/fav.svg" :rel "shortcut icon" :type "image/x-icon"}] - [:style - (garden.core/css - [:html - [:body {:font-family - "'Gotham Pro',-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;"}]])] + [:script {:type "module" :src "https://unpkg.com/@hotwired/turbo@7.3.0/dist/turbo.es2017-esm.js"}] + ] - [:script {:src "/ui/$dev-mode"}] - - (let [user (auth/user-info)] - [:body.flex.mt-8 {:style " -background-color: #0093E9; -background-image: linear-gradient(160deg, #0093E9 0%, #80D0C7 100%); -"} - - [:div.mx-auto.flex.flex-column - - [:div.shadow.rounded - {:style "backdrop-filter: blur(8px); background: rgba(255, 255, 255, 0.6)"} + [:body {:class "bg-gray-200"} + [:turbo-frame {:id "modal"}] + [:section {:class "container px-4 mx-auto"} + [:div + {:class "sm:flex sm:items-center sm:justify-between mt-4"} + [:div + [:div + {:class "flex items-center gap-x-3"} + [:h2 {:class "text-lg font-medium text-gray-800"} "Patients"] + #_[:span + {:class + "px-3 py-1 text-xs text-blue-600 bg-blue-100 rounded-full dark:bg-gray-800 dark:text-blue-400"} + "240\n patients"]]] + [:div + {:class "flex items-center gap-x-3"} + [:a + {:class + "flex items-center justify-center w-1/2 px-5 py-2 text-sm tracking-wide text-white transition-colors duration-200 bg-blue-500 rounded-lg shrink-0 sm:w-auto gap-x-2 hover:bg-gray-700 dark:hover:bg-gray-600 dark:bg-gray-600" + :data-turbo-frame "modal" + :href "/ui/patients/edit" + :data-turbo-action "GET" + } + [:svg + {:xmlns "http://www.w3.org/2000/svg", + :fill "none", + :viewBox "0 0 24 24", + :stroke-width "1.5", + :stroke "currentColor", + :class "w-5 h-5"} + [:path + {:stroke-linecap "round", + :stroke-linejoin "round", + :d "M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"}]] + [:span "Add Patient"]]]] + [:div + {:class "mt-6 md:flex md:items-center md:justify-between"} + [:div + {:class + "inline-flex overflow-hidden bg-white border divide-x rounded-lg dark:bg-gray-900 rtl:flex-row-reverse dark:border-gray-700 dark:divide-gray-700"} + [:button + {:class + "px-5 py-2 text-xs font-medium text-gray-600 transition-colors duration-200 bg-gray-100 sm:text-sm dark:bg-gray-800 dark:text-gray-300"} + "View all"] + [:button + {:class + "px-5 py-2 text-xs font-medium text-gray-600 transition-colors duration-200 sm:text-sm dark:hover:bg-gray-800 dark:text-gray-300 hover:bg-gray-100"} + "Active"] + [:button + {:class + "px-5 py-2 text-xs font-medium text-gray-600 transition-colors duration-200 sm:text-sm dark:hover:bg-gray-800 dark:text-gray-300 hover:bg-gray-100"} + "Inactive"]] + [:div + {:class "relative flex items-center md:mt-0"} + [:span + {:class "absolute"} + [:svg + {:xmlns "http://www.w3.org/2000/svg", + :fill "none", + :viewBox "0 0 24 24", + :stroke-width "1.5", + :stroke "currentColor", + :class "w-5 h-5 mx-3 text-gray-400 dark:text-gray-600"} + [:path + {:stroke-linecap "round", + :stroke-linejoin "round", + :d + "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"}]]] + [:form {:action "/ui/patients/search" + :class "mb-0"} + [:input + {:type "text", + :placeholder "Search", + :name "query" + :class + "block w-full py-1.5 pr-5 text-gray-700 bg-white border border-gray-200 rounded-lg md:w-80 placeholder-gray-400/70 pl-11 rtl:pr-11 rtl:pl-5 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-blue-300 focus:outline-none focus:ring focus:ring-opacity-40"}]]]] + [:div + {:class "flex flex-col mt-6"} + [:div + {:class "-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"} + [:div + {:class + "inline-block min-w-full py-2 align-middle md:px-6 lg:px-8"} [:div - #_[:img.px-16.mb-4.m-auto {:src "/static/assets/img/aidbox-logo.svg" :style "max-width: 100%"}] - - [:div.flex.justify-between.w-full.mb-4 - #_(for [link [{:label "userinfo" :url "/ui/auth/userinfo"} - {:label "sessions" :url "/ui/auth/sessions"} - {:label "apps" :url "/ui/auth/apps"}]] - [:a.border-b.flex-1.text-center.hover:no-underline.py-2.text-black - {:href (:url link) - :class (if (= (:uri (box/request)) (:url link)) - (str/join "," ["hover:text-black"]) - (str/join "," ["hover:text-gray-500"])) - :style (if (= (:uri (box/request)) (:url link)) - "border-color: black" - "border-color: #80D0C7")} - (:label link)])] - - [:div.px-8 - [:div.text-xl.mb-4 "Patients"] - - [:form {:method "POST" :action "/ui/patients/"} - [:input {}] - [:button "Search"]] - - ]]] + {:class + "overflow-hidden border border-gray-200 dark:border-gray-700 md:rounded-lg"} + [:div {:class "min-w-full grid grid-cols-4 bg-white"} + [:div + {:scope "col", + :class + "py-3.5 px-4 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800"} + [:button + {:class "flex items-center gap-x-3 focus:outline-none"} + [:span "Full Name"] + [:svg + {:class "h-3", + :viewBox "0 0 10 11", + :fill "none", + :xmlns "http://www.w3.org/2000/svg"} + [:path + {:d + "M2.13347 0.0999756H2.98516L5.01902 4.79058H3.86226L3.45549 3.79907H1.63772L1.24366 4.79058H0.0996094L2.13347 0.0999756ZM2.54025 1.46012L1.96822 2.92196H3.11227L2.54025 1.46012Z", + :fill "currentColor", + :stroke "currentColor", + :stroke-width "0.1"}] + [:path + {:d + "M0.722656 9.60832L3.09974 6.78633H0.811638V5.87109H4.35819V6.78633L2.01925 9.60832H4.43446V10.5617H0.722656V9.60832Z", + :fill "currentColor", + :stroke "currentColor", + :stroke-width "0.1"}] + [:path + {:d + "M8.45558 7.25664V7.40664H8.60558H9.66065C9.72481 7.40664 9.74667 7.42274 9.75141 7.42691C9.75148 7.42808 9.75146 7.42993 9.75116 7.43262C9.75001 7.44265 9.74458 7.46304 9.72525 7.49314C9.72522 7.4932 9.72518 7.49326 9.72514 7.49332L7.86959 10.3529L7.86924 10.3534C7.83227 10.4109 7.79863 10.418 7.78568 10.418C7.77272 10.418 7.73908 10.4109 7.70211 10.3534L7.70177 10.3529L5.84621 7.49332C5.84617 7.49325 5.84612 7.49318 5.84608 7.49311C5.82677 7.46302 5.82135 7.44264 5.8202 7.43262C5.81989 7.42993 5.81987 7.42808 5.81994 7.42691C5.82469 7.42274 5.84655 7.40664 5.91071 7.40664H6.96578H7.11578V7.25664V0.633865C7.11578 0.42434 7.29014 0.249976 7.49967 0.249976H8.07169C8.28121 0.249976 8.45558 0.42434 8.45558 0.633865V7.25664Z", + :fill "currentColor", + :stroke "currentColor", + :stroke-width "0.3"}]]]] + [:div + {:scope "col", + :class + "px-12 py-3.5 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800"} + "Date birth"] + [:div + {:scope "col", + :class + "px-4 py-3.5 text-sm font-normal text-left rtl:text-right text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800"} + "Gender"] + [:div + {:scope "col", :class "relative py-3.5 px-4 bg-gray-50 dark:bg-gray-800"} + [:span {:class "sr-only"} "Edit"]] + [:turbo-frame {:id "patients" + :class "contents" + :src "/ui/patients/search"}]]]]]] + #_[:div + {:class "mt-6 sm:flex sm:items-center sm:justify-between"} + [:div + {:class "text-sm text-gray-500 dark:text-gray-400"} + "Page" + [:span + {:class "font-medium text-gray-700 dark:text-gray-600"} + "1 of 10"]] + [:div + {:class "flex items-center mt-4 gap-x-4 sm:mt-0"} + [:a + {:href "#", + :class + "flex items-center justify-center w-1/2 px-5 py-2 text-sm text-gray-700 capitalize transition-colors duration-200 bg-white border rounded-md sm:w-auto gap-x-2 hover:bg-gray-100 dark:bg-gray-900 dark:text-gray-200 dark:border-gray-700 dark:hover:bg-gray-800"} + [:svg + {:xmlns "http://www.w3.org/2000/svg", + :fill "none", + :viewBox "0 0 24 24", + :stroke-width "1.5", + :stroke "currentColor", + :class "w-5 h-5 rtl:-scale-x-100"} + [:path + {:stroke-linecap "round", + :stroke-linejoin "round", + :d "M6.75 15.75L3 12m0 0l3.75-3.75M3 12h18"}]] + [:span "previous"]] + [:a + {:href "#", + :class + "flex items-center justify-center w-1/2 px-5 py-2 text-sm text-gray-700 capitalize transition-colors duration-200 bg-white border rounded-md sm:w-auto gap-x-2 hover:bg-gray-100 dark:bg-gray-900 dark:text-gray-200 dark:border-gray-700 dark:hover:bg-gray-800"} + [:span "Next"] + [:svg + {:xmlns "http://www.w3.org/2000/svg", + :fill "none", + :viewBox "0 0 24 24", + :stroke-width "1.5", + :stroke "currentColor", + :class "w-5 h-5 rtl:-scale-x-100"} + [:path + {:stroke-linecap "round", + :stroke-linejoin "round", + :d "M17.25 8.25L21 12m0 0l-3.75 3.75M21 12H3"}]]]]]] + [:script {:src "/ui/$dev-mode"}]] - [:div.text-gray-200.mx-auto.pt-2 - (format "Aidbox version %s" (box/version))]] - ])] + ]