Schedule maker, stats, and minor fixes
This commit is contained in:
parent
ee3b874110
commit
26f9dfde89
5
Gemfile
5
Gemfile
@ -35,13 +35,16 @@ gem 'geocoder'
|
||||
gem 'paper_trail', '~> 3.0.5'
|
||||
gem 'sitemap_generator'
|
||||
gem 'activerecord-session_store'
|
||||
gem 'paypal-express', '0.7.1'
|
||||
gem 'paypal-express'#, '0.7.1'
|
||||
gem 'sass-json-vars'
|
||||
gem 'premailer-rails'
|
||||
gem 'redcarpet'
|
||||
gem 'sidekiq'
|
||||
gem 'letter_opener'
|
||||
gem 'launchy'
|
||||
# gem 'axlsx'
|
||||
# gem 'excelinator'
|
||||
gem 'to_spreadsheet'#, :git => 'git://github.com/glebm/to_spreadsheet.git'
|
||||
|
||||
group :test do
|
||||
gem 'rspec'
|
||||
|
@ -15,24 +15,6 @@
|
||||
window.forEach = function(a, f) { Array.prototype.forEach.call(a, f) };
|
||||
window.forEachElement = function(s, f, p) { forEach((p || document).querySelectorAll(s), f) };
|
||||
|
||||
forEachElement('.number-field,.email-field,.text-field', function(field) {
|
||||
var input = field.querySelector('input');
|
||||
var positionLabel = function(input) {
|
||||
field.classList[input.value ? 'remove' : 'add']('empty');
|
||||
}
|
||||
positionLabel(input);
|
||||
input.addEventListener('keyup', function(event) {
|
||||
positionLabel(event.target);
|
||||
});
|
||||
input.addEventListener('blur', function(event) {
|
||||
positionLabel(event.target);
|
||||
field.classList.remove('focused');
|
||||
});
|
||||
input.addEventListener('focus', function(event) {
|
||||
field.classList.add('focused');
|
||||
});
|
||||
});
|
||||
|
||||
var overlay = document.getElementById('content-overlay');
|
||||
if (overlay) {
|
||||
var body = document.querySelector('body');
|
||||
@ -50,29 +32,35 @@
|
||||
}, true);
|
||||
function openDlg(dlg, link) {
|
||||
body.setAttribute('style', 'width: ' + body.clientWidth + 'px');
|
||||
dlg.querySelector('.message').innerHTML = decodeURI(link.dataset.confirmation);
|
||||
dlg.querySelector('.confirm').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
if (link.tagName == 'BUTTON') {
|
||||
var form = link.parentElement
|
||||
while (form && form.tagName != 'FORM') {
|
||||
var form = form.parentElement
|
||||
dlg.querySelector('.message').innerHTML = link.querySelector('.message').innerHTML
|
||||
if (link.dataset.infoTitle) {
|
||||
dlg.querySelector('.title').innerHTML = decodeURI(link.dataset.infoTitle);
|
||||
}
|
||||
confirmBtn = dlg.querySelector('.confirm');
|
||||
if (confirmBtn) {
|
||||
confirmBtn.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
if (link.tagName == 'BUTTON') {
|
||||
var form = link.parentElement
|
||||
while (form && form.tagName != 'FORM') {
|
||||
var form = form.parentElement
|
||||
}
|
||||
if (form) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'button';
|
||||
input.value = link.value;
|
||||
form.appendChild(input);
|
||||
form.submit();
|
||||
}
|
||||
} else {
|
||||
window.location.href = link.getAttribute('href');
|
||||
}
|
||||
if (form) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'button';
|
||||
input.value = link.value;
|
||||
form.appendChild(input);
|
||||
form.submit();
|
||||
}
|
||||
} else {
|
||||
window.location.href = link.getAttribute('href');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
primaryContent.setAttribute('aria-hidden', 'true');
|
||||
document.getElementById('overlay').onclick =
|
||||
dlg.querySelector('.delete').onclick = function() { closeDlg(dlg); };
|
||||
dlg.querySelector('.close').onclick = function() { closeDlg(dlg); };
|
||||
body.classList.add('has-overlay');
|
||||
dlg.removeAttribute('aria-hidden');
|
||||
dlg.setAttribute('role', 'alertdialog');
|
||||
@ -99,11 +87,14 @@
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea');
|
||||
if (errorField) {
|
||||
errorField.focus();
|
||||
var infoDlg = document.getElementById('info-dlg');
|
||||
forEachElement('[data-info-text]', function(link) {
|
||||
link.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
openDlg(infoDlg, link);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var htmlNode = document.documentElement;
|
||||
@ -120,28 +111,73 @@
|
||||
htmlNode.setAttribute('data-input', 'mouse');
|
||||
}
|
||||
});
|
||||
forEachElement('form.js-xhr', function(form) {
|
||||
if (form.addEventListener) {
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
form.classList.add('requesting');
|
||||
var data = new FormData(form);
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
form.classList.remove('requesting');
|
||||
if (request.status == 200) {
|
||||
var response = JSON.parse(request.responseText);
|
||||
for (var i = 0; i < response.length; i++) {
|
||||
form.querySelector(response[i].selector).innerHTML = response[i].html;
|
||||
|
||||
var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea');
|
||||
if (errorField) {
|
||||
errorField.focus();
|
||||
}
|
||||
|
||||
window.initNodeFunctions = [ function(node) {
|
||||
forEachElement('.number-field,.email-field,.text-field', function(field) {
|
||||
var input = field.querySelector('input');
|
||||
var positionLabel = function(input) {
|
||||
field.classList[input.value ? 'remove' : 'add']('empty');
|
||||
}
|
||||
positionLabel(input);
|
||||
input.addEventListener('keyup', function(event) {
|
||||
positionLabel(event.target);
|
||||
});
|
||||
input.addEventListener('blur', function(event) {
|
||||
positionLabel(event.target);
|
||||
field.classList.remove('focused');
|
||||
});
|
||||
input.addEventListener('focus', function(event) {
|
||||
field.classList.add('focused');
|
||||
});
|
||||
}, node || document);
|
||||
forEachElement('form.js-xhr', function(form) {
|
||||
if (form.addEventListener) {
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
form.classList.add('requesting');
|
||||
var data = new FormData(form);
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
form.classList.remove('requesting');
|
||||
if (request.status == 200) {
|
||||
var response = JSON.parse(request.responseText);
|
||||
for (var i = 0; i < response.length; i++) {
|
||||
var element;
|
||||
if (response[i].selector) {
|
||||
element = form.querySelector(response[i].selector);
|
||||
}
|
||||
if (response[i].globalSelector) {
|
||||
element = document.querySelector(response[i].globalSelector);
|
||||
}
|
||||
|
||||
if (response[i].html) {
|
||||
element.innerHTML = response[i].html;
|
||||
window.initNode(element);
|
||||
}
|
||||
if (response[i].className) {
|
||||
element.className = response[i].className;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
request.open('POST', form.getAttribute('action'), true);
|
||||
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
request.send(data);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
request.open('POST', form.getAttribute('action'), true);
|
||||
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
request.send(data);
|
||||
}, false);
|
||||
}
|
||||
}, node || document);
|
||||
} ];
|
||||
window.initNode = function(node) {
|
||||
forEach(initNodeFunctions, function(fn) {
|
||||
fn(node);
|
||||
});
|
||||
};
|
||||
initNode();
|
||||
})();
|
||||
|
@ -13,7 +13,7 @@ body {
|
||||
padding-bottom: 20vw;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, label, button, .button, dt, th, .table-th, nav.sub-menu {
|
||||
h1, h2, h3, h4, h5, h6, label, button, .button, dt, th, .table-th, nav.sub-menu {
|
||||
@include font-family(secondary);
|
||||
font-weight: normal;
|
||||
}
|
||||
@ -31,6 +31,18 @@ h3.subtitle {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 4vw;
|
||||
}
|
||||
@ -41,6 +53,7 @@ a {
|
||||
border-bottom: 0 solid;
|
||||
outline: 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
@include after {
|
||||
content: '';
|
||||
@ -101,10 +114,25 @@ table, .table {
|
||||
&:last-child {
|
||||
padding-right: 1em;
|
||||
}*/
|
||||
|
||||
&.status {
|
||||
width: 0.1rem;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
th, .table-th {
|
||||
background-color: #F8F8F8;
|
||||
|
||||
&.corner {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
tbody th {
|
||||
width: 0.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,18 +410,25 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.field-error {
|
||||
display: block;
|
||||
background-color: rgba($colour-2, 0.333);
|
||||
@include font-family(secondary);
|
||||
padding: 0.5em 1em;
|
||||
margin: 0 0.2em;
|
||||
text-align: center;
|
||||
@include default-box-shadow(top, 2);
|
||||
}
|
||||
|
||||
.input-field {
|
||||
.field-error {
|
||||
display: block;
|
||||
float: right;
|
||||
background-color: rgba($colour-2, 0.333);
|
||||
@include font-family(secondary);
|
||||
padding: 0.5em 1em;
|
||||
margin: 0 0.2em;
|
||||
text-align: center;
|
||||
@include _(transform, skewX(-15deg));
|
||||
@include _(transform-origin, 0 100%);
|
||||
@include default-box-shadow(top, 2);
|
||||
@include _(animation, bend ease-in-out 500ms infinite alternate both);
|
||||
}
|
||||
|
||||
@ -463,10 +498,9 @@ input {
|
||||
}
|
||||
|
||||
.input-field-help {
|
||||
margin-bottom: 1em;
|
||||
margin-left: 1em;
|
||||
margin: 0.5em 1em 0;
|
||||
line-height: 1.3333em;
|
||||
font-size: 1.25em;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
.check-box-field,
|
||||
@ -1136,13 +1170,10 @@ ul.warnings li,
|
||||
margin: 1em 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
// border: 0.1em solid #EEE;
|
||||
// background-color: #F8F8F8;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 0.5em 0.75em;
|
||||
//@include font-family(secondary);
|
||||
border: 0.1rem solid #EEE;
|
||||
border-top: 0;
|
||||
border-right: 0;
|
||||
@ -1158,10 +1189,6 @@ ul.warnings li,
|
||||
li {
|
||||
margin: 0;
|
||||
|
||||
&:last-child a {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.current {
|
||||
a {
|
||||
color: $white;
|
||||
@ -1171,6 +1198,44 @@ ul.warnings li,
|
||||
}
|
||||
}
|
||||
|
||||
.data-set {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.data-set-key, .data-set-value {
|
||||
display: table-cell;
|
||||
padding: 0.25em 0.5em;
|
||||
vertical-align: top;
|
||||
border-bottom: 0.1rem solid #EEE;
|
||||
}
|
||||
|
||||
.data-set-key {
|
||||
font-size: 1em;
|
||||
width: 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-set:last-child {
|
||||
.data-set-key, .data-set-value {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
||||
&.inline {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.space, .address {
|
||||
.data-set-key, .data-set-value {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-blocks {
|
||||
@include _-(display, inline-flex);
|
||||
@include _(flex-wrap, wrap);
|
||||
@ -1199,34 +1264,6 @@ ul.warnings li,
|
||||
border-bottom: 0.1rem solid #EEE;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.data-set {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.data-set-key, .data-set-value {
|
||||
display: table-cell;
|
||||
padding: 0.25em 0.5em;
|
||||
vertical-align: top;
|
||||
border-bottom: 0.1rem solid #EEE;
|
||||
}
|
||||
|
||||
.data-set:last-child {
|
||||
.data-set-key, .data-set-value {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.space, .address {
|
||||
.data-set-key, .data-set-value {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.amenities {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
@ -1342,7 +1379,7 @@ ul.warnings li,
|
||||
#guests {
|
||||
.guests {
|
||||
@include _-(display, flex);
|
||||
@include _(align-items, flex-start);
|
||||
//@include _(align-items, flex-start);
|
||||
@include _(flex-wrap, wrap);
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
@ -1366,7 +1403,7 @@ ul.warnings li,
|
||||
}
|
||||
}
|
||||
|
||||
#admin-schedule {
|
||||
#admin-workshop_times {
|
||||
.workshop-blocks {
|
||||
td, th {
|
||||
vertical-align: top;
|
||||
@ -1386,11 +1423,177 @@ ul.warnings li,
|
||||
}
|
||||
}
|
||||
|
||||
.details.org-members {
|
||||
padding: 1em;
|
||||
border: 0.1rem solid #EEE;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
table.schedule {
|
||||
width: 100%;
|
||||
margin: 0 0 1em;
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
|
||||
&.empty {
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
|
||||
&.workshop {
|
||||
background-color: lighten($colour-1, 40%);
|
||||
|
||||
&.filled {
|
||||
background-color: lighten($colour-1, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
&.event {
|
||||
background-color: lighten($colour-2, 25%);
|
||||
}
|
||||
|
||||
&.meal {
|
||||
background-color: lighten($colour-3, 25%);
|
||||
}
|
||||
|
||||
.title {
|
||||
@include font-family(secondary);
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
float: right;
|
||||
font-size: 0.9em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.conflict-score {
|
||||
text-align: right;
|
||||
|
||||
.title {
|
||||
@include font-family(secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.errors {
|
||||
position: relative;
|
||||
border-top: 0.1rem solid #666;
|
||||
margin-top: 0.5em;
|
||||
padding-top: 0.25em;
|
||||
|
||||
@include before {
|
||||
content: '!';
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: -1.2em;
|
||||
width: 1em;
|
||||
color: $white;
|
||||
@include font-family(secondary);
|
||||
}
|
||||
|
||||
@include after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0.1em;
|
||||
left: -1.333em;
|
||||
width: 0;
|
||||
height: 0;
|
||||
font-size: 1.25em;
|
||||
border: 0.5em solid;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-width: 0 0.5em 0.75em;
|
||||
color: darken($colour-2, 25%);
|
||||
}
|
||||
}
|
||||
|
||||
#main .columns & form {
|
||||
margin-top: 0;
|
||||
|
||||
button {
|
||||
margin-top: 0.5em;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#admin-schedule {
|
||||
.workshop-list {
|
||||
@include _-(display, flex);
|
||||
@include _(flex-wrap, wrap);
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
@include _(flex, 1);
|
||||
@include _(flex-basis, 48%);
|
||||
margin: 1%;
|
||||
@include _(transition, background-color 250ms ease-in-out);
|
||||
|
||||
.title {
|
||||
@include _(transition, background-color 250ms ease-in-out);
|
||||
}
|
||||
|
||||
&.booked {
|
||||
background-color: lighten($colour-1, 40%);
|
||||
|
||||
.not-booked-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.data-set-key, .data-set-value {
|
||||
border-bottom-color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
&.not-booked {
|
||||
|
||||
.booked-only {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.field-error {
|
||||
background-color: lighten($colour-2, 22.5%);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.already-booked {
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
|
||||
&.is-true {
|
||||
max-height: 3em;
|
||||
@include _(transition, max-height 150ms ease-in-out);
|
||||
}
|
||||
}
|
||||
|
||||
.workshop-description {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.details {
|
||||
margin: 0 5% 1em;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
#main .columns & form {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#main {
|
||||
position: relative;
|
||||
background-color: $white;
|
||||
padding-bottom: rems(2);
|
||||
flex: 1;
|
||||
@include _(flex, 1);
|
||||
|
||||
article {
|
||||
padding: rems(2.5) 0;
|
||||
@ -1816,6 +2019,8 @@ body {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
max-width: 50rem;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
margin: auto;
|
||||
z-index: 1001;
|
||||
background-color: $white;
|
||||
@ -1841,9 +2046,34 @@ body {
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#info-dlg .title {
|
||||
background-color: $colour-1;
|
||||
font-size: 1.5em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#info-dlg .message {
|
||||
text-align: left;
|
||||
|
||||
p, h4 {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.667em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(fade-out) {
|
||||
@ -3150,7 +3380,7 @@ html[data-ontop] {
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include _(border-radius, 0 0.15em 0.15em 0);
|
||||
@include _(border-radius, 0 0 0.15em 0.15em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -798,6 +798,39 @@ class ConferencesController < ApplicationController
|
||||
@page_title = 'articles.conference_registration.headings.Administration'
|
||||
|
||||
case @admin_step.to_sym
|
||||
when :stats
|
||||
@registrations = ConferenceRegistration.where(:conference_id => @this_conference.id)
|
||||
if request.format.xlsx?
|
||||
logger.info "Generating stats.xls"
|
||||
@excel_data = {
|
||||
columns: [:name, :email, :city, :date, :languages],
|
||||
column_types: {date: :date},
|
||||
keys: {
|
||||
name: 'forms.labels.generic.name',
|
||||
email: 'forms.labels.generic.email',
|
||||
city: 'forms.labels.generic.location',
|
||||
date: 'articles.conference_registration.terms.Date',
|
||||
languages: 'articles.conference_registration.terms.Languages'
|
||||
},
|
||||
data: [],
|
||||
}
|
||||
@registrations.each do | r |
|
||||
user = r.user_id ? User.where(id: r.user_id).first : nil
|
||||
if user.present?
|
||||
@excel_data[:data] << {
|
||||
name: user.firstname || '',
|
||||
email: user.email || '',
|
||||
date: r.created_at ? r.created_at.strftime("%F %T") : '',
|
||||
city: r.city || '',
|
||||
languages: ((r.languages || []).map { |x| view_context.language x }).join(', ').to_s
|
||||
}
|
||||
end
|
||||
end
|
||||
return respond_to do | format |
|
||||
# format.html
|
||||
format.xlsx { render xlsx: :stats, filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
|
||||
end
|
||||
end
|
||||
when :housing
|
||||
# do a full analysis
|
||||
analyze_housing
|
||||
@ -811,6 +844,13 @@ class ConferencesController < ApplicationController
|
||||
@length = 1.5
|
||||
when :meals
|
||||
@meals = Hash[@this_conference.meals.map{ |k, v| [k.to_i, v] }].sort.to_h
|
||||
when :workshop_times
|
||||
get_block_data
|
||||
@workshop_blocks << {
|
||||
'time' => nil,
|
||||
'length' => 1.0,
|
||||
'days' => []
|
||||
}
|
||||
when :schedule
|
||||
get_scheule_data
|
||||
end
|
||||
@ -820,22 +860,8 @@ class ConferencesController < ApplicationController
|
||||
|
||||
end
|
||||
|
||||
def get_scheule_data
|
||||
@meals = Hash[@this_conference.meals.map{ |k, v| [k.to_i, v] }].sort.to_h
|
||||
@events = Event.where(:conference_id => @this_conference.id).order(start_time: :asc)
|
||||
@workshops = Workshop.where(:conference_id => @this_conference.id).order(start_time: :asc)
|
||||
@locations = {}
|
||||
def get_block_data
|
||||
@workshop_blocks = @this_conference.workshop_blocks || []
|
||||
@workshop_blocks << {
|
||||
'time' => nil,
|
||||
'length' => 1.0,
|
||||
'days' => []
|
||||
}
|
||||
@workshops.each do |workshop|
|
||||
if workshop.location_id
|
||||
@locations[workshop.location_id] ||= workshop.location
|
||||
end
|
||||
end
|
||||
@block_days = []
|
||||
day = @this_conference.start_date
|
||||
while day <= @this_conference.end_date
|
||||
@ -844,6 +870,168 @@ class ConferencesController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def get_scheule_data
|
||||
@meals = Hash[@this_conference.meals.map{ |k, v| [k.to_i, v] }].sort.to_h
|
||||
@events = Event.where(:conference_id => @this_conference.id).order(start_time: :asc)
|
||||
@workshops = Workshop.where(:conference_id => @this_conference.id).order(start_time: :asc)
|
||||
@locations = {}
|
||||
|
||||
get_block_data
|
||||
|
||||
@schedule = {}
|
||||
day_1 = @this_conference.start_date.wday
|
||||
|
||||
@workshop_blocks.each_with_index do | info, block |
|
||||
info['days'].each do | block_day |
|
||||
day_diff = block_day.to_i - day_1
|
||||
day_diff += 7 if day_diff < 0
|
||||
day = (@this_conference.start_date + day_diff.days).to_date
|
||||
time = info['time'].to_f
|
||||
@schedule[day] ||= { times: {}, locations: {} }
|
||||
@schedule[day][:times][time] ||= {}
|
||||
@schedule[day][:times][time][:type] = :workshop
|
||||
@schedule[day][:times][time][:length] = info['length'].to_f
|
||||
@schedule[day][:times][time][:item] = { block: block, workshops: {} }
|
||||
end
|
||||
end
|
||||
|
||||
@workshops.each do | workshop |
|
||||
if workshop.block.present?
|
||||
block = @workshop_blocks[workshop.block['block'].to_i]
|
||||
day_diff = workshop.block['day'].to_i - day_1
|
||||
day_diff += 7 if day_diff < 0
|
||||
day = (@this_conference.start_date + day_diff.days).to_date
|
||||
|
||||
if @schedule[day].present? && @schedule[day][:times].present? && @schedule[day][:times][block['time'].to_f].present?
|
||||
@schedule[day][:times][block['time'].to_f][:item][:workshops][workshop.event_location_id] = { workshop: workshop, status: { errors: [], warnings: [], conflict_score: nil } }
|
||||
@schedule[day][:locations][workshop.event_location_id] ||= workshop.event_location
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@meals.each do | time, meal |
|
||||
day = meal['day'].to_date
|
||||
time = meal['time'].to_f
|
||||
@schedule[day] ||= {}
|
||||
@schedule[day][:times] ||= {}
|
||||
@schedule[day][:times][time] ||= {}
|
||||
@schedule[day][:times][time][:type] = :meal
|
||||
@schedule[day][:times][time][:length] = (meal['length'] || 1.0).to_f
|
||||
@schedule[day][:times][time][:item] = meal
|
||||
end
|
||||
|
||||
@events.each do | event |
|
||||
day = event.start_time.midnight.to_date
|
||||
time = event.start_time.hour.to_f + (event.start_time.min / 60.0)
|
||||
@schedule[day] ||= {}
|
||||
@schedule[day][:times] ||= {}
|
||||
@schedule[day][:times][time] ||= {}
|
||||
@schedule[day][:times][time][:type] = :event
|
||||
@schedule[day][:times][time][:length] = (event.end_time - event.start_time) / 3600.0
|
||||
@schedule[day][:times][time][:item] = event
|
||||
end
|
||||
|
||||
@schedule = @schedule.sort.to_h
|
||||
@schedule.each do | day, data |
|
||||
@schedule[day][:times] = data[:times].sort.to_h
|
||||
end
|
||||
|
||||
@schedule.each do | day, data |
|
||||
last_event = nil
|
||||
data[:times].each do | time, time_data |
|
||||
if last_event.present?
|
||||
@schedule[day][:times][last_event][:next_event] = time
|
||||
end
|
||||
last_event = time
|
||||
end
|
||||
end
|
||||
|
||||
@schedule.deep_dup.each do | day, data |
|
||||
data[:times].each do | time, time_data |
|
||||
if time_data[:next_event].present? || time_data[:length] > 0.5
|
||||
span = 0.5
|
||||
length = time_data[:next_event].present? ? time_data[:next_event] - time : time_data[:length]
|
||||
while span < length
|
||||
@schedule[day][:times][time + span] ||= {
|
||||
type: (span >= time_data[:length] ? :empty : :nil),
|
||||
length: 0.5
|
||||
}
|
||||
span += 0.5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@schedule = @schedule.sort.to_h
|
||||
@schedule.each do | day, data |
|
||||
@schedule[day][:times] = data[:times].sort.to_h
|
||||
|
||||
data[:times].each do | time, time_data |
|
||||
if time_data[:type] == :workshop && time_data[:item].present? && time_data[:item][:workshops].present?
|
||||
ids = time_data[:item][:workshops].keys
|
||||
(0..ids.length).each do | i |
|
||||
if time_data[:item][:workshops][ids[i]].present?
|
||||
workshop_i = time_data[:item][:workshops][ids[i]][:workshop]
|
||||
conflicts = {}
|
||||
|
||||
(i+1..ids.length).each do | j |
|
||||
workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
|
||||
if workshop_i.present? && workshop_j.present?
|
||||
workshop_i.active_facilitators.each do | facilitator_i |
|
||||
workshop_j.active_facilitators.each do | facilitator_j |
|
||||
if facilitator_i.id == facilitator_j.id
|
||||
@schedule[day][:times][time][:status] ||= {}
|
||||
@schedule[day][:times][time][:item][:workshops][ids[j]][:status][:errors] << {
|
||||
name: :common_facilitator,
|
||||
facilitator: facilitator_i,
|
||||
workshop: workshop_i,
|
||||
i18nVars: {
|
||||
facilitator_name: facilitator_i.name,
|
||||
workshop_title: workshop_i.title
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
(0..ids.length).each do | j |
|
||||
workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
|
||||
if workshop_i.present? && workshop_j.present? && workshop_i.id != workshop_j.id
|
||||
workshop_i.interested.each do | interested_i |
|
||||
workshop_j.interested.each do | interested_j |
|
||||
conflicts[interested_i.id] ||= true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
needs = JSON.parse(workshop_i.needs || '[]').map &:to_sym
|
||||
amenities = JSON.parse(workshop_i.event_location.amenities || '[]').map &:to_sym
|
||||
|
||||
needs.each do | need |
|
||||
@schedule[day][:times][time][:item][:workshops][ids[i]][:status][:errors] << {
|
||||
name: :need_not_available,
|
||||
need: need,
|
||||
location: workshop_i.event_location,
|
||||
workshop: workshop_i,
|
||||
i18nVars: {
|
||||
need: need.to_s,
|
||||
location: workshop_i.event_location.title,
|
||||
workshop_title: workshop_i.title
|
||||
}
|
||||
} unless amenities.include? need
|
||||
end
|
||||
|
||||
@schedule[day][:times][time][:item][:workshops][ids[i]][:status][:conflict_score] = workshop_i.interested.present? ? (conflicts.length / workshop_i.interested.size) : 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_housing_data
|
||||
@hosts = {}
|
||||
@guests = {}
|
||||
@ -948,13 +1136,24 @@ class ConferencesController < ApplicationController
|
||||
|
||||
case params[:admin_step]
|
||||
when 'edit'
|
||||
@this_conference.info = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:info]) unless @this_conference.info! == params[:info]
|
||||
case params[:button]
|
||||
when 'save'
|
||||
@this_conference.info = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:info]) unless @this_conference.info! == params[:info]
|
||||
|
||||
params[:info_translations].each do | locale, value |
|
||||
@this_conference.set_column_for_locale(:info, locale, value, current_user.id) unless value = @this_conference._info(locale)
|
||||
params[:info_translations].each do | locale, value |
|
||||
@this_conference.set_column_for_locale(:info, locale, value, current_user.id) unless value = @this_conference._info(locale)
|
||||
end
|
||||
@this_conference.save
|
||||
return redirect_to register_step_path(@this_conference.slug, :administration)
|
||||
when 'add_member'
|
||||
org = nil
|
||||
@this_conference.organizations.each do | organization |
|
||||
org = organization if organization.id == params[:org_id].to_i
|
||||
end
|
||||
org.users << (User.get params[:email])
|
||||
org.save
|
||||
return redirect_to administration_step_path(@this_conference.slug, :edit)
|
||||
end
|
||||
@this_conference.save
|
||||
return redirect_to register_step_path(@this_conference.slug, :administration)
|
||||
when 'housing'
|
||||
space = params[:button].split(':')[0]
|
||||
host_id = params[:button].split(':')[1].to_i
|
||||
@ -1091,7 +1290,7 @@ class ConferencesController < ApplicationController
|
||||
|
||||
return redirect_to administration_step_path(@this_conference.slug, :events)
|
||||
end
|
||||
when 'schedule'
|
||||
when 'workshop_times'
|
||||
case params[:button]
|
||||
when 'save_block'
|
||||
@this_conference.workshop_blocks ||= []
|
||||
@ -1101,7 +1300,67 @@ class ConferencesController < ApplicationController
|
||||
'days' => params[:days].keys
|
||||
}
|
||||
@this_conference.save
|
||||
return redirect_to administration_step_path(@this_conference.slug, :schedule)
|
||||
return redirect_to administration_step_path(@this_conference.slug, :workshop_times)
|
||||
end
|
||||
when 'schedule'
|
||||
success = false
|
||||
|
||||
case params[:button]
|
||||
when 'schedule_workshop'
|
||||
workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:id])
|
||||
booked = false
|
||||
workshop.event_location_id = params[:event_location]
|
||||
block_data = params[:workshop_block].split(':')
|
||||
workshop.block = {
|
||||
day: block_data[0].to_i,
|
||||
block: block_data[1].to_i
|
||||
}
|
||||
|
||||
# make sure this spot isn't already taken
|
||||
Workshop.where(:conference_id => @this_conference.id).each do | w |
|
||||
if request.xhr?
|
||||
if w.block.present? &&
|
||||
w.id != workshop.id &&
|
||||
w.block['day'] == workshop.block['day'] &&
|
||||
w.block['block'] == workshop.block['block'] &&
|
||||
w.event_location_id == workshop.event_location_id
|
||||
return render json: [ {
|
||||
selector: '.already-booked',
|
||||
className: 'already-booked is-true'
|
||||
} ]
|
||||
end
|
||||
else
|
||||
return redirect_to administration_step_path(@this_conference.slug, :schedule)
|
||||
end
|
||||
end
|
||||
|
||||
workshop.save!
|
||||
success = true
|
||||
when 'deschedule_workshop'
|
||||
workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:id])
|
||||
workshop.event_location_id = nil
|
||||
workshop.block = nil
|
||||
workshop.save!
|
||||
success = true
|
||||
end
|
||||
|
||||
if success
|
||||
if request.xhr?
|
||||
get_scheule_data
|
||||
schedule = render_to_string partial: 'conferences/admin/schedule'
|
||||
return render json: [ {
|
||||
globalSelector: '#schedule-preview',
|
||||
html: schedule
|
||||
}, {
|
||||
globalSelector: "#workshop-#{workshop.id}",
|
||||
className: workshop.block.present? ? 'booked' : 'not-booked'
|
||||
}, {
|
||||
globalSelector: "#workshop-#{workshop.id} .already-booked",
|
||||
className: 'already-booked'
|
||||
} ]
|
||||
else
|
||||
return redirect_to administration_step_path(@this_conference.slug, :schedule)
|
||||
end
|
||||
end
|
||||
end
|
||||
do_404
|
||||
|
@ -708,12 +708,16 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def data_set(header_type, header_key, attributes = {}, &block)
|
||||
raw_data_set(header_type, _(header_key), attributes, &block)
|
||||
end
|
||||
|
||||
def raw_data_set(header_type, header, attributes = {}, &block)
|
||||
attributes[:class] = attributes[:class].split(' ') if attributes[:class].is_a?(String)
|
||||
attributes[:class] = [attributes[:class].to_s] if attributes[:class].is_a?(Symbol)
|
||||
attributes[:class] ||= []
|
||||
attributes[:class] << 'data-set'
|
||||
content_tag(:div, attributes) do
|
||||
content_tag(header_type, _(header_key), class: 'data-set-key') +
|
||||
content_tag(header_type, header, class: 'data-set-key') +
|
||||
content_tag(:div, class: 'data-set-value', &block)
|
||||
end
|
||||
end
|
||||
@ -774,6 +778,16 @@ module ApplicationHelper
|
||||
selectfield :time_span, value, lengths, args
|
||||
end
|
||||
|
||||
def block_select(value = nil, args = {})
|
||||
blocks = {}
|
||||
@workshop_blocks.each_with_index do | info, block |
|
||||
info['days'].each do | day |
|
||||
blocks[(day.to_i * 10) + block] = [ "#{(I18n.t 'date.day_names')[day.to_i]} Block #{block + 1}", "#{day}:#{block}" ]
|
||||
end
|
||||
end
|
||||
selectfield :workshop_block, value, blocks.sort.to_h.values, args
|
||||
end
|
||||
|
||||
def location_select(value = nil, args = {})
|
||||
locations = []
|
||||
if @this_conference.event_locations.present?
|
||||
@ -844,7 +858,7 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def admin_steps
|
||||
[:edit, :stats, :broadcast, :housing, :locations, :meals, :events, :schedule]
|
||||
[:edit, :stats, :broadcast, :housing, :locations, :meals, :events, :workshop_times, :schedule]
|
||||
end
|
||||
|
||||
def valid_admin_steps
|
||||
@ -903,15 +917,29 @@ module ApplicationHelper
|
||||
def link_with_confirmation(link_text, confirmation_text, path, args = {})
|
||||
@confirmation_dlg ||= true
|
||||
args[:data] ||= {}
|
||||
args[:data][:confirmation] = CGI::escapeHTML(confirmation_text)
|
||||
link_to link_text, path, args
|
||||
args[:data][:confirmation] = true
|
||||
link_to path, args do
|
||||
(link_text.to_s + content_tag(:template, confirmation_text, class: 'message')).html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def link_info_dlg(link_text, info_text, info_title, args = {})
|
||||
@info_dlg ||= true
|
||||
args[:data] ||= {}
|
||||
args[:data]['info-title'] = info_title
|
||||
args[:data]['info-text'] = true
|
||||
content_tag(:a, args) do
|
||||
(link_text.to_s + content_tag(:template, info_text, class: 'message')).html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def button_with_confirmation(button_name, confirmation_text, args = {})
|
||||
@confirmation_dlg ||= true
|
||||
args[:data] ||= {}
|
||||
args[:data][:confirmation] = CGI::escapeHTML(confirmation_text)
|
||||
button_tag button_name, args
|
||||
args[:data][:confirmation] = true
|
||||
button_tag args do
|
||||
(button_name.to_s + content_tag(:template, confirmation_text, class: 'message')).html_safe
|
||||
end
|
||||
end
|
||||
|
||||
def richtext(text, reduce_headings = 2)
|
||||
@ -924,6 +952,10 @@ module ApplicationHelper
|
||||
html_safe
|
||||
end
|
||||
|
||||
def truncate(text)
|
||||
strip_tags(text.gsub('>', '> ')).gsub(/^(.{40,60})\s.*$/m, '\1…').html_safe
|
||||
end
|
||||
|
||||
def textarea(name, value, options = {})
|
||||
id = name.to_s.gsub('[', '_').gsub(']', '')
|
||||
label_id = "#{id}-label"
|
||||
@ -1003,6 +1035,10 @@ module ApplicationHelper
|
||||
textfield(name, value, options.merge({type: :number}))
|
||||
end
|
||||
|
||||
def emailfield(name, value, options = {})
|
||||
textfield(name, value, options.merge({type: :email}))
|
||||
end
|
||||
|
||||
def textfield(name, value, options = {})
|
||||
html = ''
|
||||
id = name.to_s.gsub('[', '_').gsub(']', '')
|
||||
|
@ -36,4 +36,18 @@ class User < ActiveRecord::Base
|
||||
return "#{name} <#{email}>"
|
||||
end
|
||||
|
||||
def self.get(email)
|
||||
user = where(email: email).first
|
||||
|
||||
unless user
|
||||
# not really a good UX so we should fix this later
|
||||
#do_404
|
||||
#return
|
||||
user = new(email: email)
|
||||
user.save!
|
||||
end
|
||||
|
||||
return user
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2,6 +2,7 @@ class Workshop < ActiveRecord::Base
|
||||
translates :info, :title
|
||||
|
||||
belongs_to :conference
|
||||
belongs_to :event_location
|
||||
|
||||
has_many :workshop_facilitators, :dependent => :destroy
|
||||
has_many :users, :through => :workshop_facilitators
|
||||
@ -83,14 +84,19 @@ class Workshop < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def interested_count
|
||||
return 0 unless id
|
||||
interested.size
|
||||
end
|
||||
|
||||
def interested
|
||||
return [] unless id
|
||||
return @interested if @interested.present?
|
||||
|
||||
collaborators = []
|
||||
workshop_facilitators.each do |f|
|
||||
collaborators << f.user_id unless f.role.to_sym == :requested || f.user_id.nil?
|
||||
end
|
||||
return 10 unless collaborators.present?
|
||||
interested = WorkshopInterest.where("workshop_id=#{id} AND user_id NOT IN (#{collaborators.join ','})") || []
|
||||
interested ? interested.size : 0
|
||||
@interested = WorkshopInterest.where("workshop_id=#{id} AND user_id NOT IN (#{collaborators.join ','})") || []
|
||||
end
|
||||
|
||||
def can_translate?(user, lang)
|
||||
|
@ -5,3 +5,15 @@
|
||||
= textarea "info_translations[#{locale.to_s}]", @this_conference._info(locale), label: 'translate.pages.Locale_Translation', vars: { language: _("languages.#{locale}") }, lang: locale, edit_on: :focus
|
||||
.actions.right
|
||||
= button_tag :save, value: :save
|
||||
%h4=_'articles.admin.edit.headings.host_organizations'
|
||||
- @this_conference.organizations.each do | organization |
|
||||
%p=organization.name
|
||||
%h5=_'articles.admin.edit.headings.members'
|
||||
.details.org-members.inline
|
||||
- organization.users.each do | user |
|
||||
= raw_data_set(:h6, user.name) do
|
||||
= user.email
|
||||
= form_tag administration_update_path(@this_conference.slug, :edit), class: 'mini-flex-form' do
|
||||
= hidden_field_tag :org_id, organization.id
|
||||
= emailfield :email, nil, required: true
|
||||
= button_tag :add_member, value: :add_member, class: :small
|
||||
|
@ -19,7 +19,7 @@
|
||||
- if registration.user.present?
|
||||
%li.guest{id: "guest-#{id}", data: { id: id, 'affected-hosts': @hosts_affected_by_guests[id].join(',') }}
|
||||
%h4= registration.user.name
|
||||
.city.on-top-only=registration.city
|
||||
.city=registration.city
|
||||
.email.on-top-only=registration.user.email
|
||||
= button_tag :set_host, type: :button, class: [:small, 'set-host', 'not-on-top']
|
||||
|
||||
|
@ -1,18 +1,98 @@
|
||||
.table.workshop-blocks
|
||||
.table-tr.header
|
||||
.table-th=_'forms.labels.generic.block_number'
|
||||
.table-th=_'forms.labels.generic.time'
|
||||
.table-th=_'forms.labels.generic.length'
|
||||
.table-th=_'forms.labels.generic.days'
|
||||
.table-th.form
|
||||
- @workshop_blocks.each_with_index do | info, block |
|
||||
- is_new = info['time'].blank?
|
||||
= form_tag administration_update_path(@this_conference.slug, :schedule), class: ['table-tr', is_new ? 'new' : 'saved'] do
|
||||
.table-th.center.big= is_new ? '' : (block + 1)
|
||||
.table-td=time_select info['time'], small: true, label: false
|
||||
.table-td=length_select info['length'], {small: true, label: false}, 0.5, 2
|
||||
.table-td=checkboxes :days, @block_days, info['days'].map(&:to_i), 'date.day_names', vertical: true, small: true
|
||||
.table-td.form
|
||||
= hidden_field_tag :workshop_block, block
|
||||
= button_tag :delete_block, value: :delete_block, class: [:small, :delete] if block == @workshop_blocks.length - 2
|
||||
= button_tag (is_new ? :add_block : :update_block), value: :save_block, class: [:small, :add]
|
||||
#schedule-preview
|
||||
- @schedule.each do | day, data |
|
||||
%h4=date(day, :weekday)
|
||||
%table.schedule{class: data[:locations].present? ? 'has-locations' : 'no-locations'}
|
||||
- if data[:locations].present?
|
||||
%thead
|
||||
%tr
|
||||
%th.corner
|
||||
- data[:locations].each do | id, location |
|
||||
%th=location.title
|
||||
%th.status
|
||||
%tbody
|
||||
- data[:times].each do | time, time_data |
|
||||
%tr
|
||||
- rowspan = (time_data[:length] * 2).to_i
|
||||
%th=time(time)
|
||||
- if time_data[:type] == :workshop
|
||||
- if time_data[:item][:workshops].present?
|
||||
- data[:locations].each do | id, location |
|
||||
- if time_data[:item][:workshops][id].present?
|
||||
- workshop = time_data[:item][:workshops][id][:workshop]
|
||||
- status = time_data[:item][:workshops][id][:status]
|
||||
- else
|
||||
- workshop = status = nil
|
||||
%td{class: [time_data[:type], workshop.present? ? :filled : nil], rowspan: rowspan}
|
||||
- if workshop.present?
|
||||
= form_tag administration_update_path(@this_conference.slug, :schedule), class: 'js-xhr' do
|
||||
.title=workshop.title
|
||||
.status
|
||||
.conflict-score
|
||||
%span.title Conflict Score:
|
||||
%span.value="#{status[:conflict_score] * 100.0}%"
|
||||
- if status[:errors].present?
|
||||
.errors
|
||||
- status[:errors].each do | error |
|
||||
.error=_"errors.messages.schedule.#{error[:name].to_s}", vars: error[:i18nVars]
|
||||
= hidden_field_tag :id, workshop.id
|
||||
= button_tag :deschedule, value: :deschedule_workshop, class: [:delete, :small]
|
||||
- else
|
||||
.title="Block #{time_data[:item][:block] + 1}"
|
||||
- else
|
||||
%td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
|
||||
.title="Block #{time_data[:item][:block] + 1}"
|
||||
%td.status{rowspan: rowspan}
|
||||
- if time_data[:status].present? && time_data[:status][:errors].present?
|
||||
%ul.errors
|
||||
- time_data[:status][:errors].each do | error |
|
||||
%li=error.to_json.to_s
|
||||
- elsif time_data[:type] != :nil
|
||||
%td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
|
||||
- case time_data[:type]
|
||||
- when :meal
|
||||
.title= time_data[:item]['title']
|
||||
.location= EventLocation.find(time_data[:item]['location'].to_i).title
|
||||
- when :event
|
||||
.title= time_data[:item].title
|
||||
.location= time_data[:item].event_location.title
|
||||
%td.status{rowspan: rowspan}
|
||||
- unless request.xhr?
|
||||
%ul.workshop-list
|
||||
- @workshops.each do | workshop |
|
||||
%li{id: "workshop-#{workshop.id}", class: workshop.block.present? ? 'booked' : 'not-booked'}
|
||||
%h4.title= workshop.title
|
||||
= form_tag administration_update_path(@this_conference.slug, :schedule), class: 'js-xhr' do
|
||||
.already-booked
|
||||
.field-error='This block is already booked'
|
||||
.workshop-description
|
||||
.details
|
||||
= data_set(:h5, 'articles.workshops.headings.interested_count') do
|
||||
= workshop.interested_count
|
||||
= data_set(:h5, 'articles.workshops.headings.facilitators') do
|
||||
- facilitators = []
|
||||
- workshop.active_facilitators.each do | facilitator |
|
||||
- facilitators << facilitator.name
|
||||
= facilitators.join ', '
|
||||
= data_set(:h5, 'articles.workshops.headings.needs') do
|
||||
- needs = []
|
||||
- JSON.parse(workshop.needs || '[]').each do | need |
|
||||
- needs << (_"workshop.options.needs.#{need}")
|
||||
= _!(needs.join ', ')
|
||||
= data_set(:h5, 'articles.workshops.headings.theme') do
|
||||
= workshop.theme.present? ? (_"workshop.options.theme.#{workshop.theme}") : ''
|
||||
= data_set(:h5, 'articles.workshops.headings.space') do
|
||||
= workshop.space.present? ? (_"workshop.options.space.#{workshop.space}") : ''
|
||||
= data_set(:h5, 'forms.labels.generic.info') do
|
||||
= link_info_dlg truncate(workshop.info), (richtext workshop.info.html_safe), workshop.title
|
||||
- if workshop.notes.present? && strip_tags(workshop.notes).length > 0
|
||||
= data_set(:h5, 'forms.labels.generic.notes') do
|
||||
= link_info_dlg truncate(workshop.notes), (richtext workshop.notes.html_safe), workshop.title
|
||||
|
||||
= hidden_field_tag :id, workshop.id
|
||||
.flex-inputs
|
||||
= location_select workshop.event_location_id, small: true, stretch: true
|
||||
= block_select workshop.block.present? ? "#{workshop.block['day']}:#{workshop.block['block']}" : nil, small: true
|
||||
.actions.next-prev
|
||||
= button_tag :deschedule, value: :deschedule_workshop, class: [:delete, 'booked-only']
|
||||
= button_tag :reschedule, value: :schedule_workshop, class: [:secondary, 'booked-only']
|
||||
= button_tag :schedule_workshop, value: :schedule_workshop, class: 'not-booked-only'
|
||||
|
@ -0,0 +1,5 @@
|
||||
.details
|
||||
= data_set(:h4, 'articles.admin.stats.headings.registrations') do
|
||||
= @registrations.size
|
||||
.actions
|
||||
= link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :stats, :format => :xlsx), class: [:button, :download]
|
18
app/views/conferences/admin/_workshop_times.html.haml
Normal file
18
app/views/conferences/admin/_workshop_times.html.haml
Normal file
@ -0,0 +1,18 @@
|
||||
.table.workshop-blocks
|
||||
.table-tr.header
|
||||
.table-th=_'forms.labels.generic.block_number'
|
||||
.table-th=_'forms.labels.generic.time'
|
||||
.table-th=_'forms.labels.generic.length'
|
||||
.table-th=_'forms.labels.generic.days'
|
||||
.table-th.form
|
||||
- @workshop_blocks.each_with_index do | info, block |
|
||||
- is_new = info['time'].blank?
|
||||
= form_tag administration_update_path(@this_conference.slug, :workshop_times), class: ['table-tr', is_new ? 'new' : 'saved'] do
|
||||
.table-th.center.big= is_new ? '' : (block + 1)
|
||||
.table-td=time_select info['time'], small: true, label: false
|
||||
.table-td=length_select info['length'], {small: true, label: false}, 0.5, 2
|
||||
.table-td=checkboxes :days, @block_days, info['days'].map(&:to_i), 'date.day_names', vertical: true, small: true
|
||||
.table-td.form
|
||||
= hidden_field_tag :workshop_block, block
|
||||
= button_tag :delete_block, value: :delete_block, class: [:small, :delete] if block == @workshop_blocks.length - 2
|
||||
= button_tag (is_new ? :add_block : :update_block), value: :save_block, class: [:small, :add]
|
18
app/views/conferences/stats.xlsx.haml
Normal file
18
app/views/conferences/stats.xlsx.haml
Normal file
@ -0,0 +1,18 @@
|
||||
- key = @excel_data[:key] || 'excel.columns'
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
- @excel_data[:columns].each do |column|
|
||||
%th=_(@excel_data[:keys][column].present? ? @excel_data[:keys][column] : "#{key}.#{column.to_s}")
|
||||
%tbody
|
||||
- @excel_data[:data].each do |row|
|
||||
%tr
|
||||
- @excel_data[:columns].each do |column|
|
||||
%td{class: @excel_data[:column_types][column].present? ? @excel_data[:column_types][column].to_s : nil}=_!row[column]
|
||||
|
||||
- format_xls 'table' do
|
||||
- workbook use_autowidth: true
|
||||
- format bg_color: '333333'
|
||||
- format 'td', font_name: 'Calibri', bg_color: 'ffffff', fg_color: '333333'
|
||||
- format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff'
|
||||
- format 'td.date', num_fmt: 22, font_name: 'Courier New', sz: 10
|
@ -2,8 +2,7 @@
|
||||
%html{ lang: I18n.locale.to_s }
|
||||
%head
|
||||
%meta{ charset: 'utf-8' }
|
||||
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0' }
|
||||
-#%title= (yield :title) + (content_for?(:title) ? (_!' | ') : '') + (_!'Bike!Bike!')
|
||||
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }
|
||||
- title = yield :title
|
||||
%title=_!('Bike!Bike!' + (content_for?(:title) ? " - #{title}" : ''))
|
||||
%meta{ name: 'description', content: (yield_or_default :description, I18n.t('page_descriptions.home')) }
|
||||
@ -14,13 +13,11 @@
|
||||
- @alt_lang_urls.each do |locale, url|
|
||||
%link{ rel: :alternate, hreflang: locale, href: url }
|
||||
- if content_for?(:og_image)
|
||||
- og_image = yield :og_image
|
||||
- og_image = request.base_url + og_image
|
||||
%meta{property: 'og:title', content: title}
|
||||
%meta{property: 'og:type', content: "website"}
|
||||
%meta{property: 'og:image', content: (yield :og_image)}
|
||||
-#%link{ href: asset_path('apple-touch-icon.png'), rel: 'apple-touch-icon' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-72x72.png'), rel: 'apple-touch-icon', sizes: '72x72' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-114x114.png'), rel: 'apple-touch-icon', sizes: '114x114' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-144x144.png'), rel: 'apple-touch-icon', sizes: '144x144' }
|
||||
%meta{property: 'og:type', content: 'website'}
|
||||
%meta{property: 'og:image', content: og_image}
|
||||
= yield :head
|
||||
|
||||
%body{ class: page_style }
|
||||
@ -45,16 +42,24 @@
|
||||
= yield
|
||||
#footer
|
||||
%footer= render 'shared/footer'
|
||||
- if @confirmation_dlg.present?
|
||||
- if @confirmation_dlg.present? || @info_dlg.present?
|
||||
#content-overlay
|
||||
#overlay
|
||||
.dlg#confirmation-dlg
|
||||
.dlg-content
|
||||
%h2.title=_'modals.confirm'
|
||||
.dlg-inner
|
||||
%p.message=''
|
||||
%a.button.confirm=_'modals.yes_button'
|
||||
%button.delete=_'modals.no_button'
|
||||
- if @confirmation_dlg.present?
|
||||
.dlg#confirmation-dlg
|
||||
.dlg-content
|
||||
%h2.title=_'modals.confirm'
|
||||
.dlg-inner
|
||||
%p.message=''
|
||||
%a.button.confirm=_'modals.yes_button'
|
||||
%button.delete.close=_'modals.no_button'
|
||||
- if @info_dlg.present?
|
||||
.dlg#info-dlg
|
||||
.dlg-content
|
||||
%h2.title=_'modals.info'
|
||||
.dlg-inner
|
||||
%p.message=''
|
||||
%button.close=_'modals.done_button'
|
||||
|
||||
= yield :footer_scripts if content_for?(:footer_scripts)
|
||||
= inline_scripts
|
||||
|
@ -58,5 +58,5 @@ BikeBike::Application.configure do
|
||||
# to deliver to the browser instead of email
|
||||
config.action_mailer.delivery_method = :letter_opener
|
||||
|
||||
Paypal.sandbox!
|
||||
# Paypal.sandbox!
|
||||
end
|
||||
|
@ -5533,6 +5533,7 @@ en:
|
||||
needs: Needs
|
||||
space: Space
|
||||
theme: Theme
|
||||
interested_count: Interested
|
||||
Delete_Workshop: Delete Workshop
|
||||
notes: Notes
|
||||
Workshops: Workshops
|
||||
|
5
db/migrate/20160703044620_add_block_to_workshops.rb
Normal file
5
db/migrate/20160703044620_add_block_to_workshops.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddBlockToWorkshops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :workshops, :block, :json
|
||||
end
|
||||
end
|
@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160630233219) do
|
||||
ActiveRecord::Schema.define(version: 20160703044620) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@ -415,6 +415,7 @@ ActiveRecord::Schema.define(version: 20160630233219) do
|
||||
t.string "locale"
|
||||
t.integer "event_location_id"
|
||||
t.boolean "needs_facilitators"
|
||||
t.json "block"
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user