Thursday, August 4, 2016

Building Your Startup: Customizing the Meeting View_part1


Meeting View Requirements

It's kind of exciting—soon, Meeting Planner will be delivering invitations to invited participants. However, to support that, we need to make sure that the meeting view page is properly configured. If you created the meeting, you have certain powers, such as inviting participants, adding proposed meeting places, dates and times, and choosing the final selections. In some cases, the organizer may want to offer some or all of these powers to participants as well.

Essentially, we have to make the application aware of who's viewing the meeting page and then customize the appearance and commands available. Yii makes most of this pretty easy, but there's a lot of detail involved.

A Brief Caveat About the User Experience

And let me say up front, there is a lot of user experience rework and polish that will need to be done iteratively over time on the way to the minimum viable product (MVP). Most of what I'm building right now is core functionality to get the alpha running for actual usage. I know it looks rough in places and won't always seem as intuitive as you want. There are also coding inefficiencies that will need to be optimized in the future. Please feel free to post your thoughts and comments below and I will take them into account for ongoing work.

The Current Meeting View
Here's a look at the existing meeting view that the creator (or owner) sees:


The Send button emails the meeting invitation with the current open options to the participant for them to offer feedback. The checkboxes below You and Them allow the viewer to express whether location(s) and time(s) work for them. The Choose checkboxes allow the viewer to determine the final place and final time. The Finalize button puts the meeting on the schedule with the chosen place and time options.

Certainly, as the product matures, we'll want to improve the user experience in a number of ways and polish it a lot, but here are a handful of functional elements we'd like to modify for participants:

  • The Send button won't be needed after the owner delivers the invitation.
  • Participants may or may not be allowed to Finalize meeting options.
  • Participants won't be able to Edit (pencil icon) the meeting detail text.
  • Participants won't be able to add People at this time (for our MVP).
  • Participants may or may not be allowed to add Places (plus icon).
  • Participants may or may not be allowed to add Dates & Times (plus icon).
  • In both Places and Dates & Times panels, we'll want to show the current viewer's choices under the You column and the other person's data in Them.
  • In both Places and Dates & Times panels, participants may or may not be able to Choose the final location and time.
All of these options need to be addressed in our work today. Let's walk through what's required to build these features.

Requirements Implementation

If you're following along with the code, the updates described here are included in this release on GitHub.

Who's the Current Viewer
The Yii Framework provides the current user_id for the viewer here:
  1. $user_id = Yii::$app->user->getId()
The Meeting model has the $owner_id property and isOwner function to help determine if the current viewer is actually the meeting's creator. If not, the viewer will have conditionally less control over the meeting.

I've created a couple of helper functions in the Meeting model to make this faster:
  1. public function setViewer() {
  2.       $this->viewer_id = Yii::$app->user->getId();
  3.       if ($this->owner_id == $this->viewer_id) {
  4.         $this->viewer = Meeting::VIEWER_ORGANIZER;
  5.       } else {
  6.         $this->viewer = Meeting::VIEWER_PARTICIPANT;
  7.       }
  8.     }
These configure the $owner_id and $viewer properties in the Meeting model.

Building for Meeting Settings
Every meeting you create will likely have different characteristics. Sometimes, you'll want to limit the participant from suggesting different times and places or finalizing the details. Other times, you won't care. When we eventually create Meeting Templates for reusing common types of meetings, e.g. morning coffee business meetings, the Templates will likely need to retain these kinds of custom settings as well. How should we implement this?

First, I'd like to create a set of default preferences for users with respect to the meetings they create.

Then, I'll create a set of MeetingSettings for every meeting. When a meeting is created from scratch, they'll inherit the default preferences from the user that creates it. Editing the settings for individual meetings can be postponed until later.

In the future, when we implement the Meeting Templates, we'll add Meeting settings for the Templates too. However, this can also be postponed.

Here are the preferences that we'd like to create to start:
  • Allow participants to add Places.
  • Allow participants to add Dates & Times.
  • Allow participants to choose Places.
  • Allow participants to choose Dates & Times.
  • Allow participants to Finalize the meeting.
