add initial lists UI with list/add/delete and some update support

currently doesn't show cards contained in any lists nor allow adding
or removing of cards to/from any lists
This commit is contained in:
Gered 2016-07-31 15:58:53 -04:00
parent ab66ca9a0a
commit a669221ad7
6 changed files with 196 additions and 1 deletions

View file

@ -25,6 +25,24 @@ html, body {
margin: 20px 0;
}
.context
{
position: relative;
}
.absolute {
position: absolute;
}
.absolute.top-right {
top: 0;
right: 0;
}
.large-font {
font-size: 20px;
}
div.popover.card-image {
max-width: none;
}

View file

@ -19,6 +19,11 @@
[]
(not (nil? @user-profile)))
(defn can-modify-data?
[]
(or (authenticated?)
(not (auth-required?))))
(defn get-username
[]
(:username @user-profile))

View file

@ -8,8 +8,10 @@
[mtgcoll.client.page :as page]
[mtgcoll.client.routes.cards :as cards]
[mtgcoll.client.routes.collection :as collection]
[mtgcoll.client.routes.lists :as lists]
[mtgcoll.client.routes.sets :as sets]
[mtgcoll.client.routes.stats :as stats]))
[mtgcoll.client.routes.stats :as stats]
[mtgcoll.client.utils :refer [parse-int]]))
(defroute "/" [] (page/page [collection/owned-cards-list]))
(defroute "/owned" [] (page/page [collection/owned-cards-list]))
@ -17,6 +19,8 @@
(defroute "/sets" [] (page/page [sets/sets-list]))
(defroute "/set/:code" [code] (page/page [sets/set-details code]))
(defroute "/card/:id" [id] (page/page [cards/card-details id 0]))
(defroute "/lists" [] (page/page [lists/lists-list]))
(defroute "/list/:id" [id] (page/page [lists/list-details (parse-int id)]))
(defroute "/stats" [] (page/page [stats/stats-page]))
(defroute "*" [] (page/barebones-page [:div "not found"]))

View file

