Signature widget working in app.

This commit is contained in:
Simon Brooke 2018-07-02 21:55:28 +01:00
parent f70d8ee2ff
commit b42a593e34
11 changed files with 126 additions and 74 deletions

View file

@ -28,6 +28,12 @@ h1 {
margin-top: 0;
}
#signature-pad {
width: 300px;
border: thin solid white;
min-height: 150px;
}
/* desktops and laptops, primarily. Adapted to mouse; targets may be small */
@media all and (min-device-width: 1025px) {

View file

@ -164,7 +164,7 @@ th {
#main-container{
}
#back-link, .back-link {
.back-link {
min-width: 8em;
padding: 0.25em 1em;
background-color: gray;
@ -174,26 +174,26 @@ th {
border-bottom-right-radius: 0.5em;
}
#back-link:hover, #back-link:active, .back-link:hover, .back-link:active, {
.back-link:hover, .back-link:active, {
text-decoration: none;
background-color: rgb(160, 160, 160);
}
#back-link:hover::before, #back-link:active::before {
.back-link:hover::before, .back-link:active::before {
content: "< ";
}
#back-link-container {
.back-link-container {
float: left;
text-align: left;
}
#back-link-container, .big-link-container {
.back-link-container, .big-link-container {
font-size: 200%;
padding: 0.5em 0;
}
#back-link-container > #back-link:hover::before, #back-link-container > #back-link:active::before {
.back-link-container > .back-link:hover::before, .back-link-container > .back-link:active::before {
}

View file

@ -1,4 +1,13 @@
{% extends "base.html" %}
{% block head %}
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" type="text/css" href="css/yyy-common.css" />
<link rel="stylesheet" type="text/css" href="css/yyy-app.css" />
<link rel="stylesheet" type="text/css" href="css/spinner.css" />
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Archivo+Narrow" rel="stylesheet"/>
<title>{{site-title}}: {{title}}</title>
{% endblock %}
{% block whole-page %}
<div id="app">
<div class="splash-screen">

View file

@ -40,8 +40,8 @@
</header>
{% endblock %}
<div id="main-container" class="container">
<div id="back-link-container">
<a href="javascript:history.back()" id="back-link">Back</a>
<div class="back-link-container">
<a href="javascript:history.back()" class="back-link">Back</a>
</div>
<div id="big-links">
{% block big-links %}

View file

@ -15,8 +15,8 @@
[youyesyet.canvasser-app.ui-utils :as ui]
[youyesyet.canvasser-app.views.about :as about]
[youyesyet.canvasser-app.views.building :as building]
[youyesyet.canvasser-app.views.dwelling :as dwelling]
[youyesyet.canvasser-app.views.elector :as elector]
[youyesyet.canvasser-app.views.electors :as electors]
[youyesyet.canvasser-app.views.followup :as followup]
[youyesyet.canvasser-app.views.gdpr :as gdpr]
[youyesyet.canvasser-app.views.issue :as issue]
@ -56,8 +56,8 @@
(defn building-page []
(building/panel))
(defn electors-page []
(electors/panel))
(defn dwelling-page []
(dwelling/panel))
(defn elector-page []
(elector/panel))
@ -81,7 +81,7 @@
{:about #'about-page
:building #'building-page
:elector #'elector-page
:electors #'electors-page
:dwelling #'dwelling-page
:followup #'followup-page
:gdpr #'gdpr-page
:issues #'issues-page
@ -114,41 +114,49 @@
;; Routes
(secretary/set-config! :prefix "#")
(defn log-and-dispatch [arg]
(js/console.log (str "Dispatching " arg))
(rf/dispatch arg))
(secretary/defroute "/" []
(rf/dispatch [:set-active-page :map]))
(log-and-dispatch [:set-active-page :map]))
(secretary/defroute "/about" []
(rf/dispatch [:set-active-page :about]))
(log-and-dispatch [:set-active-page :about]))
(secretary/defroute "/electors/:dwelling" {dwelling-id :dwelling}
(rf/dispatch [:set-dwelling dwelling-id]))
(secretary/defroute "/dwelling" []
(log-and-dispatch [:set-active-page :dwelling]))
(secretary/defroute "/dwelling/:dwelling" {dwelling-id :dwelling}
(log-and-dispatch [:set-dwelling dwelling-id])
(log-and-dispatch [:set-active-page :dwelling]))
(secretary/defroute "/building/:address" {address-id :address}
(rf/dispatch [:set-address address-id]))
(log-and-dispatch [:set-address address-id]))
(secretary/defroute "/followup" []
(rf/dispatch [:set-active-page :followup]))
(log-and-dispatch [:set-active-page :followup]))
(secretary/defroute "/gdpr" []
(rf/dispatch [:set-active-page :gdpr]))
(log-and-dispatch [:set-active-page :gdpr]))
(secretary/defroute "/gdpr/:elector" {elector-id :elector}
(rf/dispatch [:set-elector-and-page {:elector-id elector-id :page :gdpr}]))
(log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :gdpr}]))
(secretary/defroute "/issues" []
(rf/dispatch [:set-active-page :issues]))
(log-and-dispatch [:set-active-page :issues]))
(secretary/defroute "/issues/:elector" {elector-id :elector}
(rf/dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
(log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
(secretary/defroute "/issue/:issue" {issue :issue}
(rf/dispatch [:set-and-go-to-issue issue]))
(log-and-dispatch [:set-and-go-to-issue issue]))
(secretary/defroute "/map" []
(rf/dispatch [:set-active-page :map]))
(log-and-dispatch [:set-active-page :map]))
(secretary/defroute "/set-intention/:elector/:intention" {elector-id :elector intention :intention}
(rf/dispatch [:set-intention {:elector-id elector-id :intention intention}]))
(log-and-dispatch [:set-intention {:elector-id elector-id :intention intention}]))
;; -------------------------
;; History
@ -173,3 +181,4 @@
(load-interceptors!)
(hook-browser-navigation!)
(mount-components))

