Merge branch 'feature/reflow-app' into develop
This commit is contained in:
commit
53122b69b1
|
@ -362,7 +362,6 @@ th {
|
||||||
|
|
||||||
/* content of the current page in the Wiki - editable, provided by users. Within main-container */
|
/* content of the current page in the Wiki - editable, provided by users. Within main-container */
|
||||||
#content {
|
#content {
|
||||||
border: thin solid silver;
|
|
||||||
width: 80%;
|
width: 80%;
|
||||||
float: right;
|
float: right;
|
||||||
padding-bottom: 5em;
|
padding-bottom: 5em;
|
||||||
|
@ -396,7 +395,6 @@ th {
|
||||||
|
|
||||||
/* content of the current in the Wiki - editable, provided by users. Within main-container */
|
/* content of the current in the Wiki - editable, provided by users. Within main-container */
|
||||||
#content {
|
#content {
|
||||||
border: thin solid silver;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-bottom: 2em;
|
padding-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ SELECT DISTINCT request.*,
|
||||||
addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded,
|
addresses.address ||', '|| addresses.postcode ||', '|| visits.date AS visit_id_expanded,
|
||||||
request.issue_id as issue_id_expanded,
|
request.issue_id as issue_id_expanded,
|
||||||
request.method_id AS method_id_expanded,
|
request.method_id AS method_id_expanded,
|
||||||
visits.date
|
visits.date AS raised
|
||||||
FROM followuprequests as request,
|
FROM followuprequests as request,
|
||||||
ln_experts_issues_canvassers as expertise,
|
ln_experts_issues_canvassers as expertise,
|
||||||
canvassers as experts,
|
canvassers as experts,
|
||||||
|
@ -58,7 +58,7 @@ and request.visit_id = visits.id
|
||||||
and visits.address_id = addresses.id
|
and visits.address_id = addresses.id
|
||||||
and request.issue_id = expertise.issue_id
|
and request.issue_id = expertise.issue_id
|
||||||
and expertise.canvasser_id = :expert
|
and expertise.canvasser_id = :expert
|
||||||
ORDER BY visits.date desc
|
ORDER BY raised
|
||||||
|
|
||||||
--:name get-last-visit-by-canvasser :? :1
|
--:name get-last-visit-by-canvasser :? :1
|
||||||
--:doc returns the most recent visit record of the canvasser with the specified `:id`
|
--:doc returns the most recent visit record of the canvasser with the specified `:id`
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<!-- head: if you want entire custom head content, override this block. -->
|
<!-- head: if you want entire custom head content, override this block. -->
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<meta name="referrer" content="same-origin">
|
||||||
{% style "/css/yyy-common.css" %}
|
{% style "/css/yyy-common.css" %}
|
||||||
{% style "/css/yyy-site.css" %}
|
{% style "/css/yyy-site.css" %}
|
||||||
{% style "/css/spinner.css" %}
|
{% style "/css/spinner.css" %}
|
||||||
|
@ -27,9 +28,13 @@
|
||||||
<menu id="nav-menu" class="nav">
|
<menu id="nav-menu" class="nav">
|
||||||
<li class=""><a href="{{servlet-context}}/home">Home</a></li>
|
<li class=""><a href="{{servlet-context}}/home">Home</a></li>
|
||||||
<li class=""><a href="{{servlet-context}}/library">Library</a></li>
|
<li class=""><a href="{{servlet-context}}/library">Library</a></li>
|
||||||
|
{% if user %}
|
||||||
|
<li class=""><a href="{{servlet-context}}/roles">Roles</a></li>
|
||||||
|
<li class=""><a href="{{servlet-context}}/logout">Logout</a></li>
|
||||||
|
{% else %}
|
||||||
<li class=""><a href="{{servlet-context}}/register">Register</a></li>
|
<li class=""><a href="{{servlet-context}}/register">Register</a></li>
|
||||||
<li class="">{% if user %}<a href="{{servlet-context}}/logout">Logout</a>
|
<li class=""><a href="{{servlet-context}}/login">Login</a></li>
|
||||||
{% else %}<a href="{{servlet-context}}/login">Login</a>{% endif %}</li>
|
{% endif %}
|
||||||
<li class=""><a href="{{servlet-context}}/about">About</a></li>
|
<li class=""><a href="{{servlet-context}}/about">About</a></li>
|
||||||
{% if user %}
|
{% if user %}
|
||||||
<li id="user"><a href="{{servlet-context}}/profile">Logged in as {{user.username}}</a></li>
|
<li id="user"><a href="{{servlet-context}}/profile">Logged in as {{user.username}}</a></li>
|
||||||
|
|
|
@ -36,19 +36,16 @@ Add a new Followuprequest
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Id
|
Elector
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Elector_id
|
Raised
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Visit_id
|
Issue
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Issue_id
|
Method
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Method_id
|
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
|
|
||||||
|
@ -56,13 +53,10 @@ Method_id
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<input id='id' type='text' name='id' value='{{ params.id }}'/>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<input id='elector_id' type='text' name='elector_id' value='{{ params.elector_id }}'/>
|
<input id='elector_id' type='text' name='elector_id' value='{{ params.elector_id }}'/>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<input id='visit_id' type='text' name='visit_id' value='{{ params.visit_id }}'/>
|
<input id='raised' type='text' name='raised' value='{{ params.raised }}'/>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<input id='issue_id' type='text' name='issue_id' value='{{ params.issue_id }}'/>
|
<input id='issue_id' type='text' name='issue_id' value='{{ params.issue_id }}'/>
|
||||||
|
@ -79,17 +73,12 @@ Method_id
|
||||||
{% for record in records %}
|
{% for record in records %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{ record.id }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href='{{servlet-context}}/form-electors-Elector?id={{ record.elector_id }}'>
|
<a href='{{servlet-context}}/form-electors-Elector?id={{ record.elector_id }}'>
|
||||||
{{ record.elector_id_expanded }}
|
{{ record.elector_id_expanded }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href='{{servlet-context}}/form-visits-Visit?id={{ record.visit_id }}'>
|
{{ record.raised }}
|
||||||
{{ record.visit_id_expanded }}
|
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
|
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
|
||||||
|
|
|
@ -140,7 +140,8 @@
|
||||||
params :visit_id (current-visit-id request)))
|
params :visit_id (current-visit-id request)))
|
||||||
201)
|
201)
|
||||||
{:status 400
|
{:status 400
|
||||||
:body (json/write-str "create-intention requires params: `option_id`
|
:body (json/write-str
|
||||||
|
"create-intention requires params: `option_id`
|
||||||
and either `locality` or both `address_id` and `elector_id`.")})
|
and either `locality` or both `address_id` and `elector_id`.")})
|
||||||
request)))
|
request)))
|
||||||
|
|
||||||
|
@ -164,7 +165,28 @@
|
||||||
request)))
|
request)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn update-elector-signature!
|
||||||
|
"Set the `signature` in the params of this `request` as the signature for
|
||||||
|
the elector whose `id` is in the params of this `request`."
|
||||||
|
[request]
|
||||||
|
(let [params (massage-params request)]
|
||||||
|
(log/debug "Update elector signature with params: " params)
|
||||||
|
(valid-user-or-forbid
|
||||||
|
(with-params-or-error
|
||||||
|
(do-or-server-fail
|
||||||
|
(db/update-elector! db/*db* params)
|
||||||
|
201)
|
||||||
|
params
|
||||||
|
#{:id :signature})
|
||||||
|
request)))
|
||||||
|
|
||||||
|
|
||||||
(defroutes rest-routes
|
(defroutes rest-routes
|
||||||
(GET "/rest/get-local-data" request (route/restricted (get-local-data request)))
|
(GET "/rest/get-local-data"
|
||||||
(GET "/rest/create-intention" request (route/restricted (create-intention-and-visit! request)))
|
request (route/restricted (get-local-data request)))
|
||||||
(GET "/rest/create-request" request (route/restricted (create-request-and-visit! request))))
|
(GET "/rest/create-intention"
|
||||||
|
request (route/restricted (create-intention-and-visit! request)))
|
||||||
|
(GET "/rest/create-request"
|
||||||
|
request (route/restricted (create-request-and-visit! request)))
|
||||||
|
(GET "/rest/update-elector-signature"
|
||||||
|
request (route/restricted (update-elector-signature! request))))
|
||||||
|
|
|
@ -118,61 +118,57 @@
|
||||||
;; Routes
|
;; Routes
|
||||||
(secretary/set-config! :prefix "#")
|
(secretary/set-config! :prefix "#")
|
||||||
|
|
||||||
(defn log-and-dispatch [arg]
|
|
||||||
(js/console.log (str "Dispatching " arg))
|
|
||||||
(rf/dispatch arg))
|
|
||||||
|
|
||||||
(secretary/defroute "/" []
|
(secretary/defroute "/" []
|
||||||
(log-and-dispatch [:set-active-page :map]))
|
(ui/log-and-dispatch [:set-active-page :map]))
|
||||||
|
|
||||||
(secretary/defroute "/about" []
|
(secretary/defroute "/about" []
|
||||||
(log-and-dispatch [:set-active-page :about]))
|
(ui/log-and-dispatch [:set-active-page :about]))
|
||||||
|
|
||||||
(secretary/defroute "/dwelling" []
|
(secretary/defroute "/dwelling" []
|
||||||
(log-and-dispatch [:set-active-page :dwelling]))
|
(ui/log-and-dispatch [:set-active-page :dwelling]))
|
||||||
|
|
||||||
(secretary/defroute "/dwelling/:dwelling" {dwelling-id :dwelling}
|
(secretary/defroute "/dwelling/:dwelling" {dwelling-id :dwelling}
|
||||||
(log-and-dispatch [:set-dwelling dwelling-id])
|
(ui/log-and-dispatch [:set-dwelling dwelling-id])
|
||||||
(log-and-dispatch [:set-active-page :dwelling]))
|
(ui/log-and-dispatch [:set-active-page :dwelling]))
|
||||||
|
|
||||||
(secretary/defroute "/building/:address" {address-id :address}
|
(secretary/defroute "/building/:address" {address-id :address}
|
||||||
(log-and-dispatch [:set-address address-id]))
|
(ui/log-and-dispatch [:set-address address-id]))
|
||||||
|
|
||||||
(secretary/defroute "/elector" []
|
(secretary/defroute "/elector" []
|
||||||
(log-and-dispatch [:set-active-page :elector]))
|
(ui/log-and-dispatch [:set-active-page :elector]))
|
||||||
|
|
||||||
(secretary/defroute "/elector/:elector" {elector-id :elector}
|
(secretary/defroute "/elector/:elector/:consent" {elector-id :elector}
|
||||||
(log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :elector}]))
|
(ui/log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :elector}]))
|
||||||
|
|
||||||
(secretary/defroute "/elector/:elector/:consent" {elector-id :elector consent :consent}
|
(secretary/defroute "/elector/:elector/:consent" {elector-id :elector consent :consent}
|
||||||
(log-and-dispatch [:set-consent-and-page {:elector-id elector-id :consent (and true consent) :page :elector}]))
|
(ui/log-and-dispatch [:set-active-page {:page :elector}]))
|
||||||
|
|
||||||
(secretary/defroute "/elector" []
|
(secretary/defroute "/elector" []
|
||||||
(log-and-dispatch [:set-active-page :elector]))
|
(ui/log-and-dispatch [:set-active-page :elector]))
|
||||||
|
|
||||||
(secretary/defroute "/followup" []
|
(secretary/defroute "/followup" []
|
||||||
(log-and-dispatch [:set-active-page :followup]))
|
(ui/log-and-dispatch [:set-active-page :followup]))
|
||||||
|
|
||||||
(secretary/defroute "/gdpr" []
|
(secretary/defroute "/gdpr" []
|
||||||
(log-and-dispatch [:set-active-page :gdpr]))
|
(ui/log-and-dispatch [:set-active-page :gdpr]))
|
||||||
|
|
||||||
(secretary/defroute "/gdpr/:elector" {elector-id :elector}
|
(secretary/defroute "/gdpr/:elector" {elector-id :elector}
|
||||||
(log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :gdpr}]))
|
(ui/log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :gdpr}]))
|
||||||
|
|
||||||
(secretary/defroute "/issues" []
|
(secretary/defroute "/issues" []
|
||||||
(log-and-dispatch [:set-active-page :issues]))
|
(ui/log-and-dispatch [:set-active-page :issues]))
|
||||||
|
|
||||||
(secretary/defroute "/issues/:elector" {elector-id :elector}
|
(secretary/defroute "/issues/:elector" {elector-id :elector}
|
||||||
(log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
|
(ui/log-and-dispatch [:set-elector-and-page {:elector-id elector-id :page :issues}]))
|
||||||
|
|
||||||
(secretary/defroute "/issue/:issue" {issue :issue}
|
(secretary/defroute "/issue/:issue" {issue :issue}
|
||||||
(log-and-dispatch [:set-and-go-to-issue issue]))
|
(ui/log-and-dispatch [:set-and-go-to-issue issue]))
|
||||||
|
|
||||||
(secretary/defroute "/map" []
|
(secretary/defroute "/map" []
|
||||||
(log-and-dispatch [:set-active-page :map]))
|
(ui/log-and-dispatch [:set-active-page :map]))
|
||||||
|
|
||||||
(secretary/defroute "/set-intention/:elector/:intention" {elector-id :elector intention :intention}
|
(secretary/defroute "/set-intention/:elector/:intention" {elector-id :elector intention :intention}
|
||||||
(log-and-dispatch [:set-intention {:elector-id elector-id :intention intention}]))
|
(ui/log-and-dispatch [:set-intention {:elector-id elector-id :intention intention}]))
|
||||||
|
|
||||||
;; -------------------------
|
;; -------------------------
|
||||||
;; History
|
;; History
|
||||||
|
@ -193,7 +189,7 @@
|
||||||
|
|
||||||
(defn init! []
|
(defn init! []
|
||||||
(rf/dispatch-sync [:initialize-db])
|
(rf/dispatch-sync [:initialize-db])
|
||||||
(rf/dispatch-sync [:get-current-location])
|
(rf/dispatch [:get-current-location])
|
||||||
(rf/dispatch [:fetch-locality])
|
(rf/dispatch [:fetch-locality])
|
||||||
(rf/dispatch [:fetch-options])
|
(rf/dispatch [:fetch-options])
|
||||||
(rf/dispatch [:fetch-issues])
|
(rf/dispatch [:fetch-issues])
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
[re-frame.core :refer [dispatch reg-event-db reg-event-fx subscribe]]
|
[re-frame.core :refer [dispatch reg-event-db reg-event-fx subscribe]]
|
||||||
[ajax.core :refer [GET]]
|
[ajax.core :refer [GET]]
|
||||||
[ajax.json :refer [json-request-format json-response-format]]
|
[ajax.json :refer [json-request-format json-response-format]]
|
||||||
[youyesyet.canvasser-app.state :as db]
|
|
||||||
[youyesyet.locality :refer [locality]]
|
[youyesyet.locality :refer [locality]]
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -45,13 +44,14 @@
|
||||||
(.getCurrentPosition
|
(.getCurrentPosition
|
||||||
(.-geolocation js/navigator)
|
(.-geolocation js/navigator)
|
||||||
(fn [position]
|
(fn [position]
|
||||||
(js/console.log (str "Current location is: "
|
(let [lat (.-latitude (.-coords position))
|
||||||
(.-latitude (.-coords position)) ", "
|
lng (.-longitude (.-coords position))]
|
||||||
(.-longitude (.-coords position))))
|
(js/console.log (str "Current location is: " lat ", " lng))
|
||||||
(dispatch [:set-latitude (.-latitude (.-coords position))])
|
(dispatch [:set-latitude lat])
|
||||||
(dispatch [:set-longitude (.-longitude (.-coords position))])
|
(dispatch [:set-longitude lng])
|
||||||
(locality (.-latitude (.-coords position)) (.-longitude (.-coords position))))))
|
;; (.panTo @(subscribe [:view]) (.latLng js/L lat lng))
|
||||||
(js/console.log "Geolocation not available")
|
(locality lat lng))))
|
||||||
|
(js/console.log "Geolocation not available"))
|
||||||
(catch js/Object any
|
(catch js/Object any
|
||||||
(js/console.log "Exception while trying to access location: " + any)
|
(js/console.log "Exception while trying to access location: " + any)
|
||||||
0)))
|
0)))
|
||||||
|
|
|
@ -140,34 +140,6 @@
|
||||||
db/default-db))
|
db/default-db))
|
||||||
|
|
||||||
|
|
||||||
;; (reg-event-fx
|
|
||||||
;; :feedback
|
|
||||||
;; (fn [x y]
|
|
||||||
;; (js/console.log (str "Feedback event called with x = " x "; y = " y))
|
|
||||||
;; (:db x)))
|
|
||||||
|
|
||||||
|
|
||||||
;; (reg-event-fx
|
|
||||||
;; :issues
|
|
||||||
;; (fn [x y]
|
|
||||||
;; (js/console.log (str "Issues event called with x = " x "; y = " y))
|
|
||||||
;; (:db x)))
|
|
||||||
|
|
||||||
|
|
||||||
;; (reg-event-fx
|
|
||||||
;; :options
|
|
||||||
;; (fn [x y]
|
|
||||||
;; (js/console.log (str "Options event called with x = " x "; y = " y))
|
|
||||||
;; (:db x)))
|
|
||||||
|
|
||||||
|
|
||||||
;; (reg-event-fx
|
|
||||||
;; :event
|
|
||||||
;; (fn [x y]
|
|
||||||
;; (js/console.log (str "Event event called with x = " x "; y = " y))
|
|
||||||
;; (:db x)))
|
|
||||||
|
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
:fetch-locality
|
:fetch-locality
|
||||||
(fn [{db :db} _]
|
(fn [{db :db} _]
|
||||||
|
@ -205,35 +177,28 @@
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
:process-locality
|
:process-locality
|
||||||
|
;; TODO: why is this an `-fx`? Does it need to be?
|
||||||
(fn
|
(fn
|
||||||
[{db :db} [_ response]]
|
[{db :db} [_ response]]
|
||||||
(js/console.log (str "Updating locality data: " (count response) " addresses " ))
|
(js/console.log (str "Updating locality data: " (count response) " addresses " ))
|
||||||
(refresh-map-pins)
|
(refresh-map-pins)
|
||||||
{:dispatch-later [{:ms 60000 :dispatch [:fetch-locality]}
|
{:db (assoc
|
||||||
{:ms 1000 :dispatch [:get-current-location]}]
|
|
||||||
:db (assoc
|
|
||||||
(remove-from-feedback db :fetch-locality)
|
(remove-from-feedback db :fetch-locality)
|
||||||
:addresses (js->clj response))}))
|
:addresses (js->clj response))}))
|
||||||
|
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
:bad-locality
|
:bad-locality
|
||||||
|
;; TODO: why is this an `-fx`? Does it need to be?
|
||||||
(fn
|
(fn
|
||||||
[{db :db} [_ response]]
|
[{db :db} [_ response]]
|
||||||
;; TODO: signal something has failed? It doesn't matter very much, unless it keeps failing.
|
;; TODO: signal something has failed? It doesn't matter very much, unless it keeps failing.
|
||||||
(js/console.log "Failed to fetch locality data")
|
(js/console.log "Failed to fetch locality data")
|
||||||
;; loop to do it again
|
;; loop to do it again
|
||||||
(dispatch [:dispatch-later [{:ms 60000 :dispatch [:fetch-locality]}]])
|
(dispatch [:dispatch-later [{:ms 60000 :dispatch [:fetch-locality]}]])
|
||||||
(assoc
|
{:db (assoc
|
||||||
(remove-from-feedback db :fetch-locality)
|
(remove-from-feedback db :fetch-locality)
|
||||||
:error (cons :fetch-locality (:error db)))))
|
:error (cons :fetch-locality (:error db)))}))
|
||||||
|
|
||||||
|
|
||||||
;; (reg-event-fx
|
|
||||||
;; :process-outqueue
|
|
||||||
;; (fn [{db :db} _]
|
|
||||||
;; (if
|
|
||||||
;; (empty? (:outqueue db))
|
|
||||||
|
|
||||||
|
|
||||||
(reg-event-fx
|
(reg-event-fx
|
||||||
|
@ -262,12 +227,12 @@
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
;; TODO: should try again
|
|
||||||
:bad-options
|
:bad-options
|
||||||
(fn [db [_ response]]
|
(fn [db [_ response]]
|
||||||
(js/console.log "Failed to fetch options")
|
(js/console.log "Failed to fetch options")
|
||||||
|
(dispatch [:dispatch-later [{:ms 60000 :dispatch [:fetch-options]}]])
|
||||||
(assoc
|
(assoc
|
||||||
(remove-from-feedback db :fetch-options)
|
db
|
||||||
:error (:response response))))
|
:error (:response response))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,12 +266,12 @@
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
;; TODO: should try again
|
|
||||||
:bad-issues
|
:bad-issues
|
||||||
(fn [db [_ response]]
|
(fn [db [_ response]]
|
||||||
(js/console.log "Failed to fetch issues")
|
(js/console.log "Failed to fetch issues")
|
||||||
|
(dispatch [:dispatch-later [{:ms 60000 :dispatch [:fetch-issues]}]])
|
||||||
(assoc
|
(assoc
|
||||||
(remove-from-feedback db :fetch-issues)
|
db
|
||||||
:error (:response response))))
|
:error (:response response))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -347,7 +312,7 @@
|
||||||
:method_detail (-> db :method_detail)
|
:method_detail (-> db :method_detail)
|
||||||
:action :create-request})
|
:action :create-request})
|
||||||
:send-request))
|
:send-request))
|
||||||
(assoc db :error "Please supply a telephone number to call"))))
|
(assoc db :error "Please supply a telephone number/email address for elector"))))
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
|
@ -380,15 +345,34 @@
|
||||||
:page :building))))))
|
:page :building))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn do-update-elector
|
||||||
|
[db elector]
|
||||||
|
(if-not
|
||||||
|
;; if the signature has changed
|
||||||
|
(= (:signature elector) (:signature (:elector db)))
|
||||||
|
(assoc
|
||||||
|
(add-to-outqueue
|
||||||
|
(clear-messages db)
|
||||||
|
(assoc elector
|
||||||
|
:action :update-elector-signature))
|
||||||
|
:elector elector)
|
||||||
|
(assoc db
|
||||||
|
:elector elector)))
|
||||||
|
|
||||||
|
|
||||||
|
(reg-event-db
|
||||||
|
:update-elector
|
||||||
|
(fn [db [_ elector]]
|
||||||
|
(js/console.log (str "Elector is " elector))
|
||||||
|
(do-update-elector db elector)))
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
:set-consent-and-page
|
:set-consent-and-page
|
||||||
(fn [db [_ args]]
|
(fn [db [_ args]]
|
||||||
(let [page (:page args)
|
(assoc
|
||||||
consent (:consent args)
|
(do-update-elector db (:elector args))
|
||||||
elector-id (coerce-to-number (:elector-id args))
|
:page (:page args))))
|
||||||
elector (get-elector elector-id db)]
|
|
||||||
(js/console.log (str "Setting page to " page ", consent to " consent " for " (:name elector)))
|
|
||||||
(assoc (clear-messages db) :elector (assoc elector :consent true) :page page))))
|
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns ^{:doc "Canvasser app client state."
|
(ns ^{:doc "Canvasser app client state."
|
||||||
:author "Simon Brooke"}
|
:author "Simon Brooke"}
|
||||||
youyesyet.canvasser-app.state)
|
youyesyet.canvasser-app.state
|
||||||
|
(:require [youyesyet.canvasser-app.gis :as gis]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
;;;;
|
||||||
|
@ -28,129 +29,21 @@
|
||||||
;;; This is the constructor for the atom in which the state of the user interface is held.
|
;;; This is the constructor for the atom in which the state of the user interface is held.
|
||||||
;;; The atom gets updated by 'events' registered in handler.cljs, q.v.
|
;;; The atom gets updated by 'events' registered in handler.cljs, q.v.
|
||||||
|
|
||||||
(def default-db
|
|
||||||
{
|
|
||||||
:addresses
|
|
||||||
[{:locality 548223905,
|
|
||||||
:address
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF",
|
|
||||||
:phone nil,
|
|
||||||
:postcode "DG7 1RF",
|
|
||||||
:longitude -3.905045374625994,
|
|
||||||
:district_id 1,
|
|
||||||
:dwellings
|
|
||||||
[{:address_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF",
|
|
||||||
:address_id 18,
|
|
||||||
:sub_address "",
|
|
||||||
:id 17,
|
|
||||||
:id_2 17,
|
|
||||||
:address_id_2 18,
|
|
||||||
:sub_address_2 "",
|
|
||||||
:electors
|
|
||||||
[{:email nil,
|
|
||||||
:dwelling_id_2 17,
|
|
||||||
:dwelling_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
|
||||||
:intentions
|
|
||||||
[{:locality 548223905,
|
|
||||||
:visit_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, 2018-06-14 20:29:34.721522",
|
|
||||||
:option_id_expanded "Yes",
|
|
||||||
:option_id "Yes",
|
|
||||||
:option_id_2 "Yes",
|
|
||||||
:visit_id_2 1,
|
|
||||||
:elector_id_2 61,
|
|
||||||
:visit_id 1,
|
|
||||||
:elector_id 61,
|
|
||||||
:id 1,
|
|
||||||
:elector_id_expanded nil,
|
|
||||||
:id_2 1}],
|
|
||||||
:phone nil,
|
|
||||||
:phone_2 nil,
|
|
||||||
:gender_expanded "Female",
|
|
||||||
:name "Alice Sutherland",
|
|
||||||
:dwelling_id 17,
|
|
||||||
:id 61,
|
|
||||||
:gender "Female",
|
|
||||||
:gender_2 "Female",
|
|
||||||
:name_2 "Alice Sutherland",
|
|
||||||
:email_2 nil,
|
|
||||||
:id_2 61}
|
|
||||||
{:email nil,
|
|
||||||
:dwelling_id_2 17,
|
|
||||||
:dwelling_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
|
||||||
:intentions [],
|
|
||||||
:phone nil,
|
|
||||||
:phone_2 nil,
|
|
||||||
:gender_expanded "Female",
|
|
||||||
:name "Charlie Sutherland",
|
|
||||||
:dwelling_id 17,
|
|
||||||
:id 62,
|
|
||||||
:gender "Female",
|
|
||||||
:gender_2 "Female",
|
|
||||||
:name_2 "Charlie Sutherland",
|
|
||||||
:email_2 nil,
|
|
||||||
:id_2 62}
|
|
||||||
{:email nil,
|
|
||||||
:dwelling_id_2 17,
|
|
||||||
:dwelling_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
|
||||||
:intentions [],
|
|
||||||
:phone nil,
|
|
||||||
:phone_2 nil,
|
|
||||||
:gender_expanded "Male",
|
|
||||||
:name "Keith Sutherland",
|
|
||||||
:dwelling_id 17,
|
|
||||||
:id 64,
|
|
||||||
:gender "Male",
|
|
||||||
:gender_2 "Male",
|
|
||||||
:name_2 "Keith Sutherland",
|
|
||||||
:email_2 nil,
|
|
||||||
:id_2 64}
|
|
||||||
{:email nil,
|
|
||||||
:dwelling_id_2 17,
|
|
||||||
:dwelling_id_expanded
|
|
||||||
"HAZELFIELD HOUSE, CASTLE DOUGLAS, DG7 1RF, DG7 1RF, ",
|
|
||||||
:intentions [],
|
|
||||||
:phone nil,
|
|
||||||
:phone_2 nil,
|
|
||||||
:gender_expanded "Female",
|
|
||||||
:name "Lucy Sutherland",
|
|
||||||
:dwelling_id 17,
|
|
||||||
:id 63,
|
|
||||||
:gender "Female",
|
|
||||||
:gender_2 "Female",
|
|
||||||
:name_2 "Lucy Sutherland",
|
|
||||||
:email_2 nil,
|
|
||||||
:id_2 63}]}],
|
|
||||||
:id 18,
|
|
||||||
:latitude 54.8222716877376}]
|
|
||||||
|
|
||||||
;;; the currently selected address, if any.
|
(def default-db
|
||||||
:address nil
|
{ ;;; any confirmation message to display
|
||||||
;;; electors at the currently selected dwelling
|
:feedback '("Welcome to the canvasser app!")
|
||||||
:electors nil
|
|
||||||
;;; any error to display
|
|
||||||
:error nil
|
|
||||||
;;; the issue from among the issues which is currently selected.
|
|
||||||
;;; any confirmation message to display
|
|
||||||
:feedback '()
|
|
||||||
;;; the currently selected issue
|
|
||||||
:issue nil
|
|
||||||
;;; the issues selected for the issues page on this day.
|
|
||||||
:issues nil
|
|
||||||
;;; message of the day
|
;;; message of the day
|
||||||
:motd "This is a test version only. There is no real data."
|
:motd "This is a test version only. There is no real data."
|
||||||
;;; the options from among which electors can select.
|
;;; the options from among which electors can select.
|
||||||
:options nil
|
:outqueue '()
|
||||||
;;; the queue of items waiting to be transmitted.
|
;;; the view of the map we display
|
||||||
:outqueue ()
|
:view nil
|
||||||
;;; the currently displayed page within the app.
|
;;; the currently displayed page within the app.
|
||||||
:page :home
|
:page :home
|
||||||
:view nil
|
;;; initial starting coords in the centre of Scotland.
|
||||||
:latitude 54.82
|
:latitude 56
|
||||||
:longitude -3.92
|
:longitude -4
|
||||||
:zoom 12})
|
:zoom 12})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn log-and-dispatch [arg]
|
||||||
|
(js/console.log (str "Dispatching " arg))
|
||||||
|
(rf/dispatch arg))
|
||||||
|
|
||||||
|
|
||||||
(defn back-link
|
(defn back-link
|
||||||
"Generate a back link to the preceding page, or, if `target` is specified,
|
"Generate a back link to the preceding page, or, if `target` is specified,
|
||||||
to a particular page."
|
to a particular page."
|
||||||
|
@ -41,7 +46,7 @@
|
||||||
(defn big-link
|
(defn big-link
|
||||||
[text & {:keys [target handler]}]
|
[text & {:keys [target handler]}]
|
||||||
[:div.big-link-container {:key (gensym "big-link")}
|
[:div.big-link-container {:key (gensym "big-link")}
|
||||||
[:a.big-link (merge
|
[:a.big-link (merge {}
|
||||||
(if target {:href target}{})
|
(if target {:href target}{})
|
||||||
(if handler {:on-click handler}{}))
|
(if handler {:on-click handler}{}))
|
||||||
text]])
|
text]])
|
||||||
|
@ -75,4 +80,5 @@
|
||||||
(nav-link "#/map" "Map" :map collapsed?)
|
(nav-link "#/map" "Map" :map collapsed?)
|
||||||
(nav-link "#/dwelling" "Electors" :dwelling collapsed?)
|
(nav-link "#/dwelling" "Electors" :dwelling collapsed?)
|
||||||
(nav-link "#/issues" "Issues" :issues collapsed?)
|
(nav-link "#/issues" "Issues" :issues collapsed?)
|
||||||
(nav-link "#/about" "About" :about collapsed?)]]))
|
(nav-link "#/about" "About" :about collapsed?)
|
||||||
|
(nav-link "/logout" "Logout" :logout collapsed?)]]))
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
[:div
|
[:div
|
||||||
[:h1 (str "Flats at " (:address address))]
|
[:h1 (str "Flats at " (:address address))]
|
||||||
[:div.container {:id "main-container"}
|
[:div.container {:id "main-container"}
|
||||||
(ui/back-link)
|
(ui/back-link "#map")
|
||||||
[:div {:id "dwelling-list"}
|
[:div {:id "dwelling-list"}
|
||||||
(map
|
(map
|
||||||
(fn
|
(fn
|
||||||
|
|
|
@ -80,11 +80,16 @@
|
||||||
(defn panel
|
(defn panel
|
||||||
"Generate the electors panel."
|
"Generate the electors panel."
|
||||||
[]
|
[]
|
||||||
|
(js/console.log "dwelling.panel")
|
||||||
(let [dwelling @(subscribe [:dwelling])
|
(let [dwelling @(subscribe [:dwelling])
|
||||||
address @(subscribe [:address])
|
address @(subscribe [:address])
|
||||||
sub-address (:sub-address dwelling)
|
sub-address (:sub-address dwelling)
|
||||||
electors (sort-by :id (:electors dwelling))
|
electors (sort-by :id (:electors dwelling))
|
||||||
options @(subscribe [:options])]
|
options @(subscribe [:options])
|
||||||
|
back (if
|
||||||
|
(> (count (:dwellings address)) 1)
|
||||||
|
"#building"
|
||||||
|
"#map")]
|
||||||
(if address
|
(if address
|
||||||
[:div
|
[:div
|
||||||
[:h1 (if sub-address
|
[:h1 (if sub-address
|
||||||
|
@ -95,7 +100,7 @@
|
||||||
[:tbody
|
[:tbody
|
||||||
(genders-row electors)
|
(genders-row electors)
|
||||||
(names-row electors)]]
|
(names-row electors)]]
|
||||||
(ui/back-link)]]
|
(ui/back-link back)]]
|
||||||
(ui/error-panel "No address selected"))))
|
(ui/error-panel "No address selected"))))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,27 @@
|
||||||
|
|
||||||
;; OK, the idea here is a GDPR consent form to be signed by the elector
|
;; OK, the idea here is a GDPR consent form to be signed by the elector
|
||||||
|
|
||||||
|
(def sig-pad
|
||||||
|
;; something the signature pad will be bound to
|
||||||
|
(atom nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defn send-consent
|
||||||
|
"Extract the signature from the signature pad, encode it, add it to this
|
||||||
|
`elector`, and dispatch the `elector`; move on to the page `elector`."
|
||||||
|
[elector]
|
||||||
|
(dispatch
|
||||||
|
[:set-consent-and-page
|
||||||
|
{:elector-id (:id elector)
|
||||||
|
:page :elector
|
||||||
|
:elector (assoc
|
||||||
|
elector
|
||||||
|
:signature
|
||||||
|
(.toDataURL
|
||||||
|
@sig-pad
|
||||||
|
"image/svg+xml"))}])
|
||||||
|
nil)
|
||||||
|
|
||||||
(defn gdpr-render
|
(defn gdpr-render
|
||||||
[]
|
[]
|
||||||
(let [elector @(subscribe [:elector])]
|
(let [elector @(subscribe [:elector])]
|
||||||
|
@ -48,24 +69,23 @@
|
||||||
only against your electoral district, and not link it to you"]]]]
|
only against your electoral district, and not link it to you"]]]]
|
||||||
[:tr
|
[:tr
|
||||||
[:td
|
[:td
|
||||||
[:canvas {:id "signature-pad"}]]]]]]
|
[:canvas {:id "signature-pad" :on-mouse-out #(send-consent elector)}]]]]]]
|
||||||
(ui/back-link "#dwelling")
|
(ui/back-link "#dwelling")
|
||||||
(ui/big-link "I consent"
|
(ui/big-link "I consent"
|
||||||
:target (str "#elector/" (:id elector) "/true")
|
:target (str "#elector")
|
||||||
:handler #(fn [] (dispatch [:set-consent-and-page {:elector-id (:id elector) :page :elector}])))
|
:handler #(send-consent elector))
|
||||||
;; TODO: need to save the signature
|
|
||||||
(ui/big-link "I DO NOT consent"
|
(ui/big-link "I DO NOT consent"
|
||||||
:target (str "#elector/" (:id elector) "/true"))]))
|
:target (str "#elector/" (:id elector) "/false"))]))
|
||||||
;; :handler #(fn [] (dispatch [:set-elector-and-page {:elector-id (:id elector) :page :elector}])))]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn gdpr-did-mount
|
(defn gdpr-did-mount
|
||||||
[]
|
[]
|
||||||
(js/SignaturePad. (.getElementById js/document "signature-pad")))
|
(reset! sig-pad (js/SignaturePad. (.getElementById js/document "signature-pad"))))
|
||||||
|
|
||||||
|
|
||||||
(defn panel
|
(defn panel
|
||||||
"A reagent class for the GDPR consent form"
|
"A reagent class for the GDPR consent form"
|
||||||
[]
|
[]
|
||||||
|
(js/console.log "gdpr.panel")
|
||||||
(reagent/create-class {:reagent-render gdpr-render
|
(reagent/create-class {:reagent-render gdpr-render
|
||||||
:component-did-mount gdpr-did-mount}))
|
:component-did-mount gdpr-did-mount}))
|
||||||
|
|
11
test/cljs/youyesyet/canvasser_app/ui_utils_test.cljs
Normal file
11
test/cljs/youyesyet/canvasser_app/ui_utils_test.cljs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
(ns youyesyet.canvasser-app.ui-utils-test
|
||||||
|
(:require [cljs.test :refer-macros [is are deftest testing use-fixtures]]
|
||||||
|
[reagent.core :as reagent :refer [atom]]
|
||||||
|
[youyesyet.canvasser-app.ui-utils :refer :all]))
|
||||||
|
|
||||||
|
(deftest big-link-tests
|
||||||
|
(testing "big-link"
|
||||||
|
(is (= [:div.big-link-container {:key (gensym "big-link")}
|
||||||
|
[:a.big-link {}
|
||||||
|
"Test"]]
|
||||||
|
(big-link "Test" nil nil)))))
|
Loading…
Reference in a new issue