diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c019d5c..db7bb13 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -40,7 +40,9 @@
dlg.removeAttribute('aria-hidden');
dlg.setAttribute('role', 'alertdialog');
dlg.setAttribute('tabindex', '0');
- dlg.focus();
+ if (!dlg.getAttribute('data-nofocus')) {
+ dlg.focus();
+ }
setTimeout(function() { dlg.classList.add('open'); }, 100);
}
window.closeOverlay = function(dlg, primaryContent, body) {
@@ -114,6 +116,14 @@
return false;
});
});
+ var helpDlg = document.getElementById('help-dlg');
+ forEachElement('[data-help-text]', function(link) {
+ link.addEventListener('click', function(event) {
+ event.preventDefault();
+ openDlg(helpDlg, link);
+ return false;
+ });
+ });
var loginDlg = document.getElementById('login-dlg');
forEachElement('[data-sign-in]', function(link) {
link.addEventListener('click', function(event) {
diff --git a/app/assets/javascripts/registrations.js b/app/assets/javascripts/registrations.js
index f467202..db55a28 100644
--- a/app/assets/javascripts/registrations.js
+++ b/app/assets/javascripts/registrations.js
@@ -1,168 +1,200 @@
(function() {
- var searchControl = document.getElementById('search');
+ var searchControl = document.getElementById('search');
- function filterTable() {
- forEach(document.getElementById('search-table').getElementsByTagName('TBODY')[0].getElementsByTagName('TR'), function(tr) {
- if (tr.classList.contains('editable')) {
- tr.classList.remove('hidden');
+ function filterTable() {
+ forEach(document.getElementById('search-table').getElementsByTagName('TBODY')[0].getElementsByTagName('TR'), function(tr) {
+ if (tr.classList.contains('editable')) {
+ tr.classList.remove('hidden');
- var value = searchControl.value;
- if (value) {
- var words = value.split(/\s+/);
- for (var i = 0; i < words.length; i++) {
- var word = new RegExp(words[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), "i");
- if (tr.innerHTML.search(word) == -1) {
- tr.classList.add('hidden');
- }
- }
- }
- }
- });
- }
+ var value = searchControl.value;
+ if (value) {
+ var words = value.split(/\s+/);
+ for (var i = 0; i < words.length; i++) {
+ var word = new RegExp(words[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), "i");
+ if (tr.innerHTML.search(word) == -1) {
+ tr.classList.add('hidden');
+ }
+ }
+ }
+ }
+ });
+ }
- // ref = https://davidwalsh.name/element-matches-selector
- function selectorMatches(el, selector) {
- var p = Element.prototype;
- var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
- return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
- };
- return f.call(el, selector);
- }
+ // ref = https://davidwalsh.name/element-matches-selector
+ function selectorMatches(el, selector) {
+ if (el instanceof Element) {
+ var p = Element.prototype;
+ var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
+ return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
+ };
+ return f.call(el, selector);
+ }
+ return false;
+ }
- function saveRow(row) {
- if (row) {
- row.classList.remove('editing');
- var table = row.parentElement.parentElement;
- var editRow = row.nextSibling;
- var url = table.getAttribute('data-update-url');
- var data = new FormData();
- var request = new XMLHttpRequest();
- request.onreadystatechange = function() {
- if (request.readyState == 4) {
- row.classList.remove('requesting');
- if (request.status == 200 && request.responseText) {
- var tempTable = document.createElement('table');
- tempTable.innerHTML = request.responseText;
- var rows = tempTable.getElementsByTagName('tr');
- row.innerHTML = rows[0].innerHTML;
- editRow.innerHTML = rows[1].innerHTML;
- }
- }
- }
- request.open('POST', url, true);
- cells = editRow.getElementsByClassName('cell-editor');
- data.append('key', row.getAttribute('data-key'));
- data.append('button', 'update');
- var changed = false;
- for (var i = 0; i < cells.length; i++) {
- if (cells[i].value !== cells[i].getAttribute('data-value')) {
- data.append(cells[i].getAttribute('name'), cells[i].value);
- changed = true;
- }
- }
- if (changed) {
- row.classList.add('requesting');
- request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- request.send(data);
- }
- }
- }
+ function saveRow(row) {
+ if (row) {
+ row.classList.remove('editing');
+ var table = row.parentElement.parentElement;
+ var editRow = row.nextSibling;
+ var url = table.getAttribute('data-update-url');
+ var data = new FormData();
+ var request = new XMLHttpRequest();
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ row.classList.remove('requesting');
+ if (request.status == 200 && request.responseText) {
+ var tempTable = document.createElement('table');
+ tempTable.innerHTML = request.responseText;
+ var rows = tempTable.getElementsByTagName('tr');
+ row.innerHTML = rows[0].innerHTML;
+ editRow.innerHTML = rows[1].innerHTML;
+ }
+ }
+ }
+ request.open('POST', url, true);
+ cells = editRow.getElementsByClassName('cell-editor');
+ data.append('key', row.getAttribute('data-key'));
+ data.append('button', 'update');
+ var changed = false;
+ for (var i = 0; i < cells.length; i++) {
+ if (cells[i].value !== cells[i].getAttribute('data-value')) {
+ data.append(cells[i].getAttribute('name'), cells[i].value);
+ changed = true;
+ }
+ }
+ if (changed) {
+ row.classList.add('requesting');
+ request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ request.send(data);
+ }
+ }
+ }
- function editTableCell(cell) {
- if (cell && selectorMatches(cell, 'tr[data-key].editable td')) {
- editTableRow(cell.parentElement, cell);
- } else if (!cell || !selectorMatches(cell, 'tr[data-key].editable + tr, tr[data-key].editable + tr *')) {
- var currentRow = document.querySelector('tr[data-key].editable.editing');
- if (currentRow) {
- saveRow(currentRow);
- }
- }
- }
- function editTableRow(row, cell) {
- if (selectorMatches(row, 'tr[data-key].editable')) {
- var key = row.getAttribute('data-key');
- var currentRow = document.querySelector('tr[data-key].editable.editing');
- if (currentRow && currentRow.getAttribute('data-key') !== key) {
- saveRow(currentRow);
- }
- var editor = row.nextSibling;
- if (!row.classList.contains('editing')) {
- row.classList.add('editing');
- var focusElement = null;
- if (cell) {
- focusElement = editor.querySelector('td[data-column-id="' + cell.getAttribute('data-column-id') + '"] .cell-editor');
- }
- focusElement = focusElement || editor.getElementsByClassName('cell-editor')[0];
- focusElement.focus();
- if (focusElement.tagName === 'TEXTAREA' || (focusElement.tagName === 'INPUT' && focusElement.type != 'number' && focusElement.type != 'email')) {
- focusElement.setSelectionRange(0, focusElement.value.length);
- }
- }
- }
- }
- document.addEventListener('click', function (event) { editTableCell(event.target); });
- document.addEventListener('keyup', function (event) {
- if (event.code === "Enter") {
- var currentRow = document.querySelector('tr[data-key].editable.editing');
- if (currentRow) {
- event.stopPropagation();
- event.preventDefault();
- var next = event.shiftKey ? 'previousSibling' : 'nextSibling';
- var cell = document.activeElement.parentElement.getAttribute('data-column-id');
- var row = currentRow[next] ? currentRow[next][next] : null;
- if (!row) {
- rows = currentRow.parentElement.children;
- row = event.shiftKey ? rows[rows.length - 2] : rows[0];
- }
- editTableRow(row, row.querySelector('[data-column-id="' + cell + '"]'));
- }
- } else if (event.code === "Escape") {
- var currentRow = document.querySelector('tr[data-key].editable.editing');
- if (currentRow) {
- event.stopPropagation();
- event.preventDefault();
- saveRow(currentRow);
- }
- }
- });
- if (document.observe) {
- document.observe("focusin", function (event) { editTableCell(event.target); });
- } else {
- document.addEventListener("focus", function (event) { editTableCell(event.target); }, true);
- }
+ function sortBy(table, column, dir) {
+ if (!dir) {
+ dir = 'down';
+ } else if (dir == 'down') {
+ dir = 'up';
+ } else {
+ dir = 'down';
+ }
- window.onbeforeunload = function() {
- editTableCell();
- };
+ var url = table.getAttribute('data-sort-url') + '?sort_column=' + column + '&sort_dir=' + dir;
+ var tableContainer = table.parentElement;
- searchControl.addEventListener('keyup', filterTable);
- searchControl.addEventListener('search', filterTable);
+ tableContainer.classList.add('requesting');
+ var request = new XMLHttpRequest();
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ tableContainer.classList.remove('requesting');
+ if (request.status == 200 && request.responseText) {
+ tableContainer.innerHTML = request.responseText;
+ }
+ }
+ }
+ request.open('GET', url, true);
+ request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ request.send();
+ }
- forEachElement('[data-expands]', function(button) {
- button.addEventListener('click', function(event) {
- var element = document.getElementById(event.target.getAttribute('data-expands'));
- document.body.classList.add('expanded-element');
- element.classList.add('expanded');
- });
- });
- forEachElement('[data-contracts]', function(button) {
- button.addEventListener('click', function(event) {
- var element = document.getElementById(event.target.getAttribute('data-contracts'));
- document.body.classList.remove('expanded-element');
- element.classList.remove('expanded');
- });
- });
- forEachElement('[data-opens-modal]', function(button) {
- button.addEventListener('click', function(event) {
- var element = document.getElementById(event.target.getAttribute('data-opens-modal'));
- document.body.classList.add('modal-open');
- element.classList.add('open');
- });
- });
- forEachElement('[data-closes-modal]', function(element) {
- element.addEventListener('click', function(event) {
- document.getElementById(event.target.getAttribute('data-closes-modal')).classList.remove('open');
- document.body.classList.remove('modal-open');
- });
- });
+ function editTableCell(cell) {
+ if (cell && selectorMatches(cell, 'tr[data-key].editable td')) {
+ editTableRow(cell.parentElement, cell);
+ } else if (cell && selectorMatches(cell, 'th[data-colname]')) {
+ sortBy(cell.parentElement.parentElement.parentElement, cell.getAttribute('data-colname'), cell.getAttribute('data-dir'));
+ } else if (!cell || !selectorMatches(cell, 'tr[data-key].editable + tr, tr[data-key].editable + tr *')) {
+ var currentRow = document.querySelector('tr[data-key].editable.editing');
+ if (currentRow) {
+ saveRow(currentRow);
+ }
+ }
+ }
+ function editTableRow(row, cell) {
+ if (selectorMatches(row, 'tr[data-key].editable')) {
+ var key = row.getAttribute('data-key');
+ var currentRow = document.querySelector('tr[data-key].editable.editing');
+ if (currentRow && currentRow.getAttribute('data-key') !== key) {
+ saveRow(currentRow);
+ }
+ var editor = row.nextSibling;
+ if (!row.classList.contains('editing')) {
+ row.classList.add('editing');
+ var focusElement = null;
+ if (cell) {
+ focusElement = editor.querySelector('td[data-column-id="' + cell.getAttribute('data-column-id') + '"] .cell-editor');
+ }
+ focusElement = focusElement || editor.getElementsByClassName('cell-editor')[0];
+ focusElement.focus();
+ if (focusElement.tagName === 'TEXTAREA' || (focusElement.tagName === 'INPUT' && focusElement.type != 'number' && focusElement.type != 'email')) {
+ focusElement.setSelectionRange(0, focusElement.value.length);
+ }
+ }
+ }
+ }
+ document.addEventListener('click', function (event) { editTableCell(event.target); });
+ document.addEventListener('keyup', function (event) {
+ if (event.code === "Enter") {
+ var currentRow = document.querySelector('tr[data-key].editable.editing');
+ if (currentRow) {
+ event.stopPropagation();
+ event.preventDefault();
+ var next = event.shiftKey ? 'previousSibling' : 'nextSibling';
+ var cell = document.activeElement.parentElement.getAttribute('data-column-id');
+ var row = currentRow[next] ? currentRow[next][next] : null;
+ if (!row) {
+ rows = currentRow.parentElement.children;
+ row = event.shiftKey ? rows[rows.length - 2] : rows[0];
+ }
+ editTableRow(row, row.querySelector('[data-column-id="' + cell + '"]'));
+ }
+ } else if (event.code === "Escape") {
+ var currentRow = document.querySelector('tr[data-key].editable.editing');
+ if (currentRow) {
+ event.stopPropagation();
+ event.preventDefault();
+ saveRow(currentRow);
+ }
+ }
+ });
+ if (document.observe) {
+ document.observe("focusin", function (event) { editTableCell(event.target); });
+ } else {
+ document.addEventListener("focus", function (event) { editTableCell(event.target); }, true);
+ }
+
+ window.onbeforeunload = function() {
+ editTableCell();
+ };
+
+ searchControl.addEventListener('keyup', filterTable);
+ searchControl.addEventListener('search', filterTable);
+
+ forEachElement('[data-expands]', function(button) {
+ button.addEventListener('click', function(event) {
+ var element = document.getElementById(event.target.getAttribute('data-expands'));
+ document.body.classList.add('expanded-element');
+ element.classList.add('expanded');
+ });
+ });
+ forEachElement('[data-contracts]', function(button) {
+ button.addEventListener('click', function(event) {
+ var element = document.getElementById(event.target.getAttribute('data-contracts'));
+ document.body.classList.remove('expanded-element');
+ element.classList.remove('expanded');
+ });
+ });
+ forEachElement('[data-opens-modal]', function(button) {
+ button.addEventListener('click', function(event) {
+ var element = document.getElementById(event.target.getAttribute('data-opens-modal'));
+ document.body.classList.add('modal-open');
+ element.classList.add('open');
+ });
+ });
+ forEachElement('[data-closes-modal]', function(element) {
+ element.addEventListener('click', function(event) {
+ document.getElementById(event.target.getAttribute('data-closes-modal')).classList.remove('open');
+ document.body.classList.remove('modal-open');
+ });
+ });
})();
diff --git a/app/assets/stylesheets/_admin.scss b/app/assets/stylesheets/_admin.scss
index 5da4120..b9980e0 100644
--- a/app/assets/stylesheets/_admin.scss
+++ b/app/assets/stylesheets/_admin.scss
@@ -29,6 +29,61 @@ nav.sub-nav {
}
table, .table {
+ tr.spacer td {
+ border: 0;
+ height: 0.5em;
+ }
+
+ &[data-sort-url] {
+ [data-colname] {
+ position: relative;
+ cursor: pointer;
+ white-space: nowrap;
+
+ @include after {
+ content: 'ꜜ';
+ position: absolute;
+ bottom: -1em;
+ left: 50%;
+ font-size: 1.5em;
+ opacity: 0;
+ z-index: 2;
+ margin-left: -0.25em;
+ pointer-events: none;
+ @include _(transition, opacity 150ms ease-in-out);
+ }
+
+ &:hover {
+ @include after {
+ opacity: 1;
+ }
+ }
+ }
+
+ [data-dir] {
+ @include after {
+ opacity: 0.5;
+ }
+
+ &:hover {
+ @include after {
+ content: 'ꜛ';
+ }
+ }
+ }
+ [data-dir="up"] {
+ @include after {
+ content: 'ꜛ';
+ }
+
+ &:hover {
+ @include after {
+ content: 'ꜜ';
+ }
+ }
+ }
+ }
+
th, td, .table-th, .table-td {
&.center {
text-align: center;
@@ -51,45 +106,50 @@ table, .table {
width: 1.75em;
&.happy {
- background-image: url("data:image/svg+xml;utf8,");
+ @include after {
+ content: '\1F601';
+ opacity: 0.5;
+ }
}
&.unhappy {
- background-image: url('data:image/svg+xml;utf8,');
+ @include after {
+ content: '\1F621';
+ }
}
}
}
td, .table-td {
&.inner-table {
- padding: 0;
+ padding: 0.5em;
table {
margin: 0;
width: 100%;
}
- tr:first-child {
- td, th {
- border-top: 0;
- }
- }
+ // tr:first-child {
+ // td, th {
+ // border-top: 0;
+ // }
+ // }
- tr:last-child {
- td, th {
- border-bottom: 0;
- }
- }
+ // tr:last-child {
+ // td, th {
+ // border-bottom: 0;
+ // }
+ // }
- td, th {
- &:first-child {
- border-left: 0
- }
+ // td, th {
+ // &:first-child {
+ // border-left: 0
+ // }
- &:last-child {
- border-right: 0
- }
- }
+ // &:last-child {
+ // border-right: 0
+ // }
+ // }
}
&.bold {
@@ -359,6 +419,7 @@ body.expanded-element {
z-index: 1002;
background-color: #F8F8F8;
flex: 1;
+ padding-bottom: 1em;
}
.actions {
@@ -636,6 +697,12 @@ nav.sub-menu {
}
}
+#registrations-table {
+ .button {
+ margin-top: 0;
+ }
+}
+
#main article #registration-admin-menu {
margin: 1em 0 0;
padding: 0;
@@ -745,6 +812,15 @@ nav.sub-menu {
}
}
+@include keyframes(unhappy) {
+ from {
+ @include _(transform, rotate(15deg));
+ }
+ to {
+ @include _(transform, rotate(-15deg));
+ }
+}
+
#admin-housing, #admin-schedule {
.guests-housed {
margin-bottom: 1em;
@@ -759,12 +835,33 @@ nav.sub-menu {
.data {
display: inline-block;
font-size: 1.125em;
+
+ @include after {
+ margin-left: 0.5em;
+ font-size: 1.5em;
+ }
+
+ &.happy {
+ @include after {
+ content: '\1F60D';
+ }
+ }
+
+ &.unhappy {
+ @include after {
+ content: '\1F61E';
+ }
+ }
}
}
#housing-table {
@include _(transition, opacity 1s ease-in-out);
+ table {
+ margin-left: 0;
+ }
+
&.loading {
@include _(opacity, 0.5);
pointer-events: none;
@@ -780,6 +877,10 @@ nav.sub-menu {
text-align: right;
@include font-family(primary);
}
+
+ .name {
+ min-width: 10em;
+ }
}
}
@@ -788,28 +889,17 @@ nav.sub-menu {
td {
background-color: lighten($colour-1, 40%);
- &:hover {
- background-color: $colour-1;
- }
-
&.full {
background-color: $gray;
- &:hover {
- background-color: #CCC;
- }
+ .button {
+ background-color: #888;
+ }
}
}
- a {
- display: block;
- color: $white;
- text-align: center;
- @include font-family(secondary);
-
- @include after {
- display: none;
- }
+ .button {
+ display: inline-block;
}
}
@@ -819,15 +909,31 @@ nav.sub-menu {
}
td {
- vertical-align: top;
+ vertical-align: middle;
}
.state {
position: relative;
font-family: inherit;
+ padding: 0;
+ position: relative;
+ width: 2em;
+ height: 2em;
+
+ @include after {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ font-size: 1.5em;
+ }
&.unhappy {
cursor: pointer;
+
+ @include after {
+ @include _(transform-origin, bottom);
+ @include _(animation, unhappy ease-in-out 1s infinite alternate both);
+ }
}
ul {
@@ -837,7 +943,7 @@ nav.sub-menu {
top: 0;
background-color: $white;
border: 0.1em solid #CCC;
- padding: 0.25em 0.75em 0.25em 1.5em;
+ padding: 0.25em 0.75em;
margin: 0;
list-style-type: square;
@include default-box-shadow(top, 2);
@@ -846,7 +952,12 @@ nav.sub-menu {
li {
white-space: nowrap;
- margin: 0;
+ margin: 0 0 0 1em;
+
+ &:first-child:last-child {
+ list-style: none;
+ margin: 0;
+ }
}
&:hover {
@@ -893,25 +1004,71 @@ nav.sub-menu {
background-color: $white;
width: 80%;
margin: auto;
- padding: 1em;
height: 80%;
cursor: default;
@include default-box-shadow(top, 2);
h3 {
+ text-align: center;
margin: 0 0 1em;
+ padding: 0.5em 0.6667em;
+ color: $white;
+ background-color: $green;
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
}
}
}
}
+.table-scroller.no-edit {
+ box-shadow: none;
+ background-color: transparent;
+
+ table {
+ margin-bottom: 0;
+ }
+}
+
+#table, #help-dlg {
+ .legend ul {
+ @include _-(display, flex);
+ list-style: none;
+ padding: 0;
+
+ li {
+ @include _(flex, 1);
+ text-align: center;
+ margin-bottom: 0.5em;
+ padding: 0.125em 0.5em;
+ margin: 0.1em;
+ border: 0.1em solid $light-gray;
+ background-color: #F8F8F8;
+ @include font-family(secondary);
+
+ &.other-host, &.other-space, &.bad-match {
+ opacity: 0.5;
+ }
+
+ &.selected-space, &.other-space {
+ background-color: $colour-5;
+ }
+
+ &.other-host {
+ background-color: $colour-1;
+ }
+ }
+ }
+}
#table {
+ display: flex;
+ flex-direction: column;
position: relative;
overflow: auto;
height: 80%;
- height: calc(100% - 4em);
+ height: calc(100% - 6.5em);
background-color: $white;
+ margin: 1em;
@include _(transition, background-color 250ms ease-in-out);
&.loading {
@@ -924,7 +1081,7 @@ nav.sub-menu {
}
table {
- margin: 0 0 2em;
+ margin: 0 0 1em;
}
h4 {
@@ -992,45 +1149,27 @@ nav.sub-menu {
}
}
- .legend ul {
- @include _-(display, flex);
- list-style: none;
- padding: 0;
-
- li {
- @include _(flex, 1);
- text-align: center;
- margin-bottom: 0.5em;
- padding: 0.125em 0.5em;
- margin: 0.1em;
- border: 0.1em solid $light-gray;
- background-color: #F8F8F8;
- @include font-family(secondary);
+ .p {
+ max-height: 4em;
+ overflow: auto;
+ }
- &.other-host, &.other-space, &.bad-match {
- opacity: 0.5;
- }
+ .guest-table {
+ flex: 1;
+ display: flex;
+ }
- &.selected-space, &.other-space {
- background-color: $colour-5;
- }
+ td, th {
+ white-space: nowrap;
- &.other-host {
- background-color: $colour-1;
- }
+ &.break-ok {
+ white-space: normal;
+ min-width: 10em;
}
}
-
- .p {
- max-height: 4em;
- overflow: auto;
- }
}
#admin-housing {
- #table table {
- min-width: 100em;
- }
#hosts {
background-color: $white;
diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss
index 4fca4e6..a958a9a 100644
--- a/app/assets/stylesheets/_application.scss
+++ b/app/assets/stylesheets/_application.scss
@@ -141,21 +141,6 @@ table, .table {
background-color: transparent;
border: 0;
}
-
- &.state {
- background-size: 1.333em;
- background-repeat: no-repeat;
- background-position: center;
- width: 1.75em;
-
- &.happy {
- background-image: url("data:image/svg+xml;utf8,");
- }
-
- &.unhappy {
- background-image: url('data:image/svg+xml;utf8,');
- }
- }
}
th, .table-th {
@@ -167,42 +152,42 @@ table, .table {
}
}
- td, .table-td {
- &.inner-table {
- padding: 0;
+ // td, .table-td {
+ // &.inner-table {
+ // padding: 0;
- table {
- margin: 0;
- width: 100%;
- }
+ // table {
+ // margin: 0;
+ // width: 100%;
+ // }
- tr:first-child {
- td, th {
- border-top: 0;
- }
- }
+ // tr:first-child {
+ // td, th {
+ // border-top: 0;
+ // }
+ // }
- tr:last-child {
- td, th {
- border-bottom: 0;
- }
- }
+ // tr:last-child {
+ // td, th {
+ // border-bottom: 0;
+ // }
+ // }
- td, th {
- &:first-child {
- border-left: 0
- }
-
- &:last-child {
- border-right: 0
- }
- }
- }
-
- &.bold {
- @include font-family(secondary);
- }
- }
+ // td, th {
+ // &:first-child {
+ // border-left: 0
+ // }
+
+ // &:last-child {
+ // border-right: 0
+ // }
+ // }
+ // }
+
+ // &.bold {
+ // @include font-family(secondary);
+ // }
+ // }
tbody th {
width: 0.1rem;
@@ -2449,13 +2434,14 @@ a.logo {
.register-link {
font-size: 1.25em;
margin: 0.5em;
-
- .button {
- @include _(animation, radiate 2s linear infinite alternate);
- }
}
}
+.help-link {
+ float: right;
+ background-color: $red;
+}
+
.conference-details {
.links {
text-align: center;
@@ -2906,6 +2892,29 @@ body {
text-align: left;
}
+ #help-dlg {
+ .dlg-content {
+ @include _-(display, flex);
+ @include _(flex-direction, column);
+ max-width: 60rem;
+ }
+
+ .dlg-inner {
+ overflow: auto;
+ }
+
+ .title {
+ background-color: $red;
+ font-size: 2em;
+ text-align: left;
+ }
+
+ .message {
+ text-align: left;
+ font-size: 1.125em;
+ }
+ }
+
#info-dlg .message {
text-align: left;
@@ -2956,10 +2965,6 @@ body {
to { background-position: 60px 30px; }
}
-@include keyframes(radiate) {
- to { background-color: $green; }
-}
-
html :focus {
outline: 0;
}
diff --git a/app/assets/stylesheets/bumbleberry-settings.json b/app/assets/stylesheets/bumbleberry-settings.json
index 8a08ee5..fe4fa1a 100644
--- a/app/assets/stylesheets/bumbleberry-settings.json
+++ b/app/assets/stylesheets/bumbleberry-settings.json
@@ -10,7 +10,7 @@
"and_chr": ["59"],
"chrome": ["59"],
"edge": ["13"],
- "firefox": ["50"],
+ "firefox": ["52"],
"ie": ["11"],
"ios_saf": ["8", "9"]
}
diff --git a/app/controllers/conference_administration_controller.rb b/app/controllers/conference_administration_controller.rb
index f0cd794..83853ff 100644
--- a/app/controllers/conference_administration_controller.rb
+++ b/app/controllers/conference_administration_controller.rb
@@ -1,7 +1,10 @@
require 'geocoder/calculations'
require 'rest_client'
+require 'registration_controller_helper'
class ConferenceAdministrationController < ApplicationController
+ include RegistrationControllerHelper
+
def administration
set_conference
return do_403 unless @this_conference.host? current_user
@@ -224,6 +227,47 @@ class ConferenceAdministrationController < ApplicationController
return respond_to do |format|
format.xlsx { render xlsx: '../conferences/stats', filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
end
+ else
+ if params[:sort_column]
+ col = params[:sort_column].to_sym
+ @excel_data[:data].sort_by! do |row|
+ value = row[col]
+
+ if row[:raw_values].key?(col)
+ value = if row[:raw_values][col].is_a?(TrueClass)
+ 't'
+ elsif row[:raw_values][col].is_a?(FalseClass)
+ ''
+ else
+ row[:raw_values][col]
+ end
+ elsif value.is_a?(City)
+ value = value.sortable_string
+ end
+
+ if value.nil?
+ case @excel_data[:column_types][col]
+ when :datetime, [:date, :day]
+ value = Date.new
+ when :money
+ value = 0
+ else
+ value = ''
+ end
+ end
+
+ value
+ end
+
+ if params[:sort_dir] == 'up'
+ @sort_dir = :up
+ @excel_data[:data].reverse!
+ end
+
+ @sort_column = col
+ else
+ @sort_column = :name
+ end
end
@registration_count = @registrations.size
@@ -249,6 +293,10 @@ class ConferenceAdministrationController < ApplicationController
end
end
end
+
+ if request.xhr?
+ render html: view_context.html_table(@excel_data, view_context.registrations_table_options)
+ end
end
def administrate_stats
@@ -372,6 +420,7 @@ class ConferenceAdministrationController < ApplicationController
return respond_to do |format|
format.xlsx { render xlsx: '../conferences/stats', filename: "housing" }
end
+ else
end
end
@@ -428,16 +477,21 @@ class ConferenceAdministrationController < ApplicationController
columns: [
:name,
:email,
+ :date,
:status,
:is_attending,
:is_subscribed,
:registration_fees_paid,
- :date,
+ :payment_currency,
+ :payment_method,
:city,
:preferred_language
] +
User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
- [
+ [
+ :group_ride,
+ :organization,
+ :org_non_member_interest,
:arrival,
:departure,
:housing,
@@ -451,8 +505,7 @@ class ConferenceAdministrationController < ApplicationController
:last_day,
:address,
:phone
- ] + ConferenceRegistration.all_spaces +
- ConferenceRegistration.all_considerations + [
+ ] + ConferenceRegistration.all_spaces + [
:notes
],
column_types: {
@@ -460,6 +513,7 @@ class ConferenceAdministrationController < ApplicationController
date: :datetime,
email: :email,
companion_email: :email,
+ org_non_member_interest: :text,
arrival: [:date, :day],
departure: [:date, :day],
registration_fees_paid: :money,
@@ -476,6 +530,9 @@ class ConferenceAdministrationController < ApplicationController
is_subscribed: 'articles.user_settings.headings.email_subscribe',
city: 'forms.labels.generic.event_location',
date: 'articles.conference_registration.terms.Date',
+ group_ride: 'articles.conference_registration.step_names.group_ride',
+ organization: 'articles.conference_registration.step_names.org_select',
+ org_non_member_interest: 'articles.conference_registration.step_names.org_non_member_interest',
preferred_language: 'articles.conference_registration.terms.Preferred_Languages',
arrival: 'forms.labels.generic.arrival',
departure: 'forms.labels.generic.departure',
@@ -485,13 +542,15 @@ class ConferenceAdministrationController < ApplicationController
companion: 'articles.conference_registration.terms.companion',
companion_email: 'articles.conference_registration.terms.companion_email',
registration_fees_paid: 'articles.conference_registration.headings.fees_paid',
+ payment_currency: 'forms.labels.generic.payment_currency',
+ payment_method: 'forms.labels.generic.payment_method',
other: 'forms.labels.generic.other_notes',
- can_provide_housing: 'articles.conference_registration.can_provide_housing',
+ can_provide_housing: 'articles.conference_registration.housing_provider',
first_day: 'forms.labels.generic.first_day',
last_day: 'forms.labels.generic.last_day',
notes: 'forms.labels.generic.notes',
phone: 'forms.labels.generic.phone',
- address: 'forms.labels.generic.address',
+ address: 'forms.labels.generic.address_short',
contact_info: 'articles.conference_registration.headings.contact_info',
questions: 'articles.conference_registration.headings.questions',
hosting: 'articles.conference_registration.headings.hosting'
@@ -504,7 +563,7 @@ class ConferenceAdministrationController < ApplicationController
end
User.AVAILABLE_LANGUAGES.each do |l|
- @excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l.to_s}"
+ @excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l.to_s}"
end
ConferenceRegistration.all_spaces.each do |s|
@excel_data[:column_types][s] = :number
@@ -517,41 +576,51 @@ class ConferenceAdministrationController < ApplicationController
user = r.user_id ? User.where(id: r.user_id).first : nil
if user.present?
companion = view_context.companion(r)
- companion = companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}") if companion.present?
+ companion = companion.is_a?(User) ? companion.name : I18n.t("articles.conference_registration.terms.registration_status.#{companion}") if companion.present?
steps = r.steps_completed || []
if id.nil? || id == r.id
+ registration_data = r.data || {}
housing_data = r.housing_data || {}
availability = housing_data['availability'] || []
availability[0] = Date.parse(availability[0]) if availability[0].present?
availability[1] = Date.parse(availability[1]) if availability[1].present?
+ org = r.user.organizations.first
data = {
id: r.id,
name: user.firstname || '',
email: user.email || '',
- status: (view_context._"articles.conference_registration.terms.registration_status.#{view_context.registration_status(r)}"),
- is_attending: (view_context._"articles.conference_registration.questions.bike.#{r.is_attending == 'n' ? 'no' : 'yes'}"),
- is_subscribed: user.is_subscribed == false ? (view_context._'articles.conference_registration.questions.bike.no') : '',
+ status: I18n.t("articles.conference_registration.terms.registration_status.#{view_context.registration_status(r)}"),
+ is_attending: I18n.t("articles.conference_registration.questions.bike.#{r.is_attending == 'n' ? 'no' : 'yes'}"),
+ is_subscribed: user.is_subscribed == false ? I18n.t('articles.conference_registration.questions.bike.no') : '',
date: r.created_at ? r.created_at.strftime("%F %T") : '',
city: r.city || '',
preferred_language: user.locale.present? ? (view_context.language_name user.locale) : '',
arrival: r.arrival ? r.arrival.strftime("%F %T") : '',
departure: r.departure ? r.departure.strftime("%F %T") : '',
- housing: r.housing.present? ? (view_context._"articles.conference_registration.questions.housing.#{r.housing}") : '',
- bike: r.bike.present? ? (view_context._"articles.conference_registration.questions.bike.#{r.bike}") : '',
- food: r.food.present? ? (view_context._"articles.conference_registration.questions.food.#{r.food}") : '',
+ group_ride: registration_data['group_ride'].present? ? I18n.t("forms.actions.generic.#{registration_data['group_ride']}") : '',
+ organization: org.present? ? org.name : '',
+ org_non_member_interest: registration_data['non_member_interest'],
+ housing: r.housing.present? ? I18n.t("articles.conference_registration.questions.housing_short.#{r.housing}") : '',
+ bike: r.bike.present? ? I18n.t("articles.conference_registration.questions.bike.#{r.bike}") : '',
+ food: r.food.present? ? I18n.t("articles.conference_registration.questions.food.#{r.food}") : '',
companion: companion,
companion_email: (housing_data['companion'] || { 'email' => ''})['email'],
- registration_fees_paid: r.registration_fees_paid,
- other: r.allergies.present? ? "#{r.allergies}\n\n#{r.other}" : r.other,
- can_provide_housing: r.can_provide_housing ? (view_context._'articles.conference_registration.questions.bike.yes') : '',
+ registration_fees_paid: registration_data['payment_amount'],
+ payment_currency: registration_data['payment_currency'],
+ payment_method: registration_data['payment_method'].present? ? I18n.t("forms.labels.generic.payment_type.#{registration_data['payment_method']}") : '',
+ other: [r.allergies, r.other, housing_data['other']].compact.join("\n\n"),
+ can_provide_housing: r.can_provide_housing.nil? ? '' : I18n.t("articles.conference_registration.questions.bike.#{r.can_provide_housing ? 'yes' : 'no'}"),
first_day: availability[0].present? ? availability[0].strftime("%F %T") : '',
last_day: availability[1].present? ? availability[1].strftime("%F %T") : '',
notes: housing_data['notes'],
address: housing_data['address'],
phone: housing_data['phone'],
raw_values: {
+ group_ride: registration_data['group_ride'],
+ registration_fees_paid: registration_data['payment_amount'].to_f,
+ payment_method: registration_data['payment_method'],
housing: r.housing,
bike: r.bike,
food: r.food,
@@ -560,12 +629,13 @@ class ConferenceAdministrationController < ApplicationController
preferred_language: user.locale,
is_attending: r.is_attending != 'n',
is_subscribed: user.is_subscribed,
- can_provide_housing: r.can_provide_housing,
+ can_provide_housing: r.can_provide_housing.to_s,
first_day: availability[0].present? ? availability[0].to_date : nil,
last_day: availability[1].present? ? availability[1].to_date : nil
},
html_values: {
date: r.created_at.present? ? r.created_at.strftime("%F %T") : '',
+ registration_fees_paid: registration_data['payment_amount'].present? ? view_context.number_to_currency(registration_data['payment_amount'].to_f, unit: '$') : '',
arrival: r.arrival.present? ? view_context.date(r.arrival.to_date, :span_same_year_date_1) : '',
departure: r.departure.present? ? view_context.date(r.departure.to_date, :span_same_year_date_1) : '',
first_day: availability[0].present? ? view_context.date(availability[0].to_date, :span_same_year_date_1) : '',
@@ -574,17 +644,13 @@ class ConferenceAdministrationController < ApplicationController
}
User.AVAILABLE_LANGUAGES.each do |l|
can_speak = ((user.languages || []).include? l.to_s)
- data["language_#{l}".to_sym] = (can_speak ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
+ data["language_#{l}".to_sym] = (can_speak ? I18n.t('articles.conference_registration.questions.bike.yes') : '')
data[:raw_values]["language_#{l}".to_sym] = can_speak
end
ConferenceRegistration.all_spaces.each do |s|
space = (housing_data['space'] || {})[s.to_s]
data[s] = space.present? ? space.to_i : nil
- end
- ConferenceRegistration.all_considerations.each do |c|
- consideration = (housing_data['considerations'] || []).include?(c.to_s)
- data[c] = (consideration ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
- data[:raw_values][c] = consideration
+ data[:raw_values][s] = space.present? ? space.to_i : 0
end
@excel_data[:data] << data
end
@@ -593,18 +659,18 @@ class ConferenceAdministrationController < ApplicationController
if html_format
yes_no = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true],
- [(view_context._"articles.conference_registration.questions.bike.no"), false]
+ [I18n.t('forms.actions.generic.yes'), true],
+ [I18n.t('forms.actions.generic.no'), false]
]
@column_options = {
housing: ConferenceRegistration.all_housing_options.map { |h| [
- (view_context._"articles.conference_registration.questions.housing.#{h}"),
+ I18n.t("articles.conference_registration.questions.housing_short.#{h}"),
h] },
bike: ConferenceRegistration.all_bike_options.map { |b| [
- (view_context._"articles.conference_registration.questions.bike.#{b}"),
+ I18n.t("articles.conference_registration.questions.bike.#{b}"),
b] },
food: ConferenceRegistration.all_food_options.map { |f| [
- (view_context._"articles.conference_registration.questions.food.#{f}"),
+ I18n.t("articles.conference_registration.questions.food.#{f}"),
f] },
arrival: view_context.conference_days_options_list(:before_plus_one),
departure: view_context.conference_days_options_list(:after_minus_one),
@@ -615,16 +681,19 @@ class ConferenceAdministrationController < ApplicationController
is_subscribed: [yes_no.last],
can_provide_housing: yes_no,
first_day: view_context.conference_days_options_list(:before),
- last_day: view_context.conference_days_options_list(:after)
+ last_day: view_context.conference_days_options_list(:after),
+ group_ride: [:yes, :no, :maybe].map { |o| [I18n.t("forms.actions.generic.#{o}"), o] },
+ payment_currency: Conference.default_currencies.map { |c| [c, c] },
+ payment_method: ConferenceRegistration.all_payment_methods.map { |c| [I18n.t("forms.labels.generic.payment_type.#{c}"), c] }
}
User.AVAILABLE_LANGUAGES.each do |l|
@column_options["language_#{l}".to_sym] = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true]
+ [I18n.t("articles.conference_registration.questions.bike.yes"), true]
]
end
ConferenceRegistration.all_considerations.each do |c|
@column_options[c.to_sym] = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true]
+ [I18n.t("articles.conference_registration.questions.bike.yes"), true]
]
end
end
@@ -633,7 +702,7 @@ class ConferenceAdministrationController < ApplicationController
def get_housing_data
@hosts = {}
@guests = {}
- ConferenceRegistration.where(:conference_id => @this_conference.id).each do |registration|
+ ConferenceRegistration.where(conference_id: @this_conference.id).each do |registration|
if registration.can_provide_housing
@hosts[registration.id] = registration
elsif registration.housing.present? && registration.housing != 'none'
@@ -657,6 +726,7 @@ class ConferenceAdministrationController < ApplicationController
@housing_data[id][:space][s.to_sym] = size
end
end
+ @unhappy_people = Set.new
@guests_housed = 0
@@ -691,7 +761,7 @@ class ConferenceAdministrationController < ApplicationController
if (guest.housing == 'house' && space == :tent) ||
(guest.housing == 'tent' && (space == :bed_space || space == :floor_space))
- @housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: (view_context._"forms.labels.generic.#{space.to_s}"), expected: (view_context._"articles.conference_registration.questions.housing.#{guest.housing}")}
+ @housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: (view_context._"forms.labels.generic.#{space.to_s}"), expected: (view_context._"articles.conference_registration.questions.housing_short.#{guest.housing}")}
end
if data['companion'].present?
@@ -717,6 +787,7 @@ class ConferenceAdministrationController < ApplicationController
end
end
end
+ @unhappy_people << guest_id if @housing_data[host_id][:guest_data][guest_id][:errors].present? || @housing_data[host_id][:guest_data][guest_id][:warnings].present?
else
# make sure the housing data is empty if the host wasn't found, just in case something happened to the host
@guests[guest_id].housing_data ||= {}
@@ -739,6 +810,7 @@ class ConferenceAdministrationController < ApplicationController
if @housing_data[id][:guests][space].size > space_available
@housing_data[id][:warnings][:space][space] << :overbooked
+ @unhappy_people << id
end
end
end
@@ -1021,12 +1093,22 @@ class ConferenceAdministrationController < ApplicationController
registration.city_id = city.id
end
end
- when :housing, :bike, :food, :allergies, :other
- registration.send("#{key.to_s}=", value)
+ when :housing, :bike, :food
+ registration.send("#{key}=", value)
+ when :other
+ registration.housing_data ||= {}
+ registration.housing_data[key] = value
+ # delete deprecated values
+ registration.allergies = nil
+ registration.other = nil
when :registration_fees_paid
- registration.registration_fees_paid = value.to_i
+ registration.data ||= {}
+ registration.data['payment_amount'] = value.to_f
+ when :group_ride, :payment_currency, :payment_method
+ registration.data ||= {}
+ registration.data[key.to_s] = value.present? ? value.to_sym : nil
when :can_provide_housing
- registration.send("#{key.to_s}=", value.present?)
+ registration.send("#{key.to_s}=", value == 'true' ? true : (value == 'false' ? false : nil))
when :arrival, :departure
registration.send("#{key.to_s}=", value.present? ? Date.parse(value) : nil)
when :companion_email
diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb
index 57d59b8..2cb13b2 100644
--- a/app/helpers/admin_helper.rb
+++ b/app/helpers/admin_helper.rb
@@ -160,21 +160,6 @@ module AdminHelper
end
end
- # housing_data = guest.housing_data || []
-
- # if housing_data['host'].present?
- # if housing_data['host'] == host.id
- # return space == housing_data['space'] ? :selected_space : :other_space
- # end
-
- # return :other_host
- # end
-
- # if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
- # return :good_match
- # end
-
- # return :bad_match
return :good_match
end
@@ -190,6 +175,7 @@ module AdminHelper
def available_dates_match?(host, guest)
return false unless host.housing_data['availability'].present? && host.housing_data['availability'][1].present?
+ return false unless guest.arrival.present? && guest.departure.present?
if host.housing_data['availability'][0] <= guest.arrival &&
host.housing_data['availability'][1] >= guest.departure
return true
@@ -208,4 +194,10 @@ module AdminHelper
end).html_safe
end).html_safe
end
+
+ def admin_help_pages
+ return {
+ housing: :housing
+ }
+ end
end
diff --git a/app/helpers/geocoder_helper.rb b/app/helpers/geocoder_helper.rb
index 6f5d29a..b607da3 100644
--- a/app/helpers/geocoder_helper.rb
+++ b/app/helpers/geocoder_helper.rb
@@ -74,7 +74,7 @@ module GeocoderHelper
return nil unless city.present?
hash = Hash.new
- region_translation = region.present? && country.present? ? _("geography.subregions.#{country}.#{region}", locale: locale) : ''
+ region_translation = region.present? && country.present? ? I18n.t("geography.subregions.#{country}.#{region}", locale: locale, resolve: false) : ''
country_translation = country.present? ? _("geography.countries.#{country}", locale: locale) : ''
hash[:city] = _!(city) if city.present?
hash[:region] = region_translation if region_translation.present?
diff --git a/app/helpers/registration_helper.rb b/app/helpers/registration_helper.rb
index 0a71205..7b6c9c4 100644
--- a/app/helpers/registration_helper.rb
+++ b/app/helpers/registration_helper.rb
@@ -8,6 +8,7 @@ module RegistrationHelper
def registration_status(registration)
return :unregistered if registration.nil?
+ return :cancelled if registration.is_attending == 'n'
return registration.status
end
@@ -47,7 +48,7 @@ module RegistrationHelper
completed_steps = registration.steps_completed || []
last_step = nil
steps = current_registration_steps(registration) || []
- steps.each do | step |
+ steps.each do |step|
# return the last enabled step if this one is disabled
return last_step unless step[:enabled]
diff --git a/app/helpers/table_helper.rb b/app/helpers/table_helper.rb
index 4b20d3c..c355465 100644
--- a/app/helpers/table_helper.rb
+++ b/app/helpers/table_helper.rb
@@ -1,7 +1,12 @@
module TableHelper
def html_edit_table(excel_data, options = {})
attributes = { class: options[:class], id: options[:id] }
- attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
+ if options[:editable].present? || options[:sortable].present?
+ attributes[:data] = {
+ 'update-url' => options[:editable],
+ 'sort-url' => options[:sortable]
+ }
+ end
if options[:column_names].is_a? Hash
return content_tag(:table, attributes) do
@@ -9,11 +14,11 @@ module TableHelper
column_names = {}
(content_tag(:thead) do
headers = ''
- options[:column_names].each do | header_name, columns |
+ options[:column_names].each do |header_name, columns|
column_names[header_name] ||= []
headers += content_tag(:th, excel_data[:keys][header_name].present? ? _(excel_data[:keys][header_name]) : '', colspan: 2)
row_count = columns.size
- columns.each do | column |
+ columns.each do |column|
column_names[header_name] << column
if (options[:row_spans] || {})[column].present?
row_count += (options[:row_spans][column] - 1)
@@ -30,15 +35,18 @@ module TableHelper
for i in 0...max_columns
columns_html = ''
- column_names.each do | header_name, columns |
+ column_names.each do |header_name, columns|
column = columns[i]
if column.present?
attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
if (options[:row_spans] || {})[column].present?
attributes[:rowspan] = options[:row_spans][column]
end
- columns_html += content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '', rowspan: attributes[:rowspan]) +
- edit_column(nil, column, nil, attributes, excel_data, options)
+
+ column_text = excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : ''
+
+ columns_html += content_tag(:th, column_text.html_safe, rowspan: attributes[:rowspan]) +
+ edit_column(nil, column, nil, attributes, excel_data, options)
elsif column != false
columns_html += content_tag(:td, ' ', colspan: 2, class: :empty)
end
@@ -56,8 +64,9 @@ module TableHelper
if (excel_data[:column_types] || {})[column] != :table && ((options[:column_names] || []).include? column)
rows += content_tag(:tr, { class: 'always-edit', data: { key: '' } }) do
attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
- columns = content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '') +
- edit_column(nil, column, nil, attributes, excel_data, options)
+ column_text = excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : ''
+
+ columns = content_tag(:th, column_text.html_safe) + edit_column(nil, column, nil, attributes, excel_data, options)
end
end
end
@@ -70,7 +79,16 @@ module TableHelper
def html_table(excel_data, options = {})
options[:html] = true
attributes = { class: options[:class], id: options[:id] }
- attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
+
+ if options[:editable].present?
+ attributes[:data] ||= {}
+ attributes[:data]['update-url'] = options[:editable]
+ end
+ if options[:sortable].present?
+ attributes[:data] ||= {}
+ attributes[:data]['sort-url'] = options[:sortable]
+ end
+
content_tag(:table, attributes) do
(content_tag(:thead) do
content_tag(:tr, excel_header_columns(excel_data))
@@ -106,7 +124,17 @@ module TableHelper
data[:columns].each do |column|
unless data[:column_types].present? && data[:column_types][column] == :table
- columns += content_tag(:th, data[:keys][column].present? ? _(data[:keys][column]) : '', class: class_name)
+ column_text = data[:keys][column].present? ? _(data[:keys][column]) : ''
+ attrs = { class: class_name }
+
+ unless @sort_column.nil?
+ attrs[:data] = { colname: column }
+
+ if @sort_column == column
+ attrs[:data][:dir] = @sort_dir || :down
+ end
+ end
+ columns += content_tag(:th, column_text.html_safe, attrs)
end
end
@@ -214,7 +242,7 @@ module TableHelper
def edit_column(row, column, value, attributes, data, options)
attributes[:class] << 'has-editor'
- raw_value = row.present? ? (row[:raw_values][column] || value) : nil
+ raw_value = row.present? ? ((row[:raw_values] || {})[column] || value) : nil
if row.present? && options[:html] && row[:html_values].present? && row[:html_values][column].present?
value = row[:html_values][column]
@@ -299,14 +327,16 @@ module TableHelper
] + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym },
questions: [
:registration_fees_paid,
+ :payment_currency,
+ :payment_method,
:is_attending,
:arrival,
:departure,
:housing,
:bike,
:food,
+ :group_ride,
:companion_email,
- :allergies,
:other
],
hosting: [
@@ -315,14 +345,15 @@ module TableHelper
:phone,
:first_day,
:last_day
- ] + ConferenceRegistration.all_spaces +
- ConferenceRegistration.all_considerations + [
+ ] + ConferenceRegistration.all_spaces + [
:notes
]
},
row_spans: {
- allergies: 3,
- other: 2
+ other: 2,
+ address: 2,
+ city: 2,
+ notes: 4
},
required_columns: [:name, :email],
editable: administration_update_path(@this_conference.slug, @admin_step),
@@ -337,12 +368,15 @@ module TableHelper
primary_key: :id,
column_names: [
:registration_fees_paid,
+ :payment_currency,
+ :payment_method,
:is_attending,
:is_subscribed,
:city,
:preferred_language,
:arrival,
:departure,
+ :group_ride,
:housing,
:bike,
:food,
@@ -360,6 +394,7 @@ module TableHelper
ConferenceRegistration.all_spaces +
ConferenceRegistration.all_considerations,
editable: administration_update_path(@this_conference.slug, @admin_step),
+ sortable: administration_step_path(@this_conference.slug, @admin_step),
column_options: @column_options
}
end
diff --git a/app/helpers/widgets_helper.rb b/app/helpers/widgets_helper.rb
index c18fc5a..a9f4b23 100644
--- a/app/helpers/widgets_helper.rb
+++ b/app/helpers/widgets_helper.rb
@@ -99,75 +99,86 @@ module WidgetsHelper
def host_guests_table(registration)
id = registration.id
html = ''
+ first_row = true
@housing_data[id][:guests].each do |area, guests|
guest_rows = ''
- guests.each do |guest_id, guest|
- status_html = ''
+ space_size = (@housing_data[id][:space][area] || 0)
- @housing_data[id][:guest_data][guest_id][:errors].each do |error, value|
- if value.is_a?(Array)
- value.each do |v|
- status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: v))
+ if space_size > 0 || guests.size > 0
+ guests.each do |guest_id, guest|
+ status_html = ''
+
+ @housing_data[id][:guest_data][guest_id][:errors].each do |error, value|
+ if value.is_a?(Array)
+ value.each do |v|
+ status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: v))
+ end
+ else
+ status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: value))
end
- else
- status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: value))
end
- end
- @housing_data[id][:guest_data][guest_id][:warnings].each do |error, value|
- if value.is_a?(Array)
- value.each do |v|
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", v))
+ @housing_data[id][:guest_data][guest_id][:warnings].each do |error, value|
+ if value.is_a?(Array)
+ value.each do |v|
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", v))
+ end
+ else
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", vars: value))
end
- else
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", vars: value))
+ end
+
+ if status_html.present?
+ status_html = content_tag(:ul, status_html.html_safe)
+ end
+
+ guest_rows += content_tag :tr, id: "hosted-guest-#{guest_id}" do
+ (content_tag :td, guest[:guest].user.name) +
+ (content_tag :td do
+ (guest[:guest].from +
+ (content_tag :a, (_'actions.workshops.Remove'), href: '#', class: 'remove-guest', data: { guest: guest_id })).html_safe
+ end) +
+ (content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
+ end
+ end
+
+ # add empty rows to represent empty guest spots
+ for i in guests.size...space_size
+ guest_rows += content_tag :tr, class: 'empty-space' do
+ (content_tag :td, ' '.html_safe, colspan: 2) +
+ (content_tag :td)
end
end
+ status_html = ''
+ if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
+ @housing_data[id][:warnings][:space][area].each do |w|
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{w.to_s}"))
+ end
+ end
if status_html.present?
status_html = content_tag(:ul, status_html.html_safe)
end
- guest_rows += content_tag :tr, id: "hosted-guest-#{guest_id}" do
- (content_tag :td, guest[:guest].user.name) +
- (content_tag :td do
- (guest[:guest].from +
- (content_tag :a, (_'actions.workshops.Remove'), href: '#', class: 'remove-guest', data: { guest: guest_id })).html_safe
- end) +
- (content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
+ unless first_row
+ html += content_tag :tr, class: :spacer do
+ content_tag :td, '', colspan: 3
+ end
end
- end
- space_size = (@housing_data[id][:space][area] || 0)
-
- # add empty rows to represent empty guest spots
- for i in guests.size...space_size
- guest_rows += content_tag :tr, class: 'empty-space' do
- (content_tag :td, ' '.html_safe, colspan: 2) +
- (content_tag :td)
+ html += content_tag :tr do
+ (content_tag :th, (_"forms.labels.generic.#{area}"), colspan: 2) +
+ (content_tag :th, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
end
- end
-
- status_html = ''
- if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
- @housing_data[id][:warnings][:space][area].each do |w|
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{w.to_s}"))
+ html += guest_rows
+ html += content_tag :tr, class: 'place-guest' do
+ content_tag :td, class: guests.size >= space_size ? 'full' : nil, colspan: 3 do
+ content_tag :a, (_"forms.actions.generic.place_guest_in.#{area}"), class: 'select-guest button small', href: '#', data: { host: id, space: area }
+ end
end
- end
- if status_html.present?
- status_html = content_tag(:ul, status_html.html_safe)
- end
- html += content_tag :tr do
- (content_tag :th, (_"forms.labels.generic.#{area}"), colspan: 2) +
- (content_tag :th, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
- end
- html += guest_rows
- html += content_tag :tr, class: 'place-guest' do
- content_tag :td, class: guests.size >= space_size ? 'full' : nil, colspan: 3 do
- content_tag :a, (_'forms.actions.generic.place_guest'), class: 'select-guest', href: '#', data: { host: id, space: area }
- end
+ first_row = false
end
end
@@ -181,21 +192,31 @@ module WidgetsHelper
id = registration.id
@housing_data[id][:guests].each do |area, guests|
max_space = @housing_data[id][:space][area] || 0
- area_name = (_"forms.labels.generic.#{area}")
- status_html = ''
- if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
- @housing_data[id][:warnings][:space][area].each do |w|
- status_html += content_tag(:div, _("warnings.housing.space.#{w.to_s}"), class: 'warning')
+
+ # don't include the area if the host doesn't want anyone there
+ if max_space > 0 || guests.size > 0
+ area_name = (_"forms.labels.generic.#{area}")
+ status_html = ''
+ if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
+ @housing_data[id][:warnings][:space][area].each do |w|
+ status_html += content_tag(:div, _("warnings.housing.space.#{w.to_s}"), class: 'warning')
+ end
end
+ space_html = content_tag(:h5, area_name + _!(" (#{guests.size.to_s}/#{max_space.to_s})") + status_html.html_safe)
+ guest_items = ''
+ guests.each do |guest_id, guest|
+ guest_items += content_tag(:li, guest[:guest].user.name, id: "hosted-guest-#{guest_id}")
+ end
+ space_html += content_tag(:ul, guest_items.html_safe)
+
+ # see if the space is overbooked
+ booked_state = guests.size >= max_space ? (guests.size > max_space ? :overbooked : :booked) : nil
+
+ # let space be overbooked, even bed space can be overbooked if a couple is staying in the bed
+ space_html += button :place_guest, type: :button, value: "#{area}:#{id}", class: [:small, 'place-guest', 'on-top-only', booked_state, max_space > 0 ? nil : :unwanted]
+
+ html += content_tag(:div, space_html, class: [:space, area, max_space > 0 || guests.size > 0 ? nil : 'on-top-only'])
end
- space_html = content_tag(:h5, area_name + _!(" (#{guests.size.to_s}/#{max_space.to_s})") + status_html.html_safe)
- guest_items = ''
- guests.each do |guest_id, guest|
- guest_items += content_tag(:li, guest[:guest].user.name, id: "hosted-guest-#{guest_id}")
- end
- space_html += content_tag(:ul, guest_items.html_safe)
- space_html += button :place_guest, type: :button, value: "#{area}:#{id}", class: [:small, 'place-guest', 'on-top-only', guests.size >= max_space ? (guests.size > max_space ? :overbooked : :booked) : nil, max_space > 0 ? nil : :unwanted]
- html += content_tag(:div, space_html, class: [:space, area, max_space > 0 || guests.size > 0 ? nil : 'on-top-only'])
end
classes << 'status-warning' if @housing_data[id][:warnings].present?
@@ -228,6 +249,16 @@ module WidgetsHelper
end
end
+ def link_help_dlg(topic, args = {})
+ @help_dlg ||= true
+ args[:data] ||= {}
+ args[:data]['info-title'] = I18n.t("help.headings.#{topic}")
+ args[:data]['help-text'] = true
+ content_tag(:a, args) do
+ (I18n.t('help.link_text') + content_tag(:template, (render "/help/#{topic}"), class: 'message')).html_safe
+ end
+ end
+
def button_with_confirmation(button_name, confirmation_text = nil, args = {})
if confirmation_text.is_a? Hash
args = confirmation_text
diff --git a/app/views/application/user_settings.html.haml b/app/views/application/user_settings.html.haml
index 11db2c4..e81c3e8 100644
--- a/app/views/application/user_settings.html.haml
+++ b/app/views/application/user_settings.html.haml
@@ -7,10 +7,10 @@
- if @conference.present? && (@conference.registration_status == :pre || @conference.registration_status == :open)
%p=_'articles.user_settings.paragraphs.conference_registration', :t
= link_to (_'actions.conference.edit_registration'), register_path(@conference.slug), class: :button
- - if @conferences.present?
+ - if @my_conferences.present?
%h3=_'articles.user_settings.headings.Your_Conferences'
.link-dump
- - @conferences.each do | conference |
+ - @my_conferences.each do |conference|
= link_to (_!conference.title), administrate_conference_path(conference.slug), class: :button
= form_tag update_settings_path do
diff --git a/app/views/conference_administration/_hosts_table.html.haml b/app/views/conference_administration/_hosts_table.html.haml
index 804754c..d20f1a5 100644
--- a/app/views/conference_administration/_hosts_table.html.haml
+++ b/app/views/conference_administration/_hosts_table.html.haml
@@ -1,11 +1,20 @@
.guests-housed
%h5 Guests Housed:
- .data="#{@guests_housed} / #{@guests.size}"
+ .data{class: @guests_housed < @guests.size ? :unhappy : :happy }="#{@guests_housed} / #{@guests.size}"
+- if @guests_housed > 0
+ .guests-housed
+ %h5 Unhappy hosts and guests:
+ .data{class: @unhappy_people.size > 0 ? :unhappy : :happy }="#{@unhappy_people.size}"
+- first_row = true
%table.hosts.admin-edit
- - @hosts.each do | id, registration |
+ - @hosts.each do |id, registration|
+ - unless first_row
+ %tr.spacer
+ %td
%tr.host
%th
.name=registration.user.name
.address=registration.housing_data['address']
%td.inner-table{colspan: 2}=host_guests_table(registration)
+ - first_row = false
diff --git a/app/views/conference_administration/_housing.html.haml b/app/views/conference_administration/_housing.html.haml
index 42030e2..f5564b8 100644
--- a/app/views/conference_administration/_housing.html.haml
+++ b/app/views/conference_administration/_housing.html.haml
@@ -6,5 +6,5 @@
= admin_update_form class: 'guest-dlg', id: 'guest-list-table' do
%h3 Select a Guest
#table
- .actions
+ .actions.center
= link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, @admin_step, :format => :xlsx), class: [:button, :download]
diff --git a/app/views/conference_administration/_registrations.html.haml b/app/views/conference_administration/_registrations.html.haml
index 3d97266..9e00615 100644
--- a/app/views/conference_administration/_registrations.html.haml
+++ b/app/views/conference_administration/_registrations.html.haml
@@ -6,12 +6,12 @@
%a.button{data: { expands: 'registrations-table' }}='expand'
%a.button.delete{data: { contracts: 'registrations-table' }}='close'
%a.button.modify{data: { 'opens-modal': 'new-registration' }}='+'
- .table-scroller
- = html_table @excel_data, registrations_table_options
+ .table-scroller#registrations
+ = html_table(@excel_data, registrations_table_options)
= admin_update_form id: 'new-registration', class: 'modal-edit' do
.modal-edit-overlay{data: { 'closes-modal': 'new-registration' }}
.modal-edit-content
= html_edit_table @excel_data, registrations_edit_table_options
- .actions.right
- %a.button.subdued{data: { 'closes-modal': 'new-registration' }}='Cancel'
+ .actions.center
+ %a.button.subdued{data: { 'closes-modal': 'new-registration' }} Cancel
= button :save, value: :save, class: :modify
diff --git a/app/views/conference_administration/_select_guest_table.html.haml b/app/views/conference_administration/_select_guest_table.html.haml
index b0075e4..5346117 100644
--- a/app/views/conference_administration/_select_guest_table.html.haml
+++ b/app/views/conference_administration/_select_guest_table.html.haml
@@ -1,50 +1,54 @@
-= hidden_field_tag :host, host.id
-.host-field
- %h4.inline=_'forms.labels.generic.name'
- %span.plain-value= host.user.name
-.host-field
- %h4.inline=_'articles.conference_registration.headings.host.availability'
- %span.plain-value= host.housing_data['availability'].present? && host.housing_data['availability'][1].present? ? date_span(host.housing_data['availability'][0].to_date, host.housing_data['availability'][1].to_date) : ''
-- if host.housing_data['considerations'].present?
- .host-field
- %h4.inline=_'articles.conference_registration.headings.host.considerations'
- %span.plain-value= (host.housing_data['considerations'].map { | consideration | _"articles.conference_registration.host.considerations.#{consideration}" }).join(', ')
-- if sanitize(host.housing_data['notes'], tags: []).present?
- .host-field
- %h4=_'articles.conference_registration.headings.host.notes'
- %blockquote= host.housing_data['notes'].html_safe
-%table.guests.admin-edit
- %tr
- %th.corner
- %th=_'forms.labels.generic.organization'
- %th=_'forms.labels.generic.city'
- %th=_'forms.labels.generic.housing'
- %th=_'articles.admin.housing.headings.arrival_departure'
- %th=_'articles.conference_registration.headings.companion'
- %th=_'forms.labels.generic.food'
- %th=_'forms.labels.generic.other'
- - @guests.each do | id, registration |
- %tr.selectable{class: get_housing_match(host, registration, space).to_s.gsub('_', '-'), data: {host: host.id, guest: id, space: space}}
- %th=registration.user.name
- %td
- - if registration.user.organizations.present?
- = registration.user.organizations.first.name
- - else
- %em None
- %td=registration.city
- %td=registration.housing.present? ? (_"articles.conference_registration.questions.housing.#{registration.housing}") : ''
- %td=date_span(registration.arrival.to_date, registration.departure.to_date)
- - companion = companion(registration)
- %td=companion.present? ? (companion.is_a?(User) ? companion.named_email : (_"articles.conference_registration.terms.registration_status.#{companion}")) : ''
- %td=registration.food.present? ? (_"articles.conference_registration.questions.food.#{registration.food}") : ''
- %td
- .p=registration.other
+.host
+ = hidden_field_tag :host, host.id
+ %h4 Host
+ .table-scroller.no-edit
+ %table.guests.admin-edit
+ %tr
+ %th.corner
+ %th=_'forms.labels.generic.hosting_space'
+ %th=_'forms.labels.generic.first_day'
+ %th=_'forms.labels.generic.last_day'
+ %th=_'articles.conference_registration.headings.host.notes'
+ %tr
+ - availability = host.housing_data['availability']
+ %th=host.user.name
+ %td=_"forms.labels.generic.#{space}"
+ %td=(availability || [])[0].present? ? availability[0].present? ? date(availability[0].to_date, :span_same_year_date_1) : '' : ''
+ %td=(availability || [])[1].present? ? date(availability[1].to_date, :span_same_year_date_1) : ''
+ %td=((host.housing_data || {})['notes'] || '').html_safe
-.legend
- %h4 Legend
- %ul
- %li.good-match Good Match
- %li.bad-match Poor Match
- %li.selected-space Also in this space
- %li.other-space Also with this host
- %li.other-host Already hosted
+%h4 Guests
+.guest-table
+ .table-scroller.no-edit
+ %table.guests.admin-edit
+ %tr
+ %th.corner
+ %th=_'forms.labels.generic.housing'
+ %th=_'forms.labels.generic.arrival'
+ %th=_'forms.labels.generic.departure'
+ %th=_'forms.labels.generic.other_notes'
+ %th=_'forms.labels.generic.city'
+ %th=_'forms.labels.generic.organization'
+ %th=_'articles.conference_registration.headings.companion'
+ %th=_'forms.labels.generic.food'
+ - @guests.each do |id, registration|
+ %tr.selectable{class: get_housing_match(host, registration, space).to_s.gsub('_', '-'), data: {host: host.id, guest: id, space: space}}
+ %th.break-ok=registration.user.name
+ %td=registration.housing.present? ? (_"articles.conference_registration.questions.housing.#{registration.housing}") : ''
+ %td
+ - if registration.arrival.present?
+ =date(registration.arrival.to_date, :span_same_year_date_1)
+ %td
+ - if registration.departure.present?
+ =date(registration.departure.to_date, :span_same_year_date_1)
+ %td.break-ok
+ .p=[registration.allergies, registration.other, (registration.housing_data || {})['other']].compact.join("\n\n")
+ %td=registration.city
+ %td.break-ok
+ - if registration.user.organizations.present?
+ = registration.user.organizations.first.name
+ - else
+ %em None
+ - companion = companion(registration)
+ %td=companion.present? ? (companion.is_a?(User) ? companion.named_email : (_"articles.conference_registration.terms.registration_status.#{companion}")) : ''
+ %td=registration.food.present? ? (_"articles.conference_registration.questions.food.#{registration.food}") : ''
diff --git a/app/views/conference_administration/administration.html.haml b/app/views/conference_administration/administration.html.haml
index 4bd44fb..7a443a9 100644
--- a/app/views/conference_administration/administration.html.haml
+++ b/app/views/conference_administration/administration.html.haml
@@ -1,7 +1,7 @@
- body_class 'banner-bottom' unless @this_conference.poster.present?
- add_stylesheet :admin
- content_for :banner do
- = render :partial => 'application/header', :locals => { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
+ = render partial: 'application/header', locals: { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
%article
= row do
@@ -10,7 +10,7 @@
%h2=@this_conference.title
%p=_'articles.admin.paragraphs.administration', :p
%ul.break
- - administration_steps.each do | step, actions |
+ - administration_steps.each do |step, actions|
%li
.info
%h3=_"articles.admin.#{step}.heading", :t
@@ -18,7 +18,7 @@
%p=_"articles.admin.#{step}.description", :p
.actions.figures
- - actions.each do | action |
+ - actions.each do |action|
- action_text = (_"articles.admin.#{step}.headings.#{action}", :t)
.figure
= link_to administration_step_path(@this_conference.slug, action.to_s) do
diff --git a/app/views/conference_administration/administration_step.html.haml b/app/views/conference_administration/administration_step.html.haml
index dda310c..7b6396a 100644
--- a/app/views/conference_administration/administration_step.html.haml
+++ b/app/views/conference_administration/administration_step.html.haml
@@ -1,11 +1,13 @@
- body_class 'banner-bottom' unless @this_conference.poster.present?
- add_stylesheet :admin
- content_for :banner do
- = render :partial => 'application/header', :locals => { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
+ = render partial: 'application/header', locals: { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
%article{id: "admin-#{@admin_step}"}
= row do
= columns(medium: 12) do
+ - if admin_help_pages[@admin_step.to_sym]
+ = link_help_dlg("admin_#{admin_help_pages[@admin_step.to_sym]}", class: ['button', 'help-link'])
%h2.floating=(_"articles.admin.#{@admin_group}.headings.#{@admin_step}", :t)
= row do
= columns(medium: 12) do
diff --git a/app/views/conferences/index.html.haml b/app/views/conferences/index.html.haml
deleted file mode 100644
index 589f737..0000000
--- a/app/views/conferences/index.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-- page_name = 'All '+(@conference_type ? @conference_type.title+' ' : '')+' Conferences'
-- title page_name
-- banner_image '/assets/conference.jpg'
-- page_style :list
-- content_for :banner do
- .row
- .columns
- %h1=_'page.Conferences'
-
-%h2=page_name
-
-%ul.small-block-grid-1.medium-block-grid-2.large-block-grid-3.conference-list.preview-list
- - @conferences.each do |conference|
- %li=render 'preview', :conference => conference
diff --git a/app/views/help/_admin_housing.html.haml b/app/views/help/_admin_housing.html.haml
new file mode 100644
index 0000000..1839f7e
--- /dev/null
+++ b/app/views/help/_admin_housing.html.haml
@@ -0,0 +1,31 @@
+%p On this page you will see a list of hosts (housing providers) each with a list of their open spaces, beds, floor space, and tent space.
+
+%h3 How to match a guest with host
+
+%p Find a host and select a space to place a guest in, then press the "Place a guest in..." button to add a guest to that area. Once you press that button a popup will appear showing you extended details on the host and a list of guests with details. Take a look at the notes section for both the host and the guest to determine if the two will make a good match but also pay attention to the colour of the row:
+
+.legend
+ %h4 Legend
+ %ul
+ %li.good-match Good Match
+ %li.bad-match Poor Match
+ %li.selected-space Also in this space
+ %li.other-space Also with this host
+ %li.other-host Already hosted
+
+%h3 Dealing with poor matches
+
+%p A poor match can happen for several reasons:
+%ul
+ %li A host space may not be available for the entire time that a guest wishes to stay
+ %li A guest may be placed in a space (bed, floor, or tent) which is not their preferred space
+ %li A guest may be placed in a space without their companion
+ %li A host may have more guests than they indicated as their maximum
+
+%p When a match is likely good, you will see a 😁 beside their placement, when a match is poor you will see a 😡. If you see the unhappy face, you can hover over the face to show more details on why the guest or host is unhappy.
+
+%p You may wish to remove the guest and fin a better placement but keep in mind that this will not always be possible. For example, often guests will ask to be placed with many other friends but arranging this can be difficult. Speak with guests and hosts if possible to arrange a compromise, it is generally encouraged that guests stay with new people as long as they feel safe.
+
+%h3 Notifying hosts and guests of their placements
+
+%p At the moment there is no automated tool to do this for you. You will need to download the excel speadsheet at the bottom of the page to get all placements and their contact info.
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index f9f7556..8ac3abf 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -61,6 +61,13 @@
.dlg-inner
%p.message=''
%button.close=_'modals.done_button'
+ - if @help_dlg.present?
+ .dlg#help-dlg{data: { nofocus: 1 }}
+ .dlg-content
+ %h2.title=_'modals.help'
+ .dlg-inner
+ .message=''
+ %button.close=_'modals.done_button'
- if @login_dlg.present?
.dlg#login-dlg
.dlg-content
diff --git a/config/assets_cdn.yml b/config/assets_cdn.yml
deleted file mode 100644
index ef092c9..0000000
--- a/config/assets_cdn.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-development:
- enabled: false
- host: bikebike.org
-
-preview:
- enabled: false
- host: preview-cdn.bikebike.org
- protocol: https
- fallback_protocol: http
-
-production:
- enabled: true
- host: cdn.bikebike.org
- protocol: https
- fallback_protocol: http
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 92c9c17..a6d1348 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -44,7 +44,7 @@ BikeBike::Application.configure do
# enable_starttls_auto: true,
# openssl_verify_mode: 'none',
# user_name: 'info@bikebike.org',
- # password: config.app_config['email_password']
+ # password: 'Toronto@)!)'
# }
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_deliveries = true
diff --git a/config/locales/en.yml b/config/locales/en.yml
index b7a4ac3..900bfcc 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1331,6 +1331,10 @@ en:
conference:
actions:
Register: Register
+ help:
+ link_text: Help
+ headings:
+ admin_housing: Help for Housing
modals:
confirm: Please Confirm
yes_button: 'Yes'
@@ -1461,10 +1465,9 @@ en:
instead of a guest form. If you want to consider surrounding cities as
well, enter the distance from %{city} that you wish to be included as
providers. Cities are measured from center to center.
- housing: Pair each housing provider with a list of guests. Try to match
- guests with hosts and other guests who are good matches and respect their
- requests, but also keep in mind that we cannot always respect all requests
- and part of the Bike!Bike! experience is getting to know new people.
+ housing: Match guests with hosts, and fulfill their housing
+ requests to the best of your ability. Use guest notes and the face icons to determine if a guest is a good match for the host.
+ Hover over unhappy faces for details on how to find the guest a better match. Keep in mind that you may not be able to accommodate every request.
locations:
heading: Locations
description: Locations are used to schedule workshops, events, and meals.
@@ -2079,6 +2082,7 @@ en:
quiet: Quiet household
pets: House has dogs or cats
can_provide_housing: I can provide housing.
+ housing_provider: Host?
not_attending: I will not be attending the conference
questions:
bike:
@@ -2096,6 +2100,10 @@ en:
house: Indoor Location
none: I'll take care of it
tent: Tent Space
+ housing_short:
+ house: House
+ none: None
+ tent: Tent
bikes:
medium: Medium
none: "(none)"
@@ -2116,9 +2124,10 @@ en:
unregistered: Unregistered
preregistered: Preregistered
registered: Registered
+ cancelled: Cancelled
companion: Companion
companion_email: Companion Email
- Preferred_Languages: Preferred Language
+ Preferred_Languages: Language
is_attending: Attending?
about_bikebike:
paragraphs:
@@ -2282,6 +2291,8 @@ en:
paypal: Online
on_arrival: In person
none: None
+ payment_method: Payment Method
+ payment_currency: Payment Currency
space: Available Space
hosting_dates: When will you be in town?
type: Type
@@ -2295,8 +2306,8 @@ en:
other: Disabilities, housing preferences, etc.
email: Email address
allergies: Allergies
- arrival: Arrival date
- departure: Departure date
+ arrival: Arrival
+ departure: Departure
location: City, State/Province, Country
name: Name
subject: Subject
@@ -2306,10 +2317,13 @@ en:
notes: Notes
message: 'Your Message:'
address: Street Address
+ address_short: Address
phone: Phone number
+ phone_short: Phone
bed_space: Bed/Couch Space
floor_space: Floor Space
tent_space: Tent Space
+ hosting_space: Space
first_day: From
last_day: To
body: Body
@@ -2329,7 +2343,7 @@ en:
closed: Closed
open: Open
pre: Pre-Registration
- registration_status: Registration Status
+ registration_status: Status
companion: Email address
block_number: Block
days: Days
@@ -2395,6 +2409,10 @@ en:
create: Create
delete: Delete
place_guest: Place Guest
+ place_guest_in:
+ bed_space: Place a guest in a bed or couch
+ floor_space: Place a guest on the floor
+ tent_space: Place a guest in a tent space
set_host: Set Host
add_comment: Add Comment
reply: Reply