Files
integreat/src/cljs/auto_ap/views/pages/import_invoices.cljs
2021-01-06 08:16:58 -08:00

229 lines
10 KiB
Clojure

(ns auto-ap.views.pages.import-invoices
(:require [re-frame.core :as re-frame]
[reagent.core :as reagent]
[reagent.dom :as rdom]
[auto-ap.events :as events]
[auto-ap.subs :as subs]
[auto-ap.entities.clients :as client]
[auto-ap.views.components.layouts :refer [side-bar-layout]]
[auto-ap.views.components.invoices.side-bar :refer [invoices-side-bar]]
[auto-ap.views.utils :refer [dispatch-event bind-field with-user]]
[auto-ap.utils :refer [by]]
[auto-ap.entities.vendors :as vendor]
[auto-ap.views.components.typeahead :refer [typeahead-entity]]
[auto-ap.views.components.invoice-table :refer [invoice-table] :as invoice-table]
[cljs.reader :as edn]
[clojure.string :as str]
[vimsical.re-frame.cofx.inject :as inject]
[auto-ap.status :as status]
[vimsical.re-frame.fx.track :as track]
[dropzone :as dz]
[auto-ap.views.pages.data-page :as data-page]
[clojure.set :as set]
[auto-ap.effects.forward :as forward]))
(defn dropzone []
(let [client (re-frame/subscribe [::subs/client])
token (re-frame/subscribe [::subs/token])
vendor (reagent/atom nil)]
(reagent/create-class
{:display-name "dropzone"
:component-did-mount (fn [this]
(dz. (rdom/dom-node this)
(clj->js {:init (fn []
(let [^js/Dropzone t (js-this)]
(.on t "addedfiles"
(fn []
(re-frame/dispatch [::status/completed ::import])))
(.on t "success" (fn [_ files]
(re-frame/dispatch [::invalidated])))
(.on t "error" (fn [_ error]
(re-frame/dispatch [::status/error ::import [(edn/read-string error)]])))))
:paramName "file"
:headers {"Authorization" (str "Token " @token)}
:url (str "/api/invoices/upload"
(when-let [client-name (-> @client :id)]
(str "?client=" client-name)))
:previewsContainer "#dz-hidden"
:previewTemplate "<div class='dz-hidden-preview'></div>"})))
:reagent-render (fn []
{:key batch}
[:form.dz {:action "/api/invoices/upload"}
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force Location"]]
[:p.control
[:input.input {:name "location" :placeholder "SG" :size "4" :maxlength "2" :style {:display "inline"}}]]]
[:div.field.has-addons
[:p.control
[:a.button.is-static "Force vendor"]]
[:div.control {:style {:width "400px"}}
[typeahead-entity {:matches @(re-frame/subscribe [::subs/vendors])
:match->text :name
:name "vendor"
:type "typeahead"
:on-change (fn [v]
(reset! vendor v))
:value @vendor}]]
]
[:div.tile.notification
[:div.has-text-centered {:style {:padding "80px 0px" :width "100%"}}
[:span
[:span {:class "icon"}
[:i {:class "fa fa-cloud-download"}]]
"Drop any invoices you want to process here"]]]])})
))
(re-frame/reg-sub
::batch
(fn [db]
(-> db (::batch 0))))
(re-frame/reg-event-fx
::invalidated
(fn [{:keys [db]} [_ params]]
{:dispatch-n [[::params-change @(re-frame/subscribe [::data-page/params :import-invoices])]
[::data-page/reset-checked :import-invoices]]
:db (update db ::batch inc)}))
(re-frame/reg-event-fx
::mounted
(fn [cofx [_ params]]
{::track/register [{:id ::params
:subscription [::data-page/params :import-invoices]
:event-fn (fn [params]
[::params-change params])}
]
::forward/register [{:id ::received
:events #{::data-page/received}
:event-fn (fn [[_ _ {:keys [data]}]]
[::received data])}]}))
(re-frame/reg-event-fx
::unmounted
(fn [cofx [_ params]]
{::track/dispose {:id ::params}
:dispatch [::data-page/dispose :import-invoices]}))
(re-frame/reg-event-fx
::params-change
[with-user]
(fn [{:keys [user] } [_ params]]
{:graphql {:token user
:owns-state {:single [::data-page/page :import-invoices]}
:query-obj (invoice-table/query (assoc params :import-status "pending"))
:on-success (fn [result]
(let [result (set/rename-keys (first (:invoice-page result))
{:invoices :data})]
[::data-page/received :import-invoices result]))}}))
(re-frame/reg-event-fx
::received
(fn [_ [_ data]]
{:dispatch [::data-page/toggle-check :import-invoices (by :id data)]}))
(re-frame/reg-event-fx
::reject-invoices-clicked
(fn [{:keys [db]} [_ invoices on-success]]
{:graphql
{:token (-> db :user)
:owns-state {:single ::reject}
:query-obj {:venia/operation {:operation/type :mutation
:operation/name "RejectInvoices"}
:venia/queries [[:reject-invoices
{:invoices (vec invoices)}
[]]]}
:on-success [::invalidated]}}))
(re-frame/reg-event-fx
::approve-invoices-clicked
(fn [{:keys [db]} [_ invoices on-success]]
{:graphql
{:token (-> db :user)
:owns-state {:single ::approve}
:query-obj {:venia/operation {:operation/type :mutation
:operation/name "ApproveInvoices"}
:venia/queries [[:approve-invoices
{:invoices (vec invoices)}
[]]]}
:on-success [::invalidated]}}))
(re-frame/reg-event-fx
::approve-invoices
(fn [cofx [_ on-success]]
{:http {:method :post
:token (-> cofx :db :user)
:uri (str "/api/invoices/approve"
(when-let [client-id (:id @(re-frame/subscribe [::subs/client]))]
(str "?client=" client-id)))
:on-success on-success}}))
(defn approve-reject-button [checked]
[:div.buttons
[:button.button.is-primary {:on-click (dispatch-event [::approve-invoices-clicked checked])
:class (status/class-for @(re-frame/subscribe [::status/single ::approve]))
:disabled (or (not (boolean (seq checked)))
(status/disabled-for @(re-frame/subscribe [::status/single ::approve])))}
"Approve "
(when (> (count checked ))
(str
(count checked)
" invoices"))
[:span " "]]
[:button.button.is-warning {:on-click (dispatch-event [::reject-invoices-clicked checked])
:class (status/class-for @(re-frame/subscribe [::status/single ::reject]))
:disabled (or (not (boolean (seq checked)))
(status/disabled-for @(re-frame/subscribe [::status/single ::reject])))}
"Reject "
(when (> (count checked ))
(str
(count checked)
" invoices"))
[:span " "]]])
(def import-invoices-content
(with-meta
(fn []
(let [page @(re-frame/subscribe [::data-page/page :import-invoices])
batch @(re-frame/subscribe [::batch])
client (:id @(re-frame/subscribe [::subs/client]))]
^{:key (str client "-" batch)}
[:div
[:h1.title "Upload invoices"]
[status/status-notification {:statuses [[::status/single ::approve]
[::status/single ::reject]
[::status/single ::import]]}]
^{:key (str batch)}
[dropzone]
[:div.mb-4]
[:div {:class "card found-invoices",}
[:div {:class "card-header"}
[:span {:class "card-header-title"} "Found Invoices"]]
[:div {:class "card-content"}
[approve-reject-button (:checked-set page)]
(if (seq (:data (:data page)))
[invoice-table {:id :approved
:data-page :import-invoices
:overrides {:client (fn [row]
[:p (:name (:client row))
[:p [:i.is-size-7 (:client-identifier row)]]])}
:check-boxes true}]
[:span "No pending invoices"])]]]))
{:component-did-mount (fn []
(re-frame/dispatch-sync [::mounted]))
:component-will-unmount (fn []
(re-frame/dispatch-sync [::unmounted]))}))
(defn import-invoices-page []
[side-bar-layout {:side-bar [invoices-side-bar {}]
:main [import-invoices-content ]}])