Allow the users to define their own status and assign them to appointments (#244).

This commit is contained in:
Alex Tselegidis 2022-10-16 21:54:26 +03:00
parent 5431d25f0b
commit 0f6df76667
48 changed files with 452 additions and 6 deletions

View file

@ -536,12 +536,19 @@ class Booking extends EA_Controller {
$appointment['id_users_customer'] = $customer_id;
$appointment['is_unavailability'] = FALSE;
$appointment['color'] = $service['color'];
$appointment_status_options_json = setting('appointment_status_options', '[]');
$appointment_status_options = json_decode($appointment_status_options_json, TRUE) ?? [];
$appointment['status'] = $appointment_status_options[0] ?? NULL;
$this->appointments_model->only($appointment, [
'start_datetime',
'end_datetime',
'location',
'notes',
'color',
'status',
'is_unavailability',
'id_users_provider',
'id_users_customer',

View file

@ -117,6 +117,8 @@ class Calendar extends EA_Controller {
$calendar_view = request('view', $user['settings']['calendar_view']);
$appointment_status_options = setting('appointment_status_options');
script_vars([
'user_id' => $user_id,
'role_slug' => $role_slug,
@ -146,6 +148,7 @@ class Calendar extends EA_Controller {
'available_providers' => $available_providers,
'available_services' => $available_services,
'secretary_providers' => $secretary_providers,
'appointment_status_options' => json_decode($appointment_status_options, TRUE) ?? [],
'require_first_name' => setting('require_first_name'),
'require_last_name' => setting('require_last_name'),
'require_email' => setting('require_email'),
@ -248,6 +251,7 @@ class Calendar extends EA_Controller {
'location',
'notes',
'color',
'status',
'is_unavailability',
'id_users_provider',
'id_users_customer',

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -405,4 +405,7 @@ $lang['future_booking_limit_hint'] = 'Set the future limit in days customers can
$lang['api_token'] = 'API Token';
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
$lang['status'] = 'Status';
$lang['appointment_status_options'] = 'Appointment Status Options';
$lang['appointment_status_options_info'] = 'Define a list of available appointment status options that can be used in the the calendar page (the first one will automatically become the default value).';
// End

View file

@ -0,0 +1,47 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Open Source Web Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
* @license http://opensource.org/licenses/GPL-3.0 - GPLv3
* @link http://easyappointments.org
* @since v1.4.0
* ---------------------------------------------------------------------------- */
/**
* @property CI_DB_query_builder $db
* @property CI_DB_forge $dbforge
*/
class Migration_Add_appointment_status_options_setting extends CI_Migration {
/**
* Upgrade method.
*
* @throws Exception
*/
public function up()
{
if ( ! $this->db->get_where('settings', ['name' => 'appointment_status_options'])->num_rows())
{
$this->db->insert('settings', [
'name' => 'appointment_status_options',
'value' => '["Booked", "Confirmed", "Rescheduled", "Cancelled", "Draft"]'
]);
}
}
/**
* Downgrade method.
*
* @throws Exception
*/
public function down()
{
if ($this->db->get_where('settings', ['name' => 'appointment_status_options'])->num_rows())
{
$this->db->delete('settings', ['name' => 'status_options']);
}
}
}

View file

@ -0,0 +1,45 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Open Source Web Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
* @license http://opensource.org/licenses/GPL-3.0 - GPLv3
* @link http://easyappointments.org
* @since v1.4.0
* ---------------------------------------------------------------------------- */
class Migration_Add_status_column_to_appointments_table extends EA_Migration {
/**
* Upgrade method.
*/
public function up()
{
if ( ! $this->db->field_exists('status', 'appointments'))
{
$fields = [
'status' => [
'type' => 'VARCHAR',
'constraint' => '512',
'default' => '',
'after' => 'color'
]
];
$this->dbforge->add_column('appointments', $fields);
}
}
/**
* Downgrade method.
*/
public function down()
{
if ( ! $this->db->field_exists('status', 'appointments'))
{
$this->dbforge->drop_column('appointments', 'status');
}
}
}

View file

@ -38,6 +38,7 @@ class Appointments_model extends EA_Model {
'end' => 'end_datetime',
'location' => 'location',
'color' => 'color',
'status' => 'status',
'notes' => 'notes',
'hash' => 'hash',
'providerId' => 'id_users_provider',

View file

@ -0,0 +1,22 @@
<?php
/**
* @var string $attributes
*/
?>
<div class="appointment-status-options" <?= $attributes ?? '' ?>>
<ul class="list-group">
<!-- JS -->
</ul>
<button type="button" class="btn btn-outline-primary btn-sm add-appointment-status-option">
<i class="fas fa-plus-square me-2"></i>
<?= lang('add') ?>
</button>
</div>
<?php section('scripts') ?>
<script src="<?= asset_url('assets/js/components/appointment_status_options.js') ?>"></script>
<?php section('scripts') ?>

View file

@ -1,8 +1,9 @@
<?php
/**
* Local variables.
*
*
* @var array $available_services
* @var array $appointment_status_options
* @var array $timezones
* @var array $require_first_name
* @var array $require_last_name
@ -131,8 +132,16 @@
</div>
<div class="mb-3">
<label for="appointment-notes" class="form-label"><?= lang('notes') ?></label>
<textarea id="appointment-notes" class="form-control" rows="3"></textarea>
<label for="appointment-status" class="form-label">
<?= lang('status') ?>
</label>
<select id="appointment-status" class="form-control">
<?php foreach ($appointment_status_options as $appointment_status_option): ?>
<option value="<?= $appointment_status_option ?>">
<?= $appointment_status_option ?>
</option>
<?php endforeach ?>
</select>
</div>
</div>
@ -153,7 +162,8 @@
<?= lang('timezone') ?>
</label>
<div class="border rounded d-flex justify-content-between align-items-center bg-light timezone-info">
<div
class="border rounded d-flex justify-content-between align-items-center bg-light timezone-info">
<div class="border-end w-50 p-1 text-center">
<?= lang('provider') ?>:
<span class="provider-timezone">
@ -168,6 +178,11 @@
</div>
</div>
</div>
<div class="mb-3">
<label for="appointment-notes" class="form-label"><?= lang('notes') ?></label>
<textarea id="appointment-notes" class="form-control" rows="3"></textarea>
</div>
</div>
</div>
</fieldset>

View file

@ -104,6 +104,18 @@
</small>
</div>
</div>
<div class="d-flex justify-content-start align-items-center mb-3">
<h5 class="text-black-50 mb-0 me-3 fw-light">
<?= lang('appointment_status_options') ?>
</h5>
</div>
<p class="form-text text-muted mb-4">
<?= lang('appointment_status_options_info') ?>
</p>
<?php component('appointment_status_options', ['attributes' => 'id="appointment-status-options"']) ?>
</fieldset>
</form>
</div>

View file

@ -92,6 +92,7 @@
'appointments_modal',
[
'available_services' => vars('available_services'),
'appointment_status_options' => vars('appointment_status_options'),
'timezones' => vars('timezones'),
'require_first_name' => vars('require_first_name'),
'require_last_name' => vars('require_last_name'),

View file

@ -126,7 +126,6 @@
<div class="mb-3">
<label class="form-label" for="location">
<?= lang('location') ?>
</label>
<input id="location" class="form-control" disabled>
</div>

View file

@ -908,3 +908,7 @@ body .form-horizontal .controls {
#company-color {
min-height: 41px;
}
.status-list-item {
max-width: 400px;
}

View file

@ -0,0 +1,126 @@
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* Appointment status options component.
*
* This module implements the appointment status options.
*/
App.Components.AppointmentStatusOptions = (function () {
/**
* Render an appointment status option.
*
* @param {String} [appointmentStatusOption]
*
* @return {jQuery} Returns a jQuery selector with the list group item markup.
*/
function renderListGroupItem(appointmentStatusOption = '') {
return $(`
<li class="list-group-item d-flex justify-content-between align-items-center p-0 border-0 mb-3 appointment-status-option">
<label class="w-100 me-2">
<input class="form-control" value="${appointmentStatusOption}">
</label>
<button type="button" class="btn btn-outline-danger delete-appointment-status-option">
<i class="fas fa-trash"></i>
</button>
</li>
`);
}
/**
* Event: Delete Appointment Status Option "Click"
*
* @param {jQuery.Event} event
*/
function onDeleteAppointmentStatusOptionClick(event) {
$(event.currentTarget).closest('li').remove();
}
/**
* Event: Add Appointment Status Option "Click"
*
* @param {jQuery.Event} event
*/
function onAddAppointmentStatusOptionClick(event) {
const $target = $(event.currentTarget);
const $listGroup = $target.closest('.appointment-status-options').find('.list-group');
if (!$listGroup.length) {
return;
}
renderListGroupItem()
.appendTo($listGroup);
}
/**
* Get target options.
*
* @param {jQuery} $target Container element ".status-list" selector.
*
* @return {String[]}
*/
function getOptions($target) {
const $listGroup = $target.find('.list-group');
const appointmentStatusOptions = [];
$listGroup.find('li').each((index, listGroupItemEl) => {
const $listGroupItem = $(listGroupItemEl);
const appointmentStatusOption = $listGroupItem.find('input:text').val();
appointmentStatusOptions.push(appointmentStatusOption);
});
return appointmentStatusOptions;
}
/**
* Set target options.
*
* @param {jQuery} $target Container element ".status-list" selector.
* @param {String[]} appointmentStatusOptions Appointment status options.
*/
function setOptions($target, appointmentStatusOptions) {
if (!$target.length || !appointmentStatusOptions || !appointmentStatusOptions.length) {
return;
}
const $listGroup = $target.find('.list-group');
if (!$listGroup.length) {
return;
}
$listGroup.empty();
appointmentStatusOptions.forEach((appointmentStatusOption) => {
renderListGroupItem(appointmentStatusOption).appendTo($listGroup);
});
}
/**
* Initialize the module.
*/
function initialize() {
$(document).on('click', '.delete-appointment-status-option', onDeleteAppointmentStatusOptionClick);
$(document).on('click', '.add-appointment-status-option', onAddAppointmentStatusOptionClick);
}
document.addEventListener('DOMContentLoaded', initialize);
return {
getOptions,
setOptions,
initialize
};
})();

View file

@ -36,6 +36,7 @@ App.Components.AppointmentsModal = (function () {
const $saveAppointment = $('#save-appointment');
const $appointmentId = $('#appointment-id');
const $appointmentLocation = $('#appointment-location');
const $appointmentStatus = $('#appointment-status');
const $appointmentColor = $('#appointment-color');
const $appointmentNotes = $('#appointment-notes');
const $reloadAppointments = $('#reload-appointments');
@ -89,6 +90,7 @@ App.Components.AppointmentsModal = (function () {
end_datetime: endDatetime,
location: $appointmentLocation.val(),
color: App.Components.ColorSelection.getColor($appointmentColor),
status: $appointmentStatus.val(),
notes: $appointmentNotes.val(),
is_unavailability: Number(false)
};
@ -411,6 +413,8 @@ App.Components.AppointmentsModal = (function () {
$appointmentsModal.find('input, textarea').val('');
$appointmentsModal.find('.modal-message').fadeOut();
$appointmentStatus.find('option:first').prop('checked', true);
$language.val('english');
$timezone.val('UTC');

View file

@ -17,6 +17,7 @@
App.Pages.BusinessSettings = (function () {
const $saveSettings = $('#save-settings');
const $applyGlobalWorkingPlan = $('#apply-global-working-plan');
const $appointmentStatusOptions = $('#appointment-status-options')
let workingPlanManager = null;
/**
@ -77,6 +78,13 @@ App.Pages.BusinessSettings = (function () {
value: JSON.stringify(workingPlan)
});
const appointmentStatusOptions = App.Components.AppointmentStatusOptions.getOptions($appointmentStatusOptions);
businessSettings.push({
name: 'appointment_status_options',
value: JSON.stringify(appointmentStatusOptions)
});
return businessSettings;
}
@ -136,17 +144,24 @@ App.Pages.BusinessSettings = (function () {
deserialize(businessSettings);
let companyWorkingPlan = {};
let appointmentStatusOptions = [];
vars('business_settings').forEach((businessSetting) => {
if (businessSetting.name === 'company_working_plan') {
companyWorkingPlan = JSON.parse(businessSetting.value);
}
if (businessSetting.name === 'appointment_status_options') {
appointmentStatusOptions = JSON.parse(businessSetting.value);
}
});
workingPlanManager = new App.Utils.WorkingPlan();
workingPlanManager.setup(companyWorkingPlan);
workingPlanManager.timepickers(false);
workingPlanManager.addEventListeners();
App.Components.AppointmentStatusOptions.setOptions($appointmentStatusOptions, appointmentStatusOptions);
$saveSettings.on('click', onSaveSettingsClick);

View file

@ -153,6 +153,7 @@ App.Utils.CalendarDefaultView = (function () {
$appointmentsModal.find('#language').val(customer.language);
$appointmentsModal.find('#timezone').val(customer.timezone);
$appointmentsModal.find('#appointment-location').val(appointment.location);
$appointmentsModal.find('#appointment-status').val(appointment.status);
$appointmentsModal.find('#appointment-notes').val(appointment.notes);
$appointmentsModal.find('#customer-notes').val(customer.notes);
@ -411,6 +412,7 @@ App.Utils.CalendarDefaultView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('notes')
}),
$('<span/>', {
@ -605,6 +607,15 @@ App.Utils.CalendarDefaultView = (function () {
}),
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('status')
}),
$('<span/>', {
'text': info.event.extendedProps.data.status || '-',
}),
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('service')
@ -664,6 +675,7 @@ App.Utils.CalendarDefaultView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('notes')
}),
$('<span/>', {
@ -1687,6 +1699,7 @@ App.Utils.CalendarDefaultView = (function () {
$appointmentsModal.find('#language').val(customer.language);
$appointmentsModal.find('#timezone').val(customer.timezone);
$appointmentsModal.find('#appointment-location').val(appointment.location);
$appointmentsModal.find('#appointment-status').val(appointment.status);
$appointmentsModal.find('#appointment-notes').val(appointment.notes);
$appointmentsModal.find('#customer-notes').val(customer.notes);

View file

@ -229,6 +229,7 @@ App.Utils.CalendarTableView = (function () {
$appointmentsModal.find('#language').val(customer.language);
$appointmentsModal.find('#timezone').val(customer.timezone);
$appointmentsModal.find('#appointment-location').val(appointment.location);
$appointmentsModal.find('#appointment-status').val(appointment.status);
$appointmentsModal.find('#appointment-notes').val(appointment.notes);
$appointmentsModal.find('#customer-notes').val(customer.notes);
@ -436,7 +437,7 @@ App.Utils.CalendarTableView = (function () {
firstDay: firstWeekdayNumber,
onSelect: (dateText, instance) => {
const startDate = new Date(instance.currentYear, instance.currentMonth, instance.currentDay);
const endDate = moment(startDate).add(parseInt($selectFilterItem.val()) - 1, 'days').toDate();
const endDate = moment(startDate).add(parseInt($selectFilterItem.val()) - 1, 'days').toDate();
createView(startDate, endDate);
}
});
@ -1147,6 +1148,7 @@ App.Utils.CalendarTableView = (function () {
$html = $('<div/>', {
'html': [
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('start')
}),
$('<span/>', {
@ -1160,6 +1162,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('end')
}),
$('<span/>', {
@ -1173,6 +1176,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('notes')
}),
$('<span/>', {
@ -1232,6 +1236,7 @@ App.Utils.CalendarTableView = (function () {
$html = $('<div/>', {
'html': [
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('provider')
}),
$('<span/>', {
@ -1244,6 +1249,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('start')
}),
$('<span/>', {
@ -1259,6 +1265,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('end')
}),
$('<span/>', {
@ -1274,6 +1281,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('timezone')
}),
$('<span/>', {
@ -1330,6 +1338,7 @@ App.Utils.CalendarTableView = (function () {
$html = $('<div/>', {
'html': [
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('start')
}),
$('<span/>', {
@ -1343,6 +1352,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('end')
}),
$('<span/>', {
@ -1356,6 +1366,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('timezone')
}),
$('<span/>', {
@ -1364,6 +1375,16 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('status')
}),
$('<span/>', {
'text': info.event.extendedProps.data.status || '-'
}),
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('service')
}),
$('<span/>', {
@ -1372,6 +1393,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('provider')
}),
App.Utils.CalendarEventPopover.renderMapIcon(info.event.extendedProps.data.provider),
@ -1384,6 +1406,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('customer')
}),
App.Utils.CalendarEventPopover.renderMapIcon(info.event.extendedProps.data.customer),
@ -1396,6 +1419,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('email')
}),
App.Utils.CalendarEventPopover.renderMailIcon(info.event.extendedProps.data.customer.email),
@ -1405,6 +1429,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('phone')
}),
App.Utils.CalendarEventPopover.renderPhoneIcon(info.event.extendedProps.data.customer.phone_number),
@ -1414,6 +1439,7 @@ App.Utils.CalendarTableView = (function () {
$('<br/>'),
$('<strong/>', {
'class': 'd-inline-block me-2',
'text': lang('notes')
}),
$('<span/>', {

View file

@ -1737,6 +1737,10 @@ components:
type: string
location:
type: string
color:
type: string
status:
type: string
notes:
type: string
customerId:
@ -1754,6 +1758,8 @@ components:
end: '2021-01-01 18:00:00'
hash: apTWVbSvBJXR
location: Test Street 1A, 12345 Some State, Some Place
color: '#123456'
status: Booked
notes: This is a test appointment.
customerId: 5
providerId: 2
@ -1768,6 +1774,10 @@ components:
type: string
location:
type: string
color:
type: string
status:
type: string
notes:
type: string
customerId:
@ -1780,6 +1790,8 @@ components:
start: '2021-01-01 17:00:00'
end: '2021-01-01 18:00:00'
location: Test Street 1A, 12345 Some State, Some Place
color: '#123456'
status: Booked
notes: This is a test appointment.
customerId: 5
providerId: 2