Signature widget working in app.
This commit is contained in:
parent
f70d8ee2ff
commit
b42a593e34
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?)]]))
|
||||
|
|
|
@ -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)))]]]))
|
||||
|
|
|
@ -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)])
|
||||
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue