diff options
-rwxr-xr-x | public/css/style.css | 10 | ||||
-rwxr-xr-x | routes.js | 78 | ||||
-rwxr-xr-x | views/event.handlebars | 83 |
3 files changed, 159 insertions, 12 deletions
diff --git a/public/css/style.css b/public/css/style.css index ebbc375..ec43118 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -251,3 +251,13 @@ body, html { height: 2.25rem !important; } +#removeAttendee { + position: absolute; + top: 110px; + left: 53px; + color: #fff; +} + +#removeAttendee:hover { + color: #c82333; +} @@ -146,10 +146,10 @@ router.get('/:eventID', (req, res) => { else { displayDate = moment.tz(event.start, event.timezone).format('dddd D MMMM YYYY [<span class="text-muted">at</span>] h:mm a') + moment.tz(event.end, event.timezone).format(' [<span class="text-muted">–</span>] dddd D MMMM YYYY [<span class="text-muted">at</span>] h:mm a [<span class="text-muted">](z)[</span>]'); } - eventStartISO = moment.tz(event.start, event.timezone).toISOString(); - eventEndISO = moment.tz(event.end, event.timezone).toISOString(); + eventStartISO = moment.tz(event.start, "Etc/UTC").toISOString(); + eventEndISO = moment.tz(event.end, "Etc/UTC").toISOString(); parsedStart = moment.tz(event.start, event.timezone).format('YYYYMMDD[T]HHmmss'); - parsedEnd = moment.tz(event.start, event.timezone).format('YYYYMMDD[T]HHmmss'); + parsedEnd = moment.tz(event.end, event.timezone).format('YYYYMMDD[T]HHmmss'); let eventHasConcluded = false; if (moment.tz(event.end, event.timezone).isBefore(moment.tz(event.timezone))){ eventHasConcluded = true; @@ -588,6 +588,76 @@ router.post('/attendevent/:eventID', (req, res) => { }); }); +router.post('/unattendevent/:eventID', (req, res) => { + Event.update( + { id: req.params.eventID }, + { $pull: { attendees: { email: req.body.attendeeEmail } } } + ) + .then(response => { + console.log(response) + addToLog("removeEventAttendee", "success", "Attendee removed from event " + req.params.eventID); + if (sendEmails) { + if (req.body.attendeeEmail){ + const msg = { + to: req.body.attendeeEmail, + from: { + name: 'Gathio', + email: 'notifications@gath.io', + }, + templateId: 'd-56c97755d6394c23be212fef934b0f1f', + dynamic_template_data: { + subject: 'gathio: You have been removed from an event', + eventID: req.params.eventID + }, + }; + sgMail.send(msg); + } + } + res.writeHead(302, { + 'Location': '/' + req.params.eventID + }); + res.end(); + }) + .catch((err) => { + res.send('Database error, please try again :('); addToLog("removeEventAttendee", "error", "Attempt to remove attendee from event " + req.params.eventID + " failed with error: " + err); + }); +}); + +router.post('/removeattendee/:eventID/:attendeeID', (req, res) => { + Event.update( + { id: req.params.eventID }, + { $pull: { attendees: { _id: req.params.attendeeID } } } + ) + .then(response => { + console.log(response) + addToLog("removeEventAttendee", "success", "Attendee removed by admin from event " + req.params.eventID); + if (sendEmails) { + if (req.body.attendeeEmail){ + const msg = { + to: req.body.attendeeEmail, + from: { + name: 'Gathio', + email: 'notifications@gath.io', + }, + templateId: 'd-56c97755d6394c23be212fef934b0f1f', + dynamic_template_data: { + subject: 'gathio: You have been removed from an event', + eventID: req.params.eventID + }, + }; + sgMail.send(msg); + } + } + res.writeHead(302, { + 'Location': '/' + req.params.eventID + }); + res.end(); + }) + .catch((err) => { + res.send('Database error, please try again :('); addToLog("removeEventAttendee", "error", "Attempt to remove attendee by admin from event " + req.params.eventID + " failed with error: " + err); + }); +}); + router.post('/post/comment/:eventID', (req, res) => { let commentID = shortid.generate(); const newComment = { @@ -655,7 +725,7 @@ router.post('/post/reply/:eventID/:commentID', (req, res) => { event.save() .then(() => { addToLog("addEventReply", "success", "Reply added to comment " + commentID + " in event " + req.params.eventID); - if (sendEmails) { + if (sendEmails) { Event.findOne({id: req.params.eventID}).distinct('attendees.email', function(error, ids) { attendeeEmails = ids; if (!error){ diff --git a/views/event.handlebars b/views/event.handlebars index daacfc5..8d2559b 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -95,12 +95,17 @@ {{#if eventData.usersCanAttend}} <div class="card mb-4" id="eventAttendees"> - <h5 class="card-header" style="width:100%;display:flex;justify-content:space-between;align-items:center">Attendees {{#if eventData.attendees}}({{eventData.attendees.length}}){{/if}}<button type="button" id="attendEvent" class="btn btn-primary" data-toggle="modal" data-target="#attendModal"><i class="fas fa-user-plus"></i> I'm going!</button></h5> + <h5 class="card-header" style="width:100%;display:flex;justify-content:space-between;align-items:center">Attendees {{#if eventData.attendees}}({{eventData.attendees.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> + <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.attendees}} <ul class="attendeesList"> {{#each eventData.attendees}} - <li>{{this.name}}</li> + <li{{#if ../editingEnabled}} data-attendee-name="{{this.name}}" data-attendee-id="{{this._id}}"{{/if}}>{{this.name}}{{#if ../editingEnabled}}<a href="#" id="removeAttendee" data-toggle="modal" data-target="#removeAttendeeModal"><i class="fas fa-user-times"></i></a>{{/if}}</li> {{/each}} </ul> {{else}} @@ -129,8 +134,8 @@ <div class="form-group row"> <label for="attendeeEmail" class="col-sm-2 col-form-label">Your email</label> <div class="form-group col-sm-10"> - <input type="email" class="form-control" id="attendeeEmail" name="attendeeEmail" placeholder="We won't spam you <3" data-validation="email" data-validation-optional="true"> - <small class="form-text">Optional - we'll use it to send you any updates to the event.</small> + <input type="email" class="form-control" id="attendeeEmail" name="attendeeEmail" placeholder="We won't spam you <3" data-validation="email"> + <small class="form-text">We'll only use it to send you updates to the event.</small> </div> </div> </div> @@ -142,8 +147,61 @@ </div> </div> </div> + +<div class="modal fade" id="unattendModal" tabindex="-1" role="dialog" aria-labelledby="unattendModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="unattendModalLabel">Remove yourself from '{{eventData.name}}'</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <form id="unattendEventForm" action="/unattendevent/{{eventData.id}}" method="post"> + <div class="modal-body"> + <div class="form-group row"> + <label for="attendeeEmail" class="col-sm-2 col-form-label">Your email</label> + <div class="form-group col-sm-10"> + <input type="email" class="form-control" id="attendeeEmail" name="attendeeEmail" placeholder="name@domain.com" data-validation="email" data-validation-optional="true"> + <small class="form-text">Enter the email you used when signing up for this event.</small> + </div> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="submit" class="btn btn-primary">Remove myself</button> + </div> + </form> + </div> + </div> +</div> + + {{#if editingEnabled}} + <div class="modal fade" id="removeAttendeeModal" tabindex="-1" role="dialog" aria-labelledby="removeAttendeeModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="removeAttendeeModalLabel">Remove attendee from '{{eventData.name}}'</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <form id="removeAttendeeForm" action="/removeattendee/{{eventData.id}}/" method="post"> + <div class="modal-body"> + <p>Are you sure you want to remove this attendee from the event? This action cannot be undone.</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="submit" class="btn btn-danger">Remove attendee</button> + </div> + </form> + </div> + </div> + </div> + {{/if}} {{/if}} + {{#if eventData.usersCanComment}} <div class="card mb-4" id="eventComments"> <h5 class="card-header">Discussion</h5> @@ -220,7 +278,6 @@ {{#if editingEnabled}} {{#unless eventHasConcluded}} -<script type="text/javascript" src="/js/generate-timezones.js"></script> {{> editeventmodal }} {{/unless}} @@ -248,9 +305,10 @@ {{/if}} - +{{#unless eventHasConcluded}} +<script type="text/javascript" src="/js/generate-timezones.js"></script> +{{/unless}} <script> - $.validate({ lang: 'en', errorElementClass: "is-invalid", @@ -258,6 +316,15 @@ successElementClass: "is-valid" }); {{#if editingEnabled}} + + $('#removeAttendeeModal').on('show.bs.modal', function (event) { + var listItem = $(event.relatedTarget).closest('li'); // List element enclosing button + var attendeeName = listItem.data('attendee-name'); + var attendeeID = listItem.data('attendee-id'); + var modal = $(this); + modal.find('.modal-title').text('Remove ' + attendeeName + ' from {{eventData.name}}') + modal.find('#removeAttendeeForm').attr('action', '/removeattendee/{{eventData.id}}/' + attendeeID); + }) {{#unless eventHasConcluded}} $('#eventStart').datepicker({ language: 'en', @@ -287,7 +354,7 @@ $(this).closest(".comment").find(".replyContainer").slideToggle(); }) $(document).ready(function() { - + $.uploadPreview({ input_field: "#image-upload", preview_box: "#image-preview", |