summaryrefslogtreecommitdiff
path: root/src/chef/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'src/chef/frontend')
-rw-r--r--src/chef/frontend/admin.clj103
-rw-r--r--src/chef/frontend/admin/recipe_editor.clj63
-rw-r--r--src/chef/frontend/visitor/home.clj41
-rw-r--r--src/chef/frontend/visitor/recipe.clj55
-rw-r--r--src/chef/frontend/visitor/recipe/thumbnail.clj10
-rw-r--r--src/chef/frontend/visitor/search.clj38
6 files changed, 310 insertions, 0 deletions
diff --git a/src/chef/frontend/admin.clj b/src/chef/frontend/admin.clj
new file mode 100644
index 0000000..89a2462
--- /dev/null
+++ b/src/chef/frontend/admin.clj
@@ -0,0 +1,103 @@
+(ns chef.frontend.admin
+ (:require [chef.utils :as cutils]
+ [hiccup2.core :as html]
+ [ring.util.response :as ruresp]
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
+
+(defn- render-category [data children]
+ [:li
+ [:p {:style {:display :inline-block}}
+ (if (pos? (:categories/id data))
+ [:input {:type :text :placeholder "Name"
+ :value (:categories/name data)
+ :name "name"
+ :hx-post (str "/api/admin/category/" (:categories/id data))
+ :hx-trigger "change"}]
+ "Startseite")]
+ (when (or (neg? (:categories/id data))
+ (->> (:categories/id data)
+ clcategories/find-categories-with-parent
+ count
+ pos?))
+ (list [:p {:style {:display :inline-block
+ :margin-left "1em"
+ :margin-right "1em"}} "->"]
+ [:input {:type :text :placeholder "Frage"
+ :style {:display :inline-block
+ :width :auto}
+ :value (:categories/question data)
+ :name "question"
+ :hx-post (str "/api/admin/category/" (:categories/id data))
+ :hx-trigger "change"}]))
+ [:img {:src "/static/icons/plus.svg" :height "30em"
+ :style {:vertical-align :middle
+ :margin-left "1em"}
+ :hx-post (str "/api/admin/category/create/"
+ (when (pos? (:categories/id data)) (str "?parent=" (:categories/id data))))
+ :hx-swap "none"}]
+ (when (pos? (:categories/id data))
+ [:img {:src "/static/icons/trash.svg" :height "30em"
+ :style {:vertical-align :middle
+ :margin-left "1em"}
+ :hx-delete (str "/api/admin/category/" (:categories/id data))
+ :hx-swap "none"}])
+ [:ul
+ (for [child children]
+ (->> (:categories/id child)
+ clcategories/find-categories-with-parent
+ (render-category child)))]])
+
+(defn- render-recipe-table-row [recipe]
+ (let [tr-id (str "recipe-" (:recipes/id recipe))]
+ [:tr {:id tr-id}
+ [:td
+ [:p (:recipes/title recipe)]]
+ [:td
+ (let [category (clcategories/get-category (:recipes/category recipe))]
+ [:p (clcategories/generate-path category)])]
+ [:td
+ [:div
+ [:button {:class ["button" "primary"]
+ :onclick (str "window.open(\"/admin/recipe-editor/"
+ (:recipes/id recipe)
+ "\", \"\", \"width=900,height=900\")")}
+ "Bearbeiten"]
+ [:button {:class ["button error"]
+ :hx-trigger "click"
+ :hx-swap :none
+ :hx-delete (str "/api/admin/recipe/" (:recipes/id recipe))}
+ "Löschen"]]]]))
+
+(defn- render []
+ (cutils/gen-page "chef - Admin"
+ [:div {:style {:margin-left "1em"}}
+ [:h1 "chef - Admin"]
+ [:h2 "Kategorien"]
+ [:ul
+ (render-category (clcategories/get-category -1)
+ (clcategories/find-categories-with-parent -1))]
+ [:h2 "Rezepte"]
+ [:table
+ [:tr
+ [:th "Titel"]
+ [:th "Kategorie"]
+ [:th "Aktionen"]]
+ (for [recipe (clrecipes/get-all-recipes)]
+ (render-recipe-table-row recipe))]
+ [:button {:class "button primary"
+ :hx-trigger "click"
+ :hx-swap :none
+ :hx-post "/api/admin/recipe/create/"}
+ "Rezept erstellen"]]))
+
+(defn handler [req]
+ (let [access-token (get-in req [:oauth2/access-tokens :auth])
+ resp (-> (render)
+ html/html
+ str
+ ruresp/response)]
+ (if (some? access-token)
+ (assoc resp :session (assoc (:session req) :oauth-token access-token))
+ (cutils/auth-only req resp))))
diff --git a/src/chef/frontend/admin/recipe_editor.clj b/src/chef/frontend/admin/recipe_editor.clj
new file mode 100644
index 0000000..69c84ba
--- /dev/null
+++ b/src/chef/frontend/admin/recipe_editor.clj
@@ -0,0 +1,63 @@
+(ns chef.frontend.admin.recipe-editor
+ (:require [hiccup2.core :as html]
+ [hiccup.util :as hutil]
+ [ring.util.response :as ruresp]
+ [chef.utils :as cutils]
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
+
+(defn render [recipe]
+ (cutils/gen-page "chef - Rezept bearbeiten"
+ [:div {:style {:margin-left "1em"}}
+ [:h1 "Rezept bearbeiten"]
+ [:form {:style {:width "50%"}
+ :hx-post (str "/api/admin/recipe/" (:recipes/id recipe))
+ :hx-swap "none"
+ :enctype "multipart/form-data"}
+ [:input {:type :text :name "title" :placeholder "Titel"
+ :value (:recipes/title recipe)}]
+ [:div {:style {:display :flex}}
+ [:p {:style {:margin-right "0.5em"}} "Thumbnail: "]
+ [:input {:type :file :name "thumbnail"
+ :style {:height :fit-content
+ :padding "0.3em"}}]]
+ [:button {:class ["button" "error"]
+ :hx-trigger "click"
+ :hx-delete (str "/api/admin/recipe/" (:recipes/id recipe) "/thumbnail/")
+ :hx-swap :none}
+ "Thumbnail entfernen"]
+ [:h2 "Kategorie"]
+ [:select {:name "category"}
+ (for [category (clcategories/get-all-categories)]
+ [:option {:value (:categories/id category)
+ :selected (= (:categories/id category) (:recipes/category recipe))}
+ (clcategories/generate-path category)])]
+ [:h2 "Zutaten"]
+ [:div {:style {:display :flex}}
+ [:p {:style {:margin-right "0.5em"}} "Pro"]
+ [:select {:name "ingredients-unit"
+ :style {:height :fit-content
+ :padding "0.3em"}}
+ [:option {:value 0 :selected (= (:recipes/unit recipe) 0)} "Portion"]
+ [:option {:value 1 :selected (= (:recipes/unit recipe) 1)} "Person"]]
+ [:p ":"]]
+ [:textarea {:name "ingredients"}
+ (:recipes/ingredients recipe)]
+ [:p "(Je Zeile eine Zutat, in dem Format " [:code "[Beschreibung der Zutat]=[Menge als Zahl][Einheit der Menge]"] ".)"]
+ [:h2 "Zubereitung"]
+ [:textarea {:name "preparation"}
+ (:recipes/preparation recipe)]
+ [:button {:type :submit
+ :style {:margin-top "1em"}} "Speichern"]]]
+ [:script (hutil/raw-string "window.addEventListener(\"htmx:afterRequest\", () => window.close())")]))
+
+(defn handler [req]
+ (cutils/auth-only req
+ (if-let [id (cutils/s->int-or-nil (get-in req [:path-params :id]))]
+ (->> (clrecipes/get-recipe id)
+ render
+ html/html
+ str
+ ruresp/response)
+ (ruresp/bad-request "Bad request."))))
diff --git a/src/chef/frontend/visitor/home.clj b/src/chef/frontend/visitor/home.clj
new file mode 100644
index 0000000..6503983
--- /dev/null
+++ b/src/chef/frontend/visitor/home.clj
@@ -0,0 +1,41 @@
+(ns chef.frontend.visitor.home
+ (:require [hiccup2.core :as html]
+ [ring.util.response :as ruresp]
+ [chef.utils :as cutils]
+
+ [chef.frontend.visitor.search :as cfvsearch]
+
+ [chef.logic.categories :as clcategories]))
+
+(defn- render [req]
+ (cutils/gen-page "chef"
+ (let [category (clcategories/get-category (or (get-in req [:params "category"]) -1))]
+ [:div {:style {:text-align :center}}
+ [:h1 "chef"]
+ [:h2 "Finde das perfekte Gericht für dich!"]
+ [:b (:categories/question category)]
+ [:div
+ (for [child-category (clcategories/find-categories-with-parent (:categories/id category))]
+ [:div
+ [:button {:style {:margin-bottom "1em"}
+ :onclick (str "window.location = \"/?category=" (:categories/id child-category) "\"")}
+ (:categories/name child-category)]
+ [:br]])]
+ (when (pos? (:categories/id category))
+ [:h3 (clcategories/generate-path category)])
+ [:input {:type :text
+ :style {:width "90%" :margin :auto}
+ :placeholder "Suche"
+ :hx-get (str "/components/search?category=" (:categories/id category))
+ :name "query"
+ :hx-swap "innerHTML"
+ :hx-target "#search-results"}]
+ [:div {:id "search-results"}
+ (cfvsearch/render "" (:categories/id category))]])))
+
+(defn handler [req]
+ (-> req
+ render
+ html/html
+ str
+ ruresp/response))
diff --git a/src/chef/frontend/visitor/recipe.clj b/src/chef/frontend/visitor/recipe.clj
new file mode 100644
index 0000000..e3dbf97
--- /dev/null
+++ b/src/chef/frontend/visitor/recipe.clj
@@ -0,0 +1,55 @@
+(ns chef.frontend.visitor.recipe
+ (:require [chef.utils :as cutils]
+ [clojure.string :as cstr]
+ [hiccup2.core :as html]
+ [ring.util.response :as ruresp]
+
+ [chef.logic.recipes :as clrecipes]
+ [chef.logic.categories :as clcategories]))
+
+(defn- render [portions recipe]
+ (cutils/gen-page (str "chef - " (:recipes/title recipe))
+ [:div {:style {:margin-left "1em"}}
+ [:div
+ [:h1 {:style {:display :inline-block
+ :margin-right "0.5em"}}
+ (:recipes/title recipe)]
+ [:i (str "(" (-> (:recipes/category recipe)
+ clcategories/get-category
+ clcategories/generate-path) ")")]]
+ (when (some? (clrecipes/get-recipe-thumbnail (:recipes/id recipe)))
+ [:img {:src (str "/recipes/" (:recipes/id recipe) "/thumbnail")
+ :width "50%"}])
+ [:h2
+ "Zutaten pro "
+ [:input {:type :number
+ :value portions
+ :style {:width "3em"
+ :display :inline-block}
+ "_" "on change go to url `?portions=${value of me}`"}]
+ (condp = (:recipes/unit recipe)
+ 0 " Portion(en)"
+ 1 " Person(en)"
+ "")
+ ":"]
+ [:ul (for [ingredient (-> recipe
+ :recipes/ingredients
+ cutils/parse-ingredients)]
+ [:li
+ [:b (:description ingredient)] ": "
+ (* (:amount ingredient) portions)
+ (:unit ingredient)])]
+ [:h2 "Zubereitung"]
+ (->> (:recipes/preparation recipe)
+ cstr/split-lines
+ (map #(if (cstr/blank? %)
+ [:br]
+ [:p %])))]))
+
+(defn handler [req]
+ (->> (clrecipes/get-recipe (get-in req [:path-params :id]))
+ (render (or (cutils/s->int-or-nil (get-in req [:params "portions"]))
+ 1))
+ html/html
+ str
+ ruresp/response))
diff --git a/src/chef/frontend/visitor/recipe/thumbnail.clj b/src/chef/frontend/visitor/recipe/thumbnail.clj
new file mode 100644
index 0000000..c14f491
--- /dev/null
+++ b/src/chef/frontend/visitor/recipe/thumbnail.clj
@@ -0,0 +1,10 @@
+(ns chef.frontend.visitor.recipe.thumbnail
+ (:require [ring.util.response :as ruresp]
+ [chef.logic.recipes :as clrecipes])
+ (:import java.io.File))
+
+(defn handler [req]
+ (if-let [id (get-in req [:path-params :id])]
+ (when-let [thumbnail-file (clrecipes/get-recipe-thumbnail id)]
+ (ruresp/file-response (.getPath ^File thumbnail-file)))
+ (ruresp/bad-request "Bad request.")))
diff --git a/src/chef/frontend/visitor/search.clj b/src/chef/frontend/visitor/search.clj
new file mode 100644
index 0000000..7a2db93
--- /dev/null
+++ b/src/chef/frontend/visitor/search.clj
@@ -0,0 +1,38 @@
+(ns chef.frontend.visitor.search
+ (:require [chef.utils :as cutils]
+ [clojure.string :as cstr]
+ [hiccup2.core :as html]
+ [ring.util.response :as ruresp]
+
+ [chef.logic.categories :as clcategories]
+ [chef.logic.recipes :as clrecipes]))
+
+(defn render [query category]
+ [:table
+ [:tr
+ [:th "Rezept"]
+ [:th "Kategorie"]]
+ (for [recipe (clrecipes/get-all-recipes)
+ :let [recipe-category (clcategories/get-category (:recipes/category recipe))]]
+ (when (or (= category -1)
+ (and (cstr/includes? (-> recipe
+ :recipes/title
+ cstr/lower-case)
+ query)
+ (some #(= (:categories/id %) category)
+ (clcategories/get-parents recipe-category))))
+ [:tr
+ [:td
+ [:b [:a {:href (str "/recipes/" (:recipes/id recipe))} (:recipes/title recipe)]]]
+ [:td
+ (-> (:recipes/category recipe)
+ clcategories/get-category
+ clcategories/generate-path)]]))])
+
+(defn handler [req]
+ (if-let [query (get-in req [:params "query"])]
+ (-> (render query (or (cutils/s->int-or-nil (get-in req [:params "category"])) -1))
+ html/html
+ str
+ ruresp/response)
+ (ruresp/bad-request "No search query provide.")))