Since we're all coming back to the series after some time because of my health absence, I'll go through a bit more detail on some of the work.

First, we'll create the Meeting Settings migration:
  1. $ ./yii migrate/create meeting_setting_table
  2. Yii Migration Tool (based on Yii v2.0.7)
  3.  
  4. Create new migration '/Users/Jeff/Sites/mp/console/migrations/m160401_203412_meeting_setting_table.php'? (yes|no) [no]:yes
  5. New migration created successfully.
That creates the migration file we need to write the code which builds the database table according to our schema:
  1. <?php
  2. use yii\db\Schema;
  3. use yii\db\Migration;
  4.  
  5. class m160401_203412_meeting_setting_table extends Migration
  6. {
  7.   public function up()
  8.    {
  9.        $tableOptions = null;
  10.        if ($this->db->driverName === 'mysql') {
  11.            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
  12.        }
  13.  
  14.        $this->createTable('{{%meeting_setting}}', [
  15.            'id' => Schema::TYPE_PK,
  16.            'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL',
  17.            'participant_add_place' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
  18.            'participant_add_date_time' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
  19.            'participant_choose_place' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
  20.            'participant_choose_date_time' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
  21.            'participant_finalize' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
  22.            'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
  23.            'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
  24.        ], $tableOptions);
  25.        $this->addForeignKey('fk_meeting_setting', '{{%meeting_setting}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE');
  26.    }
  27.  
  28.    public function down()
  29.    {
  30.        $this->dropForeignKey('fk_meeting_setting', '{{%meeting_setting}}');    
  31.        $this->dropTable('{{%meeting_setting}}');
  32.    }
  33. }

Each meeting essentially has a row of MeetingSettings with boolean properties for the various participant options I've shown above.

Then, we instruct Yii to migrate up and create the table:
  1. $ ./yii migrate/up
  2. Yii Migration Tool (based on Yii v2.0.7)
  3. Total 1 new migration to be applied:
  4.     m160401_203412_meeting_setting_table
  5. Apply the above migration? (yes|no) [no]:yes
  6. *** applying m160401_203412_meeting_setting_table
  7.     > create table {{%meeting_setting}} ... done (time: 0.010s)
  8.     > add foreign key fk_meeting_setting: {{%meeting_setting}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.011s)
  9. *** applied m160401_203412_meeting_setting_table (time: 0.040s)
  10. 1 migration was applied.
  11. Migrated up successfully.
Our foreign key creates a relation between the Meeting table and the MeetingSetting table.

Next, we'll use Yii's Gii to auto-generate code for viewing and updating the settings. To begin, I return to http://localhost:8888/mp/index.php/gii/. We'll start with generating the model:


Then, we'll generate the Create, Read, Update, Delete code (CRUD):


Since we don't need all that code right now, Gii lets us select just the functions that we do need: the controller, view, _form and update:


Gii shows you a list of the files it creates with each step:


But what about the user's default meeting settings? Essentially, their typical meeting preferences?

Extending the User Preferences

For that, we'll add parallel meeting setting properties to the user_setting table. Again, we'll create a migration:
  1. $ ./yii migrate/create extend_user_setting_table
  2. Yii Migration Tool (based on Yii v2.0.7)
  3.  
  4. Create new migration '/Users/Jeff/Sites/mp/console/migrations/m160401_210852_extend_user_setting_table.php'? (yes|no) [no]:yes
  5. New migration created successfully.
