mirror of
https://github.com/penpot/penpot.git
synced 2025-05-09 06:15:54 +02:00
🚧 More work on forms refactoring.
This commit is contained in:
parent
ae2d8330ca
commit
04a5038ff4
7 changed files with 161 additions and 192 deletions
|
@ -7,11 +7,10 @@
|
||||||
(ns uxbox.main.data.users
|
(ns uxbox.main.data.users
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[struct.core :as s]
|
||||||
[struct.core :as stt]
|
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.util.i18n :as i18n :refer (tr)]
|
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||||
[uxbox.util.messages :as uum]
|
[uxbox.util.messages :as uum]
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.storage :refer [storage]]))
|
[uxbox.util.storage :refer [storage]]))
|
||||||
|
@ -59,15 +58,15 @@
|
||||||
|
|
||||||
;; --- Update Profile
|
;; --- Update Profile
|
||||||
|
|
||||||
(stt/defs update-profile-spec
|
(s/defs update-profile-spec
|
||||||
{:fullname [stt/required stt/string]
|
{:fullname [s/required s/string]
|
||||||
:email [stt/required stt/email]
|
:email [s/required s/email]
|
||||||
:username [stt/required stt/string]
|
:username [s/required s/string]
|
||||||
:language [stt/required stt/string]})
|
:language [s/required s/string]})
|
||||||
|
|
||||||
(defn update-profile
|
(defn update-profile
|
||||||
[data {:keys [on-success on-error]}]
|
[data {:keys [on-success on-error]}]
|
||||||
{:pre [(stt/valid? update-profile-spec data)
|
{:pre [(s/valid? update-profile-spec data)
|
||||||
(fn? on-error)
|
(fn? on-error)
|
||||||
(fn? on-success)]}
|
(fn? on-success)]}
|
||||||
(reify
|
(reify
|
||||||
|
@ -90,33 +89,28 @@
|
||||||
|
|
||||||
;; --- Update Password (Form)
|
;; --- Update Password (Form)
|
||||||
|
|
||||||
(deftype UpdatePassword [data on-success on-error]
|
(s/defs update-password-spec
|
||||||
ptk/WatchEvent
|
{:password-1 [s/required s/string]
|
||||||
(watch [_ state s]
|
:password-2 [s/required s/string [s/identical-to :password-1]]
|
||||||
(let [params {:old-password (:password-old data)
|
:password-old [s/required s/string]})
|
||||||
:password (:password-1 data)}]
|
|
||||||
(->> (rp/req :update/profile-password params)
|
|
||||||
(rx/catch rp/client-error? (fn [e]
|
|
||||||
(on-error (:payload e))
|
|
||||||
(rx/empty)))
|
|
||||||
(rx/do on-success)
|
|
||||||
(rx/ignore)))))
|
|
||||||
|
|
||||||
(s/def ::password-1 string?)
|
|
||||||
(s/def ::password-2 string?)
|
|
||||||
(s/def ::password-old string?)
|
|
||||||
|
|
||||||
(s/def ::update-password
|
|
||||||
(s/keys :req-un [::password-1
|
|
||||||
::password-2
|
|
||||||
::password-old]))
|
|
||||||
|
|
||||||
(defn update-password
|
(defn update-password
|
||||||
[data & {:keys [on-success on-error]}]
|
[data {:keys [on-success on-error]}]
|
||||||
{:pre [(us/valid? ::update-password data)
|
{:pre [(s/valid? update-password-spec data)
|
||||||
(fn? on-success)
|
(fn? on-success)
|
||||||
(fn? on-error)]}
|
(fn? on-error)]}
|
||||||
(UpdatePassword. data on-success on-error))
|
(reify
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state s]
|
||||||
|
(let [params {:old-password (:password-old data)
|
||||||
|
:password (:password-1 data)}]
|
||||||
|
(->> (rp/req :update/profile-password params)
|
||||||
|
(rx/catch rp/client-error? (fn [e]
|
||||||
|
(on-error (:payload e))
|
||||||
|
(rx/empty)))
|
||||||
|
(rx/do on-success)
|
||||||
|
(rx/ignore))))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Update Photo
|
;; --- Update Photo
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(ns uxbox.main.ui.auth.login
|
(ns uxbox.main.ui.auth.login
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[struct.core :as s]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.main.data.auth :as da]
|
[uxbox.main.data.auth :as da]
|
||||||
|
@ -18,9 +19,9 @@
|
||||||
[uxbox.util.i18n :refer [tr]]
|
[uxbox.util.i18n :refer [tr]]
|
||||||
[uxbox.util.router :as rt]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
(def login-form-spec
|
(s/defs login-form-spec
|
||||||
{:username [fm/required fm/string fm/non-empty-string]
|
{:username [fm/required fm/string]
|
||||||
:password [fm/required fm/string fm/non-empty-string]})
|
:password [fm/required fm/string]})
|
||||||
|
|
||||||
(defn- on-submit
|
(defn- on-submit
|
||||||
[event form]
|
[event form]
|
||||||
|
@ -41,8 +42,7 @@
|
||||||
|
|
||||||
(mf/defc login-form
|
(mf/defc login-form
|
||||||
[]
|
[]
|
||||||
(let [{:keys [data] :as form} (fm/use-form {:initial {}
|
(let [{:keys [data] :as form} (fm/use-form {:initial {} :spec login-form-spec})]
|
||||||
:spec login-form-spec})]
|
|
||||||
[:form {:on-submit #(on-submit % form)}
|
[:form {:on-submit #(on-submit % form)}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
(when cfg/isdemo
|
(when cfg/isdemo
|
||||||
|
@ -52,25 +52,26 @@
|
||||||
{:name "username"
|
{:name "username"
|
||||||
:tab-index "2"
|
:tab-index "2"
|
||||||
:value (:username data "")
|
:value (:username data "")
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :username)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :username)
|
||||||
:placeholder (tr "auth.email-or-username")
|
:placeholder (tr "auth.email-or-username")
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "password"
|
{:name "password"
|
||||||
:tab-index "3"
|
:tab-index "3"
|
||||||
:value (:password data "")
|
:value (:password data "")
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :password)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :password)
|
||||||
:placeholder (tr "auth.password")
|
:placeholder (tr "auth.password")
|
||||||
:type "password"}]
|
:type "password"}]
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:name "login"
|
{:name "login"
|
||||||
:tab-index "4"
|
:tab-index "4"
|
||||||
:class (when (:errors form) "btn-disabled")
|
:class (when-not (:valid form) "btn-disabled")
|
||||||
:disabled (boolean (:errors form))
|
:disabled (not (:valid form))
|
||||||
:value (tr "auth.signin")
|
:value (tr "auth.signin")
|
||||||
:type "submit"}]
|
:type "submit"}]
|
||||||
|
|
||||||
[:div.login-links
|
[:div.login-links
|
||||||
[:a {:on-click #(st/emit! (rt/nav :auth/recovery-request))
|
[:a {:on-click #(st/emit! (rt/nav :auth/recovery-request))
|
||||||
:tab-index "5"}
|
:tab-index "5"}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
[uxbox.util.i18n :as t :refer [tr]]))
|
[uxbox.util.i18n :as t :refer [tr]]))
|
||||||
|
|
||||||
(def project-form-spec
|
(def project-form-spec
|
||||||
{:name [fm/required fm/string fm/non-empty-string]
|
{:name [fm/required fm/string]
|
||||||
:width [fm/required fm/number-str]
|
:width [fm/required fm/number-str]
|
||||||
:height [fm/required fm/number-str]})
|
:height [fm/required fm/number-str]})
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@
|
||||||
:type "text"
|
:type "text"
|
||||||
:name "name"
|
:name "name"
|
||||||
:value (:name data)
|
:value (:name data)
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :name)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :name)
|
||||||
:auto-focus true}]
|
:auto-focus true}]
|
||||||
[:div.project-size
|
[:div.project-size
|
||||||
[:div.input-element.pixels
|
[:div.input-element.pixels
|
||||||
|
@ -63,8 +63,8 @@
|
||||||
:type "number"
|
:type "number"
|
||||||
:min 0
|
:min 0
|
||||||
:max 5000
|
:max 5000
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :width)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :width)
|
||||||
:value (:width data)}]]
|
:value (:width data)}]]
|
||||||
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
||||||
[:div.input-element.pixels
|
[:div.input-element.pixels
|
||||||
|
@ -75,8 +75,8 @@
|
||||||
:name "height"
|
:name "height"
|
||||||
:min 0
|
:min 0
|
||||||
:max 5000
|
:max 5000
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :height)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :height)
|
||||||
:value (:height data)}]]]
|
:value (:height data)}]]]
|
||||||
|
|
||||||
;; Submit
|
;; Submit
|
||||||
|
|
|
@ -2,15 +2,13 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
|
||||||
;; Copyright (c) 2016-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.password
|
(ns uxbox.main.ui.settings.password
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[struct.core :as stt]
|
[struct.core :as s]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.main.data.users :as udu]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
@ -19,79 +17,65 @@
|
||||||
[uxbox.util.i18n :refer [tr]]
|
[uxbox.util.i18n :refer [tr]]
|
||||||
[uxbox.util.messages :as um]))
|
[uxbox.util.messages :as um]))
|
||||||
|
|
||||||
(stt/defs password-form-spec
|
|
||||||
{:password-1 [stt/required stt/string]
|
|
||||||
:password-2 [stt/required stt/string]
|
|
||||||
:password-old [stt/required stt/string]})
|
|
||||||
|
|
||||||
(defn- on-submit
|
(defn- on-submit
|
||||||
[event form]
|
[event form]
|
||||||
(dom/prevent-default event)
|
(letfn [(on-error [error]
|
||||||
(prn "on-submit" form)
|
(case (:code error)
|
||||||
#_(let [data (:clean-data form)
|
:uxbox.services.users/old-password-not-match
|
||||||
opts {:on-success #(prn "On Success" %)
|
(swap! form assoc-in [:errors :password-old]
|
||||||
:on-error #(on-error % form)}]
|
{:type ::api :message "settings.password.wrong-old-password"})
|
||||||
(st/emit! (udu/update-profile data opts))))
|
|
||||||
|
|
||||||
|
:else (throw (ex-info "unexpected" {:error error}))))
|
||||||
|
|
||||||
|
(on-success [_]
|
||||||
|
(st/emit! (um/info (tr "settings.password.password-saved"))))]
|
||||||
|
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(let [data (:clean-data form)
|
||||||
|
opts {:on-success on-success
|
||||||
|
:on-error on-error}]
|
||||||
|
(st/emit! (udu/update-password data opts)))))
|
||||||
|
|
||||||
;; #_(let [data (mx/deref form-data)
|
(s/defs password-form-spec
|
||||||
;; errors (mx/react form-errors)
|
{:password-1 [s/required s/string]
|
||||||
;; valid? (fm/valid? ::password-form data)]
|
:password-2 [s/required s/string [s/identical-to :password-1]]
|
||||||
;; (letfn [(on-change [field event]
|
:password-old [s/required s/string]})
|
||||||
;; (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)))]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc password-form
|
(mf/defc password-form
|
||||||
[props]
|
[props]
|
||||||
(let [{:keys [data] :as form} (fm/use-form {:initial {} :spec password-form-spec})]
|
(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)}
|
[:form.password-form {:on-submit #(on-submit % form)}
|
||||||
[:span.user-settings-label (tr "settings.password.change-password")]
|
[:span.user-settings-label (tr "settings.password.change-password")]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:name "password-old"
|
:name "password-old"
|
||||||
:class (fm/error-class form :password-old)
|
|
||||||
:value (:password-old data "")
|
:value (:password-old data "")
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :password-old)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :password-old)
|
||||||
|
:on-change (fm/on-input-change form :password-old)
|
||||||
:placeholder (tr "settings.password.old-password")}]
|
:placeholder (tr "settings.password.old-password")}]
|
||||||
[:& fm/error-input {:form form :field :password-old}]
|
[:& fm/field-error {:form form :field :password-old}]
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:name "password-1"
|
:name "password-1"
|
||||||
:class (fm/error-class form :password-1)
|
|
||||||
:value (:password-1 data "")
|
:value (:password-1 data "")
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :password-1)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :password-1)
|
||||||
|
:on-change (fm/on-input-change form :password-1)
|
||||||
:placeholder (tr "settings.password.new-password")}]
|
:placeholder (tr "settings.password.new-password")}]
|
||||||
[:& fm/error-input {:form form :field :password-1}]
|
[:& fm/field-error {:form form :field :password-1}]
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:name "password-2"
|
:name "password-2"
|
||||||
:class (fm/error-class form :password-2)
|
|
||||||
:value (:password-2 data "")
|
:value (:password-2 data "")
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :password-2)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :password-2)
|
||||||
|
:on-change (fm/on-input-change form :password-2)
|
||||||
:placeholder (tr "settings.password.confirm-password")}]
|
:placeholder (tr "settings.password.confirm-password")}]
|
||||||
[:& fm/error-input {:form form :field :password-2}]
|
[:& fm/field-error {:form form :field :password-2}]
|
||||||
|
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:type "submit"
|
{:type "submit"
|
||||||
:class (when-not (:valid form) "btn-disabled")
|
:class (when-not (:valid form) "btn-disabled")
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.profile
|
(ns uxbox.main.ui.settings.profile
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[struct.core :as s]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.main.data.users :as udu]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.util.dom :as dom]
|
|
||||||
[uxbox.util.data :refer [read-string]]
|
[uxbox.util.data :refer [read-string]]
|
||||||
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as fm]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||||
[uxbox.util.interop :refer [iterable->seq]]))
|
[uxbox.util.interop :refer [iterable->seq]]))
|
||||||
|
@ -30,23 +30,25 @@
|
||||||
(-> (l/key :profile)
|
(-> (l/key :profile)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def profile-form-spec
|
(s/defs profile-form-spec
|
||||||
{:fullname [fm/required fm/string fm/non-empty-string]
|
{:fullname [fm/required fm/string]
|
||||||
:username [fm/required fm/string fm/non-empty-string]
|
:username [fm/required fm/string]
|
||||||
:email [fm/required fm/email]
|
:email [fm/required fm/email]
|
||||||
:language [fm/required fm/string]})
|
:language [fm/required fm/string]})
|
||||||
|
|
||||||
(defn- on-error
|
(defn- on-error
|
||||||
[error {:keys [errors] :as form}]
|
[error form]
|
||||||
(prn "on-error" error form)
|
(prn "on-error" error form)
|
||||||
(case (:code error)
|
(case (:code error)
|
||||||
:uxbox.services.users/email-already-exists
|
:uxbox.services.users/email-already-exists
|
||||||
(swap! form assoc-in [:errors :email]
|
(swap! form assoc-in [:errors :email]
|
||||||
{:message "errors.api.form.email-already-exists"})
|
{:type ::api
|
||||||
|
:message "errors.api.form.email-already-exists"})
|
||||||
|
|
||||||
:uxbox.services.users/username-already-exists
|
:uxbox.services.users/username-already-exists
|
||||||
(swap! form assoc-in [:errors :username]
|
(swap! form assoc-in [:errors :username]
|
||||||
{:message "errors.api.form.username-already-exists"})))
|
{:type ::api
|
||||||
|
:message "errors.api.form.username-already-exists"})))
|
||||||
|
|
||||||
(defn- initial-data
|
(defn- initial-data
|
||||||
[]
|
[]
|
||||||
|
@ -66,39 +68,53 @@
|
||||||
[props]
|
[props]
|
||||||
(let [{:keys [data] :as form} (fm/use-form {:initial initial-data
|
(let [{:keys [data] :as form} (fm/use-form {:initial initial-data
|
||||||
:spec profile-form-spec})]
|
:spec profile-form-spec})]
|
||||||
|
(prn "profile-form" form)
|
||||||
[:form.profile-form {:on-submit #(on-submit % form)}
|
[:form.profile-form {:on-submit #(on-submit % form)}
|
||||||
[:span.user-settings-label (tr "settings.profile.section-basic-data")]
|
[:span.user-settings-label (tr "settings.profile.section-basic-data")]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "text"
|
{:type "text"
|
||||||
:name "fullname"
|
:name "fullname"
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :fullname)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :fullname)
|
||||||
|
:on-change (fm/on-input-change form :fullname)
|
||||||
:value (:fullname data "")
|
:value (:fullname data "")
|
||||||
:placeholder (tr "settings.profile.your-name")}]
|
:placeholder (tr "settings.profile.your-name")}]
|
||||||
[:& fm/error-input {:form form :field :fullname}]
|
|
||||||
|
[:& fm/field-error {:form form
|
||||||
|
:type #{::api}
|
||||||
|
:field :fullname}]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "text"
|
{:type "text"
|
||||||
:name "username"
|
:name "username"
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :username)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :username)
|
||||||
|
:on-change (fm/on-input-change form :username)
|
||||||
:value (:username data "")
|
:value (:username data "")
|
||||||
:placeholder (tr "settings.profile.your-username")}]
|
:placeholder (tr "settings.profile.your-username")}]
|
||||||
[:& fm/error-input {:form form :field :username}]
|
|
||||||
|
[:& fm/field-error {:form form
|
||||||
|
:type #{::api}
|
||||||
|
:field :username}]
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "email"
|
{:type "email"
|
||||||
:name "email"
|
:name "email"
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :email)
|
||||||
:on-change (fm/on-input-change form)
|
:on-blur (fm/on-input-blur form :email)
|
||||||
|
:on-change (fm/on-input-change form :email)
|
||||||
:value (:email data "")
|
:value (:email data "")
|
||||||
:placeholder (tr "settings.profile.your-email")}]
|
:placeholder (tr "settings.profile.your-email")}]
|
||||||
[:& fm/error-input {:form form :field :email}]
|
|
||||||
|
[:& fm/field-error {:form form
|
||||||
|
:type #{::api}
|
||||||
|
:field :email}]
|
||||||
|
|
||||||
[:span.user-settings-label (tr "settings.profile.section-i18n-data")]
|
[:span.user-settings-label (tr "settings.profile.section-i18n-data")]
|
||||||
[:select.input-select {:value (:language data)
|
[:select.input-select {:value (:language data)
|
||||||
:name "language"
|
:name "language"
|
||||||
:on-blur (fm/on-input-blur form)
|
:class (fm/error-class form :language)
|
||||||
:on-change (fm/on-input-change form)}
|
:on-blur (fm/on-input-blur form :language)
|
||||||
|
:on-change (fm/on-input-change form :language)}
|
||||||
[:option {:value "en"} "English"]
|
[:option {:value "en"} "English"]
|
||||||
[:option {:value "fr"} "Français"]]
|
[:option {:value "fr"} "Français"]]
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
(def page-form-spec
|
(def page-form-spec
|
||||||
{:id [fm/uuid]
|
{:id [fm/uuid]
|
||||||
:project [fm/uuid]
|
:project [fm/uuid]
|
||||||
:name [fm/required fm/string fm/non-empty-string]
|
:name [fm/required fm/string]
|
||||||
:width [fm/required fm/number-str]
|
:width [fm/required fm/number-str]
|
||||||
:height [fm/required fm/number-str]})
|
:height [fm/required fm/number-str]})
|
||||||
|
|
||||||
|
@ -59,8 +59,8 @@
|
||||||
{:placeholder "Page name"
|
{:placeholder "Page name"
|
||||||
:type "text"
|
:type "text"
|
||||||
:name "name"
|
:name "name"
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :name)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :name)
|
||||||
:value (:name data)
|
:value (:name data)
|
||||||
:auto-focus true}]
|
:auto-focus true}]
|
||||||
[:div.project-size
|
[:div.project-size
|
||||||
|
@ -72,8 +72,8 @@
|
||||||
:type "number"
|
:type "number"
|
||||||
:min 0
|
:min 0
|
||||||
:max 5000
|
:max 5000
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :width)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :width)
|
||||||
:value (:width data)}]]
|
:value (:width data)}]]
|
||||||
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
||||||
[:div.input-element.pixels
|
[:div.input-element.pixels
|
||||||
|
@ -84,8 +84,8 @@
|
||||||
:type "number"
|
:type "number"
|
||||||
:min 0
|
:min 0
|
||||||
:max 5000
|
:max 5000
|
||||||
:on-blur (fm/on-input-blur form)
|
:on-blur (fm/on-input-blur form :height)
|
||||||
:on-change (fm/on-input-change form)
|
:on-change (fm/on-input-change form :height)
|
||||||
:value (:height data)}]]]
|
:value (:height data)}]]]
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:value "Go go go!"
|
{:value "Go go go!"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[rumext.core :as mx]
|
[rumext.core :as mx]
|
||||||
[struct.core :as stt]
|
[struct.core :as st]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.i18n :refer [tr]]))
|
[uxbox.util.i18n :refer [tr]]))
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@
|
||||||
|
|
||||||
(defn validate
|
(defn validate
|
||||||
[data spec]
|
[data spec]
|
||||||
(stt/validate data spec))
|
(st/validate data spec))
|
||||||
|
|
||||||
(defn valid?
|
(defn valid?
|
||||||
[data spec]
|
[data spec]
|
||||||
(stt/valid? data spec))
|
(st/valid? data spec))
|
||||||
|
|
||||||
;; --- Handlers Helpers
|
;; --- Handlers Helpers
|
||||||
|
|
||||||
|
@ -45,17 +45,16 @@
|
||||||
([self f x y more] (update-fn #(apply f % x y more))))))
|
([self f x y more] (update-fn #(apply f % x y more))))))
|
||||||
|
|
||||||
(defn- translate-error-type
|
(defn- translate-error-type
|
||||||
[type]
|
[code]
|
||||||
(case type
|
(case code
|
||||||
::stt/string "errors.should-be-string"
|
::st/string "errors.form.string"
|
||||||
::stt/number "errors.should-be-number"
|
::st/number "errors.form.number"
|
||||||
::stt/number-str "errors.should-be-number"
|
::st/number-str "errors.form.number"
|
||||||
::stt/integer "errors.should-be-integer"
|
::st/integer "errors.form.integer"
|
||||||
::stt/integer-str "errors.should-be-integer"
|
::st/integer-str "errors.form.integer"
|
||||||
::stt/required "errors.required"
|
::st/required "errors.form.required"
|
||||||
::stt/email "errors.should-be-valid-email"
|
::st/email "errors.form.email"
|
||||||
::stt/uuid "errors.should-be-uuid"
|
::st/identical-to "errors.form.does-not-match"
|
||||||
::stt/uuid-str "errors.should-be-valid-uuid"
|
|
||||||
"errors.undefined-error"))
|
"errors.undefined-error"))
|
||||||
|
|
||||||
(defn- translate-errors
|
(defn- translate-errors
|
||||||
|
@ -63,7 +62,7 @@
|
||||||
(reduce-kv (fn [acc key val]
|
(reduce-kv (fn [acc key val]
|
||||||
(if (string? (:message val))
|
(if (string? (:message val))
|
||||||
(assoc acc key val)
|
(assoc acc key val)
|
||||||
(->> (translate-error-type (:type val))
|
(->> (translate-error-type (:code val))
|
||||||
(assoc val :message)
|
(assoc val :message)
|
||||||
(assoc acc key))))
|
(assoc acc key))))
|
||||||
{} errors))
|
{} errors))
|
||||||
|
@ -73,8 +72,8 @@
|
||||||
(let [[state update-state] (mf/useState {:data (if (fn? initial) (initial) initial)
|
(let [[state update-state] (mf/useState {:data (if (fn? initial) (initial) initial)
|
||||||
:errors {}
|
:errors {}
|
||||||
:touched {}})
|
:touched {}})
|
||||||
[errors' clean-data] (validate spec (:data state))
|
[errors clean-data] (validate spec (:data state))
|
||||||
errors (merge (translate-errors errors')
|
errors (merge (translate-errors errors)
|
||||||
(:errors state))]
|
(:errors state))]
|
||||||
(-> (assoc state
|
(-> (assoc state
|
||||||
:errors errors
|
:errors errors
|
||||||
|
@ -83,10 +82,9 @@
|
||||||
(impl-mutator update-state))))
|
(impl-mutator update-state))))
|
||||||
|
|
||||||
(defn on-input-change
|
(defn on-input-change
|
||||||
[{:keys [data] :as form}]
|
[{:keys [data] :as form} field]
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [target (dom/get-target event)
|
(let [target (dom/get-target event)
|
||||||
field (keyword (.-name target))
|
|
||||||
value (dom/get-value target)]
|
value (dom/get-value target)]
|
||||||
(swap! form (fn [state]
|
(swap! form (fn [state]
|
||||||
(-> state
|
(-> state
|
||||||
|
@ -94,22 +92,28 @@
|
||||||
(update :errors dissoc field)))))))
|
(update :errors dissoc field)))))))
|
||||||
|
|
||||||
(defn on-input-blur
|
(defn on-input-blur
|
||||||
[{:keys [touched] :as form}]
|
[{:keys [touched] :as form} field]
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [target (dom/get-target event)
|
(let [target (dom/get-target event)]
|
||||||
field (keyword (.-name target))]
|
|
||||||
(when-not (get touched field)
|
(when-not (get touched field)
|
||||||
(swap! form assoc-in [:touched field] true)))))
|
(swap! form assoc-in [:touched field] true)))))
|
||||||
|
|
||||||
;; --- Helper Components
|
;; --- Helper Components
|
||||||
|
|
||||||
(mf/defc error-input
|
(mf/defc field-error
|
||||||
[{:keys [form field] :as props}]
|
[{:keys [form field type]
|
||||||
|
:or {only (constantly true)}
|
||||||
|
:as props}]
|
||||||
(let [touched? (get-in form [:touched field])
|
(let [touched? (get-in form [:touched field])
|
||||||
error (get-in form [:errors field])]
|
{:keys [message code] :as error} (get-in form [:errors field])]
|
||||||
(when (and touched? error)
|
(when (and touched? error
|
||||||
|
(cond
|
||||||
|
(nil? type) true
|
||||||
|
(ifn? type) (type (:type error))
|
||||||
|
(keyword? type) (= (:type error) type)
|
||||||
|
:else false))
|
||||||
[:ul.form-errors
|
[:ul.form-errors
|
||||||
[:li {:key (:type error)} (tr (:message error))]])))
|
[:li {:key code} (tr message)]])))
|
||||||
|
|
||||||
(defn error-class
|
(defn error-class
|
||||||
[form field]
|
[form field]
|
||||||
|
@ -119,50 +123,20 @@
|
||||||
|
|
||||||
;; --- Additional Validators
|
;; --- Additional Validators
|
||||||
|
|
||||||
(def non-empty-string
|
(def string (assoc st/string :message "errors.should-be-string"))
|
||||||
{:message "errors.empty-string"
|
(def number (assoc st/number :message "errors.should-be-number"))
|
||||||
:optional true
|
(def number-str (assoc st/number-str :message "errors.should-be-number"))
|
||||||
:validate #(not (str/empty? %))})
|
(def integer (assoc st/integer :message "errors.should-be-integer"))
|
||||||
|
(def integer-str (assoc st/integer-str :message "errors.should-be-integer"))
|
||||||
(def string (assoc stt/string :message "errors.should-be-string"))
|
(def required (assoc st/required :message "errors.required"))
|
||||||
(def number (assoc stt/number :message "errors.should-be-number"))
|
(def email (assoc st/email :message "errors.should-be-valid-email"))
|
||||||
(def number-str (assoc stt/number-str :message "errors.should-be-number"))
|
(def uuid (assoc st/uuid :message "errors.should-be-uuid"))
|
||||||
(def integer (assoc stt/integer :message "errors.should-be-integer"))
|
(def uuid-str (assoc st/uuid-str :message "errors.should-be-valid-uuid"))
|
||||||
(def integer-str (assoc stt/integer-str :message "errors.should-be-integer"))
|
|
||||||
(def required (assoc stt/required :message "errors.required"))
|
|
||||||
(def email (assoc stt/email :message "errors.should-be-valid-email"))
|
|
||||||
(def uuid (assoc stt/uuid :message "errors.should-be-uuid"))
|
|
||||||
(def uuid-str (assoc stt/uuid-str :message "errors.should-be-valid-uuid"))
|
|
||||||
|
|
||||||
;; DEPRECATED
|
;; DEPRECATED
|
||||||
|
|
||||||
;; --- Form Validation Api
|
;; --- Form Validation Api
|
||||||
|
|
||||||
(defn- interpret-problem
|
|
||||||
[acc {:keys [path pred val via in] :as problem}]
|
|
||||||
(cond
|
|
||||||
(and (empty? path)
|
|
||||||
(= (first pred) 'contains?))
|
|
||||||
(let [path (conj path (last pred))]
|
|
||||||
(update-in acc path assoc :missing))
|
|
||||||
|
|
||||||
(and (seq path)
|
|
||||||
(= 1 (count path)))
|
|
||||||
(update-in acc path assoc :invalid)
|
|
||||||
|
|
||||||
:else acc))
|
|
||||||
|
|
||||||
;; (defn validate
|
|
||||||
;; [spec data]
|
|
||||||
;; (when-not (s/valid? spec data)
|
|
||||||
;; (let [report (s/explain-data spec data)]
|
|
||||||
;; (reduce interpret-problem {} (::s/problems report)))))
|
|
||||||
|
|
||||||
;; (defn valid?
|
|
||||||
;; [spec data]
|
|
||||||
;; (s/valid? spec data))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Form Specs and Conformers
|
;; --- Form Specs and Conformers
|
||||||
|
|
||||||
(def ^:private email-re
|
(def ^:private email-re
|
||||||
|
|
Loading…
Add table
Reference in a new issue