summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlowercasename <raphaelkabo@gmail.com>2019-09-14 10:08:09 +0100
committerlowercasename <raphaelkabo@gmail.com>2019-09-14 10:08:09 +0100
commit9d3da3563ce13d54673cfa3468911caeb4836741 (patch)
treeb5a751b303552d68e1e406ea4ae110892f235153
parent3b15d58ef86c041b44523462460fb3d70352ab1b (diff)
Functionality to limit number of attendees
-rwxr-xr-xapp.js24
-rwxr-xr-xmodels/Event.js3
-rwxr-xr-xpublic/css/style.css17
-rwxr-xr-xroutes.js14
-rwxr-xr-xviews/event.handlebars37
-rw-r--r--views/partials/editeventmodal.handlebars32
-rwxr-xr-xviews/partials/neweventform.handlebars43
7 files changed, 150 insertions, 20 deletions
diff --git a/app.js b/app.js
index 9a82abe..a8d2889 100755
--- a/app.js
+++ b/app.js
@@ -16,8 +16,28 @@ const app = express();
// View engine //
-
-app.engine('handlebars', hbs({defaultLayout: 'main'}));
+hbsInstance = hbs.create({
+ defaultLayout: 'main',
+ partialsDir: ['views/partials/'],
+ helpers: {
+ plural: function(number, text) {
+ var singular = number === 1;
+ // If no text parameter was given, just return a conditional s.
+ if (typeof text !== 'string') return singular ? '' : 's';
+ // Split with regex into group1/group2 or group1(group3)
+ var match = text.match(/^([^()\/]+)(?:\/(.+))?(?:\((\w+)\))?/);
+ // If no match, just append a conditional s.
+ if (!match) return text + (singular ? '' : 's');
+ // We have a good match, so fire away
+ return singular && match[1] // Singular case
+ ||
+ match[2] // Plural case: 'bagel/bagels' --> bagels
+ ||
+ match[1] + (match[3] || 's'); // Plural case: 'bagel(s)' or 'bagel' --> bagels
+ }
+ }
+});
+app.engine('handlebars', hbsInstance.engine);
app.set('view engine', 'handlebars');
// Static files //
diff --git a/models/Event.js b/models/Event.js
index 90c8117..43af171 100755
--- a/models/Event.js
+++ b/models/Event.js
@@ -155,6 +155,9 @@ const EventSchema = new mongoose.Schema({
default: true
},
attendees: [Attendees],
+ maxAttendees: {
+ type: Number
+ },
comments: [CommentSchema]
});
diff --git a/public/css/style.css b/public/css/style.css
index 43d9997..122c6ad 100755
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -292,6 +292,23 @@ body, html {
margin-top: 0.5rem;
}
+#maxAttendeesContainer {
+ display: none;
+}
+#maxAttendeesCheckboxContainer {
+ display: none;
+}
+
+.edit-buttons {
+ text-align: right;
+}
+
+@media (max-width: 1199.98px) {
+ .edit-buttons {
+ text-align: left;
+ }
+}
+
@media (min-width: 577px) {
#sidebar {
border-right: 2px solid #e0e0e0;
diff --git a/routes.js b/routes.js
index fde1b86..4e665ba 100755
--- a/routes.js
+++ b/routes.js
@@ -244,6 +244,13 @@ router.get('/:eventID', (req, res) => {
}
}
let eventAttendees = event.attendees.sort((a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0));
+ let spotsRemaining, noMoreSpots;
+ if (event.maxAttendees) {
+ spotsRemaining = event.maxAttendees - eventAttendees.length;
+ if (spotsRemaining <= 0) {
+ noMoreSpots = true;
+ }
+ }
let metadata = {
title: event.name,
description: marked(event.description, { renderer: render_plain()}).split(" ").splice(0,40).join(" ").trim(),
@@ -256,6 +263,8 @@ router.get('/:eventID', (req, res) => {
escapedName: escapedName,
eventData: event,
eventAttendees: eventAttendees,
+ spotsRemaining: spotsRemaining,
+ noMoreSpots: noMoreSpots,
eventStartISO: eventStartISO,
eventEndISO: eventEndISO,
parsedLocation: parsedLocation,
@@ -334,6 +343,7 @@ router.post('/newevent', (req, res) => {
usersCanAttend: req.body.joinCheckbox ? true : false,
showUsersList: req.body.guestlistCheckbox ? true : false,
usersCanComment: req.body.interactionCheckbox ? true : false,
+ maxAttendees: req.body.maxAttendees,
firstLoad: true
});
event.save()
@@ -470,7 +480,9 @@ router.post('/editevent/:eventID/:editToken', (req, res) => {
image: eventImageFilename,
usersCanAttend: req.body.joinCheckbox ? true : false,
showUsersList: req.body.guestlistCheckbox ? true : false,
- usersCanComment: req.body.interactionCheckbox ? true : false
+ usersCanComment: req.body.interactionCheckbox ? true : false,
+ maxAttendees: req.body.maxAttendeesCheckbox ? req.body.maxAttendees : null,
+
}
Event.findOneAndUpdate({id: req.params.eventID}, updatedEvent, function(err, raw) {
if (err) {
diff --git a/views/event.handlebars b/views/event.handlebars
index fdbc7cf..a4d714d 100755
--- a/views/event.handlebars
+++ b/views/event.handlebars
@@ -8,7 +8,7 @@
<h3 id="eventName">{{eventData.name}}</h3>
</div>
{{#if editingEnabled}}
- <div class="col-lg-2 ml-2">
+ <div class="col-lg-2 ml-2 edit-buttons">
<div class="btn-group" role="group" aria-label="Event controls">
<button type="button" id="editEvent" class="btn btn-success" data-toggle="modal" data-target="#editModal" {{#if eventHasConcluded}}disabled{{/if}}><i class="fas fa-edit"></i></button>
<button type="button" id="deleteEvent" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal"><i class="fas fa-trash"></i></button>
@@ -102,11 +102,20 @@
<div class="card mb-4" id="eventAttendees">
<h5 class="card-header">Attendees {{#if eventAttendees}}({{eventAttendees.length}}){{/if}}
<div class="btn-group" role="group" aria-label="Attendance controls">
- <button type="button" id="attendEvent" class="btn btn-success" data-toggle="modal" data-target="#attendModal"><i class="fas fa-user-plus"></i> Add me</button>
+ {{#unless noMoreSpots}}
+ <button type="button" id="attendEvent" class="btn btn-success" data-toggle="modal" data-target="#attendModal"><i class="fas fa-user-plus"></i> Add me</button>
+ {{/unless}}
<button type="button" id="unattendEvent" class="btn btn-secondary" data-toggle="modal" data-target="#unattendModal"><i class="fas fa-user-times"></i> Remove me</button>
</div>
</h5>
<div class="card-body">
+ {{#if eventData.maxAttendees}}
+ {{#if noMoreSpots}}
+ <div class="alert alert-warning text-center">This event is at capacity.</div>
+ {{else}}
+ <div class="alert alert-warning text-center">{{spotsRemaining}} {{plural spotsRemaining "spot(s)"}} remaining - add yourself now!</div>
+ {{/if}}
+ {{/if}}
{{#if eventAttendees}}
<ul class="attendeesList">
{{#each eventAttendees}}
@@ -384,6 +393,30 @@
setTimeout(function(){ $("#copyEventLink").html('<i class="fas fa-copy"></i> Copy');}, 5000);
})
$(".daysToDeletion").html(moment("{{eventEndISO}}").add(7, 'days').fromNow());
+ if ($("#joinCheckbox").is(':checked')){
+ $("#maxAttendeesCheckboxContainer").css("display","flex");
+ }
+ $("#maxAttendeesCheckbox").on("click", function() {
+ if ($(this).is(':checked')) {
+ $("#maxAttendeesContainer").slideDown('fast').css("display","flex");
+ $("#maxAttendees").attr("data-validation-optional","false");
+ }
+ else {
+ $("#maxAttendeesContainer").slideUp('fast');
+ $("#maxAttendees").attr("data-validation-optional","true").val("").removeClass('is-valid is-invalid');
+ }
+ });
+ $("#joinCheckbox").on("click", function() {
+ if ($(this).is(':checked')) {
+ $("#maxAttendeesCheckboxContainer").slideDown('fast').css("display","flex");
+ }
+ else {
+ $("#maxAttendeesCheckboxContainer").slideUp('fast');
+ $("#maxAttendeesCheckbox").prop("checked",false);
+ $("#maxAttendeesContainer").slideUp('fast');
+ $("#maxAttendees").attr("data-validation-optional","true").val("").removeClass('is-valid is-invalid');
+ }
+ });
});
</script>
diff --git a/views/partials/editeventmodal.handlebars b/views/partials/editeventmodal.handlebars
index ceb172b..68c8f80 100644
--- a/views/partials/editeventmodal.handlebars
+++ b/views/partials/editeventmodal.handlebars
@@ -7,7 +7,7 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
- <form id="editEventForm" action="/editevent/{{eventData.id}}/{{eventData.editToken}}" method="post" enctype="multipart/form-data">
+ <form id="editEventForm" action="/editevent/{{eventData.id}}/{{eventData.editToken}}" method="post" enctype="multipart/form-data" autocomplete="off">
<div class="modal-body">
<div class="form-group row">
<label for="eventName" class="col-sm-2 col-form-label">Event name</label>
@@ -34,7 +34,7 @@
</div>
</div>
<div class="form-group row">
- <label for="timezone" class="col-sm-2 col-form-label">Timezone</label>
+ <label for="timezone" class="col-sm-2 col-form-label">Timezone</label>
<div class="form-group col-sm-10">
<select class="select2" id="timezone" name="timezone"></select>
</div>
@@ -71,20 +71,32 @@
<div class="form-group row">
<div class="col-sm-2">Options</div>
<div class="col-sm-10">
+ <div class="form-check">
+ <input class="form-check-input" type="checkbox" id="interactionCheckbox" name="interactionCheckbox" {{#if eventData.usersCanComment}}checked{{/if}}>
+ <label class="form-check-label" for="interactionCheckbox">
+ Users can post comments on this event
+ </label>
+ </div>
<div class="form-check">
- <input class="form-check-input" type="checkbox" id="joinCheckbox" name="joinCheckbox" {{#if eventData.usersCanAttend}}checked{{/if}}>
+ <input class="form-check-input {{#unless eventData.usersCanAttend}}unchecked{{/unless}}" type="checkbox" id="joinCheckbox" name="joinCheckbox" {{#if eventData.usersCanAttend}}checked{{/if}}>
<label class="form-check-label" for="joinCheckbox">
Users can mark themselves as attending this event
</label>
</div>
- <div class="form-check">
- <input class="form-check-input" type="checkbox" id="interactionCheckbox" name="interactionCheckbox" {{#if eventData.usersCanComment}}checked{{/if}}>
- <label class="form-check-label" for="interactionCheckbox">
- Users can post comments on this event
- </label>
- </div>
+ <div class="form-check" id="maxAttendeesCheckboxContainer" {{#if eventData.maxAttendees}}style="display:flex"{{/if}}>
+ <input class="form-check-input" type="checkbox" id="maxAttendeesCheckbox" name="maxAttendeesCheckbox" {{#if eventData.maxAttendees}}checked{{/if}}>
+ <label class="form-check-label" for="maxAttendeesCheckbox">
+ Set a limit on the maximum number of attendees
+ </label>
+ </div>
</div>
</div>
+ <div class="form-group row" id="maxAttendeesContainer" {{#if eventData.maxAttendees}}style="display:flex"{{/if}}>
+ <label for="maxAttendees" class="col-sm-2 col-form-label">Attendee limit</label>
+ <div class="form-group col-sm-10">
+ <input type="number" class="form-control" id="maxAttendees" name="maxAttendees" placeholder="Enter a number." data-validation="number" data-validation-optional="true" value="{{eventData.maxAttendees}}">
+ </div>
+ </div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
@@ -93,4 +105,4 @@
</form>
</div>
</div>
-</div> \ No newline at end of file
+</div>
diff --git a/views/partials/neweventform.handlebars b/views/partials/neweventform.handlebars
index bf6d0ed..51d1695 100755
--- a/views/partials/neweventform.handlebars
+++ b/views/partials/neweventform.handlebars
@@ -25,7 +25,7 @@
</div>
</div>
<div class="form-group row">
- <label for="timezone" class="col-sm-2 col-form-label">Timezone</label>
+ <label for="timezone" class="col-sm-2 col-form-label">Timezone</label>
<div class="form-group col-sm-10">
<select class="select2" id="timezone" name="timezone"></select>
</div>
@@ -81,19 +81,31 @@
<div class="col-sm-2">Options</div>
<div class="col-sm-10">
<div class="form-check">
+ <input class="form-check-input" type="checkbox" id="interactionCheckbox" name="interactionCheckbox">
+ <label class="form-check-label" for="interactionCheckbox">
+ Users can post comments on this event
+ </label>
+ </div>
+ <div class="form-check">
<input class="form-check-input" type="checkbox" id="joinCheckbox" name="joinCheckbox">
<label class="form-check-label" for="joinCheckbox">
Users can mark themselves as attending this event
</label>
</div>
- <div class="form-check">
- <input class="form-check-input" type="checkbox" id="interactionCheckbox" name="interactionCheckbox">
- <label class="form-check-label" for="interactionCheckbox">
- Users can post comments on this event
+ <div class="form-check" id="maxAttendeesCheckboxContainer">
+ <input class="form-check-input" type="checkbox" id="maxAttendeesCheckbox" name="maxAttendeesCheckbox">
+ <label class="form-check-label" for="maxAttendeesCheckbox">
+ Set a limit on the maximum number of attendees
</label>
</div>
</div>
</div>
+ <div class="form-group row" id="maxAttendeesContainer">
+ <label for="maxAttendees" class="col-sm-2 col-form-label">Attendee limit</label>
+ <div class="form-group col-sm-10">
+ <input type="number" class="form-control" id="maxAttendees" name="maxAttendees" placeholder="Enter a number." data-validation="number" data-validation-optional="true">
+ </div>
+ </div>
<div class="form-group row">
<div class="col-sm-12 pt-3 pb-3 text-center">
<button type="submit" class="btn btn-primary w-50">Create</button>
@@ -114,5 +126,26 @@
no_label: false
});
autosize($('textarea'));
+ $("#maxAttendeesCheckbox").on("click", function() {
+ if ($(this).is(':checked')) {
+ $("#maxAttendeesContainer").slideDown('fast').css("display","flex");
+ $("#maxAttendees").attr("data-validation-optional","false");
+ }
+ else {
+ $("#maxAttendeesContainer").slideUp('fast');
+ $("#maxAttendees").attr("data-validation-optional","true").val("").removeClass('is-valid is-invalid');
+ }
+ });
+ $("#joinCheckbox").on("click", function() {
+ if ($(this).is(':checked')) {
+ $("#maxAttendeesCheckboxContainer").slideDown('fast').css("display","flex");
+ }
+ else {
+ $("#maxAttendeesCheckboxContainer").slideUp('fast');
+ $("#maxAttendeesCheckbox").prop("checked",false);
+ $("#maxAttendeesContainer").slideUp('fast');
+ $("#maxAttendees").attr("data-validation-optional","true").val("").removeClass('is-valid is-invalid');
+ }
+ });
});
</script>