天天看點

建立您的初創企業:與多個參與者開會

建立您的初創企業:與多個參與者開會

您将要創造的

本教程是 Envato Tuts +上的“ 使用PHP建構啟動”系列 的一部分。 在本系列文章中,我 将以 我的 Meeting Planner 應用程式作為真實示例 ,指導您完成從概念到現實的啟動 。 在此過程的每一步中,我都會将Meeting Planner代碼作為開放源代碼示例釋出,您可以從中學習。 我還将解決與啟動相關的業務問題。

小組會議簡介

與多個參與者安排會議始終是我計劃的一部分,但不屬于最早的最低可行産品(MVP)的一部分。 Meeting Planner的Alpha版本僅以1:1的計劃啟動。 支援小組日程安排的目标像登上珠穆朗瑪峰一樣位于任務清單上,目标是針對七個峰會的登山者(我什至都​​不是戶外登山者)。

安排多個參與者會議是最具挑戰性的,是以對于會議計劃器産品而言非常有價值。 當Beta任務清單達到可以開始從事此工作的程度時,我感到很興奮。

我從一開始就一直在計劃,設計和編碼小組會議。 我希望為此功能更新站點将不需要進行大量的UX更改或編碼更新。 事實證明,這需要一條中間路線,需要7到10天的重點工作和測試,而無需進行重大重新架構。

實際上,事實證明,測試是建構此功能最困難的方面。 它還有助于揭示早期代碼中的缺點。 隻是這并不容易...發送到多個電子郵件位址,檢查每個電子郵件位址都收到所有正确的通知,但沒有收到錯誤的通知-并檢視整個站點中所有正确的菜單選項。

在今天的教程中,我将介紹啟用多個參與者,更新組的UX,任命組織者,删除參與者,以及根據參與者的受歡迎程度對日期,時間和地點選項進行排序。

在下一個教程中,我将描述其餘的工作:複查受多個參與者會議影響的站點的所有區域,處理并智能顯示各種狀态的收件人清單,适當地管理組的通知和通知過濾,最後更新最近啟動的請求會議更改功能 。

嘗試安排小組會議

請今天安排一個小組會議 ! 在下面的評論中分享您的想法和回報。

我确實參與了讨論,但是您也可以在Twitter上與我聯系@reifman 。 我總是樂于接受Meeting Planner的新功能建議以及有關未來系列劇集的建議。

提醒一下,Meeting Planner的所有代碼都是開源提供的,并使用PHP的Yii2架構編寫。 如果您想了解有關Yii2的更多資訊,請檢視我的平行系列“ 使用Yii2程式設計” 。 我聽說過有關Laravel的很棒的事情,但是Yii2總是可以輕松快捷地滿足我的需求。

回頭看

當我第一次設計Meeting Planner計劃界面時 ,它在其自己的列中顯示了其他參與者的目前可用性。 由于存在禁用的控件,這有點令人困惑。

建立您的初創企業:與多個參與者開會

當時,我擔心如何騰出空間來顯示群組的可用性。

幸運的是,當我重建UX以獲得更好的響應體驗時 ,我用一個小的文本摘要替換了參與者的可用性列:

建立您的初創企業:與多個參與者開會

可用性的文本摘要恰好适合小組會議。

通過首先針對移動裝置進行重新設計,我解決了多次參加會議的最大使用者體驗障礙!

小組會議的編碼

讓我們開始周遊所有代碼并測試多個參與者會議所需的内容。

允許多個參與者

建立您的初創企業:與多個參與者開會

小組會議最有趣的方面是激活會議很簡單。 我隻需要在計劃階段關閉“ 誰”面闆上的加号圖示按鈕的禁用功能即可:

<div class="col-lg-2 col-md-2 col-xs-2">
      <div style="float:right;">
        <?= Html::a(Yii::t('frontend', ''), ['/participant/create', 'meeting_id' => $model->id], ['class' => 'btn btn-primary '.($model->status>=$model::STATUS_CONFIRMED?'disabled':'').' glyphicon glyphicon-plus']) ?>
      </div>
    </div>
           

然後,我開始建立一個 參與者模型中的

MEETING_LIMIT

class Participant extends \yii\db\ActiveRecord
{
    ...

