この記事では、カスタマーポータルのMy Tickets(マイチケット)タブでチケットを表示する際に、お客様がチケットをフィルタリングできるようにするカスタムチケットフィルターの定義方法について説明します。

以下の例では、複数の条件(テキスト検索、チケットステータスによるフィルター、チケットの公開タグによるフィルター、チケット作成日・解決日・変更日に基づくフィルター)を使ってフィルタリングを行います。これらのフィルターをすべて使う必要はありませんが、実際のニーズに合わせてMy tickets custom menu HTMLの例のコードを調整する必要があります。この実装にはいくつかのステップがあります。まずカスタムフィルター自体のHTMLコードを定義し、次に見た目を良くするためのカスタムCSSを追加し、さらに機能のためのサードパーティスクリプトを組み込みます。

以下のカスタムフィルターの例は、Montanaカスタマーポータルテーマで最も見栄えが良くなるように設計されています。LiveAgentで異なるカスタマーポータルテーマを使用している場合は、実際のカスタマーポータルのデザインに合わせてCSSを調整する必要があります。

カスタムチケットフィルターを追加すると、お客様や訪問者はLiveAgentのカスタマーポータルの「My tickets」(マイチケット)タブでこれを見つけられるようになります。このタブはログインしたユーザーにのみ表示されます。

  1. これがLiveAgentに追加する必要のある実際のカスタムフィルターのHTMLコードです。LiveAgentの管理パネルを開き、Customer portal(カスタマーポータル)タブ->Settings(設定)->Configuration(設定)->Design(デザイン)->Own HTML(独自HTML)->My tickets custom menu HTMLに移動します。フィルターコードを追加したら、必ず変更を保存してください。
<div class="panel panel-default margin-top margin-bottom">
    <div class="panel-heading">
      {if !isset($code) || $code == ""}
          Ticket Filters
      {elseif $code != ""}
         <a href="#" onclick="window.history.back();">Back to Ticket Filters</a>
      {/if}
  </div>
  {if !isset($code) || $code == ""}
    <div class="panel-body">
    <div class="filter-body">
        <div class="filter_label"></div>
            <input type="text" id="filterQuery" name="filterQuery"  value="{$query|escape}" placeholder="Search text" />
    </div>
    <div class="filter-body">
        <div class="filter_label">Status</div>
            <select class="status-select" name="status">
            <option value="A" {if $status == "A"}selected{/if}>Any</option>
            <option value="O" {if $status == "O"}selected{/if}>Open</option>
            <option value="AN" {if $status == "AN"}selected{/if}>Answered</option>
            <option value="RD" {if $status == "RD"}selected{/if}>Resolved</option>
            </select>
    </div>
    <div class="filter-body">
        <div class="filter_label">Tags</div>
            <select class="tags-select" name="tags[]" multiple="multiple">
                {foreach from=$publicTags item="tag" key="tag_id"}
            <option value="{$tag_id}" {if $selectedTags.$tag_id.name > ""}selected{/if} style="color:#{$tag.color}; background-color:#{$tag.backgroundcolor};">{$tag.name}</option>
                {/foreach}
            </select>
    </div>
    <div class="filter-body">
        <div class="filter_label">Created date</div>
        <div class="dateFilter input-group">
            <input id="createdFromInput" class="form-control" placeholder="from RRRR-MM-DD"  value="{$createdFrom}">
            <div id="createdFrom"></div>
            <div class="input-group-btn"><button id="createdFromToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
        <div class="dateFilter input-group">
            <input id="createdToInput" class="form-control" placeholder="to RRRR-MM-DD" value="{$createdTo}">
            <div id="createdTo"></div>
            <div class="input-group-btn"><button id="createdToToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
    </div>
    <div class="filter-body">
        <div class="filter_label">Changed date</div>
        <div class="dateFilter input-group">
            <input id="changedFromInput" class="form-control" placeholder="from RRRR-MM-DD">
            <div id="changedFrom"></div>
            <div class="input-group-btn"><button id="changedFromToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
        <div class="dateFilter input-group">
            <input id="changedToInput" class="form-control" placeholder="to RRRR-MM-DD">
            <div id="changedTo"></div>
            <div class="input-group-btn"><button id="changedToToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
    </div>

    <div class="filter-body">
        <div class="filter_label">Resolved date</div>
        <div class="dateFilter input-group">
            <input id="resolvedFromInput" class="form-control" placeholder="from RRRR-MM-DD">
            <div id="resolvedFrom"></div>
            <div class="input-group-btn"><button id="resolvedFromToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
        <div class="dateFilter input-group">
            <input id="resolvedToInput" class="form-control" placeholder="to RRRR-MM-DD">
            <div id="resolvedTo"></div>
            <div class="input-group-btn"><button id="resolvedToToggle" class="btn btn-default glyphicon glyphicon-calendar"></button></div>
        </div>
    </div>

    <input type="button" value="Apply" class="btn btn-primary" onclick="submitFilter('{$myTicketsUrl}');" />
    <input type="button" value="Cancel" class="btn btn-default" onclick="clearFilter('{$myTicketsUrl}');" />
    </div>
