diff --git a/app/views/nonprofits/payments/index.html.erb b/app/views/nonprofits/payments/index.html.erb
index ebf54a76..3611db16 100644
--- a/app/views/nonprofits/payments/index.html.erb
+++ b/app/views/nonprofits/payments/index.html.erb
@@ -4,13 +4,42 @@
<% content_for :stylesheets do %>
<%= stylesheet_link_tag 'nonprofits/payments/index/page' %>
+ <%= IncludeAsset.css '/client/css/bootstrap.css' %>
<% end %>
<% content_for :javascripts do %>
+
<%= IncludeAsset.js '/client/js/nonprofits/payments/index/page.js' %>
+ <%= IncludeAsset.js '/app/react.js' %>
+ <%= IncludeAsset.js '/app/react-dom.js' %>
+ <%= IncludeAsset.js '/app/vendor.js' %>
+ <%= IncludeAsset.js '/app/edit_payment_panex.js' %>
+
+
<% end %>
<%= render '/components/trial_bar' if QueryBillingSubscriptions.currently_in_trial?(@nonprofit.id) %>
diff --git a/app/views/nonprofits/payments/show.rabl b/app/views/nonprofits/payments/show.rabl
index 5419f918..62561f57 100644
--- a/app/views/nonprofits/payments/show.rabl
+++ b/app/views/nonprofits/payments/show.rabl
@@ -19,13 +19,13 @@ child :donation, object_root: false do
child :campaign, object_root: false do
- attributes :name, :url
+ attributes :name, :url, :id
end
node(:campaign_gift){|d| {name: d.campaign_gifts.any? ? d.campaign_gifts.last.campaign_gift_option.name : nil}}
child :event, object_root: false do
- attributes :name, :url
+ attributes :name, :url, :id
end
child :recurring_donation, object_root: false do
@@ -49,7 +49,7 @@ end
node(:ticket) do |payment|
event = GetData.obj(payment.tickets.last, :event)
h = {
- event: {name: GetData.obj(event, :name), url: GetData.obj(event, :url)},
+ event: {name: GetData.obj(event, :name), url: GetData.obj(event, :url), id: GetData.obj(event, :id)},
levels: payment.tickets.map{|t| "#{GetData.chain(t.ticket_level, :name)} (#{t.quantity}x)"}.join(", "),
discount: payment.tickets.map{|t| t.event_discount ? "#{t.event_discount.name} (#{t.event_discount.percent}%)" : nil}.compact.join(", ")
}
@@ -68,3 +68,7 @@ child :supporter do
attributes :name, :email, :city, :state_code, :address, :zip_code, :phone, :id, :country
end
+
+child :nonprofit do
+ attributes :id
+end
diff --git a/app/views/nonprofits/show.html.erb b/app/views/nonprofits/show.html.erb
index f6fb010a..99f417c6 100755
--- a/app/views/nonprofits/show.html.erb
+++ b/app/views/nonprofits/show.html.erb
@@ -90,7 +90,7 @@
<%= render 'contact' %>
-
+
<%= t("organization_page.promote") %>
<%= render 'common/social_buttons' %>
diff --git a/app/views/nonprofits/supporters/index.html.erb b/app/views/nonprofits/supporters/index.html.erb
index 97e50379..09b15fc6 100644
--- a/app/views/nonprofits/supporters/index.html.erb
+++ b/app/views/nonprofits/supporters/index.html.erb
@@ -2,10 +2,42 @@
<% content_for(:dont_load_optimizely) {'true'} %>
<% content_for(:footer_hidden) {'hidden'} %>
-<%= content_for(:stylesheets) {stylesheet_link_tag 'nonprofits/supporters/index/page'} %>
+<% content_for(:stylesheets) do %>
+ <%= stylesheet_link_tag 'nonprofits/supporters/index/page' %>
+ <%= IncludeAsset.css '/client/css/bootstrap.css' %>
+<% end %>
<%= content_for :javascripts do %>
<%= IncludeAsset.js '/client/js/nonprofits/supporters/index/page.js' %>
<%= render 'common/froala' %>
+ <%= IncludeAsset.js '/app/react.js' %>
+ <%= IncludeAsset.js '/app/react-dom.js' %>
+ <%= IncludeAsset.js '/app/vendor.js' %>
+ <%= IncludeAsset.js '/app/create_new_offsite_payment_panex.js' %>
+
+
<% end %>
<%= render '/components/trial_bar' if nonprofit_in_trial?(@nonprofit.id) %>
@@ -58,3 +90,5 @@
<%= render 'donations/new_offline_modal' %>
+
+
diff --git a/client/js/explore/index/page.js b/client/js/explore/index/page.js
deleted file mode 100644
index a937b58a..00000000
--- a/client/js/explore/index/page.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// License: LGPL-3.0-or-later
-require('../../common/vendor/masonry')
-require('../../common/onboard')
diff --git a/client/js/nonprofits/donate/dedication-form.js b/client/js/nonprofits/donate/dedication-form.js
index e6ac6a3a..9771b513 100644
--- a/client/js/nonprofits/donate/dedication-form.js
+++ b/client/js/nonprofits/donate/dedication-form.js
@@ -21,7 +21,7 @@ function view(state) {
, type: 'radio'
, id: radioId1
, value: 'honor'
- , selected: data.dedication_type === 'honor'
+ , checked: !data.dedication_type || data.dedication_type === 'honor'
}})
, h('label', {props: {htmlFor: radioId1}}, I18n.t('nonprofits.donate.dedication.in_honor_label'))
])
@@ -31,7 +31,7 @@ function view(state) {
, type: 'radio'
, value: 'memory'
, id: radioId2
- , selected: data.dedication_type === 'memory'
+ , checked: data.dedication_type === 'memory'
}})
, h('label', {props: {htmlFor: radioId2}}, I18n.t('nonprofits.donate.dedication.in_memory_label'))
])
diff --git a/client/js/nonprofits/donate/followup-step.js b/client/js/nonprofits/donate/followup-step.js
index 77c0b650..32f52df3 100644
--- a/client/js/nonprofits/donate/followup-step.js
+++ b/client/js/nonprofits/donate/followup-step.js
@@ -14,7 +14,7 @@ function view(state) {
h('a.button--small.facebook.u-width--full.share-button', {
props: {
target: '_blank'
- , href: 'https://www.facebook.com/dialog/feed?app_id='+app.facebook_app_id +"display=popup&caption=" + (app.campaign.name || app.nonprofit.name) + "&link="+window.location.href
+ , href: 'https://www.facebook.com/dialog/feed?app_id='+app.facebook_app_id +"&display=popup&caption=" + (app.campaign.name || app.nonprofit.name) + "&link="+window.location.href
}
}, [h('i.fa.fa-facebook-square'), ` ${I18n.t('nonprofits.donate.followup.share.facebook')}`] )
])
diff --git a/client/js/nonprofits/donate/payment-step.js b/client/js/nonprofits/donate/payment-step.js
index 29d3de88..524f5f33 100644
--- a/client/js/nonprofits/donate/payment-step.js
+++ b/client/js/nonprofits/donate/payment-step.js
@@ -166,7 +166,7 @@ function view(state) {
])
, weekly
, dedic && (dedic.first_name || dedic.last_name)
- ? h('p.u-centered', `${dedic.dedication_type === 'memory' ? I18n.t('nonprofits.donate.dedication.in_memory_label') : I18n.t('nonprofits.donate.dedication.in_honor_label')} ` + `${dedic.first_name} ${dedic.last_name}`)
+ ? h('p.u-centered', `${dedic.dedication_type === 'memory' ? I18n.t('nonprofits.donate.dedication.in_memory_label') : I18n.t('nonprofits.donate.dedication.in_honor_label')} ` + `${dedic.first_name || ''} ${dedic.last_name || ''}`)
: ''
, paymentTabs(state)
])
diff --git a/client/js/nonprofits/donate/wizard.js b/client/js/nonprofits/donate/wizard.js
index 73c6b6d4..1d4a547f 100644
--- a/client/js/nonprofits/donate/wizard.js
+++ b/client/js/nonprofits/donate/wizard.js
@@ -127,7 +127,9 @@ const setDonationDedication = (don, dedication) => {
, JSON.stringify({
supporter_id: dedication.supporter.id
, name: dedication.supporter.name
- , contact: R.join(" - ", [dedication.supporter.email, dedication.supporter.phone, dedication.supporter.address])
+ , contact: {email: dedication.supporter.email,
+ phone: dedication.supporter.phone,
+ address: dedication.supporter.address}
, note: dedication.note
, type: dedication.type
})
diff --git a/client/js/nonprofits/payments/index/payment_details.js b/client/js/nonprofits/payments/index/payment_details.js
index 8c416680..5eb5e4d4 100644
--- a/client/js/nonprofits/payments/index/payment_details.js
+++ b/client/js/nonprofits/payments/index/payment_details.js
@@ -89,27 +89,15 @@ appl.def('get_payment_purchase_object', function(payment) {
}
})
-appl.def('update_donation', function(donation) {
- if(!donation) return
+appl.def('start_loading', function(){
appl.def('loading', true)
- donation.gross_amount = format.dollarsToCents(donation.gross_amount)
- donation.fee_total = format.dollarsToCents(donation.fee_total)
- var formattedDate = appl.readable_date_time_to_iso(donation.date)
- if(formattedDate && formattedDate != "Invalid date") {
- donation.date = formattedDate
- } else {
- appl.notify('Please enter a valid date')
- appl.def('loading', false)
- return
- }
- request.put('/nonprofits/' + app.nonprofit_id + '/donations/' + donation.id)
- .send({donation: donation})
- .end(function(err, resp) {
- appl.ajax_payment_details.fetch(appl.payment_details.data.id)
- appl.def('loading', false)
- appl.close_modal()
- appl.notify('Donation successfully updated!')
- })
+})
+
+appl.def('update_donation__success', function() {
+ appl.ajax_payment_details.fetch(appl.payment_details.data.id)
+ appl.def('loading', false)
+ // appl.close_modal()
+ appl.notify('Donation successfully updated!')
})
appl.def('delete_offline_donation', function() {
@@ -161,14 +149,17 @@ appl.def('format_dedication', function(dedic, node) {
var json
try { json = JSON.parse(dedic) } catch(e) {}
if(json) {
+ let supporter_link = (json.supporter_id && json.supporter_id != '') ?
+ `
${json.name}` :
+ json.name
inner = `
- Donation made in ${dedic.type || 'honor'} of
-
${json.name}.
+ Donation made in ${json.type || 'honor'} of
+ ${supporter_link}.
${json.note ? `
Note:
${json.note}.` : ''}
`
} else {
// Print plaintext dedication
- inner = dedic
+ inner = ''
}
}
td.innerHTML = inner
diff --git a/client/js/nonprofits/supporters/index/sidepanel/index.js b/client/js/nonprofits/supporters/index/sidepanel/index.js
index f9818b89..19e97962 100644
--- a/client/js/nonprofits/supporters/index/sidepanel/index.js
+++ b/client/js/nonprofits/supporters/index/sidepanel/index.js
@@ -31,6 +31,7 @@ const init = _ => {
, newNote$: flyd.stream()
, editNote$: flyd.stream()
, deleteNote$: flyd.stream()
+ , newDonation$: flyd.stream()
}
const supporterID$ = R.compose(
@@ -95,7 +96,6 @@ const init = _ => {
, flyd.map(()=> 'replyGmailModal', state.threadId$)
, flyd.map(()=> 'newSupporterNoteModal', state.editNoteData$)
, flyd.map(()=> null, state.gmail.sendResponse$)
- , flyd.map(()=> null, state.offsiteDonationForm.saved$)
, flyd.map(()=> null, state.supporterNoteForm.saved$)
])
@@ -143,7 +143,7 @@ const view = state => {
, activities.view(state)
, composeModal(state)
, notification.view(state.notification)
- , offsiteDonationForm.view(R.merge(state.offsiteDonationForm, {modalID$: state.modalID$}))
+ , offsiteDonationForm.view(R.merge(state.offsiteDonationForm))
, supporterNoteForm.view(R.merge(state.supporterNoteForm, {modalID$: state.modalID$}))
, replyModal(state)
, confirm.view(state.confirmDelete, 'Are you sure you want to delete this note?')
diff --git a/client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js b/client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js
index 5220f2b4..a75b722a 100644
--- a/client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js
+++ b/client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js
@@ -8,168 +8,26 @@ const format = require('../../../../common/format')
const moment = require('moment')
const request = require('../../../../common/request')
const serialize = require('form-serialize')
-const flyd_filter = require('flyd/module/filter')
+
const flyd_flatMap = require('flyd/module/flatmap')
const flyd_mergeAll = require('flyd/module/mergeall')
-var rootUrl = `/nonprofits/${app.nonprofit_id}`
-
-const getFundraisers = type => {
- var response$ = request({
- method: 'get'
- , path: `${rootUrl}/${type}/name_and_id`
- }).load
-
- var body$ = R.compose(
- flyd.map(x => x.body)
- , flyd_filter(x => x.status === 200 || x.status === 304)
- )(response$)
-
- return flyd.merge(flyd.stream([]), body$)
-}
-
function init(parentState) {
var state = {
submit$: flyd.stream()
, supporter$: parentState.supporter$
- , campaigns$: getFundraisers('campaigns')
- , events$: getFundraisers('events')
+ , saved$: flyd.stream()
}
- const resp$ = flyd_flatMap(
- form => request({
- method: 'post'
- , path: `${rootUrl}/donations/create_offsite`
- , send: {donation: setDefaults(serialize(form, {hash: true}))}
- }).load
- , state.submit$ )
- state.saved$ = flyd_filter(resp => resp.status === 200, resp$)
- state.error$ = flyd_filter(resp => resp.status !== 200, resp$)
- state.loading$ = flyd_mergeAll([
- flyd.map(()=> true, state.submit$)
- , flyd.map(() => false, resp$)
- ])
+
+
return state
}
-const setDefaults = formData =>
- R.evolve({
- amount: format.dollarsToCents
- , date: d => moment(d).format("YYYY-MM-DD")
- }, formData)
function view(state) {
- var body = form(state)
-
- return h('div', [
- modal({
- id$: state.modalID$
- , thisID: 'newOffsiteDonationModal'
- , title: 'New Offsite Contribution'
- , body
- })
- ])
+
+ return h('div', {id$: 'offsite_donation_form_modal'})
}
-const form = state => {
- return h('form', {
- on: {submit: ev => {ev.preventDefault(); state.submit$(ev.currentTarget)}}
- }, [
- h('input', {
- props: {
- type: 'hidden'
- , name: 'nonprofit_id'
- , value: app.nonprofit_id
- }
- })
- , h('input', {
- props: {
- type: 'hidden'
- , name: 'supporter_id'
- , value: state.supporter$().id
- }
- })
- , h('div.layout--four', [
- h('fieldset', [
- h('label', 'Amount')
- , h('div.prepend--dollar', [
- h('input', {
- props: {
- name: 'amount'
- , step: 'any'
- , type: 'number'
- , min: 0
- , required: true
- }
- })
- ])
- ])
- , h('fieldset', [
- h('label', 'Date')
- , h('input', {
- props: {
- id: 'js-offsiteDonationDate'
- , name: 'date'
- , type: 'text'
- , placeholder: 'MM/DD/YYYY'
- }
- })
- ])
- , h('fieldset', [
- h('label', 'Type')
- , h('select', {props: {name: 'offsite_payment[kind]'}}, [
- h('option', {props: {selected: true, value: 'check'}}, 'Check')
- , h('option', {props: {value: 'cash'}}, 'Cash')
- , h('option', {props: {value: ''}}, 'Other')
- ])
- ])
- , h('fieldset', [
- h('label', 'Check Number')
- , h('input', {
- props: {
- name: 'offsite_payment[check_number]'
- , type: 'text'
- , placeholder: '1234'
- }
- })
- ])
- ])
- , h('div.layout--two', [
- h('fieldset', [
- h('label', ['Towards an Event', h('small', ' (optional) ')])
- , fundraiserSelects('event', state.events$())
- ])
- , h('fieldset', [
- h('label', ['Towards a Campaign ', h('small', ' (optional) ')])
- , fundraiserSelects('campaign', state.campaigns$())
- ])
- ])
- , h('div.layout--two', [
- h('fieldset', [
- h('label', ['In Memory/Honor Of ', h('small', ' (optional) ')])
- , h('textarea', {props: {rows: 3, name: 'dedication', placeholder: 'In Memory/Honor Of'}})
- ])
- , h('fieldset', [
- h('label', ['Designation ', h('small', ' (optional) ')])
- , h('textarea', {props: {rows: 3, name: 'designation', placeholder: 'Designation'}})
- ])
- ])
- , h('fieldset', [
- h('label', ['Notes ', h('small', ' (optional) ')])
- , h('textarea', {props: {rows: 3, name: 'comment', placeholder: 'Notes'}})
- ])
- , h('div.u-centered', [
- button({loading$: state.loading$, error$: state.error$})
- ])
- ])
-}
-
-const fundraiserSelects = (type, arr) =>
- h('select', {props: {name: `${type}_id`}}
- , R.concat(
- [h('option', {props: {value: ''}}, 'Select One')]
- , R.map(x => h('option', {props: {value: x.id}}, x.name), arr)
- )
- )
-
module.exports = {init, view}
diff --git a/client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js b/client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js
index c0629f5d..6fd4a969 100644
--- a/client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js
+++ b/client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js
@@ -5,6 +5,7 @@ const flyd = require('flyd')
const h = require('snabbdom/h')
flyd.mergeAll = require('flyd/module/mergeall')
+
const button = (text, stream) =>
h('button.button--tiny.u-marginRight--10', {on: {click: stream}}
, [h('i.fa.fa-plus.u-marginRight--5') , text ])
@@ -13,8 +14,12 @@ const view = state =>
h('section.timeline-actions.u-padding--10', [
button('Note', state.newNote$)
, button('Email', state.clickComposing$)
- , button('Donation', [state.modalID$, 'newOffsiteDonationModal'])
- ])
+ , button('Donation', () => appl.open_donation_modal(state.supporter$().id,
+ () => {state.offsiteDonationForm.saved$(Math.random())}
+ )
+ )
+ ]
+ )
module.exports = {view}
diff --git a/client/js/onboard/page.js b/client/js/onboard/page.js
deleted file mode 100644
index 7b854e1b..00000000
--- a/client/js/onboard/page.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// License: LGPL-3.0-or-later
-require('../common/onboard')
-
diff --git a/client/js/pricing/page.js b/client/js/pricing/page.js
deleted file mode 100644
index 7b854e1b..00000000
--- a/client/js/pricing/page.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// License: LGPL-3.0-or-later
-require('../common/onboard')
-
diff --git a/config/default_organization.yml b/config/default_organization.yml
index e24b45a8..155cc596 100644
--- a/config/default_organization.yml
+++ b/config/default_organization.yml
@@ -27,3 +27,6 @@ intntl:
symbol: "€"
abbv: "eur"
format: "%n%u"
+
+api_domain:
+ url: "http://localhost:5000"
diff --git a/config/environment.rb b/config/environment.rb
index 31707397..74e95206 100755
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -284,6 +284,11 @@ Config.schema do
required(:url).filled?(:str)
end
+ # the domain for your api. Usually will be your CDN.url
+ optional(:api_domain).schema do
+ required(:url).filled?(:str)
+ end
+
end
Settings.reload!
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 903bffd6..a745be9f 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -10,6 +10,8 @@ en:
body: 'Comment content'
organization:
name: "Organisation"
+ organization_page:
+ promote: "Promote this organization"
donation:
amount: "Total Amount"
date: "Transaction Date"
@@ -29,6 +31,7 @@ en:
transfer_label_html: "
Donation %{nonprofit_statement}."
oneoff_donation_html: "Your donation towards
%{nonprofit_name} was successful!"
recurring_donation_html: "Your recurring donation towards
%{nonprofit_name}, started on %{start_date}, has been successfully paid."
+ recurring_donation_cancel_modify_html: "If you need to update your card or cancel your recurring donation, please follow this link:
%{management_url}"
donor_direct_debit_notification:
subject: "Donation receipt for %{nonprofit_name}"
transfer_info_html: "This transfer will appear on your bank statement as %{label}"
diff --git a/config/settings.yml b/config/settings.yml
index 560c403a..3d74efd0 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -73,3 +73,6 @@ source_tokens:
nonprofits_must_be_vetted: false
+api_domain:
+ url: "http://localhost:5000"
+
diff --git a/db/migrate/20181002160627_correct_dedications.rb b/db/migrate/20181002160627_correct_dedications.rb
new file mode 100644
index 00000000..40657773
--- /dev/null
+++ b/db/migrate/20181002160627_correct_dedications.rb
@@ -0,0 +1,30 @@
+class CorrectDedications < ActiveRecord::Migration
+ def up
+ execute <<-SQL
+ create or replace function is_valid_json(p_json text)
+ returns boolean
+as
+$$
+begin
+ return (p_json::json is not null);
+exception
+ when others then
+ return false;
+end;
+$$
+language plpgsql
+immutable;
+ SQL
+
+ dedications = MaintainDedications.retrieve_non_json_dedications
+
+ MaintainDedications.create_json_dedications_from_plain_text(dedications)
+
+ dedications = MaintainDedications.retrieve_json_dedications
+ MaintainDedications.add_honor_to_any_json_dedications_without_type(dedications)
+ end
+
+ def down
+ end
+end
+
diff --git a/db/migrate/20181003212559_correct_dedication_contacts.rb b/db/migrate/20181003212559_correct_dedication_contacts.rb
new file mode 100644
index 00000000..9631d9ba
--- /dev/null
+++ b/db/migrate/20181003212559_correct_dedication_contacts.rb
@@ -0,0 +1,52 @@
+class CorrectDedicationContacts < ActiveRecord::Migration
+ def up
+ json_dedications = Qx.select('id', 'dedication').from(:donations)
+ .where("dedication IS NOT NULL AND dedication != ''")
+ .and_where("is_valid_json(dedication)").ex
+ parsed_dedications = json_dedications.map{|i| {id: i['id'], dedication: JSON.parse(i['dedication'])}}
+ with_contact_to_correct = parsed_dedications.select {|i| !i[:dedication]['contact'].blank? && i[:dedication]['contact'].is_a?(String)}
+ really_icky_dedications, easy_to_split_strings = with_contact_to_correct.partition{|i| i[:dedication]['contact'].split(" - ").count > 3}
+
+ easy_to_split_strings.map do |i|
+ split_contact = i[:dedication]['contact'].split(' - ')
+ i[:dedication]['contact'] = {
+ email: split_contact[0],
+ phone:split_contact[1],
+ address: split_contact[2],
+ }
+ puts i
+ i
+ end.each_with_index do |i, index|
+ unless (i[:id])
+ raise Error("Item at index #{index} is invalid. Object:#{i}")
+ end
+ Qx.update(:donations).where("id = $id", id:i[:id]).set(dedication: JSON.generate(i[:dedication])).ex
+ end
+
+ puts "Corrected #{easy_to_split_strings.count} records."
+
+ puts ""
+ puts ""
+ puts "You must manually fix the following dedications: "
+ really_icky_dedications.each do |i|
+ puts i
+ end
+ end
+
+ def down
+ json_dedications = Qx.select('id', 'dedication').from(:donations)
+ .where("dedication IS NOT NULL AND dedication != ''")
+ .and_where("is_valid_json(dedication)").ex
+
+ parsed_dedications = json_dedications.map{|i| {'id' => i['id'], 'dedication' => JSON.parse(i['dedication'])}}
+
+ with_contact_to_correct = parsed_dedications.select {|i| i['dedication']['contact'].is_a?(Hash)}
+
+ puts "#{with_contact_to_correct.count} to revert"
+ with_contact_to_correct.each do |i|
+ contact_string = "#{i['dedication']['contact']['email']} - #{i['dedication']['contact']['phone']} - #{i['dedication']['contact']['address']}"
+ i['dedication']['contact'] = contact_string
+ Qx.update(:donations).where("id = $id", id:i['id']).set(dedication: JSON.generate(i['dedication'])).ex
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 5609c55f..0844371c 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -57,6 +57,22 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;
COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)';
+--
+-- Name: is_valid_json(text); Type: FUNCTION; Schema: public; Owner: -
+--
+
+CREATE FUNCTION public.is_valid_json(p_json text) RETURNS boolean
+ LANGUAGE plpgsql IMMUTABLE
+ AS $$
+begin
+ return (p_json::json is not null);
+exception
+ when others then
+ return false;
+end;
+$$;
+
+
--
-- Name: update_supporter_assoc_search_vectors(); Type: FUNCTION; Schema: public; Owner: -
--
@@ -4327,3 +4343,7 @@ INSERT INTO schema_migrations (version) VALUES ('20180713215825');
INSERT INTO schema_migrations (version) VALUES ('20180713220028');
+INSERT INTO schema_migrations (version) VALUES ('20181002160627');
+
+INSERT INTO schema_migrations (version) VALUES ('20181003212559');
+
diff --git a/docker/build/Dockerfile b/docker/build/Dockerfile
index a0052615..9b99c574 100644
--- a/docker/build/Dockerfile
+++ b/docker/build/Dockerfile
@@ -13,4 +13,5 @@ WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
-CMD
+CMD rake db:create db:structure:load test:prepare && rake spec && npx jest && npm run build-all
+
diff --git a/javascripts/app/create_new_offsite_payment_pane.tsx b/javascripts/app/create_new_offsite_payment_pane.tsx
new file mode 100644
index 00000000..f27ef0d3
--- /dev/null
+++ b/javascripts/app/create_new_offsite_payment_pane.tsx
@@ -0,0 +1,33 @@
+// License: LGPL-3.0-or-later
+// require a root component here. This will be treated as the root of a webpack package
+import Root from "../src/components/common/Root"
+import CreateOffsitePaymentPane from "../src/components/create_offsite_payment_pane/CreateOffsitePaymentPane"
+
+import * as ReactDOM from 'react-dom'
+import * as React from 'react'
+
+export interface FundraiserInfo {
+ id: number
+ name: string
+}
+
+function LoadReactPage(element:HTMLElement, events: FundraiserInfo[],
+ campaigns: FundraiserInfo[],
+ nonprofitId: number,
+ supporterId:number,
+ preupdateDonationAction:() => void,
+ postUpdateSuccess: () => void,
+
+ //from ModalProps
+ onClose: () => void,
+ modalActive: boolean,
+ nonprofitTimezone?: string) {
+ ReactDOM.render(
, element)
+}
+
+
+(window as any).LoadReactCreateOffsiteDonationPane = LoadReactPage
\ No newline at end of file
diff --git a/javascripts/app/edit_payment_pane.tsx b/javascripts/app/edit_payment_pane.tsx
new file mode 100644
index 00000000..6f9a4d81
--- /dev/null
+++ b/javascripts/app/edit_payment_pane.tsx
@@ -0,0 +1,27 @@
+// License: LGPL-3.0-or-later
+// require a root component here. This will be treated as the root of a webpack package
+import Root from "../src/components/common/Root"
+import EditPaymentPane, {FundraiserInfo} from "../src/components/edit_payment_pane/EditPaymentPane"
+
+import * as ReactDOM from 'react-dom'
+import * as React from 'react'
+
+function LoadReactPage(element:HTMLElement, data:any, campaigns:FundraiserInfo[],
+ events:FundraiserInfo[],
+ onClose:() => void,
+ modalActive:boolean,
+ preupdateDonationAction: () => void,
+ postUpdateSuccess: () => void,
+ nonprofitTimezone?:string
+
+ ) {
+ ReactDOM.render(
, element)
+}
+
+
+(window as any).LoadReactEditPaymentPane = LoadReactPage
\ No newline at end of file
diff --git a/javascripts/src/components/common/Modal.spec.tsx b/javascripts/src/components/common/Modal.spec.tsx
new file mode 100644
index 00000000..dcad6ee5
--- /dev/null
+++ b/javascripts/src/components/common/Modal.spec.tsx
@@ -0,0 +1,28 @@
+// License: LGPL-3.0-or-later
+import * as React from 'react';
+import 'jest';
+import Modal, {ModalProps} from './Modal'
+import {shallow} from "enzyme";
+import {toJS} from "mobx";
+import toJson from "enzyme-to-json";
+
+describe('Modal', () => {
+ test('nothing displayed if inactive', () => {
+ let modal = shallow(
}/>)
+
+ expect(toJson(modal)).toMatchSnapshot()
+ })
+
+ test('active modal displays', () => {
+ let onCloseWasCalled = false
+ let modal = shallow( { onCloseWasCalled = true}}
+ childGenerator={() => }/>)
+ expect(toJson(modal)).toMatchSnapshot()
+ let modalComponent = modal.instance() as React.Component //casting to modal didn't work for reasons?
+ modalComponent.props.onClose()
+ expect(onCloseWasCalled).toBeTruthy()
+ })
+})
\ No newline at end of file
diff --git a/javascripts/src/components/common/Modal.tsx b/javascripts/src/components/common/Modal.tsx
new file mode 100644
index 00000000..679c8728
--- /dev/null
+++ b/javascripts/src/components/common/Modal.tsx
@@ -0,0 +1,41 @@
+// License: LGPL-3.0-or-later
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import AriaModal = require('react-aria-modal');
+
+export interface ModalProps
+{
+ onClose?: () => void // if you want your modal to close, this needs to set modalActive to false
+ modalActive?: boolean
+ titleText?: string
+ focusDialog?:boolean
+ dialogStyle?:any
+ childGenerator:() => any
+}
+
+class Modal extends React.Component {
+
+ static defaultProps = {
+ dialogStyle: {minWidth:'768px'}
+ }
+
+ render() {
+ const modal = this.props.modalActive ?
+
+
+ {this.props.titleText}
+
+
+ {this.props.childGenerator()}
+
+ : false;
+
+ return modal
+ }
+}
+
+export default observer(Modal)
+
+
+
diff --git a/javascripts/src/components/common/StandardFieldComponent.tsx b/javascripts/src/components/common/StandardFieldComponent.tsx
index 48c23aaf..14380c43 100644
--- a/javascripts/src/components/common/StandardFieldComponent.tsx
+++ b/javascripts/src/components/common/StandardFieldComponent.tsx
@@ -30,7 +30,7 @@ export default class StandardFieldComponent extends React.Component{stickyErrorMessage}
- {this.renderChildren()}
+ {this.props.children}
{errorDiv}
{stickyErrorDiv }
diff --git a/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap b/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap
new file mode 100644
index 00000000..bf79ae7c
--- /dev/null
+++ b/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Modal active modal displays 1`] = `
+
+`;
+
+exports[`Modal nothing displayed if inactive 1`] = `""`;
diff --git a/javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap b/javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap
index eaae44af..acfd395c 100644
--- a/javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap
+++ b/javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap
@@ -2,10 +2,7 @@
exports[`StandardFieldComponent sets error message properly 1`] = `