better upload experience, without dropzone

This commit is contained in:
2024-06-01 12:26:41 -07:00
parent 495751df48
commit 3db0629895
2 changed files with 156 additions and 116 deletions

File diff suppressed because one or more lines are too long

View File

@@ -328,20 +328,10 @@
[:force-location {:optional true} [:force-location {:optional true}
[:maybe [:string {:decode/string strip :min 2 :max 2}]]]]) [:maybe [:string {:decode/string strip :min 2 :max 2}]]]])
(def grid-page (defn upload-form [{:keys [form-params form-errors] :as request}]
(helper/build {:id "entity-table"
:nav com/main-aside-nav
:check-boxes? true
:page-specific-nav filters
:above-grid (fn [{:keys [form-params form-errors] :as request}]
(com/content-card {} (com/content-card {}
[:div.px-4.py-3.space-y-4 [:div.px-4.py-3.space-y-4
{:x-data (hx/json {"a" false})
"@started" "a=true"
"@completed" "a=false"}
#_[:div.bg-red-50.p-8 {:x-show "a"} "HELLO THERE"]
[:div.flex.justify-between.items-center [:h1.text-2xl.mb-3.font-bold "Import new invoices"] [:div.flex.justify-between.items-center [:h1.text-2xl.mb-3.font-bold "Import new invoices"]
@@ -353,9 +343,11 @@
[:form [:form
{:action (bidi/path-for ssr-routes/only-routes {:hx-post (bidi/path-for ssr-routes/only-routes
::route/import-file) ::route/import-file)
:method "POST" :hx-encoding "multipart/form-data"
:hx-target "#page-notification"
:hx-swap "outerHTML"
:id "upload"} :id "upload"}
(fc/start-form (fc/start-form
form-params form-errors form-params form-errors
@@ -390,29 +382,45 @@
:url (bidi/path-for ssr-routes/only-routes :vendor-search) :url (bidi/path-for ssr-routes/only-routes :vendor-search)
:value (fc/field-value) :value (fc/field-value)
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))]) :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))])
[:div {":class" "a ? 'htmx-request' : ''"}
[:div.bg-blue-100.border-2.border-dashed.rounded-lg.border-blue-300.p-4.w-full.text-center.cursor-pointer.h-64.flex.items-center.justify-center.text-blue-700.text-lg
[:div.htmx-indicator.flex.items-center
(svg/spinner {:class "inline w-4 h-4 text-blue-700"})
[:div.ml-3 "Loading..."]]
[:div.htmx-indicator-hidden "Drop files to upload here"]]
[:script
(hiccup/raw
"ezcater_dropzone = new Dropzone (\"#upload\", {
init: function() {
this.on('addedfile', () => { document.getElementById('upload').dispatchEvent(new Event('started', {bubbles:true})) });
},
success: function (file, response) { [:div.border-2.border-dashed.rounded-lg.p-4.w-full.text-center.cursor-pointer.h-64.flex.items-center.justify-center.text-lg.relative
document.getElementById(\"page-notification\").innerHTML = response; { :x-data (hx/json {"files" nil
document.getElementById(\"page-notification\").style[\"display\"] = \"block\"; "hovering" false})
document.getElementById('upload').dispatchEvent(new Event('completed', {bubbles:true})) ":class" "{'bg-blue-100': !hovering,
document.body.dispatchEvent(new Event('invalidated')); 'border-blue-300': !hovering,
}, 'text-blue-700': !hovering,
acceptedFiles: '.xls,.xlsx,.pdf,.csv', 'bg-green-100': hovering,
disablePreviews: true 'border-green-300': hovering,
}) 'text-green-700': hovering
")]]]])) }"
:x-ref "box"}
[:input {:type "file"
:name "file"
:multiple "multiple"
:class "absolute inset-0 m-0 p-0 w-full h-full outline-none opacity-0",
:x-on:change "files = $event.target.files; console.log($event.target.files);",
:x-on:dragover "hovering = true",
:x-on:dragleave "hovering = false",
:x-on:drop "hovering = false"}]
[:div.flex.flex-col.space-2
[:div
[:ul {:x-show "files != null"}
[:template {:x-for "f in files" }
[:li (com/pill {:color :primary :x-text "f.name"}) ]
]]]
[:div.htmx-indicator-hidden "Drop files to upload here"]]]
(com/button {:color :primary :class "w-32 mt-3"} "Upload")]]))
(def grid-page
(helper/build {:id "entity-table"
:nav com/main-aside-nav
:check-boxes? true
:page-specific-nav filters
:above-grid upload-form
:fetch-page fetch-page :fetch-page fetch-page
:oob-render :oob-render
@@ -550,7 +558,7 @@
(count ids)) (count ids))
:invalidated "invalidated"})}))) :invalidated "invalidated"})})))
(defn upload-invoices [{{files :file #_(defn upload-invoices [{{files :file
files-2 "file" files-2 "file"
client :client client :client
client-2 "client" client-2 "client"
@@ -650,7 +658,8 @@
(defn import-uploaded-invoice [user imports] (defn import-uploaded-invoice [user imports]
(alog/info ::importing-uploaded :count (count imports)) (alog/info ::importing-uploaded :count (count imports)
:bc (or user "NOO"))
(let [potential-invoices (->> imports (let [potential-invoices (->> imports
(map import->invoice) (map import->invoice)
(map #(validate-invoice % user)) (map #(validate-invoice % user))
@@ -669,10 +678,7 @@
{}))) {})))
tx))) tx)))
(defn import-file [request] (defn import-internal [tempfile filename force-client force-location force-vendor identity]
(let [{:keys [file force-client force-vendor force-location]} (:multipart-params request)
{:keys [filename tempfile]} file]
(mu/with-context {:parsing-file filename} (mu/with-context {:parsing-file filename}
(try (try
(let [extension (last (str/split (.getName (io/file filename)) #"\.")) (let [extension (last (str/split (.getName (io/file filename)) #"\."))
@@ -692,17 +698,51 @@
:source-url (str "https://" (:data-bucket env) :source-url (str "https://" (:data-bucket env)
"/" "/"
s3-location))))] s3-location))))]
(import-uploaded-invoice (:identity request) imports)) (import-uploaded-invoice identity imports))
(html-response [:div.bg-primary-50.p-4.text-primary-700.rounded-lg "Invoices have been successfully uploaded."])
(catch Exception e (catch Exception e
(alog/warn ::couldnt-import-upload (alog/warn ::couldnt-import-upload
:error e) :error e)
(html-response [:div.bg-red-50.p-4.text-red-700.rounded-lg (.getMessage e)]) (throw e)))))
#_{:status 400
:body (pr-str {:message (.getMessage e) (defn import-file [request]
:error (.toString e) #_(html-response [:div])
:data (ex-data e)}) (let [{:keys [file force-client force-vendor force-location]} (:multipart-params request)
:headers {"Content-Type" "application/edn"}}))))) file (if (vector? file)
file
[file])
results (reduce
(fn [result {:keys [filename tempfile]}]
(try
(import-internal tempfile filename force-client force-location force-vendor (:identity request) )
(update result :results conj {:filename filename
:response "success!"})
(catch Exception e
(-> result
(assoc :error? true)
(update :results conj {:filename filename
:response (.getMessage e)})))))
{:error? false :results []}
file)]
(html-response [:div#page-notification.p-4.rounded-lg
{:class (if (:error? results)
"bg-red-50 text-red-700"
"bg-primary-50 text-primary-700")}
[:table
(for [r (:results results)]
[:tr
[:td.p-2.border
{:class (if (:error? results)
"bg-red-50 text-red-700 border-red-300"
"bg-primary-50 text-primary-700 border-green-500")}
(:filename r)]
[:td.p-2.border
{:class (if (:error? results)
"bg-red-50 text-red-700 border-red-300"
"bg-primary-50 text-primary-700 border-green-500")}
(:response r)]])]]
:headers
{"hx-trigger" "invalidated"})
))
#_(defn wrap-test [handler] #_(defn wrap-test [handler]
(fn [request] (fn [request]