@ -39,6 +39,7 @@
[bs/Nav
[bs/NavItem {:href "#/owned" :active (= :owned active-breadcrumb)} "Owned"]
[bs/NavItem {:href "#/all" :active (= :all active-breadcrumb)} "All"]
[bs/NavItem {:href "#/lists" :active (= :lists active-breadcrumb)} "Lists"]
[bs/NavItem {:href "#/sets" :active (= :sets active-breadcrumb)} "Sets"]
[bs/NavItem {:href "#/stats" :active (= :stats active-breadcrumb)} "Statistics"]]
(if (auth/auth-required?)

View file

@ -0,0 +1,162 @@
(ns mtgcoll.client.routes.lists
(:require
[clojure.string :as string]
[reagent.core :as r]
[views.reagent.client.component :as vc :refer [view-cursor] :refer-macros [defvc]]
[webtools.reagent.bootstrap :as bs]
[webtools.cljs.ajax :as ajax]
[webtools.cljs.utils :refer [->url redirect!]]
[mtgcoll.client.auth :as auth]
[mtgcoll.client.page :refer [set-active-breadcrumb! set-error!]]
[mtgcoll.client.utils :refer [get-field-value]]
[mtgcoll.client.components.utils :refer [click-to-edit-textarea markdown confirm-modal]]))
(defn create-list-form
[visibility-atom]
(let [values (r/atom nil)
error (r/atom nil)
on-close (fn []
(reset! values nil)
(reset! error nil)
(reset! visibility-atom false))
on-submit (fn []
(reset! error nil)
(let [{:keys [name requires-qualities? public?]} @values]
(if (string/blank? name)
(reset! error "List name must be provided.")
(ajax/POST (->url "/lists/add")
:params {:name name :public? public? :requires-qualities? requires-qualities?}
:on-error #(reset! error "Could not create list. Make sure list name is unique.")
:on-success (fn [response]
(let [new-list-id (:id (clojure.walk/keywordize-keys response))]
(redirect! (str "#/list/" new-list-id))))))))
on-key-up #(if (= 13 (.-keyCode %))
(on-submit))]
(fn []
[bs/Modal
{:show (boolean @visibility-atom)
:on-hide #(reset! visibility-atom false)}
[bs/Modal.Header [bs/Modal.Title "Create List"]]
[bs/Modal.Body
(if @error
[bs/Alert {:bsStyle "danger"} @error])
[bs/Form {:horizontal true}
[bs/FormGroup
[bs/Col {:class "text-right" :sm 4} [bs/ControlLabel "List Name"]]
[bs/Col {:sm 6}
[bs/FormControl
{:type "text"
:default-value (or (:name @values) "")
:on-change #(swap! values assoc :name (get-field-value %))
:on-key-up on-key-up}]]]
[bs/FormGroup
[bs/Col {:class "text-right" :sm 4} [bs/ControlLabel "Card Qualities"]]
[bs/Col {:sm 6}
[bs/Checkbox
{:on-change (fn [e]
(let [checked? (-> e .-target .-checked)]
(swap! values assoc :requires-qualities?
(if checked? true false))))}]]]
[bs/FormGroup
[bs/Col {:class "text-right" :sm 4} [bs/ControlLabel "Public"]]
[bs/Col {:sm 6}
[bs/Checkbox
{:on-change (fn [e]
(let [checked? (-> e .-target .-checked)]
(swap! values assoc :public?
(if checked? true false))))}]]]]]
[bs/Modal.Footer
[bs/Button {:bsStyle "primary" :on-click on-submit} "OK"]
[bs/Button {:on-click on-close} "Cancel"]]])))
(defvc lists-list
[]
(let [show-create-form? (r/atom false)]
(fn []
(let [lists (view-cursor :lists-list (auth/get-username))]
(set-active-breadcrumb! :lists)
[:div.context
(if (auth/can-modify-data?)
[:div.absolute.top-right
[bs/Button {:on-click #(reset! show-create-form? true)} "Create List"]])
[bs/PageHeader "Lists"]
[create-list-form show-create-form?]
(if (vc/loading? lists)
[:div "Loading ..."]
(if (empty? @lists)
[bs/Alert {:bsStyle "warning"} "No lists found."]
[bs/Table
{:bordered true :striped true :condensed true :hover true}
[:thead
[:tr
[:th "Name"]
[:th "Cards"]]]
[:tbody
(doall
(map
(fn [{:keys [id name is_public]}]
^{:key id}
[:tr
(if (and (auth/authenticated?) (not is_public))
{:class "warning"})
[:td [:a {:href (->url "#/list/" id)} [:div name]]]
[:td "--"]])
@lists))]]))]))))
(defn on-update-list-notes!
[list-id notes]
(ajax/POST (->url "/lists/update-note")
:params {:list-id list-id :note notes}
:on-error #(set-error! "Server error while updating list notes.")))
(defn change-list-visibility!
[list-id public?]
(ajax/POST (->url "/lists/update-visibility")
:params {:list-id list-id :public? public?}
:on-error #(set-error! "Server error while updating list public/private visibility.")))
(defn delete-list!
[list-id]
(ajax/POST (->url "/lists/remove")
:params {:list-id list-id}
:on-error #(set-error! "Server error while deleting the list.")
:on-success #(redirect! "#/lists")))
(defvc list-details
[list-id]
(let [show-delete-confirm? (r/atom false)]
(fn [list-id]
(set-active-breadcrumb! :lists)
(let [list (view-cursor :list-info list-id (auth/get-username))]
(cond
(and (not (vc/loading? list))
(nil? @list))
[:div "List not found."]
(vc/loading? list)
[:div "Loading ..."]
:else
[:div.context
(if (auth/can-modify-data?)
[:div.absolute.top-right
(if-not (:is_public @list) [:span.large-font [bs/Label {:bsStyle "danger"} "Private"] " "])
(if (:require_qualities @list) [:span.large-font [bs/Label {:bsStyle "primary"} "Card Qualities"] " "])
[bs/DropdownButton {:title "Actions"}
[bs/MenuItem {:on-click #(js/alert "TODO: Copy to Owned")} "Copy to Owned"]
[bs/MenuItem {:on-click #(change-list-visibility! list-id (not (:is_public @list)))} (if (:is_public @list) "Make Private" "Make Public")]
[bs/MenuItem {:on-click #(reset! show-delete-confirm? true)} "Delete"]]])
[bs/PageHeader (:name @list)]
(if (auth/can-modify-data?)
[click-to-edit-textarea
(:notes @list)
{:placeholder "List Notes"
:rows 10
:on-update #(on-update-list-notes! list-id %)
:render markdown}]
[markdown (:notes @list)])
[confirm-modal
show-delete-confirm?
{:title "Confirm Delete"
:body [:p "Are you sure you want to delete the " [:strong (:name @list)] " list? This cannot be undone."]
:on-yes #(delete-list! list-id)}]])))))

View file

@ -63,3 +63,8 @@
[n & [number-only?]]
(if n
(str (if-not number-only? "$") (pprint/cl-format nil "~,2f" n))))
(defn parse-int
[s]
(if s
(js/parseInt (string/trim s) 10)))