Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give appointments a status #244 #252

Closed
wants to merge 10 commits into from
3 changes: 3 additions & 0 deletions src/application/controllers/Appointments.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@ public function ajax_register_appointment() {
$appointment['is_unavailable'] = (int)$appointment['is_unavailable']; // needs to be type casted
$appointment['id'] = $this->appointments_model->add($appointment);
$appointment['hash'] = $this->appointments_model->get_value('hash', $appointment['id']);
$appointment['attendance_status'] = !isset($appointment['attendance_status'])
? 'registered'
: $appointment['attendance_status'];

$provider = $this->providers_model->get_row($appointment['id_users_provider']);
$service = $this->services_model->get_row($appointment['id_services']);
Expand Down
5 changes: 5 additions & 0 deletions src/application/language/chinese/translations_lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
$lang['service'] = '服务项目';
$lang['provider'] = '服务人员';
$lang['customer'] = '客户';
$lang['attendance_status'] = '状态';
$lang['attendance_status_registered'] = '已注册';
$lang['attendance_status_checked_in'] = '已签到';
$lang['attendance_action_check_in'] = ' 签到';
$lang['attendance_action_undo_check_in'] = '撤销签到';
$lang['start'] = '开始';
$lang['end'] = '结束';
$lang['name'] = '名字';
Expand Down
5 changes: 5 additions & 0 deletions src/application/language/english/translations_lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
$lang['service'] = 'Service';
$lang['provider'] = 'Provider';
$lang['customer'] = 'Customer';
$lang['attendance_status'] = 'Status';
$lang['attendance_status_registered'] = 'Registered';
$lang['attendance_status_checked_in'] = 'Checked in ✓';
$lang['attendance_action_check_in'] = 'Check In';
$lang['attendance_action_undo_check_in'] = 'Undo Check In';
$lang['start'] = 'Start';
$lang['end'] = 'End';
$lang['name'] = 'Name';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ private function test_add_appointment_insert() {
// record is inserted.
unset($db_data['hash']);
unset($db_data['book_datetime']);
unset($db_data['attendance_status']);
unset($db_data['id_google_calendar']);

$this->ci->unit->run($appointment, $db_data, 'Test if add() appointment (insert '
Expand Down Expand Up @@ -493,6 +494,7 @@ private function test_get_row() {
// Get the appointment row from the database.
$db_data = $this->ci->appointments_model->get_row($appointment['id']);
unset($db_data['book_datetime']);
unset($db_data['attendance_status']);
unset($db_data['id_google_calendar']);

// Check if this is the record we seek.
Expand Down Expand Up @@ -722,4 +724,4 @@ private function test_validate_data_invalid_service_id() {
}

/* End of file Unit_tests_appointments_model.php */
/* Location: ./application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php */
/* Location: ./application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php */
10 changes: 10 additions & 0 deletions src/application/views/backend/calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@
</div>
</div>

<div class="form-group">
<label for="attendance-status" class="control-label col-sm-3" ><?php echo $this->lang->line('attendance_status'); ?></label>
<div class="col-sm-7">
<select id="attendance-status" class="required form-control">
<option value="registered" class="as as_registered">Registered</option>
<option value="checked_in" class="as as_checked_in">Checked in</option>
</select>
</div>
</div>

<div class="form-group">
<label for="appointment-notes" class="control-label col-sm-3" ><?php echo $this->lang->line('notes'); ?></label>
<div class="col-sm-7">
Expand Down
4 changes: 4 additions & 0 deletions src/application/views/emails/appointment_details.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
<td class="label" style="padding: 3px;font-weight: bold;">End</td>
<td style="padding: 3px;">$appointment_end_date</td>
</tr>
<tr>
<td class="label" style="padding: 3px;font-weight: bold;">Status</td>
<td style="padding: 3px;">$attendance_status</td>
</tr>
</table>

<h2>Customer Details</h2>
Expand Down
1 change: 1 addition & 0 deletions src/assets/css/backend.css
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ root {
margin: auto;
display: block;
max-width: 100%;
transform: scale(.5);
}

.modal-message {
Expand Down
2 changes: 2 additions & 0 deletions src/assets/js/backend_calendar_appointments_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ window.BackendCalendarAppointmentsModal = window.BackendCalendarAppointmentsModa

var startDatetime = $dialog.find('#start-datetime').datetimepicker('getDate').toString('yyyy-MM-dd HH:mm:ss');
var endDatetime = $dialog.find('#end-datetime').datetimepicker('getDate').toString('yyyy-MM-dd HH:mm:ss');
var attendanceStatus = $dialog.find('select#attendance-status').val();

var appointment = {
id_services: $dialog.find('#select-service').val(),
id_users_provider: $dialog.find('#select-provider').val(),
start_datetime: startDatetime,
end_datetime: endDatetime,
notes: $dialog.find('#appointment-notes').val(),
attendance_status: attendanceStatus,
is_unavailable: false
};

Expand Down
131 changes: 129 additions & 2 deletions src/assets/js/backend_calendar_default_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
$dialog.find('#appointment-id').val(appointment['id']);
$dialog.find('#select-service').val(appointment['id_services']).trigger('change');
$dialog.find('#select-provider').val(appointment['id_users_provider']);
$dialog.find('#attendance-status').val(appointment['attendance_status']);

// Set the start and end datetime of the appointment.
var startDatetime = Date.parseExact(appointment['start_datetime'],
Expand Down Expand Up @@ -246,6 +247,114 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
return (result > 500) ? result : 500; // Minimum height is 500px
}

/**
* Check in an appointment ajax function
*
* @param appointment {Object} the original appointment
* @param doCheckIn {Boolean} the action is check in or not (undo check in)
* @param successCallback Do upon a successful request
* @private
*/
function _checkInAppointment(appointment, doCheckIn, successCallback) {
// Prepare appointment data.
appointment = GeneralFunctions.clone(appointment);

// Must delete the following because only appointment data should be provided to the ajax call.
delete appointment['customer'];
delete appointment['provider'];
delete appointment['service'];

appointment['attendance_status'] = doCheckIn ? 'checked_in' : 'registered';

var postUrl = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_save_appointment';
var postData = {
csrfToken: GlobalVariables.csrfToken,
appointment_data: JSON.stringify(appointment)
};

var localSuccessCallback = function (response) {
var undoFunction = function () {
_checkInAppointment(appointment, !doCheckIn, successCallback)
};

Backend.displayNotification(EALang['appointment_updated'], [
{
'label' : 'Undo',
'function': undoFunction
}
]);

if (successCallback !== undefined) {
successCallback(response);
}

$('#select-filter-item').trigger('change');

// Set timer to hide the notification
if (GlobalVariables.NOTIFICATION_BLIND_TIMER) {
clearTimeout(GlobalVariables.NOTIFICATION_BLIND_TIMER);
GlobalVariables.NOTIFICATION_BLIND_TIMER = undefined;
}
GlobalVariables.NOTIFICATION_BLIND_TIMER = setTimeout(function () {
$('#notification').hide('blind');
}, 5000)
};

$.post(postUrl, postData, function(response) {
$('#notification').hide('blind');

localSuccessCallback && localSuccessCallback(response);
}, 'json').fail(GeneralFunctions.ajaxFailureHandler);

}

/**
* Attach below events when appointment is checked in (or undo check in)
* @param eventData event.data, appointment object
* @param isCheckedIn the current status of checked in
* @private
*/
function _setCheckedInStatusEvent(eventData, isCheckedIn) {
if (isCheckedIn) {
// When checked in
$('#checked-in-btn')
.unbind('mouseenter mouseleave')
.off('click')
.on('mouseenter', function () {
$(this).addClass('btn-warning').removeClass('btn-success').text(EALang['attendance_action_undo_check_in']);
})
.on('mouseleave', function () {
$(this).addClass('btn-success').removeClass('btn-warning').text(EALang['attendance_status_checked_in']);
})
.click(function () {
_checkInAppointment(eventData, false, function successCallback() {
$(this)
.addClass('btn-success')
.removeClass('btn-warning')
.text(EALang['attendance_action_check_in'])
.off('hover')
.off('click');
_setCheckedInStatusEvent(eventData, false);
}.bind(this));
});
} else {
// When not checked in
$('#checked-in-btn')
.unbind('mouseenter mouseleave')
.off('click')
.click(function () {
_checkInAppointment(eventData, true, function successCallback() {
$(this)
.addClass('btn-success')
.removeClass('btn-warning')
.text(EALang['attendance_status_checked_in'])
.off('click');
_setCheckedInStatusEvent(eventData, true);
}.bind(this));
});
}
}

/**
* Calendar Event "Click" Callback
*
Expand Down Expand Up @@ -322,6 +431,11 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
'<strong>' + EALang['customer'] + '</strong> '
+ event.data['customer']['first_name'] + ' '
+ event.data['customer']['last_name']
+ '<br>' +
'<strong>' + EALang['attendance_status'] + '</strong> '
+ '<button id="checked-in-btn" class="btn btn-xs btn-success">'
+ (event.data['attendance_status'] === 'checked_in' ? EALang['attendance_status_checked_in'] : EALang['attendance_action_check_in'])
+ '</button>'
+ '<hr>' +
'<center>' +
'<button class="edit-popover btn btn-primary ' + displayEdit + '">' + EALang['edit'] + '</button>' +
Expand All @@ -346,6 +460,17 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
if ($('.popover').length > 0) {
if ($('.popover').position().top < 200) $('.popover').css('top', '200px');
}

// Attach event shown click events
if (event.data['attendance_status'] === 'checked_in') {
$(jsEvent.target).on('shown.bs.popover', function () {
_setCheckedInStatusEvent(event.data, true);
});
} else {
$(jsEvent.target).on('shown.bs.popover', function () {
_setCheckedInStatusEvent(event.data, false);
});
}
}

/**
Expand Down Expand Up @@ -754,7 +879,8 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
start: appointment['start_datetime'],
end: appointment['end_datetime'],
allDay: false,
data: appointment // Store appointment data for later use.
data: appointment, // Store appointment data for later use.
color: appointment['attendance_status'] === 'checked_in' ? '#35b66f' : ''
};

calendarEvents.push(event);
Expand Down Expand Up @@ -1145,6 +1271,7 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
$dialog.find('#appointment-id').val(appointment['id']);
$dialog.find('#select-service').val(appointment['id_services']).change();
$dialog.find('#select-provider').val(appointment['id_users_provider']);
$dialog.find('#attendance-status').val(appointment['attendance_status']);

// Set the start and end datetime of the appointment.
var startDatetime = Date.parseExact(appointment['start_datetime'],
Expand Down Expand Up @@ -1198,7 +1325,7 @@ window.BackendCalendarDefaultView = window.BackendCalendarDefaultView || {};
// Fine tune the footer's position only for this page.
if (window.innerHeight < 700) {
$('#footer').css('position', 'static');
}
}
};

})(window.BackendCalendarDefaultView);
1 change: 1 addition & 0 deletions src/assets/sql/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS `ea_appointments` (
`end_datetime` datetime DEFAULT NULL,
`notes` text,
`hash` text,
`attendance_status` VARCHAR(32) DEFAULT 'registered' COMMENT '[registered, checked_in]',
`is_unavailable` tinyint(4) DEFAULT '0',
`id_users_provider` bigint(20) unsigned DEFAULT NULL,
`id_users_customer` bigint(20) unsigned DEFAULT NULL,
Expand Down
5 changes: 5 additions & 0 deletions src/engine/Api/V1/Parsers/Appointments.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public function encode(array &$response) {
'end' => $response['end_datetime'],
'hash' => $response['hash'],
'notes' => $response['notes'],
'attendanceStatus' => $response['attendance_status'],
'customerId' => $response['id_users_customer'] !== null ? (int)$response['id_users_customer'] : null,
'providerId' => $response['id_users_provider'] !== null ? (int)$response['id_users_provider'] : null,
'serviceId' => $response['id_services'] !== null ? (int)$response['id_services'] : null,
Expand Down Expand Up @@ -74,6 +75,10 @@ public function decode(array &$request, array $base = null) {
$decodedRequest['notes'] = $request['notes'];
}

if (!empty($request['attendanceStatus'])) {
$decodedRequest['attendance_status'] = $request['attendanceStatus'];
}

if (!empty($request['customerId'])) {
$decodedRequest['id_users_customer'] = $request['customerId'];
}
Expand Down
5 changes: 5 additions & 0 deletions src/engine/Api/V1/Parsers/Unavailabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function encode(array &$response) {
'start' => $response['start_datetime'],
'end' => $response['end_datetime'],
'notes' => $response['notes'],
'attendanceStatus' => $response['attendance_status'],
'providerId' => $response['id_users_provider'] !== null ? (int)$response['id_users_provider'] : null,
'googleCalendarId' => $response['id_google_calendar'] !== null ? (int)$response['id_google_calendar'] : null
];
Expand Down Expand Up @@ -67,6 +68,10 @@ public function decode(array &$request, array $base = null) {
$decodedRequest['notes'] = $request['notes'];
}

if (!empty($request['attendanceStatus'])) {
$decodedRequest['attendance_status'] = $request['attendanceStatus'];
}

if (!empty($request['providerId'])) {
$decodedRequest['id_users_provider'] = $request['providerId'];
}
Expand Down
4 changes: 3 additions & 1 deletion src/engine/Notifications/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public function sendAppointmentDetails(array $appointment, array $provider, arra
'$appointment_provider' => $provider['first_name'] . ' ' . $provider['last_name'],
'$appointment_start_date' => date('d/m/Y H:i', strtotime($appointment['start_datetime'])),
'$appointment_end_date' => date('d/m/Y H:i', strtotime($appointment['end_datetime'])),
'$attendance_status' => $this->framework->lang->line('attendance_status_' . $appointment['attendance_status']),
'$appointment_link' => $appointmentLink->get(),
'$company_link' => $company['company_link'],
'$company_name' => $company['company_name'],
Expand All @@ -119,7 +120,8 @@ public function sendAppointmentDetails(array $appointment, array $provider, arra
'Email' => $this->framework->lang->line('email'),
'Phone' => $this->framework->lang->line('phone'),
'Address' => $this->framework->lang->line('address'),
'Appointment Link' => $this->framework->lang->line('appointment_link_title')
'Appointment Link' => $this->framework->lang->line('appointment_link_title'),
'Status' => $this->framework->lang->line('attendance_status')
);

$html = file_get_contents(__DIR__ . '/../../application/views/emails/appointment_details.php');
Expand Down