View file

@ -36,18 +36,33 @@
(merge state {:error nil :feedback nil}))
(defn coerce-to-number [v]
(if (number? v) v
(try
(read-string (str v))
(catch js/Object any
(js/console.log (str "Could not coerce '" v "' to number: " any))))))
(defn get-elector
"Return the elector at this address (or the current address if not specified)
with this id."
([elector-id state]
(get-elector elector-id state (:address state)))
([elector-id state address]
(try
(first
(remove
nil?
(map
#(if (= elector-id (:id %)) %)
(:electors address))))))
#(if (= (coerce-to-number elector-id) (:id %)) %)
(:electors state))))
(catch js/Object _
(str
"Failed to find id '"
elector-id
"' among '"
(:electors state) "'")))))
(reg-event-db
@ -60,7 +75,7 @@
:send-intention
(fn [db [_ args]]
(let [intention (:intention args)
elector-id (:elector-id args)
elector-id (coerce-to-number (:elector-id args))
old-elector (first
(remove nil?
(map
@ -134,17 +149,19 @@
(reg-event-db
:set-address
(fn [db [_ address-id]]
(let [id (read-string address-id)
(let [id (coerce-to-number address-id)
address (first (remove nil? (map #(if (= id (:id %)) %) (:addresses db))))]
(if
(= (count (:dwellings address)) 1)
(assoc (clear-messages db)
:address address
:dwelling (first (:dwellings address))
:page :electors)
:electors (:electors (first (:dwellings address)))
:page :dwelling)
(assoc (clear-messages db)
:address address
:dwelling nil
:electors nil
:page :building)))))
@ -152,24 +169,28 @@
:set-consent-and-page
(fn [db [_ args]]
(let [page (:page args)
elector-id (read-string (:elector-id args))
elector-id (coerce-to-number (:elector-id args))
elector (get-elector elector-id db)]
(js/console.log (str "Setting page to " page ", consent to true for " elector))
(assoc (clear-messages db) :elector (assoc elector :consent true) :page page))))
(reg-event-db
:set-dwelling
(fn [db [_ dwelling-id]]
(let [id (read-string dwelling-id)
(let [id (coerce-to-number dwelling-id)
dwelling (first
(remove
nil?
(map
#(if (= id (:id %)) %)
(mapcat :dwellings (:addresses db)))))]
(assoc (clear-messages db) :dwelling dwelling :page :electors))))
(if dwelling
(assoc
(clear-messages db)
:dwelling dwelling
:electors (:electors dwelling)
:page :dwelling)))))
(reg-event-db
@ -183,7 +204,7 @@
:set-elector-and-page
(fn [db [_ args]]
(let [page (:page args)
elector-id (read-string (:elector-id args))
elector-id (coerce-to-number (:elector-id args))
elector (get-elector elector-id db)]
(js/console.log (str "Setting page to " page ", elector to " elector))
(assoc (clear-messages db) :elector elector :page page))))
@ -192,7 +213,7 @@
(reg-event-db
:set-elector
(fn [db [_ elector-id]]
(let [elector (get-elector (read-string elector-id) db)]
(let [elector (get-elector (coerce-to-number elector-id) db)]
(js/console.log (str "Setting elector to " elector))
(assoc (clear-messages db) :elector elector))))
@ -206,14 +227,14 @@
(reg-event-db
:set-latitude
(fn [db [_ issue]]
(assoc db :latitude issue)))
(fn [db [_ v]]
(assoc db :latitude (coerce-to-number v))))
(reg-event-db
:set-longitude
(fn [db [_ issue]]
(assoc db :longitude issue)))
(fn [db [_ v]]
(assoc db :longitude (coerce-to-number v))))
(reg-event-db

View file

@ -46,14 +46,14 @@
{:id 4 :name "Andy Anderson" :intention :yes}]}]}
{:id 2 :address "15 Imaginary Terrace, IM1 3TE" :latitude 55.8252354 :longitude -4.2572778
:dwellings [{:id 2
:electors [{:id 1 :name "Beryl Brown" :gender :female}
{:id 2 :name "Betty Black" :gender :female}]}]}
:electors [{:id 5 :name "Beryl Brown" :gender :female}
{:id 6 :name "Betty Black" :gender :female}]}]}
{:id 3 :address "17 Imaginary Terrace, IM1 3TE" :latitude 55.825166 :longitude -4.257026
:dwellings [{:id 3 :sub-address "Flat 1"
:electors [{:id 1 :name "Catriona Crathie" :gender :female :intention :yes}
{:id 2 :name "Colin Caruthers" :gender :male :intention :yes}
{:id 3 :name "Calum Crathie" :intention :yes}]}
:electors [{:id 7 :name "Catriona Crathie" :gender :female :intention :yes}
{:id 8 :name "Colin Caruthers" :gender :male :intention :yes}
{:id 9 :name "Calum Crathie" :intention :yes}]}
{:id 4 :sub-address "Flat 2"
:electors [{:id 1 :name "David Dewar" :gender :male :intention :no}]}]}]
;;; electors at the currently selected dwelling

View file

@ -34,21 +34,22 @@
([]
(back-link "javascript:history.back()"))
([target]
[:div.back-link-container {:id "back-link-container"}
[:a {:href target :id "back-link"} "Back"]]))
[:div.back-link-container {:key (gensym "back-link")}
[:a.back-link {:href target} "Back"]]))
(defn big-link
[text & {:keys [target handler]}]
[:div.big-link-container {:key target}
[:div.big-link-container {:key (gensym "big-link")}
[:a.big-link (merge
(if target {:href target}{})
(if handler {:on-click handler}))
(if handler {:on-click handler}{}))
text]])
(defn nav-link [uri title page collapsed?]
(let [selected-page (rf/subscribe [:page])]
(let [selected-page @(rf/subscribe [:page])]
[:li.nav-item
{:class (when (= page @selected-page) "active")}
{:class (when (= page selected-page) "active")
:key (gensym "nav-link")}
[:a.nav-link
{:href uri
:on-click #(reset! collapsed? true)} title]]))
@ -70,6 +71,6 @@
:on-click #(swap! collapsed? not)}]
[:menu.nav {:id "nav-menu" :class (if @collapsed? "hidden" "shown")}
(nav-link "#/map" "Map" :map collapsed?)
(nav-link "#/electors" "Electors" :electors collapsed?)
(nav-link "#/dwelling" "Electors" :dwelling collapsed?)
(nav-link "#/issues" "Issues" :issues collapsed?)
(nav-link "#/about" "About" :about collapsed?)]]))

View file

@ -48,7 +48,7 @@
[dwelling]
(ui/big-link
(:sub-address dwelling)
:target (str "#electors/" (:id dwelling))) )
(sort
#(< (:sub-address %1) (:sub-address %2))
:target (str "#/dwelling/" (:id dwelling))) )
(sort-by
:sub-address
(:dwellings address)))]]]))

