Whenever the user creates a new meeting, we have to load their default settings and copy them to the individual meeting's settings. initializeMeetingSetting is called when a new meeting is created to do this:
- public function initializeMeetingSetting($meeting_id,$owner_id) {
- // load meeting creator (owner) user settings to initialize meeting_settings
- $user_setting = UserSetting::find()->where(['user_id' => $owner_id])->one();
- $meeting_setting = new MeetingSetting();
- $meeting_setting->meeting_id = $meeting_id;
- $meeting_setting->participant_add_place=$user_setting->participant_add_place;
- $meeting_setting->participant_add_date_time=$user_setting->participant_add_date_time;
- $meeting_setting->participant_choose_place=$user_setting->participant_choose_place;
- $meeting_setting->participant_choose_date_time=$user_setting->participant_choose_date_time;
- $meeting_setting->participant_finalize=$user_setting->participant_finalize;
- $meeting_setting->save();
- }
Reviewing the Meeting Owner View
Now, let's consider the state of our meeting view based on the meeting creator or owner. Here's a meeting invitation I've recently created to invite my friend Rob to drinks:
![]() |
Before Send and Finalize should be enabled, there must be a person invited and at least one place and time. If there are more than one place and time, one must be chosen for the meeting to be finalized.
The Cancel (X icon) and Edit (pencil icon) meeting buttons are also enabled for creators.
People
For the MVP, we're limiting meeting invitations to one participant at first. So, once a person has been invited, the Add (plus icon) button is disabled.
Places and Date & Times
The creator can add Places and Date & Times up to our site's maximum (e.g. seven per meeting) and they can indicate their availability and acceptance. And, finally, when there is more than one, they can choose which location and time will be used.
Notes
The creator can always add notes to the meeting. Notes allow the creator and participants to communicate with each other.
Ultimately, we'll put the bulk of our work into improving the AJAX functionality so that as the owner chooses places and times, the Send and Finalize buttons are properly enabled (or disabled in some cases).
Here's an example of a meeting with two possible times. The Finalize button can't be enabled until one time is chosen:
![]() |
Reviewing the Participant View
When we view the invitation from the participant's point of view, there's a lot less initial capability:
![]() |
Additionally, let's say the meeting was created with settings that allowed the participant to choose the location, date and time but not finalize the meeting. That would need to look like this:
![]() |
It turned out that supporting all of this required a lot of new code to update the system, but this is beginning to dive into the heart of the product—the scheduling meetings user experience. I'll walk you through a handful of the changes that were needed.
Coding the Meeting Requirements
Implementing the Meeting Settings
In the meeting-time and meeting-place panels, we need to use the meeting settings to determine if we need to show the choice selector. In the _panel.php view, it looks like this:
- <table class="table">
- <thead>
- <tr class="small-header">
- <td></td>
- <td ><?=Yii::t('frontend','You') ?></td>
- <td ><?=Yii::t('frontend','Them') ?></td>
- <td >
- <?php
- if ($timeProvider->count>1 && ($isOwner || $model->meetingSettings->participant_choose_date_time)) echo Yii::t('frontend','Choose');
- ?>
- </td>
- </tr>
- </thead>
- <?= ListView::widget([
- 'dataProvider' => $timeProvider,
- 'itemOptions' => ['class' => 'item'],
- 'layout' => '{items}',
- 'itemView' => '_list',
- 'viewParams' => ['timeCount'=>$timeProvider->count,'isOwner'=>$isOwner,'participant_choose_date_time'=>$model->meetingSettings['participant_choose_date_time']],
- ]) ?>
- </table>
- <td style>
- <?php
- if ($timeCount>1) {
- if ($model->status == $model::STATUS_SELECTED) {
- $value = $model->id;
- } else {
- $value = 0;
- }
- if ($isOwner || $participant_choose_date_time) {
- // value has to match for switch to be on
- echo SwitchInput::widget([
- 'type' => SwitchInput::RADIO,
- 'name' => 'time-chooser',
- 'items' => [
- [ 'value' => $model->id],
- ],
- 'value' => $value,
- 'pluginOptions' => [ 'size' => 'mini','handleWidth'=>60,'onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>'],
- 'labelOptions' => ['style' => 'font-size: 12px'],
- ]);
- }
- }
- ?>
- </td>
![]() |
I created canSend() and canFinalize() functions, which support the code generally and the AJAX requests to determine the active state of Send and Finalize buttons.
Here's canSend():
- public function canSend($sender_id) {
- // check if an invite can be sent
- // req: a participant, at least one place, at least one time
- if ($this->owner_id == $sender_id
- && count($this->participants)>0
- && count($this->meetingPlaces)>0
- && count($this->meetingTimes)>0
- ) {
- $this->isReadyToSend = true;
- } else {
- $this->isReadyToSend = false;
- }
- return $this->isReadyToSend;
- }
Here's canFinalize():
- public function canFinalize($user_id) {
- $this->isReadyToFinalize = false;
- // check if meeting can be finalized by viewer
- // check if overall meeting state can be sent by owner
- if (!$this->canSend($this->owner_id)) return false;
- $chosenPlace = false;
- if (count($this->meetingPlaces)==1) {
- $chosenPlace = true;
- } else {
- foreach ($this->meetingPlaces as $mp) {
- if ($mp->status == MeetingPlace::STATUS_SELECTED) {
- $chosenPlace = true;
- break;
- }
- }
- }
- $chosenTime = false;
- if (count($this->meetingTimes)==1) {
- $chosenTime = true;
- } else {
- foreach ($this->meetingTimes as $mt) {
- if ($mt->status == MeetingTime::STATUS_SELECTED) {
- $chosenTime = true;
- break;
- }
- }
- }
- if ($this->owner_id == $user_id ||
- $this->meetingSettings->participant_finalize) {
- if ($chosenPlace && $chosenTime) {
- $this->isReadyToFinalize = true;
- }
- }
- return $this->isReadyToFinalize;
- }
Basically, as changes are made, you'll see the state of the Send and Finalize buttons change:
![]() |
- <?php
- if (isset(Yii::$app->params['urlPrefix'])) {
- $urlPrefix = Yii::$app->params['urlPrefix'];
- } else {
- $urlPrefix ='';
- }
- $script = <<< JS
- function refreshSend() {
- $.ajax({
- url: '$urlPrefix/meeting/cansend',
- data: {id: $model->id, 'viewer_id': $viewer},
- success: function(data) {
- if (data)
- $('#actionSend').removeClass("disabled");
- else
- $('#actionSend').addClass("disabled");
- return true;
- }
- });
- }
- function refreshFinalize() {
- $.ajax({
- url: '$urlPrefix/meeting/canfinalize',
- data: {id: $model->id, 'viewer_id': $viewer},
- success: function(data) {
- if (data)
- $('#actionFinalize').removeClass("disabled");
- else
- $('#actionFinalize').addClass("disabled");
- return true;
- }
- });
- }
- JS;
- $position = \yii\web\View::POS_READY;
- $this->registerJs($script, $position);
- ?>
In the current user interface, we show the viewer's place and time selections in the leftmost or first column. The code has to be customized to reverse this when participants are viewing:
![]() |
- <td style>
- <?php
- if ($isOwner) {
- showTimeOwnerStatus($model,$isOwner);
- } else {
- showTimeParticipantStatus($model,$isOwner);
- }
- ?>
- </td>
- <td style>
- <?php
- if (!$isOwner) {
- showTimeOwnerStatus($model,$isOwner);
- } else {
- showTimeParticipantStatus($model,$isOwner);
- }
- ?>
- </td>
- <?php
- use \kartik\switchinput\SwitchInput;
- function showTimeOwnerStatus($model,$isOwner) {
- foreach ($model->meetingTimeChoices as $mtc) {
- if ($mtc->user_id == $model->meeting->owner_id) {
- if ($mtc->status == $mtc::STATUS_YES)
- $value = 1;
- else
- $value =0;
- echo SwitchInput::widget([
- 'type' => SwitchInput::CHECKBOX,
- 'name' => 'meeting-time-choice',
- 'id'=>'mtc-'.$mtc->id,
- 'value' => $value,
- 'disabled' => !$isOwner,
- 'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
- ]);
- }
- }
- }
- function showTimeParticipantStatus($model,$isOwner) {
- foreach ($model->meetingTimeChoices as $mtc) {
- if (count($model->meeting->participants)==0) break;
- if ($mtc->user_id == $model->meeting->participants[0]->participant_id) {
- if ($mtc->status == $mtc::STATUS_YES)
- $value = 1;
- else if ($mtc->status == $mtc::STATUS_NO)
- $value =0;
- else if ($mtc->status == $mtc::STATUS_UNKNOWN)
- $value =-1;
- echo SwitchInput::widget([
- 'type' => SwitchInput::CHECKBOX,
- 'name' => 'meeting-time-choice',
- 'id'=>'mtc-'.$mtc->id,
- 'tristate'=>true,
- 'indeterminateValue'=>-1,
- 'indeterminateToggle'=>false,
- 'disabled'=>$isOwner,
- 'value' => $value,
- 'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
- ]);
- }
- }
- }
- ?>
Ultimately, there are a lot of improvements to make to this code going forward. In places, I'm making AJAX calls to the server two or three times when I could code these more efficiently into a single request. In other places, I can do more locally with JavaScript. And the user interface will need to keep improving, and the code will need to change to adapt to that. But, from a functional perspective, today's work represents a lot of overall progress towards the MVP.
Written by Jeff Reifman
If you found this post interesting, follow and support us.
Suggest for you:
Learning PHP 7: From the Basics to Application Development
The Complete PHP 7 Guide for Web Developers
Learn PHP 7 This Way to Rise Above & Beyond Competion!
PHP MySQL Database Connections
The Complete PHP with MySQL Developer Course (New)







No comments:
Post a Comment