diff --git a/src/com/spicy/app.clj b/src/com/spicy/app.clj index 25be004..2cc726e 100644 --- a/src/com/spicy/app.clj +++ b/src/com/spicy/app.clj @@ -5,6 +5,7 @@ [com.spicy.middleware :as mid] [com.spicy.movements.core :as movements] [com.spicy.results.core :as results] + [com.spicy.results.transform :as t] [com.spicy.results.ui :as r] [com.spicy.settings :as settings] [com.spicy.sugarwod.core :as sugar.core] @@ -52,11 +53,11 @@ [:p "Log a WOD result to see it show up here!"] [:a.btn {:href "/app/workouts"} "View Workouts"]] [:div (map (fn [result] - (let [{:keys [workout name date] :as normalized} (r/normalized-result result)] + (let [{:keys [workout name date] :as normalized} (t/normalized-result result)] [:div.mb-2 [:a.text-lg.font-sans {:href (str "/app/workouts/" (:xt/id workout))} [:h3.font-sans name]] [:div - [:span.font-bold (w/display-summed-score normalized)] + [:span.font-bold (t/display-summed-score normalized)] [:span.text-gray-700.ml-2 date]]])) wod-results)])] [:div.p-4.border-2.border-black.bg-white [:h2.text-2xl.font-bold "Latest Lifts"] @@ -66,7 +67,7 @@ [:a.btn {:href "/app/movements"} "View Movements"]] [:div (map (fn [result] - (let [{:keys [movement name date sets] :as normalized} (r/normalized-result result)] + (let [{:keys [movement name date sets] :as _normalized} (t/normalized-result result)] [:div.mb-2 [:a.text-lg.font-sans {:href (str "/app/movements/" (:xt/id movement))} [:h3.font-sans.capitalize name]] [:div diff --git a/src/com/spicy/calendar.clj b/src/com/spicy/calendar.clj index 7a8c432..4d371aa 100644 --- a/src/com/spicy/calendar.clj +++ b/src/com/spicy/calendar.clj @@ -2,7 +2,7 @@ (:require [clojure.string :as string] [com.biffweb :as biff] - [com.spicy.results.ui :as r] + [com.spicy.results.transform :as t] [java-time.api :as jt])) @@ -114,7 +114,7 @@ (map (comp (fn [{:keys [name href]}] [:li [:a.group.flex {:href href} - [:p.flex-auto.truncate.font-medium.font-semibold.text-gray-900.group-hover:text-brand-teal name]]]) r/normalized-result) results)])])) + [:p.flex-auto.truncate.font-medium.font-semibold.text-gray-900.group-hover:text-brand-teal name]]]) t/normalized-result) results)])])) (defn mobile-day @@ -201,7 +201,7 @@ [:div.flex-auto [:p.font-semibold.text-gray-900 name]] [:a.ml-6.flex-none.self-center.bg-white.px-3.py-2.font-semibold.text-gray-900.ring-1.ring-inset.ring-black.hover:ring-gray-400 {:href href} "View" - [:span.sr-only (str ", " name)]]]) r/normalized-result) results)]] + [:span.sr-only (str ", " name)]]]) t/normalized-result) results)]] [:div.px-4.py-10.sm:px-6.lg:hidden {:id "mobile-day-view"} [:h2.mb-4 (jt/format "EEE, MMMM dd" date)] diff --git a/src/com/spicy/results/core.clj b/src/com/spicy/results/core.clj index cf6e529..121a08b 100644 --- a/src/com/spicy/results/core.clj +++ b/src/com/spicy/results/core.clj @@ -6,15 +6,22 @@ [com.spicy.middleware :as mid] [com.spicy.movements.ui :refer [strength-set-inputs]] [com.spicy.results.score :refer [scores->tx ->scores]] - [com.spicy.results.ui :refer [result-ui result-form normalized-result]] + [com.spicy.results.transform :as t] + [com.spicy.results.ui :refer [result-ui result-form inline-result-ui]] [com.spicy.route-helpers :refer [wildcard-override]] [com.spicy.ui :as ui] [java-time.api :as jt] [xtdb.api :as xt])) +(defn workouts-referer? + [{:keys [headers] :as _ctx}] + (let [referer (get headers "referer")] + (re-matches #".*/workouts/.*" referer))) + + (defn show - [{:keys [biff/db session path-params]}] + [{:keys [biff/db session path-params] :as ctx}] (let [result (first (biff/q db '{:find (pull result [* {:result/type [* {:result/workout [*]} {:result/movement [*]} @@ -23,7 +30,9 @@ :where [[result :xt/id result-id] [result :result/user user]]} [(parse-uuid (:id path-params)) (:uid session)]))] - (result-ui result))) + (if (workouts-referer? ctx) + (inline-result-ui result) + (result-ui result)))) (defn update-handler @@ -80,7 +89,7 @@ workout? (seq (:result/workout type)) movement? (seq (:result/movement type))] [:div#edit-result - [:h2.text-2xl.font-bold (:name (normalized-result result))] + [:h2.text-2xl.font-bold (:name (t/normalized-result result))] (when movement? (biff/form {:hidden {:sets (count (-> result :result/type :result-set/_parent)) :strength-result-id (:xt/id type) @@ -126,11 +135,10 @@ (defn index [{:keys [biff/db session] :as ctx}] (let [date-and-results (biff/q db '{:find [date (pull result - [* - {:result/type [* - {:result/workout [*]} - {:result/movement [*]} - {:result-set/_parent [*]}]}])] + [* {:result/type [* + {:result/workout [*]} + {:result/movement [*]} + {:result-set/_parent [*]}]}])] :in [[user]] :where [[result :result/user user] [result :result/date date]] @@ -260,7 +268,7 @@ :rounds-to-score rounds-to-score})))] (biff/submit-tx ctx (concat result-tx wod-sets-tx))) - (let [{:xt/keys [id] :as w} (xt/entity db (parse-uuid (:workout params)))] + (let [{:xt/keys [id] :as _w} (xt/entity db (parse-uuid (:workout params)))] {:status 303 :headers {"location" (str "/app/workouts/" id)}})) diff --git a/src/com/spicy/results/transform.clj b/src/com/spicy/results/transform.clj new file mode 100644 index 0000000..2ea280f --- /dev/null +++ b/src/com/spicy/results/transform.clj @@ -0,0 +1,78 @@ +(ns com.spicy.results.transform + (:require + [clojure.string :as string] + [com.biffweb :as biff] + [com.spicy.movements.core :refer [sets-n-reps]] + [com.spicy.results.score :refer [REPS_MULTIPLIER]] + [com.spicy.time :as t])) + + +(defn ->rounds-reps + [reps-per-round score] + (if (int? score) + (if (> 0 score) + "0+0" + (str (quot score reps-per-round) + "+" + (rem score reps-per-round))) + (let [rounds (.intValue score) + reps (.intValue (* REPS_MULTIPLIER (- (bigdec score) rounds)))] + (str rounds "+" reps)))) + + +(defn display-score + [{:keys [result-set workout]}] + (case (:workout/scheme workout) + :rounds-reps (->rounds-reps (:workout/reps-per-round workout) (:result-set/score result-set)) + :time (t/->time (:result-set/score result-set)) + (:result-set/score result-set))) + + +(defn merge-set-score-with + [sets f] + (apply (partial merge-with f) (map #(select-keys % [:result-set/score]) sets))) + + +(defn display-summed-score + [{:keys [workout sets] :as _a}] + (when (and (not-empty workout) + (not-empty sets)) + (display-score (merge {:workout workout} {:result-set (merge-set-score-with sets +)})))) + + +(defn normalized-result + [result] + (let [workout (-> result :result/type :result/workout) + movement (-> result :result/type :result/movement) + sets (-> result :result/type :result-set/_parent) + notes (-> result :result/type :result/notes) + scale (-> result :result/type :result/scale) + description (if (seq workout) + (-> workout :workout/description) + (str (sets-n-reps sets))) + score (if (seq workout) + (display-summed-score {:workout workout + :sets sets}) + (let [best-set (first + (sort-by :result-set/weight > + (filter + #(= :pass (:result-set/status %)) sets)))] + (str (:result-set/reps best-set) "x" (:result-set/weight best-set)))) + name (or (some-> movement + :movement/name + string/capitalize + (str " (" (sets-n-reps sets) ")")) + (:workout/name workout)) + href (if (:workout/name workout) + (str "/app/workouts/" (:xt/id workout)) + (str "/app/movements/" (:xt/id movement)))] + {:workout workout + :movement movement + :sets sets + :href href + :name name + :description description + :notes notes + :score score + :scale scale + :date (biff/format-date (:result/date result) "YYYY-MM-dd")})) diff --git a/src/com/spicy/results/ui.clj b/src/com/spicy/results/ui.clj index cb19c27..7ef7745 100644 --- a/src/com/spicy/results/ui.clj +++ b/src/com/spicy/results/ui.clj @@ -2,47 +2,10 @@ (:require [clojure.string :as string] [com.biffweb :as biff] - [com.spicy.movements.core :refer [sets-n-reps]] - [com.spicy.workouts.ui :refer [display-summed-score]] + [com.spicy.results.transform :as t] [java-time.api :as jt])) -(defn normalized-result - [result] - (let [workout (-> result :result/type :result/workout) - movement (-> result :result/type :result/movement) - sets (-> result :result/type :result-set/_parent) - notes (-> result :result/type :result/notes) - description (if (seq workout) - (-> workout :workout/description) - (str (sets-n-reps sets))) - score (if (seq workout) - (display-summed-score {:workout workout - :sets sets}) - (let [best-set (first - (sort-by :result-set/weight > - (filter - #(= :pass (:result-set/status %)) sets)))] - (str (:result-set/reps best-set) "x" (:result-set/weight best-set)))) - name (or (some-> movement - :movement/name - string/capitalize - (str " (" (sets-n-reps sets) ")")) - (:workout/name workout)) - href (if (:workout/name workout) - (str "/app/workouts/" (:xt/id workout)) - (str "/app/movements/" (:xt/id movement)))] - {:workout workout - :movement movement - :sets sets - :href href - :name name - :description description - :notes notes - :score score - :date (biff/format-date (:result/date result) "YYYY-MM-dd")})) - - (defn card [{:keys [name href date description notes score]} & children] [:div.flex.flex-col.max-w-sm.sm:max-w-xl.mx-auto#result-ui @@ -73,13 +36,49 @@ (defn result-ui [result] - (card (normalized-result result) + (card (t/normalized-result result) [:button {:hx-get (str "/app/results/" (:xt/id result) "/edit") :hx-target "closest #result-ui" :hx-swap "outerHTML" :class (str "self-end btn-no-shadow bg-white text-sm px-4 py-2 font-normal ")} "edit"])) +(defn inline-result + [result & children] + (let [{:keys [score date notes scale] :as _normalized} (t/normalized-result result)] + (if (not-empty notes) + [:div.flex.flex-col.max-w-sm.sm:max-w-xl.mx-auto#result-ui + [:.flex.flex-row.items-baseline.justify-between.mb-3 + [:div.text-2xl.font-bold score] + [:p.whitespace-pre-wrap.sm:text-left.max-w-xs.text-gray-700.mb-0 date]] + [:.flex.justify-between + [:div.flex.flex-col.gap-2 + [:div.flex.justify-between + (when (not-empty notes) + [:span + notes])]] + (when (not-empty notes) + children)]] + [:div.flex.gap-3.flex-col#result-ui + [:.flex.justify-between.flex-wrap.gap-2 + [:div.text-2xl.font-bold.self-center score + [:span.grow.pl-2.font-normal (name scale)]] + [:div.flex.gap-2 + [:div.self-center (biff/format-date + (:result/date result) "EEE, YYYY-MM-dd")] + children]] + (when notes [:div notes])]))) + + +(defn inline-result-ui + [result] + (inline-result result + [:button {:hx-get (str "/app/results/" (:xt/id result) "/edit") + :hx-target "closest #result-ui" + :hx-swap "outerHTML" + :class (str "self-end btn-no-shadow bg-white text-sm px-4 py-2 font-normal ")} "edit"])) + + (defn scheme-forms [{:keys [workout score identifier] :or {identifier 0}}] @@ -148,7 +147,7 @@ (if (= 1 rounds-to-score) [:div (scheme-forms (assoc {} - :score (display-summed-score {:workout w :sets (-> workout-result :result-set/_parent)}) + :score (t/display-summed-score {:workout w :sets (-> workout-result :result-set/_parent)}) :workout w)) [:input {:type "hidden" :name "id-0" @@ -159,7 +158,7 @@ [:p.m-0.w-2 (str (inc i) ".")] (scheme-forms (assoc {} :workout w - :score (display-summed-score + :score (t/display-summed-score {:workout workout :sets (filter (complement nil?) [(nth (-> workout-result :result-set/_parent) i)])}) :identifier i)) diff --git a/src/com/spicy/sugarwod/transform.clj b/src/com/spicy/sugarwod/transform.clj index 40fd2da..5b59e92 100644 --- a/src/com/spicy/sugarwod/transform.clj +++ b/src/com/spicy/sugarwod/transform.clj @@ -31,16 +31,17 @@ "Push Press" "push press" "Sotts Press" "sotts press" "Clean & Jerk" "clean and jerk" - "Power Clean" "power clean" + "Power Clean" "power clean" "Power Clean & Jerk" "power clean and jerk" "Clean" "clean" "Muscle Clean" "muscle clean" "Hang Clean" "hang clean" - "Hang Power Clean" "hang power clean" "Clean Pull" "clean pull" + "Hang Power Clean" "hang power clean" + "Clean Pull" "clean pull" "Hang Squat Clean" "hang squat clean" "Squat Clean" "squat clean" "Squat Clean Thruster" "squat clean thruster (cluster)" - "Thruster" "thruster" + "Thruster" "thruster" "Snatch" "snatch" "Hang Power Snatch" "hang power snatch" "Snatch Grip Push Press" "snatch grip push press" @@ -59,8 +60,7 @@ "Overhead Squat" "overhead squat" "Box Squat" "box squat" "Front Squat" "front squat" - "Front Pause Squat" "front pause squat" - }) + "Front Pause Squat" "front pause squat"}) (defn sugar-lift->xt-id diff --git a/src/com/spicy/sugarwod/workouts.clj b/src/com/spicy/sugarwod/workouts.clj index 2322e59..ddf8ea0 100644 --- a/src/com/spicy/sugarwod/workouts.clj +++ b/src/com/spicy/sugarwod/workouts.clj @@ -3,6 +3,7 @@ [cheshire.core :as json] [clojure.java.io :as io] [clojure.string :as string] + [com.biffweb :as biff] [com.spicy.numbers :as n] [com.spicy.sugarwod.transform :as t])) @@ -86,3 +87,19 @@ (defn ->spicy [sugar-wod] (t/transformer sgw->spw sugar-wod)) + + +(defn add-tx-data + [spicy-wod] + (assoc spicy-wod + :workout/created-at :db/now + :db/doc-type :workout)) + + +(comment + (require '[com.spicy.repl :refer [get-context]]) + (def workouts (concat girls heroes)) + (def spicy-ws (mapv (comp add-tx-data ->spicy) workouts)) + + (let [ctx (get-context)] + (biff/submit-tx ctx spicy-ws))) diff --git a/src/com/spicy/ui.clj b/src/com/spicy/ui.clj index 34bf58b..05185d9 100644 --- a/src/com/spicy/ui.clj +++ b/src/com/spicy/ui.clj @@ -60,6 +60,7 @@ :tabindex "-1" :id "menu-item-0"}) children])) + (defn page [{:keys [session reitit.core/match] :as ctx} & body] (base @@ -185,7 +186,7 @@ :title (str "View " name) :aria-label (str "View " name)} [:div.flex.items-center.justify-between.pb-2.sm:pb-4.w-full - [:h2.text-3xl.cursor-default name] + [:h2.text-3xl.cursor-default.capitalize name] [:div.block.sm:hidden (when (some? children) children)] diff --git a/src/com/spicy/workouts/ui.clj b/src/com/spicy/workouts/ui.clj index 36a1e4e..63144d5 100644 --- a/src/com/spicy/workouts/ui.clj +++ b/src/com/spicy/workouts/ui.clj @@ -1,53 +1,10 @@ (ns com.spicy.workouts.ui (:require [com.biffweb :as biff] - [com.spicy.results.score :refer [REPS_MULTIPLIER]] - [com.spicy.time :as t] + [com.spicy.results.ui :as r] [com.spicy.ui :as ui])) -(defn ->rounds-reps - [reps-per-round score] - (if (int? score) - (if (> 0 score) - "0+0" - (str (quot score reps-per-round) - "+" - (rem score reps-per-round))) - (let [rounds (.intValue score) - reps (.intValue (* REPS_MULTIPLIER (- (bigdec score) rounds)))] - (str rounds "+" reps)))) - - -(comment - (->rounds-reps 30 60) - (->rounds-reps 30 61) - (->rounds-reps 30 300) - (->rounds-reps 30 301) - (->rounds-reps 30 329) - (->rounds-reps 30 330)) - - -(defn display-score - [{:keys [result-set workout]}] - (case (:workout/scheme workout) - :rounds-reps (->rounds-reps (:workout/reps-per-round workout) (:result-set/score result-set)) - :time (t/->time (:result-set/score result-set)) - (:result-set/score result-set))) - - -(defn merge-set-score-with - [sets f] - (apply (partial merge-with f) (map #(select-keys % [:result-set/score]) sets))) - - -(defn display-summed-score - [{:keys [workout sets] :as _a}] - (when (and (not-empty workout) - (not-empty sets)) - (display-score (merge {:workout workout} {:result-set (merge-set-score-with sets +)})))) - - (defn workout-results [{:keys [user workout biff/db]}] (let [results (biff/q db '{:find (pull result [* {:result/type [* @@ -58,23 +15,16 @@ [result :result/type wod] [wod :result/workout workout-id]]} [user workout])] - [:div {:class (str "flex flex-col relative h-full md:min-h-[60vh] border-2 border-black p-8 m-4 bg-white ")} + [:div {:class (str "flex flex-col relative h-full md:min-h-[60vh] border-2 border-black m-4 py-8 bg-white ")} [:div {:class "absolute h-full -z-10 overflow-visible inset-0 bg-[url(/img/grid.svg)] bg-center "}] - [:h2.text-3xl "Log Book"] + [:h2.text-3xl.px-8 "Log Book"] (if (zero? (count results)) [:p {:class (str " w-fit m-auto ")} "Log a workout to see your history!"] - [:ul.list-none.list-inside.gap-3.pl-0.ml-0 - (map (fn [{{:result/keys [scale notes workout] - sets :result-set/_parent} :result/type - :result/keys [date]}] - [:li {:class (str "w-1/2")} - [:div.flex.gap-3.flex-col - [:.flex.justify-between.flex-wrap.gap-2 - [:div.text-2xl.font-bold.self-center (display-summed-score {:workout workout :sets sets}) - [:span.pl-2.font-normal (name scale)]] - [:div.self-center (biff/format-date - date "EEE, YYYY-MM-dd")]] - (when notes [:div notes])]]) results)])])) + [:ul.flex.flex-col.list-none.list-inside.gap-2.pl-0.ml-0 + (map (fn [result] + [:li.even:bg-gray-50.odd:bg-white.py-2 + [:div.px-8 + (r/inline-result-ui result)]]) results)])])) (defn workout-logbook-ui