View file

@ -1,13 +1,13 @@
(ns ^{:doc "Canvasser app electors in household panel."
:author "Simon Brooke"}
youyesyet.canvasser-app.views.electors
youyesyet.canvasser-app.views.dwelling
(:require [reagent.core :refer [atom]]
[re-frame.core :refer [reg-sub subscribe dispatch]]
[youyesyet.canvasser-app.ui-utils :as ui]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; youyesyet.canvasser-app.views.electors: electors view for youyesyet.
;;;; youyesyet.canvasser-app.views.dwelling: dweling view for youyesyet.
;;;;
;;;; This program is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License
@ -42,15 +42,20 @@
;;; 2. the elector's name;
;;; The mechanics of how this panel is laid out don't matter.
(defn go-to-gdpr-for-elector [elector]
(dispatch [:set-elector-and-page {:elector-id (:id elector)
:page :gdpr}]))
(defn gender-cell
[elector]
(let [gender (:gender elector)
image (if gender (name gender) "unknown")]
[:td {:key (str "gender-" (:id elector))}
[:a {:href (str "#gdpr/" (:id elector))}
[:img {:src (str "img/gender/" image ".png") :alt image
:on-click #(dispatch
[:set-elector-and-page {:elector-id (:id elector)
:page "gdpr"}])}]]))
;; :on-click #(go-to-gdpr-for-elector elector)
}]]]))
(defn genders-row
@ -63,10 +68,7 @@
(defn name-cell
[elector]
[:td {:key (str "name-" (:id elector))
:on-click #(dispatch
[:set-elector-and-page
{:elector-id (:id elector)
:page "gdpr"}])}
:on-click #(go-to-gdpr-for-elector elector)}
(:name elector)])

View file

@ -36,7 +36,6 @@
[:div
[:h1 "GDPR Consent"]
[:div.container {:id "main-container"}
(ui/back-link "#electors")
[:table
[:tbody
[:tr
@ -51,16 +50,21 @@
only against your electoral district, and not link it to you"]]]]
[:tr
[:td
[:canvas {:id "signature-pad" :style "width: 100%; min-width 300px; min-height 200px;"}]]]]]]
[:canvas {:id "signature-pad"}]]]]]]
(ui/back-link "#dwelling")
(ui/big-link "I consent"
:handler (fn [] (dispatch [:set-consent-and-page elector "#/gdpr"])))
:handler #(fn [] (dispatch [:set-consent-and-page {:elector-id (:id elector)
:page :elector}])))
;; TODO: need to save the signature
(ui/big-link "I DO NOT consent" :target "#elector")]))
(ui/big-link "I DO NOT consent"
:handler
#(fn [] (dispatch [:set-elector-and-page {:elector-id (:id elector)
:page :elector}])))]))
(defn gdpr-did-mount
[]
(js/SignaturePad (.getElementById js/document "signature-pad")))
(js/SignaturePad. (.getElementById js/document "signature-pad")))
(defn panel