Here are the columns we need to add:
  1. class m160401_210852_extend_user_setting_table extends Migration
  2. {
  3.   public function up()
  4.   {
  5.     $tableOptions = null;
  6.     if ($this->db->driverName === 'mysql') {
  7.         $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
  8.     }    
  9.      
  10.     $this->addColumn('{{%user_setting}}','participant_add_place',Schema::TYPE_SMALLINT.' NOT NULL');
  11.     $this->addColumn('{{%user_setting}}','participant_add_date_time',Schema::TYPE_SMALLINT.' NOT NULL');
  12.     $this->addColumn('{{%user_setting}}','participant_choose_place',Schema::TYPE_SMALLINT.' NOT NULL');
  13.     $this->addColumn('{{%user_setting}}','participant_choose_date_time',Schema::TYPE_SMALLINT.' NOT NULL');
  14.     $this->addColumn('{{%user_setting}}','participant_finalize',Schema::TYPE_SMALLINT.' NOT NULL');
  15.   }
  16.  
  17.   public function down()
  18.   {
  19.     $this->dropColumn('{{%user_setting}}','participant_finalize');
  20.     $this->dropColumn('{{%user_setting}}','participant_choose_date_time');
  21.     $this->dropColumn('{{%user_setting}}','participant_choose_place');
  22.     $this->dropColumn('{{%user_setting}}','participant_add_date_time');
  23.     $this->dropColumn('{{%user_setting}}','participant_add_place');
  24.   }
  25. }
Then, we'll run the migration:
  1. $ ./yii migrate/up
  2. Yii Migration Tool (based on Yii v2.0.7)
  3.  
  4. Total 1 new migration to be applied:
  5.     m160401_210852_extend_user_setting_table
  6.  
  7. Apply the above migration? (yes|no) [no]:yes
  8. *** applying m160401_210852_extend_user_setting_table
  9.     > add column participant_add_place smallint NOT NULL to table {{%user_setting}} ... done (time: 0.012s)
  10.     > add column participant_add_date_time smallint NOT NULL to table {{%user_setting}} ... done (time: 0.007s)
  11.     > add column participant_choose_place smallint NOT NULL to table {{%user_setting}} ... done (time: 0.010s)
  12.     > add column participant_choose_date_time smallint NOT NULL to table {{%user_setting}} ... done (time: 0.009s)
  13.     > add column participant_finalize smallint NOT NULL to table {{%user_setting}} ... done (time: 0.009s)
  14. *** applied m160401_210852_extend_user_setting_table (time: 0.061s)
  15.  
  16. 1 migration was applied.
  17. Migrated up successfully.
Rather than force an overwrite of our UserSetting.php model with Gii, we'll use Gii's diff option


And, from there, we'll hand pick the new additions to the file and paste them in:


Functionally, we'll add a meeting settings tab to the Update Your Settings property page:

Add caption
We'll add the following code to /frontend/views/user-setting/_form.php to support our new properties:
  1. <div class="col-md-8">
  2.          <!-- Nav tabs -->
  3.          <ul class="nav nav-tabs" role="tablist">
  4.            <li class="active"><a href="#general" role="tab" data-toggle="tab"><?= Yii::t('frontend','General Settings') ?></a></li>
  5.            <li><a href="#preferences" role="tab" data-toggle="tab"><?= Yii::t('frontend','Meeting Preferences') ?></a></li>
  6.            <li><a href="#photo" role="tab" data-toggle="tab"><?= Yii::t('frontend','Upload Photo') ?></a></li>
  7.          </ul>
  8.          <!-- Tab panes -->
  9.          <div class="tab-content">
  10.             ...
  11.          </div>
  12.            <div class="tab-pane vertical-pad" id="preferences">
  13.              <?= $form->field($model, 'participant_add_place')->checkbox(['uncheck' =>  $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?> 
  14.              <?= $form->field($model, 'participant_add_date_time')->checkbox(['uncheck' =>  $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?> 
  15.              <?= $form->field($model, 'participant_choose_place')->checkbox(['uncheck' =>  $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?> 
  16.              <?= $form->field($model, 'participant_choose_date_time')->checkbox(['uncheck' =>  $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?> 
  17.              <?= $form->field($model, 'participant_finalize')->checkbox(['uncheck' =>  $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?> 
  18.  
  19.             </div> <!-- end of upload meeting-settings tab -->
  20.             <div class="tab-pane vertical-pad" id="photo">    
  21.         ...
Here's the updated form:


Written by Jeff Reifman
If you found this post interesting, please follow and support us.
Suggest for you:

Learning PHP 7: From the Basics to Application Development

The Complete PHP 7 Guide for Web Developers

Up to Speed with PHP 7

Learn PHP 7 This Way to Rise Above & Beyond Competion!

The Complete PHP with MySQL Developer Course (New)


No comments:

Post a Comment