プリザンターの管理画面にチュートリアルを実装してみた

# はじめに
プリザンターは非常に機能が多くやれることも多い為、マニュアルを参照することが多いです。
私も普段業務で利用している際にマニュアルをよく見に行くのですが、画面上でマニュアルが確認できたら少しは楽になるのにな～と思っていました。
何かいい方法はないかと色々探していたところ、こんな記事が見つかりました。
[プリザンターにオンラインチュートリアルを実装する](https://qiita.com/YoshikiSawada/items/209734e95b3d8ab6d6cc)
__intro.js__ 初めて聞きましたが、見た目もわかりやすく実装が簡単そうということで早速試すことにしました。

# intro.jsとは
Intro.jsはWebページ上に簡単にチュートリアルが作れるJavaScriptライブラリです。
外部ファイル(intro.js, introjs.css)を読み込み、指定のタグをHTML属性に追加するだけでWebページ上に簡単にチュートリアルを作ることが出来るのが特徴で、私のような初心者でも簡単に実装ができました。

プリザンターに実装する場合、スクリプト機能を使用することになりますが、読み込みに必要なものはこれだけ。
あとは決まった形に添って実装していけばチュートリアルが完成します。
```Javascript
    // intro.js読み込み
    var introjs = document.createElement("script");
    introjs.src = "https://unpkg.com/intro.js/intro.js";
    document.body.appendChild(introjs);

    // css読み込み
    var link = document.createElement('link');
    link.href = 'https://unpkg.com/intro.js/introjs.css';
    link.rel = 'stylesheet';
    link.type = 'text/css';
    var h = document.getElementsByTagName('head')[0];
    h.appendChild(link);
```

# 実際に実装してみた
Q.自分がユーザだったとしてプリザンターを操作する上で一番チュートリアルが必要な場所はどこか？
A.「テーブルの管理」だと思います。
プリザンターのテーブルの管理は、画面項目の設定からスクリプトの記載、通知の設定とあらゆる設定が画面上から行えますが、どの項目で何が設定できるのか迷ってしまうユーザは多いと思います。
そこでテーブルの管理に画面上で参照できるチュートリアルを作成することとします。

##### 問題点：プリザンターのスクリプト機能ではテーブルの管理にボタンを追加できませんでした。

もしかしたらやりようはあるのかもしれませんが、少なくとも正攻法ではテーブルの管理に対してスクリプトでどうこうするのは難しそうです。
そこで[拡張スクリプト機能](https://pleasanter.org/manual/extended-script)を使用し、テーブルの管理にチュートリアル用のボタンを新規実装します。

```Javascript
var currentUrl = window.location.href;
if(currentUrl == "http://localhost/items/{対象のサイトID}/edit"){
    // コマンドボタンを追加
    var button= '<button id="tutorial" class="button button-icon" type="button" data-icon="ui-icon-lightbulb">使い方</button>';
    var element = document.getElementById("MainCommands");
    element.insertAdjacentHTML('afterbegin', button);

    //-----ここから下は触らない-----
    // intro.js読み込み
    var introjs = document.createElement("script");
    introjs.src = "https://unpkg.com/intro.js/intro.js";
    document.body.appendChild(introjs);

    // css読み込み
    var link = document.createElement('link');
    link.href = 'https://unpkg.com/intro.js/introjs.css';
    link.rel = 'stylesheet';
    link.type = 'text/css';
    var h = document.getElementsByTagName('head')[0];
    h.appendChild(link);
    //-----ここまで触らない-----

    window.onload = function(){
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/8fb23adb-ed01-8a47-70d9-77d08b6742e1.png)

ここまでは先駆者の記事をほぼそのまま流用することができました。

##### 新たな問題点：タブ切り替えによってチュートリアルが分岐しない
選択しているタブによってチュートリアルの表示を分岐させる必要がありますがどう分岐させるかが全く思いつかずかなりの時間を要してしまいました。

結果として"aria-controls"の値によってどのチュートリアルを表示させるかを分岐させることで実装することが実現できましたが、もっとスマートな方法が分かる方教えていただけると嬉しいです。
```Javascript
    window.onload = function(){
        var intro = introJs();        
        // ボタンクリック時の処理
        document.getElementById("tutorial").onclick = function() {
            // 現在の選択されているタブを取得する
            var selectedTab = document.querySelector("#EditorTabs [tabindex='0']");
            console.log(selectedTab)
            // もし選択されているタブが特定のタブである場合に処理を実行する
            if (selectedTab && selectedTab.getAttribute("aria-controls") === "FieldSetGeneral") {
                // タブが選択されている場合に要素にフォーカスを当てる
                intro.setOptions({
                steps: [
                    // ＝＝＝＝　ここで説明を記述する　＝＝＝＝
                    {element: document.querySelector('li[tabindex="0"]'), intro: '「全般タブ」ではサイトの設定を管理します。'},
                    {element: document.querySelector('#Sites_Title'), intro: 'サイトのタイトルを設定します。'},
                    {element: document.querySelector('#Sites_SiteName'), intro: 'サイト統合で使用可能なサイト名を設定します。'},
                    {element: document.querySelector('#Sites_SiteGroupName'), intro: 'サイト統合で使用可能なサイトグループ名を設定します。'},
                    {element: document.querySelector('#Sites_Body\\.viewer'), intro: 'サイト説明を記載します。'}
                    ],
                    'nextLabel': '次へ →',
                    'prevLabel': '← 戻る',
                    'doneLabel': '完了',        
                });
    
            } else if (selectedTab && selectedTab.getAttribute("aria-controls") === "GuideEditor"){
                // タブが選択されている場合に要素にフォーカスを当てる
                intro.setOptions({
                    steps: [
                    // ＝＝＝＝　ここで説明を記述する　＝＝＝＝
                    {element: document.querySelector('li[tabindex="0"]'), intro: '「ガイドタブ」ではサイト上部に表示するガイドを管理します。'},
                    {element: document.querySelector('#Sites_GridGuide\\.viewer'), intro: '一覧表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_EditorGuide\\.viewer'), intro: '編集画面表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_CalendarGuide\\.viewer'), intro: 'カレンダー表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_CrosstabGuide\\.viewer'), intro: 'クロス集計表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_TimeSeriesGuide\\.viewer'), intro: '時系列チャート表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_AnalyGuide\\.viewer'), intro: '分析チャート表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_KambanGuide\\.viewer'), intro: 'カンバン表示時のガイドを設定します。'},
                    {element: document.querySelector('#Sites_ImageLibGuide\\.viewer'), intro: '画像ライブラリ表示時のガイドを設定します。'},
                    ],
                    'nextLabel': '次へ →',
                    'prevLabel': '← 戻る',
                    'doneLabel': '完了',        
                });
            } else if (selectedTab && selectedTab.getAttribute("aria-controls") === "EditorSettingsEditor"){
                // タブが選択されている場合に要素にフォーカスを当てる
                intro.setOptions({
                    steps: [
                    // ＝＝＝＝　ここで説明を記述する　＝＝＝＝
                    {element: document.querySelector('li[tabindex="0"]'), intro: '「エディタタブ」では編集画面で使用する項目を管理します。'},
                    {element: document.querySelector('#EditorSettingsEditor .fieldset.cf.enclosed'), intro: '編集画面で使用する項目を操作します。'},
                    {element: document.querySelector('#EditorColumnsWrapper'), intro: '有効化されている項目です。'},
                    {element: document.querySelector('#EditorSourceColumnsWrapper'), intro: '無効化されている項目です。'},
                    {element: document.querySelector('#ToEnableEditorColumns'), intro: '任意の項目を選択後押下することで有効化されます。'},
                    {
                        element: document.querySelector('#OpenEditorColumnDialog'),
                        intro: '選択した項目の詳細設定を行います。詳細は <a href="https://pleasanter.org/manual?search=%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E7%AE%A1%E7%90%86%EF%BC%9A%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%EF%BC%9A%E9%A0%85%E7%9B%AE%E3%81%AE%E8%A9%B3%E7%B4%B0%E8%A8%AD%E5%AE%9A%EF%BC%9A" target="_blank">プリザンターのマニュアル</a> を参照してください。'
                    },
                    {element: document.querySelector('#EditorSettingsEditor .fieldset.cf.enclosed-thin'), intro: '編集画面で使用するタブを操作します。'},
                    ],
                    'nextLabel': '次へ →',
                    'prevLabel': '← 戻る',
                    'doneLabel': '完了',        
                });
            }
        // チュートリアル実行
        intro.start();
        }
    }

