From ae2d8330cae2cdfa10f450074f26ad4bac5f07ba Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 28 Aug 2019 19:41:11 +0200 Subject: [PATCH] :construction: Initial work on password form. --- frontend/src/uxbox/main/data/users.cljs | 19 +- .../src/uxbox/main/ui/settings/password.cljs | 167 +++++++++--------- .../src/uxbox/main/ui/settings/profile.cljs | 6 +- frontend/src/uxbox/util/forms.cljs | 42 +++-- 4 files changed, 129 insertions(+), 105 deletions(-) diff --git a/frontend/src/uxbox/main/data/users.cljs b/frontend/src/uxbox/main/data/users.cljs index f657212c1..f2840287a 100644 --- a/frontend/src/uxbox/main/data/users.cljs +++ b/frontend/src/uxbox/main/data/users.cljs @@ -8,6 +8,7 @@ (:require [beicon.core :as rx] [cljs.spec.alpha :as s] + [struct.core :as stt] [potok.core :as ptk] [uxbox.main.repo :as rp] [uxbox.util.i18n :as i18n :refer (tr)] @@ -58,20 +59,15 @@ ;; --- Update Profile -(s/def ::fullname string?) -(s/def ::email us/email?) -(s/def ::username string?) -(s/def ::language string?) - -(s/def ::update-profile - (s/keys :req-un [::fullname - ::email - ::language - ::username])) +(stt/defs update-profile-spec + {:fullname [stt/required stt/string] + :email [stt/required stt/email] + :username [stt/required stt/string] + :language [stt/required stt/string]}) (defn update-profile [data {:keys [on-success on-error]}] - {:pre [(us/valid? ::update-profile data) + {:pre [(stt/valid? update-profile-spec data) (fn? on-error) (fn? on-success)]} (reify @@ -85,7 +81,6 @@ (assoc :email (:email data)) (assoc :username (:username data)) (assoc-in [:metadata :language] (:language data)))] - (prn "update-profile" data) (->> (rp/req :update/profile data) (rx/map :payload) (rx/do on-success) diff --git a/frontend/src/uxbox/main/ui/settings/password.cljs b/frontend/src/uxbox/main/ui/settings/password.cljs index 8eaf2eb06..f6a014435 100644 --- a/frontend/src/uxbox/main/ui/settings/password.cljs +++ b/frontend/src/uxbox/main/ui/settings/password.cljs @@ -6,95 +6,102 @@ ;; Copyright (c) 2016-2017 Juan de la Cruz (ns uxbox.main.ui.settings.password - (:require [cljs.spec.alpha :as s :include-macros true] - [lentes.core :as l] - [cuerdas.core :as str] - [potok.core :as ptk] - [uxbox.main.store :as st] - [uxbox.main.data.users :as udu] - [uxbox.builtins.icons :as i] - [uxbox.main.ui.messages :refer [messages-widget]] - [uxbox.main.ui.settings.header :refer [header]] - [uxbox.util.i18n :refer [tr]] - [uxbox.util.forms :as fm] - [uxbox.util.dom :as dom] - [uxbox.util.messages :as um] - [rumext.core :as mx :include-macros true])) + (:require + [cuerdas.core :as str] + [lentes.core :as l] + [rumext.alpha :as mf] + [struct.core :as stt] + [uxbox.builtins.icons :as i] + [uxbox.main.data.users :as udu] + [uxbox.main.store :as st] + [uxbox.util.dom :as dom] + [uxbox.util.forms :as fm] + [uxbox.util.i18n :refer [tr]] + [uxbox.util.messages :as um])) -(def form-data (fm/focus-data :profile-password st/state)) -(def form-errors (fm/focus-errors :profile-password st/state)) +(stt/defs password-form-spec + {:password-1 [stt/required stt/string] + :password-2 [stt/required stt/string] + :password-old [stt/required stt/string]}) -(def assoc-value (partial fm/assoc-value :profile-password)) -(def assoc-error (partial fm/assoc-error :profile-password)) -(def clear-form (partial fm/clear-form :profile-password)) +(defn- on-submit + [event form] + (dom/prevent-default event) + (prn "on-submit" form) + #_(let [data (:clean-data form) + opts {:on-success #(prn "On Success" %) + :on-error #(on-error % form)}] + (st/emit! (udu/update-profile data opts)))) -;; TODO: add better password validation -(s/def ::password-1 ::fm/non-empty-string) -(s/def ::password-2 ::fm/non-empty-string) -(s/def ::password-old ::fm/non-empty-string) -(s/def ::password-form - (s/keys :req-un [::password-1 - ::password-2 - ::password-old])) -(mx/defc password-form - {:mixins [mx/reactive mx/static]} - [] - (let [data (mx/react form-data) - errors (mx/react form-errors) - valid? (fm/valid? ::password-form data)] - (letfn [(on-change [field event] - (let [value (dom/event->value event)] - (st/emit! (assoc-value field value)))) - (on-success [] - (st/emit! (um/info (tr "settings.password.password-saved")))) - (on-error [{:keys [code] :as payload}] - (case code - :uxbox.services.users/old-password-not-match - (st/emit! (assoc-error :password-old (tr "settings.password.wrong-old-password"))) + ;; #_(let [data (mx/deref form-data) + ;; errors (mx/react form-errors) + ;; valid? (fm/valid? ::password-form data)] + ;; (letfn [(on-change [field event] + ;; (let [value (dom/event->value event)] + ;; (st/emit! (assoc-value field value)))) + ;; (on-success [] + ;; (st/emit! (um/info (tr "settings.password.password-saved")))) + ;; (on-error [{:keys [code] :as payload}] + ;; (case code + ;; :uxbox.services.users/old-password-not-match + ;; (st/emit! (assoc-error :password-old (tr "settings.password.wrong-old-password"))) - :else - (throw (ex-info "unexpected" {:error payload})))) - (on-submit [event] - (st/emit! (udu/update-password data - :on-success on-success - :on-error on-error)))] - [:form.password-form - [:span.user-settings-label (tr "settings.password.change-password")] - [:input.input-text - {:type "password" - :class (fm/error-class errors :password-old) - :value (:password-old data "") - :on-change (partial on-change :password-old) - :placeholder (tr "settings.password.old-password")}] - (fm/input-error errors :password-old) - [:input.input-text - {:type "password" - :class (fm/error-class errors :password-1) - :value (:password-1 data "") - :on-change (partial on-change :password-1) - :placeholder (tr "settings.password.new-password")}] - (fm/input-error errors :password-1) - [:input.input-text - {:type "password" - :class (fm/error-class errors :password-2) - :value (:password-2 data "") - :on-change (partial on-change :password-2) - :placeholder (tr "settings.password.confirm-password")}] - (fm/input-error errors :password-2) - [:input.btn-primary - {:type "button" - :class (when-not valid? "btn-disabled") - :disabled (not valid?) - :on-click on-submit - :value (tr "settings.update-settings")}]]))) + ;; :else + ;; (throw (ex-info "unexpected" {:error payload})))) + ;; (on-submit [event] + ;; (st/emit! (udu/update-password data + ;; :on-success on-success + ;; :on-error on-error)))] + + + +(mf/defc password-form + [props] + (let [{:keys [data] :as form} (fm/use-form {:initial {} :spec password-form-spec})] + (prn "password-form" form) + [:form.password-form {:on-submit #(on-submit % form)} + [:span.user-settings-label (tr "settings.password.change-password")] + [:input.input-text + {:type "password" + :name "password-old" + :class (fm/error-class form :password-old) + :value (:password-old data "") + :on-blur (fm/on-input-blur form) + :on-change (fm/on-input-change form) + :placeholder (tr "settings.password.old-password")}] + [:& fm/error-input {:form form :field :password-old}] + + [:input.input-text + {:type "password" + :name "password-1" + :class (fm/error-class form :password-1) + :value (:password-1 data "") + :on-blur (fm/on-input-blur form) + :on-change (fm/on-input-change form) + :placeholder (tr "settings.password.new-password")}] + [:& fm/error-input {:form form :field :password-1}] + [:input.input-text + {:type "password" + :name "password-2" + :class (fm/error-class form :password-2) + :value (:password-2 data "") + :on-blur (fm/on-input-blur form) + :on-change (fm/on-input-change form) + :placeholder (tr "settings.password.confirm-password")}] + [:& fm/error-input {:form form :field :password-2}] + [:input.btn-primary + {:type "submit" + :class (when-not (:valid form) "btn-disabled") + :disabled (not (:valid form)) + :value (tr "settings.update-settings")}]])) ;; --- Password Page -(mx/defc password-page - [] +(mf/defc password-page + [props] [:section.dashboard-content.user-settings [:section.user-settings-content - (password-form)]]) + [:& password-form]]]) diff --git a/frontend/src/uxbox/main/ui/settings/profile.cljs b/frontend/src/uxbox/main/ui/settings/profile.cljs index 3d7b2fb1a..06f83f14b 100644 --- a/frontend/src/uxbox/main/ui/settings/profile.cljs +++ b/frontend/src/uxbox/main/ui/settings/profile.cljs @@ -41,10 +41,12 @@ (prn "on-error" error form) (case (:code error) :uxbox.services.users/email-already-exists - (swap! form assoc-in [:errors :email] "errors.api.form.email-already-exists") + (swap! form assoc-in [:errors :email] + {:message "errors.api.form.email-already-exists"}) :uxbox.services.users/username-already-exists - (swap! form assoc-in [:errors :username] "errors.api.form.username-already-exists"))) + (swap! form assoc-in [:errors :username] + {:message "errors.api.form.username-already-exists"}))) (defn- initial-data [] diff --git a/frontend/src/uxbox/util/forms.cljs b/frontend/src/uxbox/util/forms.cljs index 403479237..b244ef450 100644 --- a/frontend/src/uxbox/util/forms.cljs +++ b/frontend/src/uxbox/util/forms.cljs @@ -44,9 +44,29 @@ ([self f x y] (update-fn #(f % x y))) ([self f x y more] (update-fn #(apply f % x y more)))))) -(defn- simplify-errors +(defn- translate-error-type + [type] + (case type + ::stt/string "errors.should-be-string" + ::stt/number "errors.should-be-number" + ::stt/number-str "errors.should-be-number" + ::stt/integer "errors.should-be-integer" + ::stt/integer-str "errors.should-be-integer" + ::stt/required "errors.required" + ::stt/email "errors.should-be-valid-email" + ::stt/uuid "errors.should-be-uuid" + ::stt/uuid-str "errors.should-be-valid-uuid" + "errors.undefined-error")) + +(defn- translate-errors [errors] - (reduce-kv #(assoc %1 %2 (:message %3)) {} errors)) + (reduce-kv (fn [acc key val] + (if (string? (:message val)) + (assoc acc key val) + (->> (translate-error-type (:type val)) + (assoc val :message) + (assoc acc key)))) + {} errors)) (defn use-form [{:keys [initial spec] :as opts}] @@ -54,7 +74,7 @@ :errors {} :touched {}}) [errors' clean-data] (validate spec (:data state)) - errors (merge (reduce-kv #(assoc %1 %2 (:message %3)) {} errors') + errors (merge (translate-errors errors') (:errors state))] (-> (assoc state :errors errors @@ -86,11 +106,16 @@ (mf/defc error-input [{:keys [form field] :as props}] (let [touched? (get-in form [:touched field]) - error? (get-in form [:errors field])] - (when (and touched? error?) + error (get-in form [:errors field])] + (when (and touched? error) [:ul.form-errors - [:li {:key error?} (tr error?)]]))) + [:li {:key (:type error)} (tr (:message error))]]))) +(defn error-class + [form field] + (when (and (get-in form [:errors field]) + (get-in form [:touched field])) + "invalid")) ;; --- Additional Validators @@ -298,11 +323,6 @@ [:ul.form-errors [:li {:key error} (tr error)]])) -(defn error-class - [errors field] - (when (get errors field) - "invalid")) - (defn clear-mixin [store type] {:will-unmount (fn [own]