    const MEETING_LIMIT = 15;
           

它在送出時用于

ParticipantController::actionCreate()

中:

public function actionCreate($meeting_id)
 {
    if (!Participant::withinLimit($meeting_id)) {
       Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, you have reached the maximum number of participants per meeting. Contact support if you need additional help or want to offer feedback.'));
       return $this->redirect(['/meeting/view', 'id' => $meeting_id]);
    }
           

推進使用者體驗和相關功能

很長一段時間以來,我一直希望允許會議組織者删除參與者,地點和日期時間,而又不會使使用者界面混亂。 同樣,我意識到可能會有多個指令要對參與者執行。

在“ 進階指令”教程的緊湊型Bootstrap下拉按鈕中找到了很多實用程式之後,我決定将其用于顯示會議與會者:

建立您的初創企業:與多個參與者開會

組織者以星号表示。 拒絕參加會議的與會者以橙色顯示。 組織者删除的與會者以紅色顯示。

這是我新的部分/frontend/views/participant/_buttons.php中的代碼:

<div class="btn-group btn-participant">
  <button type="button" class="btn btn-default btn-sm dropdown-toggle " data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
      <span class="glyphicon glyphicon-star red-star aria-hidden="true"></span>
    <?= MiscHelpers::getDisplayName($model->owner_id) ?>
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu">
      <li><?= Html::a(Yii::t('frontend','Send a message'),Url::to('mailto:'.$model->owner->email))?></li>
  </ul>
</div>
           

現在任何人都可以向任何參與者發送消息(會議記錄功能目前已分發給所有會議參與者)。

組織者看到了更深的下拉菜單,這使他們可以任命其他組織者,即Make Organer 。 現在,這是一個非常酷的功能。 組織者将在整個計劃階段收到更完整的通知,并擁有更多權力。 他們還可以删除參與者 。

将AJAX功能建構到參與者按鈕中

我一時興起就決定将所有這些菜單選項都AJAXify。 事實證明,這需要幾個小時的編碼。

這是定義初始按鈕菜單并準備JavaScript的代碼:

<?php
if (count($model->participants)>0) {
  foreach ($model->participants as $p) {
    if ($p->participant->id==Yii::$app->user->getId()) {
      continue;
    }
    $btn_color = 'btn-default';
    if ($p->status == Participant::STATUS_DECLINED) {
      $btn_color = 'btn-warning';
    } else if ($p->status == Participant::STATUS_REMOVED || $p->status == Participant::STATUS_DECLINED_REMOVED) {
      $btn_color = 'btn-danger';
    }
  ?>
  <div class="btn-group btn-participant">
    <button id="btn_<?= $p->id ?>" type="button" class="btn <?= $btn_color ?> btn-sm dropdown-toggle " data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        <span id="star_<?= $p->id ?>" class="glyphicon glyphicon-star red-star <?= (!$p->isOrganizer())?'hidden':''?>" aria-hidden="true"></span>
      <?= MiscHelpers::getDisplayName($p->participant->id) ?>
      <span class="caret"></span>
    </button>
    <ul class="dropdown-menu">
        <li><?= Html::a(Yii::t('frontend','Send a message'),Url::to('mailto:'.$p->participant->email))?></li>
        <?php if ($model->isOrganizer()) {
          ?>
          <li role="separator" class="divider"></li>
            <li id="mo_<?= $p->id ?>" class="<?= ($p->isOrganizer())?'hidden':''?>"><?= Html::a(Yii::t('frontend','Make organizer'),'javascript:void(0);',['onclick' => "toggleOrganizer($p->id,true);return false;"]); ?></li>
            <li id="ro_<?= $p->id ?>" class="<?= (!$p->isOrganizer())?'hidden':''?>"><?= Html::a(Yii::t('frontend','Revoke organizer role'),'javascript:void(0);',['onclick' => "toggleOrganizer($p->id,false);return false;"]); ?></li>
          <li id="rp_<?= $p->id ?>" class="<?= ($p->status == Participant::STATUS_REMOVED || $p->status == Participant::STATUS_DECLINED_REMOVED)?'hidden':''?>"><?= Html::a(Yii::t('frontend','Remove participant'),'javascript:void(0);',['onclick' => "toggleParticipant($p->id,false,$p->status);return false;"]); ?></li>
          <li id="rstp_<?= $p->id ?>" class="<?= ($p->status != Participant::STATUS_REMOVED && $p->status != Participant::STATUS_DECLINED_REMOVED)?'hidden':''?>"><?= Html::a(Yii::t('frontend','Restore participant'),'javascript:void(0);',['onclick' => "toggleParticipant($p->id,true,$p->status);return false;"]); ?></li>
          <?php
          }
          ?>
    </ul>
  </div>
           

當頁面上以互動方式進行更改時,要更新的按鈕狀态,顔色和星号太多,是以代碼變得相當複雜。 我将功能添加到

toggleOrganizer()

JavaScript檔案中的

toggleOrganizer()

,即make / unset

toggleParticipant()

toggleParticipant()

,   即删除/恢複參與者作為參與者。

function toggleOrganizer(id, val) {
  if (val === true) {
    arg2 = 1;
  } else {
    arg2 =0;
  }
  $.ajax({
     url: $('#url_prefix').val()+'/participant/toggleorganizer',
     data: {id: id, val: arg2},
     success: function(data) {
       if (data) {
         if (val===false) {
            $('#star_'+id).addClass("hidden");
            $('#ro_'+id).addClass("hidden");
            $('#mo_'+id).removeClass("hidden");
         } else {
           $('#star_'+id).removeClass("hidden");
           $('#ro_'+id).removeClass("hidden");
           $('#mo_'+id).addClass("hidden");
         }
       }
        return true;
     }
  });
}

function toggleParticipant(id, val, original_status) {
  if (val === true) {
    arg2 = 1;
  } else {
    arg2 =0;
  }
  $.ajax({
     url: $('#url_prefix').val()+'/participant/toggleparticipant',
     data: {id: id, val: arg2, original_status: original_status},
     success: function(data) {
       if (data) {
         if (val===false) {
            $('#rp_'+id).addClass("hidden");
            $('#rstp_'+id).removeClass("hidden");
            $('#btn_'+id).addClass("btn-danger");
            $('#btn_'+id).removeClass("btn-default");
         } else {
           $('#rp_'+id).removeClass("hidden");
           $('#rstp_'+id).addClass("hidden");
           if (original_status==100) {
             $('#btn_'+id).addClass("btn-warning");
             $('#btn_'+id).removeClass("btn-danger");
           } else {
             $('#btn_'+id).addClass("btn-default");
             $('#btn_'+id).removeClass("btn-danger");
           }
         }
       }
        return true;
     }
  });
}
           

這些在ParticipantController.php中需要随附的JSON控制器方法來處理切換請求和更新資料庫:

public function actionToggleorganizer($id,$val) {
  Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
  // change setting
  $p=Participant::findOne($id);
  if ($p->meeting->isOrganizer()) {
    $p->email = $p->participant->email;
    if ($val==1) {
      $p->participant_type=Participant::TYPE_ORGANIZER;
    } else {
      $p->participant_type=Participant::TYPE_DEFAULT;
    }
    $p->update();
    return true;
  } else {
    return false;
  }

}

public function actionToggleparticipant($id,$val) {
  Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
  // change setting
  $p=Participant::findOne($id);
  if ($p->meeting->isOrganizer()) {
    $p->email = $p->participant->email;
    if ($val==0) {
      if ($p->status == Participant::STATUS_DECLINED) {
          $p->status=Participant::STATUS_DECLINED_REMOVED;
      } else {
        $p->status=Participant::STATUS_REMOVED;
      }
    } else {
      if ($p->status == Participant::STATUS_DECLINED_REMOVED) {
          $p->status=Participant::STATUS_DECLINED;
      } else {
        $p->status=Participant::STATUS_DEFAULT;
      }
    }
    $p->update();
    return true;
  } else {
    return false;
  }
}
           

在面闆上激活手風琴功能

建立您的初創企業:與多個參與者開會

這時,我還意識到,随着會議計劃的複雜性增加,接收者和選項的增加,滾動的内容也會越來越多。 我決定為會議視圖上的所有面闆實施Bootstrap手風琴功能 。

換句話說,您現在可以單擊标題以折疊或打開每個和/或所有面闆。

這是對聚會場所_panel.php的局部的更改:

<div class="panel panel-default">
  <!-- Default panel contents -->
  <div class="panel-heading" role="tab" id="headingWhere">
    <div class="row">
      <div class="col-lg-10 col-md-10 col-xs-10" ><h4 class="meeting-place">
      <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseWhere" target="_blank" rel="external nofollow"  aria-expanded="true" aria-controls="collapseWhere"><?= Yii::t('frontend','Where') ?></a>
      </h4><p>
        <div class="hint-text heading-pad">
        <?php if ($placeProvider->count<=1) { ?>
          <?= Yii::t('frontend','add places for participants or switch to \'virtual\'') ?>
      <?php } elseif ($placeProvider->count>1) { ?>
          <?= Yii::t('frontend','are listed places okay?&nbsp;') ?>
        <?php
          }
        ?>
...
<div id="collapseWhere" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingWhere">
    <div class="panel-body">
      <?php
        $style = ($model->switchVirtual==$model::SWITCH_VIRTUAL?'none':'block');
       ?>
      <div id ="meeting-place-list" style="display:<?php echo $style; ?>">
      <?php
       if ($placeProvider->count>0):
      ?>
      <table class="table">
        <?= ListView::widget([
               'dataProvider' => $placeProvider,
               'itemOptions' => ['class' => 'item'],
               'layout' => '{items}',
               'itemView' => '_list',
               'viewParams' => ['placeCount'=>$placeProvider->count,'isOwner'=>$isOwner,'participant_choose_place'=>$model->meetingSettings['participant_choose_place'],'whereStatus'=>$whereStatus],
           ]) ?>
      </table>
           

注意上面

panel-heading

上的設定,然後注意後面的

panel-body

周圍的div。 這些控制每個面闆的打開和折疊。

這導緻了一些小的外觀問題,例如在項目清單周圍出現不必要的填充,我将來需要清理這些問題。

小組會議的模型基礎架構

盡管我幾乎從一開始就計劃有多個參與者,但在基礎設施方面有一些次要的改進以支援他們。

雖然

MeetingTimeChoice

MeetingPlaceChoice

模型跟蹤參與者是否喜歡特定的日期時間和地點,但我想跟蹤每個日期和地點所有參與者的總體可用性。 這将使我能夠根據地點和時間的受歡迎程度對其進行排序,并在面闆頂部顯示最受歡迎的設定。

首先,我建立了一個遷移,以将其添加到兩個模型中。 我的遷移很少會影響多個模型,這使得這種特殊情況變得如此:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m160824_235517_extend_meeting_place_and_time extends Migration
{
  public function up()
  {
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
    }
    $this->addColumn('{{%meeting_time}}','availability',Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0');
    $this->addColumn('{{%meeting_place}}','availability',Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0');
  }

  public function down()
  {
    $this->dropColumn('{{%meeting_time}}','availability');
    $this->dropColumn('{{%meeting_place}}','availability');
  }
}
           

有了這種能力,我就可以從

MeetingController::actionView()

開始顯示可能的會議日期時間和地點,

MeetingController::actionView()

參加者的受歡迎程度排序:

$timeProvider = new ActiveDataProvider([
            'query' => MeetingTime::find()->where(['meeting_id'=>$id]),
            'sort' => [
              'defaultOrder' => [
                'availability'=>SORT_DESC
              ]
            ],
        ]);
        $placeProvider = new ActiveDataProvider([
            'query' => MeetingPlace::find()->where(['meeting_id'=>$id]),
            'sort' => [
              'defaultOrder' => [
                'availability'=>SORT_DESC
              ]
            ],
        ]);
           

您可以在下面的計劃螢幕截圖中看到這一點:

建立您的初創企業:與多個參與者開會

為了跟蹤參與者是否是組織者,并允許将來退出特定會議的通知,我為“參與者”表添加了此遷移:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m160825_074740_extend_participant_add_type extends Migration
{
  public function up()
  {
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
        $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
    }
    $this->addColumn('{{%participant}}','participant_type',Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0');
    $this->addColumn('{{%participant}}','notify',Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0');
  }

  public function down()
  {
    $this->dropColumn('{{%participant}}','participant_type');
    $this->dropColumn('{{%participant}}','notify');
  }
}
           

我還在Participant.php中添加了一些常量以使用這些屬性:

class Participant extends \yii\db\ActiveRecord
{
    const TYPE_DEFAULT = 0;
    const TYPE_ORGANIZER = 10;

    const NOTIFY_ON = 0;
    const NOTIFY_OFF = 1;

    const STATUS_DEFAULT = 0;
    const STATUS_REMOVED = 90;
    const STATUS_DECLINED = 100;
    const STATUS_DECLINED_REMOVED = 110;
           

我知道在大型會議模型中具有一些輔助功能會很有幫助。 例如,

IsOrganizer()

告訴我目前檢視者是否是會議組織者:

public function isOrganizer() {
      $user_id = Yii::$app->user->getId();
      if ($user_id == $this->owner_id) {
        return true;
      } else {
        foreach ($this->participants as $p) {
          if ($user_id == $p->participant_id) {
            if ($p->participant_type == Participant::TYPE_ORGANIZER) {
              return true;
            } else {
              return false;
            }
          }
      }
    }
    return false;
  }
           

等等,還有嗎?

如您所見,建構此功能有很多基礎。 在下一個情節中,我将介紹啟動多個參與者會議所需的開發和測試的後半部分:收件人字元串,通知,請求以及對請求的響應。

如果還沒有,請安排與Meeting Planner的第一次會議,然後嘗試所有這些。 請在下面的評論中分享您的回報。

有關衆籌的教程也正在編寫中 ,是以請關注我們的WeFunder Meeting Planner頁面 。

您也可以與我聯系@reifman 。 我總是樂于接受新功能的想法和主題建議,以供将來的教程使用。

請閱讀“ 用PHP建構您的啟動”系列,繼續關注所有這些以及以後的教程。

相關連結

  • 會議策劃人
  • 關注會議計劃者的資金概況
  • 使用Yii2系列進行程式設計(Envato Tuts +)
翻譯自: https://code.tutsplus.com/tutorials/building-your-startup-meetings-with-multiple-participants--cms-27132