```
やっていることはif文で選択されたタブに応じて表示するチュートリアルを分岐させているだけです。

# 実際に動かしてみた

##### テーマ：Sunny
![20240418_190930.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/566fb125-e5b2-1278-f829-94c1e1baf46a.gif)

##### テーマ：Cerulean
![20240531_142249.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/746217c9-dde5-5310-410d-8d11b2574d27.gif)

いかがでしょうか。
中々いい感じにチュートリアルとして機能していると思います。
改善すべきポイントとして、ダイアログを表示した場合にそのダイアログに対してチュートリアルを出す方法が分からず、項目の詳細設定ボタンを押下した際のダイアログに対してチュートリアルを実装できなかったのが心残りです。
どうにも思いつかなかったため、チュートリアル上から直接マニュアルへ遷移できるようにしました。
```Javascript
{
    element: document.querySelector('#OpenEditorColumnDialog'),
    intro: '選択した項目の詳細設定を行います。詳細は <a href="https://pleasanter.org/manual?search=%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E7%AE%A1%E7%90%86%EF%BC%9A%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%EF%BC%9A%E9%A0%85%E7%9B%AE%E3%81%AE%E8%A9%B3%E7%B4%B0%E8%A8%AD%E5%AE%9A%EF%BC%9A" target="_blank">プリザンターのマニュアル</a> を参照してください
},
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/18ba8ca8-9f25-21fb-7d4e-a86de04e2bc3.png)
遷移後は「エディタ：項目の詳細設定」で検索された状態になっているので1から検索するよりは早い？

# 最後に
今まではプリザンター上の機能を利用して考えたりすることが多かったですが、今回は初めて外部のライブラリを使用してみました。  
ただ、残念なことに拡張スクリプトを使用する為、簡単に使用してもらうには少々ハードルが高くなってしまいました。

今後の展望として、インストールしてプリザンターを開いたとき現在は真っ白な画面で自分でテーブルを作成する必要がありますが、1つチュートリアル用のテーブルが存在して、そこにintro.jsで実装されたチュートリアル画面があると少しはプリザンターにとっつきやすくなるのかな、と妄想しています。


intro.jsについてですが、シンプルで分かりやすい反面、細かく調整すると意外と時間がかかりました。
今回は基本の実装をしましたが他のページに行ったときにチュートリアルを引き継げる機能もあるようなので今後はそちらも試していきたいと思います。