<script>
{literal}
WindowDatePicker.createLanguage('en', {
    DAYS_ABBR: ['', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    MONTHS: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    MONTHS_ABBR: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    AM_PM: ['AM', 'PM'],
    BUTTONS: ['Apply', 'Cancel'],
    INVALID_DATE: 'Invalid Date'
});
{/literal}
  const createdFrom = new
{literal}WindowDatePicker({{/literal}
    el: '#createdFrom',
    lang: 'en',
    toggleEl: '#createdFromToggle',
    inputEl: '#createdFromInput',
    value: '{$createdFrom}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});
  const createdTo = new WindowDatePicker({{/literal}
    el: '#createdTo',
    lang: 'en',
    toggleEl: '#createdToToggle',
    inputEl: '#createdToInput',
    value: '{$createdTo}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});{/literal}

  const changedFrom = new
{literal}WindowDatePicker({{/literal}
    el: '#changedFrom',
    lang: 'en',
    toggleEl: '#changedFromToggle',
    inputEl: '#changedFromInput',
    value: '{$changedFrom}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});
  const changedTo = new WindowDatePicker({{/literal}
    el: '#changedTo',
    lang: 'en',
    toggleEl: '#changedToToggle',
    inputEl: '#changedToInput',
    value: '{$changedTo}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});{/literal}

  const resolvedFrom = new
{literal}WindowDatePicker({{/literal}
    el: '#resolvedFrom',
    lang: 'en',
    toggleEl: '#resolvedFromToggle',
    inputEl: '#resolvedFromInput',
    value: '{$resolvedFrom}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});
  const resolvedTo = new WindowDatePicker({{/literal}
    el: '#resolvedTo',
    lang: 'en',
    toggleEl: '#resolvedToToggle',
    inputEl: '#resolvedToInput',
    value: '{$resolvedTo}',
    type: 'DATE',
    dateType: 'YYYY-MM-DD',
{literal}});{/literal}
</script>
  {/if}
</div>
  1. Custom CSS(カスタムCSS)タブ(Customer portal->Settings->Configuration->Design->Custom CSS)に切り替えます。このCSSを追加して変更を保存してください。実際のデザインニーズに合わせて自由にCSSを調整してください。
/* =====================
   GLYPHICONS FONT
   ===================== */
@font-face {
    font-family: 'Glyphicons Halflings';
    src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.eot');
    src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
         url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.woff2') format('woff2'),
         url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.woff') format('woff'),
         url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
         url('https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}

.glyphicon {
    position: relative;
    top: 1px;
    display: inline-block;
    font-family: 'Glyphicons Halflings' !important;
    font-style: normal;
    font-weight: normal;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.glyphicon-calendar:before {
    content: "\e109";
}

/* =====================
   SELECT2
   ===================== */
.select2-container {width: auto !important; display:block !important;}
.select2-container > span {display:block !important;}

.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    color: inherit !important;
    font-size: 14px;
    line-height: 15px;
    vertical-align: bottom;
}

.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
    opacity: 0.5;
}

.select2-container--default .select2-selection--multiple .select2-selection__choice {
    border-color: rgba(0,0,0,0.4) !important;
    float: none !important;
    font-size: 12px !important;
    text-overflow: ellipsis;
    overflow: hidden;
}

.select2-results__options li[aria-selected="true"] {
    display: none;
}

.select2-results__option {
    border: 3px solid #fff;
    border-radius: 7px;
    font-size: 12px;
    line-height: 12px;
}

.select2-results__option:hover {
    opacity: 0.8;
}

/* =====================
   FILTER GENERAL
   ===================== */
.filter_label {
    color: #666;
    font-size: 12px;
}

.filter-body {
    margin-bottom: 10px;
}

#filterQuery {
    border-radius: 4px;
    border: 1px solid #aaa;
    padding: 12px 8px;
    width: 100%;
}

.btn {
    height: auto;
}

/* =====================
   DATE FILTER
   ===================== */
.dateFilter {
    margin-bottom: 3px;
}

.dateFilter.input-group {
    display: flex !important;
    align-items: center !important;
    flex-wrap: nowrap !important;
    position: relative;
    gap: 6px;
}

/* Zero-width flex item — stays in flow so WindowDatePicker
   can position itself correctly, but takes up no space */
#createdFrom, #createdTo,
#changedFrom, #changedTo,
#resolvedFrom, #resolvedTo {
    flex: 0 0 0 !important;
    min-width: 0 !important;
    max-width: 0 !important;
    overflow: visible !important;
    padding: 0 !important;
    margin: 0 !important;
    border: 0 !important;
    z-index: 9999;
}

/* Input fills remaining space */
.dateFilter.input-group .form-control {
    flex: 1 1 auto !important;
    width: auto !important;
    font-size: 13px;
    padding: 9px 5px;
    box-shadow: none;
}

/* Button pinned to the right */
.dateFilter.input-group .input-group-btn {
    flex: 0 0 auto !important;
    display: flex !important;
    align-items: center !important;
    width: auto !important;
}

.dateFilter.input-group .input-group-btn > .btn {
    height: 34px !important;
    width: 34px !important;
    padding: 0 !important;
    margin: 0 !important;
    line-height: 34px !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    font-size: 14px;
}
  1. Tracking codes(トラッキングコード)タブ(Customer portal->Settings->Configuration->Tracking codes)に切り替えます。ここで必要なスクリプトと関数を追加します。 a) Before (の前)セクションにこのコードを追加します。
<link href="https://www.cssscript.com/demo/window-date-time-picker/dist/css/window-date-picker.css"  rel="stylesheet" type="text/css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.10/css/select2.min.css" rel="stylesheet" />
<script src="https://www.cssscript.com/demo/window-date-time-picker/dist/js/window-date-picker.js"></script>
b) **Before </BODY>**(</BODY>の前)セクションにこのコードを追加し、変更を保存します。
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.10/js/select2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
   $('.tags-select').select2();
});
$(document).ready(function() {
   $('.status-select').select2();
});
function clearFilter(url) {
 window.location.href = url;
}
function submitFilter(url) {
   filterQuery = [];
   if ($('.tags-select').val() != [])  {
       filterQuery.push("t=" + $('.tags-select').val().join());
   }
   if ($('.status-select').val() != "")  {
       filterQuery.push("s=" + $('.status-select').val());
   }
   if ($('#createdFromInput').val() != "")  {
       filterQuery.push("created_from=" + $('#createdFromInput').val());
   }
   if ($('#createdToInput').val() != "")  {
       filterQuery.push("created_to=" + $('#createdToInput').val());
   }
   if ($('#changedFromInput').val() != "")  {
       filterQuery.push("changed_from=" + $('#changedFromInput').val());
   }
   if ($('#changedToInput').val() != "")  {
       filterQuery.push("changed_to=" + $('#changedToInput').val());
   }
   if ($('#resolvedFromInput').val() != "")  {
       filterQuery.push("resolved_from=" + $('#resolvedFromInput').val());
   }
   if ($('#resolvedToInput').val() != "")  {
       filterQuery.push("resolved_to=" + $('#resolvedToInput').val());
   }
   if ($('#filterQuery').val() != "")  {
       filterQuery.push("q=" + $('#filterQuery').val());
   }
   window.location.href = url + "?" + filterQuery.join("&");
}
</script>