(ns auto-ap.views.components.vendor-dialog (:require [re-frame.core :as re-frame] [auto-ap.views.utils :refer [dispatch-event horizontal-field bind-field with-user active-when]] [auto-ap.views.components.modal :refer [action-modal]] [auto-ap.views.components.address :refer [address-field]] [auto-ap.views.components.typeahead :refer [typeahead-entity]] [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]] [auto-ap.events :as events] [clj-fuzzy.metrics :refer [jaccard jaro-winkler]] [clojure.spec.alpha :as s] [clojure.string :as str] [auto-ap.entities.vendors :as entity] [auto-ap.entities.contact :as contact] [auto-ap.subs :as subs] [auto-ap.forms :as forms])) (defn ngrams [text len] (mapv #(.toLowerCase (str/join "" %)) (partition len 1 text))) (defn partial-matches-vendor? [vendor vendors] (when (> (count (:name vendor)) 5) (let [text (.toLowerCase (:name vendor))] (->> vendors (filter #(not= (:id %) (:id vendor))) (map :name) (mapcat (fn [v] (mapv (fn [n] [v (jaro-winkler text n ) n text]) (ngrams v (count text))))) (filter #(> (second %) 0.9)) #_(map (fn [x] (doto x println))) (map first) first)))) (re-frame/reg-sub ::selected-override (fn [db] (::selected-override db))) (re-frame/reg-event-db ::override-selected (fn [db [_ which]] (assoc db ::selected-override which))) (re-frame/reg-event-db ::override-added (fn [db [_ z]] (println "selected" z) (assoc db ::selected-override "101"))) (re-frame/reg-event-fx ::started (fn [{:keys [db]} [_ vendor]] {:db (-> db (forms/start-form ::vendor-form (-> vendor (update :hidden #(if (nil? %) false %)) (update :default-account (fn [da] @(re-frame/subscribe [::subs/account (:id da)])))))) :dispatch [::events/modal-status ::dialog {:visible? true}]})) (re-frame/reg-event-fx ::save-complete [(forms/triggers-stop ::vendor-form)] (fn [{:keys [db]} [_ {vendor :upsert-vendor} ]] {:dispatch [::events/modal-completed ::dialog ] :db (-> db (assoc-in [:vendors (:id vendor)] vendor))})) (re-frame/reg-event-fx ::save [with-user (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)] (fn [{:keys [user] {:keys [data]} :db} _] (when (s/valid? ::entity/vendor data) { :graphql {:token user :query-obj {:venia/operation {:operation/type :mutation :operation/name "UpsertVendor"} :venia/queries [{:query/data [:upsert-vendor {:vendor (-> data (assoc :default-account-id (:id (:default-account data))) (dissoc :default-account))} events/vendor-query]}]} :on-success [::save-complete] :on-error [::forms/save-error ::vendor-form]}}))) (defn form-content [{:keys [data change-event selected-override]}] (let [chooseable-expense-accounts @(re-frame/subscribe [::subs/chooseable-expense-accounts]) root-path (cond-> [] selected-override (into [:overrides selected-override]))] [:div [horizontal-field [:label.label "Name"] [:div.control [bind-field [:input.input {:type "text" :auto-focus true :field (conj root-path :name) :spec ::entity/name :event change-event :subscription data}]]]] [horizontal-field [:label.label "Print Checks As"] [:div.control [bind-field [:input.input {:type "text" :field (conj root-path :print-as) :spec ::entity/print-as :event change-event :subscription data}]]]] [horizontal-field [:label.label "Terms"] [:div.control [bind-field [:input.input {:type "number" :step "1" :field (conj root-path :terms) :spec ::entity/terms :event change-event :subscription data}]]]] [horizontal-field [:label.label "Hidden"] [:div.control [bind-field [:input {:type "checkbox" :field (conj root-path :hidden) :spec ::entity/hidden :event change-event :subscription data}]]]] [:h2.subtitle "Expense Accounts"] [horizontal-field [:label.label "Default"] [bind-field [typeahead-entity {:matches chooseable-expense-accounts :match->text (fn [x ] (str (:numeric-code x) " - " (:name x))) :type "typeahead-entity" :field (conj root-path :default-account) :event change-event :subscription data}]]] [:h2.subtitle "Address"] [address-field {:field (conj root-path :address) :event change-event :subscription data}] [:h2.subtitle "Contact"] [horizontal-field [:label.label "Primary"] [:div.control.has-icons-left [bind-field [:input.input.is-expanded {:type "text" :field (into root-path [:primary-contact :name]) :spec ::contact/name :event change-event :subscription data}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]] [:div.control.has-icons-left [:span.icon.is-small.is-left [:i.fa.fa-envelope]] [bind-field [:input.input {:type "email" :field (into root-path [:primary-contact :email]) :spec ::contact/email :event change-event :subscription data}]]] [:div.control.has-icons-left [bind-field [:input.input {:type "phone" :field (into root-path [:primary-contact :phone]) :spec ::contact/phone :event change-event :subscription data}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]] [horizontal-field [:label.label "Secondary"] [:div.control.has-icons-left [bind-field [:input.input.is-expanded {:type "text" :field (into root-path [:secondary-contact :name]) :spec ::contact/name :event change-event :subscription data}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]] [:div.control.has-icons-left [:span.icon.is-small.is-left [:i.fa.fa-envelope]] [bind-field [:input.input {:type "email" :field (into root-path [:secondary-contact :email]) :spec ::contact/email :event change-event :subscription data}]]] [:div.control.has-icons-left [bind-field [:input.input {:type "phone" :field (into root-path [:secondary-contact :phone]) :spec ::contact/phone :event change-event :subscription data}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]] [horizontal-field [:label.label "Invoice Reminders"] [:div.control [:label.radio [bind-field [:input {:type "radio" :name "schedule" :value "Weekly" :field (conj root-path :invoice-reminder-schedule) :spec ::entity/invoice-reminder-schedule :event change-event :subscription data}]] " Send weekly"] [:label.radio [bind-field [:input {:type "radio" :name "schedule" :value "Never" :field (conj root-path :invoice-reminder-schedule) :spec ::entity/invoice-reminder-schedule :event change-event :subscription data}]] " Never"]]]])) (defn vendor-dialog [{:keys [save-event] }] (let [clients @(re-frame/subscribe [::subs/clients]) all-vendors @(re-frame/subscribe [::subs/vendors]) selected-override @(re-frame/subscribe [::selected-override]) {:keys [data error ] :as f} @(re-frame/subscribe [::forms/form ::vendor-form]) change-event [::forms/change ::vendor-form]] [action-modal {:id ::dialog :title [:span (if (:id data) (str "Edit " (or (:name data) "")) (str "Add " (or (:name data) ""))) (when error [:span.icon.has-text-danger [:i.fa.fa-exclamation-triangle]])] :status-from ::vendor-form #_#_:warning (when-let [vendor (partial-matches-vendor? data all-vendors)] (str "Are you sure you don't mean " vendor "?")) :action-text "Save" :save-event [::save] :can-submit? (s/valid? ::entity/vendor data)} [:div [:div.level [:div.level-left [:div.tabs [:ul [:li {:class (active-when selected-override = nil)} [:a {:on-click (dispatch-event [::override-selected nil])} "All"]] [:li {:class (active-when selected-override = "123")} [:a {:on-click (dispatch-event [::override-selected "123"])} [:span "Campbell Brewing Company"]]] ]]] [:div.level-right [drop-down {:id [::add-new-override] :is-right? true :header [:a.button.badge {:aria-haspopup true :on-click (dispatch-event [::events/toggle-menu [::add-new-override]])} [:span.icon [:span.icon-add]] [:span "Override"]]} [drop-down-contents [:div [:div.field [:label.label "Client"] [typeahead-entity {:matches clients :match->text :name :type "typeahead-entity" :event [:override-added]}]]]]]]] [form-content {:data data :change-event change-event :selected-override selected-override}]]]))