メインコンテンツへスキップ

🛠️ Splunk 既存ダッシュボード 図形の追加・修正・編集ガイド


    ~チャート上に図形を自由に追加・変更する方法~


    📚 この記事で学べること

    項目 内容 図形の追加 新しい四角・丸・棒・三角を追加 位置の変更 図形を好きな場所に配置 色の変更 ステータスや条件で色を変える サイズ変更 図形の大きさを調整 グループ化 複数図形をまとめて管理


    🔷 Part 1: 図形追加の基本パターン

    1-1. 追加の全体像

    【既存ダッシュボード】          【追加後】
    ┌─────────────────────┐       ┌─────────────────────┐
    │  ■ ● ▬ ▼          │  →   │  ■ ● ▬ ▼ ★ ◆   │
    │  A  B  C  D         │       │  A  B  C  D  E  F   │
    └─────────────────────┘       └─────────────────────┘
    
    追加に必要な作業:
    1. SPL: 新しいデータ行を追加
    2. CSS: 新しい図形のスタイルを定義
    3. JavaScript: 図形生成ロジックを修正
    

    1-2. 新しい図形を追加する3ステップ

    ステップ1: SPLにデータを追加

    | makeresults count=6
    | streamstats count as id
    | eval device_name = case(
        id==1, "サーバーA",
        id==2, "サーバーB", 
        id==3, "ネットワーク機器C",
        id==4, "データベースD",
        id==5, "ストレージE",      /* ★追加★ */
        id==6, "ファイアウォールF"  /* ★追加★ */
      )
    | eval status = case(
        id==1, "正常",
        id==2, "警告",
        id==3, "異常",
        id==4, "停止",
        id==5, "正常",    /* ★追加★ */
        id==6, "警告"     /* ★追加★ */
      )
    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3
      )
    | table device_name, status, status_code
    
    【変更ポイント】
    ┌────────────────────────────────────────────────────────┐
    │  count=4  →  count=6                                  │
    │  ↑ 生成する行数を増やす                               │
    │                                                        │
    │  case文に新しい条件を追加                              │
    │  id==5, "ストレージE"                                  │
    │  id==6, "ファイアウォールF"                            │
    └────────────────────────────────────────────────────────┘
    

    ステップ2: CSSに新しい図形スタイルを追加

    /* ===== 既存の図形 ===== */
    .rectangle { ... }
    .circle { ... }
    .bar { ... }
    .triangle { ... }
    
    /* ===== ★新規追加★ 星形 ===== */
    .star {
      /* 星形はSVGまたはクリップパスで作成 */
      width: 60px;
      height: 60px;
      background-color: #808080 ;
      clip-path: polygon(
        50% 0%,      /* 上の頂点 */
        61% 35%,     /* 右上内側 */
        98% 35%,     /* 右上外側 */
        68% 57%,     /* 右下内側 */
        79% 91%,     /* 右下外側 */
        50% 70%,     /* 下の内側 */
        21% 91%,     /* 左下外側 */
        32% 57%,     /* 左下内側 */
        2% 35%,      /* 左上外側 */
        39% 35%      /* 左上内側 */
      );
      display: flex;
      justify-content: center;
      align-items: center;
      transition: background-color 0.3s ease;
    }
    
    /* ===== ★新規追加★ ひし形 ===== */
    .diamond {
      width: 50px;
      height: 50px;
      background-color: #808080 ;
      /* 45度回転させて正方形をひし形に */
      transform: rotate(45deg);
      display: flex;
      justify-content: center;
      align-items: center;
      transition: background-color 0.3s ease;
    }
    
    /* ひし形の中のテキストを正位置に */
    .diamond span {
      transform: rotate(-45deg);
      color: white;
      font-size: 10px;
      font-weight: bold;
    }
    
    /* ===== ★新規追加★ 六角形 ===== */
    .hexagon {
      width: 60px;
      height: 52px;
      background-color: #808080 ;
      clip-path: polygon(
        25% 0%,    /* 左上 */
        75% 0%,    /* 右上 */
        100% 50%,  /* 右 */
        75% 100%,  /* 右下 */
        25% 100%,  /* 左下 */
        0% 50%     /* 左 */
      );
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-size: 11px;
      font-weight: bold;
      transition: background-color 0.3s ease;
    }
    

    ステップ3: JavaScriptの図形配列を更新

    // 【変更前】4種類
    var shapeTypes = ['rectangle', 'circle', 'bar', 'triangle'];
    
    // 【変更後】6種類に増やす
    var shapeTypes = ['rectangle', 'circle', 'bar', 'triangle', 'star', 'diamond'];
    //                                                          ↑追加  ↑追加
    
    // 図形生成部分も追加
    if (shapeType === 'rectangle') {
      shapeHtml = '<div class="rectangle ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'circle') {
      shapeHtml = '<div class="circle ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'bar') {
      shapeHtml = '<div class="bar ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'triangle') {
      shapeHtml = '<div class="triangle ' + statusClass + '"></div>';
    }
    // ★追加★
    else if (shapeType === 'star') {
      shapeHtml = '<div class="star ' + statusClass + '"></div>';
    }
    // ★追加★
    else if (shapeType === 'diamond') {
      shapeHtml = '<div class="diamond ' + statusClass + '"><span>' + status + '</span></div>';
    }
    

    🔷 Part 2: 図形の位置変更

    2-1. Flexboxで配置を変更

    左寄せ(デフォルト)

    .shapes-container {
      display: flex;
      justify-content: flex-start;  /* ← 左寄せ */
      gap: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │ ■  ●  ▬  ▼                            │
    │                                         │
    └─────────────────────────────────────────┘
    

    中央寄せ

    .shapes-container {
      display: flex;
      justify-content: center;  /* ← 中央寄せ */
      gap: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │          ■  ●  ▬  ▼                   │
    │                                         │
    └─────────────────────────────────────────┘
    

    右寄せ

    .shapes-container {
      display: flex;
      justify-content: flex-end;  /* ← 右寄せ */
      gap: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │                            ■  ●  ▬  ▼ │
    │                                         │
    └─────────────────────────────────────────┘
    

    均等配置

    .shapes-container {
      display: flex;
      justify-content: space-between;  /* ← 両端に配置、間を均等 */
      gap: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │ ■          ●          ▬          ▼   │
    │                                         │
    └─────────────────────────────────────────┘
    

    均等配置(周囲も含む)

    .shapes-container {
      display: flex;
      justify-content: space-around;  /* ← 周囲も含めて均等 */
      gap: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │    ■       ●       ▬       ▼         │
    │                                         │
    └─────────────────────────────────────────┘
    

    2-2. CSS Gridで格子状に配置

    3列のグリッド

    .shapes-container {
      display: grid;
      grid-template-columns: repeat(3, 1fr);  /* 3列 */
      gap: 20px;
      padding: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │    ■         ●         ▬              │
    │    A         B         C               │
    │                                         │
    │    ▼         ★         ◆              │
    │    D         E         F               │
    └─────────────────────────────────────────┘
    

    4列のグリッド

    .shapes-container {
      display: grid;
      grid-template-columns: repeat(4, 1fr);  /* 4列 */
      gap: 15px;
      padding: 20px;
    }
    
    ┌─────────────────────────────────────────┐
    │   ■      ●      ▬      ▼             │
    │   A      B      C      D              │
    │                                         │
    │   ★      ◆                            │
    │   E      F                             │
    └─────────────────────────────────────────┘
    

    列幅を固定

    .shapes-container {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
      /* ↑ 最小120px、最大1frで自動的に列数を調整 */
      gap: 20px;
    }
    

    2-3. 絶対位置指定(完全自由配置)

    基本設定

    /* コンテナを基準点にする */
    .shapes-container {
      position: relative;  /* ★重要★ 子要素の基準点になる */
      width: 100%;
      height: 400px;       /* 高さを固定 */
      background-color: #f5f5f5 ;
      border: 1px solid #ddd ;
    }
    
    /* 各図形アイテムを絶対位置指定 */
    .shape-item {
      position: absolute;  /* ★重要★ 親要素を基準に配置 */
    }
    

    個別の位置指定(CSS)

    /* 1番目の図形:左上 */
    .shape-item:nth-child(1) {
      top: 20px;
      left: 30px;
    }
    
    /* 2番目の図形:右上 */
    .shape-item:nth-child(2) {
      top: 20px;
      right: 30px;
    }
    
    /* 3番目の図形:中央 */
    .shape-item:nth-child(3) {
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);  /* 中心に配置 */
    }
    
    /* 4番目の図形:左下 */
    .shape-item:nth-child(4) {
      bottom: 20px;
      left: 30px;
    }
    
    ┌─────────────────────────────────────────┐
    │ ■                                  ●   │
    │ A                                  B   │
    │                                         │
    │               ▬                        │
    │               C                        │
    │                                         │
    │ ▼                                      │
    │ D                                      │
    └─────────────────────────────────────────┘
    

    JavaScriptで動的に位置指定

    // データに位置情報を含める方法
    | makeresults count=4
    | streamstats count as id
    | eval device_name = case(id==1,"A", id==2,"B", id==3,"C", id==4,"D")
    | eval status = case(id==1,"正常", id==2,"警告", id==3,"異常", id==4,"停止")
    | eval status_code = case(status=="正常",0, status=="警告",1, status=="異常",2, status=="停止",3)
    | eval pos_top = case(id==1,"20px", id==2,"20px", id==3,"150px", id==4,"150px")
    | eval pos_left = case(id==1,"30px", id==2,"250px", id==3,"30px", id==4,"250px")
    | table device_name, status, status_code, pos_top, pos_left
    
    // JavaScript側で位置を適用
    var topIndex = fields.indexOf('pos_top');
    var leftIndex = fields.indexOf('pos_left');
    
    var posTop = row[topIndex];
    var posLeft = row[leftIndex];
    
    // スタイル属性で位置を指定
    var itemHtml = 
      '<div class="shape-item" style="top: ' + posTop + '; left: ' + posLeft + ';">' +
        shapeHtml +
        '<div class="device-label">' + deviceName + '</div>' +
      '</div>';
    

    🔷 Part 3: 色の変更方法

    3-1. 新しいステータス色を追加

    CSS定義

    /* ===== 既存のステータス色 ===== */
    .status-normal  { background-color: #28a745 ; }  /* 緑:正常 */
    .status-warning { background-color: #ffc107 ; }  /* 黄:警告 */
    .status-error   { background-color: #dc3545 ; }  /* 赤:異常 */
    .status-stop    { background-color: #6c757d ; }  /* 灰:停止 */
    
    /* ===== ★新規追加★ ===== */
    .status-info    { background-color: #17a2b8 ; }  /* 水色:情報 */
    .status-success { background-color: #20c997 ; }  /* ティール:成功 */
    .status-danger  { background-color: #e83e8c ; }  /* ピンク:危険 */
    .status-dark    { background-color: #343a40 ; }  /* 濃灰:不明 */
    .status-primary { background-color: #007bff ; }  /* 青:メイン */
    .status-secondary { background-color: #6610f2 ; } /* 紫:サブ */
    

    SPL側でステータスコードを追加

    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3,
        status=="情報", 4,      /* ★追加★ */
        status=="成功", 5,      /* ★追加★ */
        status=="危険", 6,      /* ★追加★ */
        status=="不明", 7       /* ★追加★ */
      )
    

    JavaScript側でswitch文を追加

    var statusClass = '';
    switch(statusCode) {
      case 0: statusClass = 'status-normal'; break;
      case 1: statusClass = 'status-warning'; break;
      case 2: statusClass = 'status-error'; break;
      case 3: statusClass = 'status-stop'; break;
      // ★追加★
      case 4: statusClass = 'status-info'; break;
      case 5: statusClass = 'status-success'; break;
      case 6: statusClass = 'status-danger'; break;
      case 7: statusClass = 'status-dark'; break;
      default: statusClass = 'status-stop';
    }
    

    3-2. 数値範囲で色を変える

    SPLで数値に応じたステータスを設定

    | makeresults count=5
    | streamstats count as id
    | eval value = case(id==1, 95, id==2, 75, id==3, 50, id==4, 25, id==5, 10)
    | eval status = case(
        value >= 80, "正常",      /* 80以上:緑 */
        value >= 60, "注意",      /* 60-79:黄緑 */
        value >= 40, "警告",      /* 40-59:黄 */
        value >= 20, "異常",      /* 20-39:オレンジ */
        value < 20, "危険"        /* 20未満:赤 */
      )
    | eval status_code = case(
        status=="正常", 0,
        status=="注意", 1,
        status=="警告", 2,
        status=="異常", 3,
        status=="危険", 4
      )
    | table id, value, status, status_code
    

    対応するCSS

    .status-0 { background-color: #28a745 ; }  /* 緑:80以上 */
    .status-1 { background-color: #7cb342 ; }  /* 黄緑:60-79 */
    .status-2 { background-color: #ffc107 ; }  /* 黄:40-59 */
    .status-3 { background-color: #ff9800 ; }  /* オレンジ:20-39 */
    .status-4 { background-color: #dc3545 ; }  /* 赤:20未満 */
    

    3-3. グラデーション色

    /* グラデーション背景 */
    .status-gradient-good {
      background: linear-gradient(135deg, #28a745  0%, #20c997  100%);
    }
    
    .status-gradient-warning {
      background: linear-gradient(135deg, #ffc107  0%, #ff9800  100%);
    }
    
    .status-gradient-bad {
      background: linear-gradient(135deg, #dc3545  0%, #c82333  100%);
    }
    

    3-4. 点滅アニメーション(異常時)

    /* 点滅アニメーション定義 */
    @keyframes blink {
      0%   { opacity: 1; }
      50%  { opacity: 0.3; }
      100% { opacity: 1; }
    }
    
    /* 異常ステータスに点滅を適用 */
    .status-error {
      background-color: #dc3545 ;
      animation: blink 1s ease-in-out infinite;
      /* ↑ 1秒間隔で無限に点滅 */
    }
    
    /* 危険ステータスは高速点滅 */
    .status-danger {
      background-color: #e83e8c ;
      animation: blink 0.5s ease-in-out infinite;
      /* ↑ 0.5秒間隔で高速点滅 */
    }
    

    🔷 Part 4: サイズ変更

    4-1. 図形サイズの変更

    小サイズ

    .shape-small {
      transform: scale(0.7);  /* 70%に縮小 */
    }
    
    /* または個別に指定 */
    .rectangle-small {
      width: 70px;   /* 100px → 70px */
      height: 42px;  /* 60px → 42px */
    }
    
    .circle-small {
      width: 50px;   /* 70px → 50px */
      height: 50px;
    }
    

    大サイズ

    .shape-large {
      transform: scale(1.5);  /* 150%に拡大 */
    }
    
    /* または個別に指定 */
    .rectangle-large {
      width: 150px;  /* 100px → 150px */
      height: 90px;  /* 60px → 90px */
    }
    
    .circle-large {
      width: 100px;  /* 70px → 100px */
      height: 100px;
    }
    

    SPLでサイズを指定

    | eval size_class = case(
        status=="正常", "shape-small",
        status=="警告", "",              /* 通常サイズ */
        status=="異常", "shape-large",   /* 異常は大きく目立たせる */
        status=="停止", "shape-small"
      )
    | table device_name, status, status_code, size_class
    

    JavaScript側でサイズクラスを適用

    var sizeIndex = fields.indexOf('size_class');
    var sizeClass = row[sizeIndex] || '';  // nullの場合は空文字
    
    // 図形にサイズクラスを追加
    shapeHtml = '<div class="rectangle ' + statusClass + ' ' + sizeClass + '">' + status + '</div>';
    

    🔷 Part 5: 図形のグループ化

    5-1. カテゴリ別にグループ化

    SPLでカテゴリを追加

    | makeresults count=6
    | streamstats count as id
    | eval device_name = case(
        id==1, "Webサーバー1",
        id==2, "Webサーバー2",
        id==3, "DBサーバー1",
        id==4, "DBサーバー2",
        id==5, "ネットワーク1",
        id==6, "ネットワーク2"
      )
    | eval category = case(
        id<=2, "Web層",
        id<=4, "DB層",
        id<=6, "NW層"
      )
    | eval status = case(id==1,"正常", id==2,"警告", id==3,"正常", id==4,"異常", id==5,"正常", id==6,"停止")
    | eval status_code = case(status=="正常",0, status=="警告",1, status=="異常",2, status=="停止",3)
    | table category, device_name, status, status_code
    

    CSS:グループスタイル

    /* グループコンテナ */
    .category-group {
      border: 2px solid #ddd ;
      border-radius: 10px;
      padding: 15px;
      margin-bottom: 20px;
      background-color: #f9f9f9 ;
    }
    
    /* グループタイトル */
    .category-title {
      font-size: 16px;
      font-weight: bold;
      color: #333 ;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 2px solid #007bff ;
    }
    
    /* グループ内の図形コンテナ */
    .category-shapes {
      display: flex;
      gap: 15px;
      flex-wrap: wrap;
    }
    

    JavaScript:グループ別に描画

    results.on("data", function() {
      if (results.hasData()) {
        var rows = results.data().rows;
        var fields = results.data().fields;
        
        var container = document.getElementById('shapesContainer');
        container.innerHTML = '';
        
        // カテゴリごとにデータをグループ化
        var groups = {};
        
        rows.forEach(function(row) {
          var categoryIndex = fields.indexOf('category');
          var category = row[categoryIndex];
          
          // グループが存在しなければ作成
          if (!groups[category]) {
            groups[category] = [];
          }
          
          // グループにデータを追加
          groups[category].push(row);
        });
        
        // グループごとにHTMLを生成
        Object.keys(groups).forEach(function(category) {
          // グループコンテナ開始
          var groupHtml = '<div class="category-group">';
          groupHtml += '<div class="category-title">' + category + '</div>';
          groupHtml += '<div class="category-shapes">';
          
          // グループ内の各図形を生成
          groups[category].forEach(function(row, index) {
            var deviceIndex = fields.indexOf('device_name');
            var statusIndex = fields.indexOf('status');
            var codeIndex = fields.indexOf('status_code');
            
            var deviceName = row[deviceIndex];
            var status = row[statusIndex];
            var statusCode = parseInt(row[codeIndex]);
            
            var statusClass = '';
            switch(statusCode) {
              case 0: statusClass = 'status-normal'; break;
              case 1: statusClass = 'status-warning'; break;
              case 2: statusClass = 'status-error'; break;
              case 3: statusClass = 'status-stop'; break;
            }
            
            // 図形HTML
            groupHtml += '<div class="shape-item">';
            groupHtml += '<div class="circle ' + statusClass + '">' + status + '</div>';
            groupHtml += '<div class="device-label">' + deviceName + '</div>';
            groupHtml += '</div>';
          });
          
          groupHtml += '</div>';  // category-shapes終了
          groupHtml += '</div>';  // category-group終了
          
          container.innerHTML += groupHtml;
        });
      }
    });
    

    表示結果

    ┌─────────────────────────────────────────────────────┐
    │  Web層                                              │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 警告  │                           │
    │  │  Web1     Web2    │                            │
    │  └────────────────────┘                            │
    ├─────────────────────────────────────────────────────┤
    │  DB層                                               │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 異常  │                           │
    │  │  DB1      DB2     │                            │
    │  └────────────────────┘                            │
    ├─────────────────────────────────────────────────────┤
    │  NW層                                               │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 停止  │                           │
    │  │  NW1      NW2     │                            │
    │  └────────────────────┘                            │
    └─────────────────────────────────────────────────────┘
    

    🔷 Part 6: 既存コードの修正チェックリスト

    6-1. 図形を追加するときのチェックリスト

    □ 1. SPL修正
      □ makeresults count の数を増やした
      □ eval の case文に新しいデータを追加した
      
    □ 2. CSS修正
      □ 新しい図形のクラスを定義した
      □ 新しいステータス色を定義した(必要な場合)
      □ 三角形など特殊図形の場合、border-colorも定義した
      
    □ 3. JavaScript修正
      □ shapeTypes配列に新しい図形タイプを追加した
      □ if-else文に新しい図形の生成コードを追加した
      □ switch文に新しいステータスコードを追加した(必要な場合)
    

    6-2. よくある修正パターン

    パターン1: 図形の色だけ変えたい

    修正箇所: CSSのみ
    
    .status-normal { background-color: #28a745 ; }
             ↓ 変更
    .status-normal { background-color: #00ff00 ; }  /* より明るい緑に */
    

    パターン2: 図形のサイズだけ変えたい

    修正箇所: CSSのみ
    
    .rectangle {
      width: 100px;   →  width: 150px;
      height: 60px;   →  height: 90px;
    }
    

    パターン3: 図形の配置を変えたい

    修正箇所: CSSのみ
    
    .shapes-container {
      justify-content: flex-start;  →  justify-content: center;
    }
    

    パターン4: 新しいデバイスを追加したい

    修正箇所: SPLのみ
    
    count=4  →  count=5
    case文に id==5 の条件を追加
    

    パターン5: 新しい図形タイプを追加したい

    修正箇所: CSS + JavaScript
    
    1. CSSに .newShape { ... } を追加
    2. JSの shapeTypes に 'newShape' を追加
    3. JSの if-else に newShape の分岐を追加
    

    🔷 Part 7: 完全版テンプレート(カスタマイズ用)

    7-1. フル機能版コード

    <dashboard>
      <label>カスタマイズ可能図形ダッシュボード</label>
      
      <!-- ========== データ取得サーチ ========== -->
      <search id="mainSearch">
        <query>
          | makeresults count=8
          | streamstats count as id
          
          | eval device_name = case(
              id==1, "Webサーバー1",
              id==2, "Webサーバー2",
              id==3, "APサーバー1",
              id==4, "APサーバー2",
              id==5, "DBサーバー1",
              id==6, "DBサーバー2",
              id==7, "FW1",
              id==8, "FW2"
            )
          
          | eval category = case(
              id&lt;=2, "Web層",
              id&lt;=4, "AP層",
              id&lt;=6, "DB層",
              id&lt;=8, "NW層"
            )
          
          | eval status = case(
              id==1, "正常",
              id==2, "正常",
              id==3, "警告",
              id==4, "正常",
              id==5, "異常",
              id==6, "正常",
              id==7, "停止",
              id==8, "正常"
            )
          
          | eval status_code = case(
              status=="正常", 0,
              status=="警告", 1,
              status=="異常", 2,
              status=="停止", 3
            )
          
          | eval shape_type = case(
              id&lt;=2, "circle",
              id&lt;=4, "rectangle",
              id&lt;=6, "hexagon",
              id&lt;=8, "diamond"
            )
          
          | eval size_class = case(
              status=="異常", "shape-large",
              status=="停止", "shape-large",
              1==1, ""
            )
          
          | table category, device_name, status, status_code, shape_type, size_class
        </query>
        <earliest>-24h@h</earliest>
        <latest>now</latest>
      </search>
      
      <row>
        <panel>
          <title>システム監視ダッシュボード</title>
          <html>
            <style>
              /* ========== メインコンテナ ========== */
              .main-container {
                padding: 20px;
                background-color: #f8f9fa ;
              }
              
              /* ========== グループスタイル ========== */
              .category-group {
                border: 2px solid #dee2e6 ;
                border-radius: 12px;
                padding: 20px;
                margin-bottom: 25px;
                background-color: white;
                box-shadow: 0 2px 8px rgba(0,0,0,0.1);
              }
              
              .category-title {
                font-size: 18px;
                font-weight: bold;
                color: #495057 ;
                margin-bottom: 15px;
                padding-bottom: 10px;
                border-bottom: 3px solid #007bff ;
              }
              
              .category-shapes {
                display: flex;
                gap: 25px;
                flex-wrap: wrap;
                justify-content: flex-start;
              }
              
              /* ========== 図形アイテム共通 ========== */
              .shape-item {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 8px;
                padding: 10px;
                border-radius: 8px;
                transition: transform 0.2s ease, box-shadow 0.2s ease;
              }
              
              .shape-item:hover {
                transform: translateY(-5px);
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
              }
              
              /* ========== 四角形 ========== */
              .rectangle {
                width: 90px;
                height: 55px;
                background-color: #808080 ;
                border-radius: 6px;
                border: 2px solid #333 ;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 13px;
                transition: all 0.3s ease;
              }
              
              /* ========== 丸 ========== */
              .circle {
                width: 65px;
                height: 65px;
                background-color: #808080 ;
                border-radius: 50%;
                border: 2px solid #333 ;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 12px;
                transition: all 0.3s ease;
              }
              
              /* ========== 六角形 ========== */
              .hexagon {
                width: 70px;
                height: 60px;
                background-color: #808080 ;
                clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 11px;
                transition: all 0.3s ease;
              }
              
              /* ========== ひし形 ========== */
              .diamond {
                width: 50px;
                height: 50px;
                background-color: #808080 ;
                transform: rotate(45deg);
                display: flex;
                justify-content: center;
                align-items: center;
                transition: all 0.3s ease;
              }
              
              .diamond span {
                transform: rotate(-45deg);
                color: white;
                font-size: 10px;
                font-weight: bold;
              }
              
              /* ========== ラベル ========== */
              .device-label {
                font-size: 12px;
                color: #333 ;
                font-weight: bold;
                text-align: center;
              }
              
              .status-label {
                font-size: 11px;
                color: #666 ;
              }
              
              /* ========== ステータス色 ========== */
              .status-normal  { background-color: #28a745  !important; }
              .status-warning { background-color: #ffc107  !important; color: #333  !important; }
              .status-error   { background-color: #dc3545  !important; }
              .status-stop    { background-color: #6c757d  !important; }
              
              /* ========== サイズ変更 ========== */
              .shape-large { transform: scale(1.3); }
              .shape-small { transform: scale(0.8); }
              
              /* ========== 点滅(異常・停止時) ========== */
              @keyframes pulse {
                0%   { transform: scale(1); opacity: 1; }
                50%  { transform: scale(1.05); opacity: 0.8; }
                100% { transform: scale(1); opacity: 1; }
              }
              
              .status-error, .status-stop {
                animation: pulse 1.5s ease-in-out infinite;
              }
              
              .shape-large.status-error,
              .shape-large.status-stop {
                animation: pulse 1s ease-in-out infinite;
              }
            </style>
            
            <div class="main-container" id="mainContainer">
              <!-- JavaScriptで生成 -->
            </div>
            
            <script>
              require([
                'splunkjs/mvc',
                'splunkjs/mvc/simplexml/ready!'
              ], function(mvc) {
                
                var searchManager = mvc.Components.get("mainSearch");
                var results = searchManager.data("results");
                
                results.on("data", function() {
                  if (results.hasData()) {
                    var rows = results.data().rows;
                    var fields = results.data().fields;
                    
                    var container = document.getElementById('mainContainer');
                    container.innerHTML = '';
                    
                    // カテゴリごとにグループ化
                    var groups = {};
                    
                    rows.forEach(function(row) {
                      var catIndex = fields.indexOf('category');
                      var category = row[catIndex];
                      
                      if (!groups[category]) {
                        groups[category] = [];
                      }
                      groups[category].push(row);
                    });
                    
                    // グループごとに描画
                    Object.keys(groups).forEach(function(category) {
                      var groupHtml = '<div class="category-group">';
                      groupHtml += '<div class="category-title">' + category + '</div>';
                      groupHtml += '<div class="category-shapes">';
                      
                      groups[category].forEach(function(row) {
                        var deviceIndex = fields.indexOf('device_name');
                        var statusIndex = fields.indexOf('status');
                        var codeIndex = fields.indexOf('status_code');
                        var shapeIndex = fields.indexOf('shape_type');
                        var sizeIndex = fields.indexOf('size_class');
                        
                        var deviceName = row[deviceIndex];
                        var status = row[statusIndex];
                        var statusCode = parseInt(row[codeIndex]);
                        var shapeType = row[shapeIndex];
                        var sizeClass = row[sizeIndex] || '';
                        
                        var statusClass = '';
                        switch(statusCode) {
                          case 0: statusClass = 'status-normal'; break;
                          case 1: statusClass = 'status-warning'; break;
                          case 2: statusClass = 'status-error'; break;
                          case 3: statusClass = 'status-stop'; break;
                        }
                        
                        var shapeHtml = '';
                        var allClasses = statusClass + ' ' + sizeClass;
                        
                        if (shapeType === 'rectangle') {
                          shapeHtml = '<div class="rectangle ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'circle') {
                          shapeHtml = '<div class="circle ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'hexagon') {
                          shapeHtml = '<div class="hexagon ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'diamond') {
                          shapeHtml = '<div class="diamond ' + allClasses + '"><span>' + status + '</span></div>';
                        }
                        
                        groupHtml += '<div class="shape-item">';
                        groupHtml += shapeHtml;
                        groupHtml += '<div class="device-label">' + deviceName + '</div>';
                        groupHtml += '</div>';
                      });
                      
                      groupHtml += '</div></div>';
                      container.innerHTML += groupHtml;
                    });
                  }
                });
              });
            </script>
          </html>
        </panel>
      </row>
    </dashboard>
    

    🎯 クイックリファレンス

    追加・修正早見表

    やりたいこと 修正箇所 修正内容 データを増やす SPL count=N を増やす、case文に追加 新しい図形を追加 CSS + JS クラス定義 + shapeTypes配列 + if-else 色を変える CSS .status-xxx { background-color: #xxx ; } サイズを変える CSS width / height または transform: scale() 配置を変える CSS justify-content / grid-template-columns グループ化する SPL + JS categoryフィールド追加 + グループ処理 点滅させる CSS @keyframes + animation ホバー効果を追加 CSS .shape-item:hover { ... }


    これで既存ダッシュボードへの図形追加・修正・編集が自由自在です!🎉

    ★コピペセクション

    # 🛠️ Splunk 既存ダッシュボード 図形の追加・修正・編集ガイド
    ## ~チャート上に図形を自由に追加・変更する方法~
    
    ---
    
    ## 📚 この記事で学べること
    
    | 項目 | 内容 |
    |------|------|
    | 図形の追加 | 新しい四角・丸・棒・三角を追加 |
    | 位置の変更 | 図形を好きな場所に配置 |
    | 色の変更 | ステータスや条件で色を変える |
    | サイズ変更 | 図形の大きさを調整 |
    | グループ化 | 複数図形をまとめて管理 |
    
    ---
    
    # 🔷 Part 1: 図形追加の基本パターン
    
    ## 1-1. 追加の全体像
    
    ```
    【既存ダッシュボード】          【追加後】
    ┌─────────────────────┐       ┌─────────────────────┐
    │  ■ ● ▬ ▼          │  →   │  ■ ● ▬ ▼ ★ ◆   │
    │  A  B  C  D         │       │  A  B  C  D  E  F   │
    └─────────────────────┘       └─────────────────────┘
    
    追加に必要な作業:
    1. SPL: 新しいデータ行を追加
    2. CSS: 新しい図形のスタイルを定義
    3. JavaScript: 図形生成ロジックを修正
    ```
    
    ---
    
    ## 1-2. 新しい図形を追加する3ステップ
    
    ### ステップ1: SPLにデータを追加
    
    ```spl
    | makeresults count=6
    | streamstats count as id
    | eval device_name = case(
        id==1, "サーバーA",
        id==2, "サーバーB", 
        id==3, "ネットワーク機器C",
        id==4, "データベースD",
        id==5, "ストレージE",      /* ★追加★ */
        id==6, "ファイアウォールF"  /* ★追加★ */
      )
    | eval status = case(
        id==1, "正常",
        id==2, "警告",
        id==3, "異常",
        id==4, "停止",
        id==5, "正常",    /* ★追加★ */
        id==6, "警告"     /* ★追加★ */
      )
    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3
      )
    | table device_name, status, status_code
    ```
    
    ```
    【変更ポイント】
    ┌────────────────────────────────────────────────────────┐
    │  count=4  →  count=6                                  │
    │  ↑ 生成する行数を増やす                               │
    │                                                        │
    │  case文に新しい条件を追加                              │
    │  id==5, "ストレージE"                                  │
    │  id==6, "ファイアウォールF"                            │
    └────────────────────────────────────────────────────────┘
    ```
    
    ### ステップ2: CSSに新しい図形スタイルを追加
    
    ```css
    /* ===== 既存の図形 ===== */
    .rectangle { ... }
    .circle { ... }
    .bar { ... }
    .triangle { ... }
    
    /* ===== ★新規追加★ 星形 ===== */
    .star {
      /* 星形はSVGまたはクリップパスで作成 */
      width: 60px;
      height: 60px;
      background-color: #808080 ;
      clip-path: polygon(
        50% 0%,      /* 上の頂点 */
        61% 35%,     /* 右上内側 */
        98% 35%,     /* 右上外側 */
        68% 57%,     /* 右下内側 */
        79% 91%,     /* 右下外側 */
        50% 70%,     /* 下の内側 */
        21% 91%,     /* 左下外側 */
        32% 57%,     /* 左下内側 */
        2% 35%,      /* 左上外側 */
        39% 35%      /* 左上内側 */
      );
      display: flex;
      justify-content: center;
      align-items: center;
      transition: background-color 0.3s ease;
    }
    
    /* ===== ★新規追加★ ひし形 ===== */
    .diamond {
      width: 50px;
      height: 50px;
      background-color: #808080 ;
      /* 45度回転させて正方形をひし形に */
      transform: rotate(45deg);
      display: flex;
      justify-content: center;
      align-items: center;
      transition: background-color 0.3s ease;
    }
    
    /* ひし形の中のテキストを正位置に */
    .diamond span {
      transform: rotate(-45deg);
      color: white;
      font-size: 10px;
      font-weight: bold;
    }
    
    /* ===== ★新規追加★ 六角形 ===== */
    .hexagon {
      width: 60px;
      height: 52px;
      background-color: #808080 ;
      clip-path: polygon(
        25% 0%,    /* 左上 */
        75% 0%,    /* 右上 */
        100% 50%,  /* 右 */
        75% 100%,  /* 右下 */
        25% 100%,  /* 左下 */
        0% 50%     /* 左 */
      );
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-size: 11px;
      font-weight: bold;
      transition: background-color 0.3s ease;
    }
    ```
    
    ### ステップ3: JavaScriptの図形配列を更新
    
    ```javascript
    // 【変更前】4種類
    var shapeTypes = ['rectangle', 'circle', 'bar', 'triangle'];
    
    // 【変更後】6種類に増やす
    var shapeTypes = ['rectangle', 'circle', 'bar', 'triangle', 'star', 'diamond'];
    //                                                          ↑追加  ↑追加
    ```
    
    ```javascript
    // 図形生成部分も追加
    if (shapeType === 'rectangle') {
      shapeHtml = '<div class="rectangle ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'circle') {
      shapeHtml = '<div class="circle ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'bar') {
      shapeHtml = '<div class="bar ' + statusClass + '">' + status + '</div>';
    } else if (shapeType === 'triangle') {
      shapeHtml = '<div class="triangle ' + statusClass + '"></div>';
    }
    // ★追加★
    else if (shapeType === 'star') {
      shapeHtml = '<div class="star ' + statusClass + '"></div>';
    }
    // ★追加★
    else if (shapeType === 'diamond') {
      shapeHtml = '<div class="diamond ' + statusClass + '"><span>' + status + '</span></div>';
    }
    ```
    
    ---
    
    # 🔷 Part 2: 図形の位置変更
    
    ## 2-1. Flexboxで配置を変更
    
    ### 左寄せ(デフォルト)
    
    ```css
    .shapes-container {
      display: flex;
      justify-content: flex-start;  /* ← 左寄せ */
      gap: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │ ■  ●  ▬  ▼                            │
    │                                         │
    └─────────────────────────────────────────┘
    ```
    
    ### 中央寄せ
    
    ```css
    .shapes-container {
      display: flex;
      justify-content: center;  /* ← 中央寄せ */
      gap: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │          ■  ●  ▬  ▼                   │
    │                                         │
    └─────────────────────────────────────────┘
    ```
    
    ### 右寄せ
    
    ```css
    .shapes-container {
      display: flex;
      justify-content: flex-end;  /* ← 右寄せ */
      gap: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │                            ■  ●  ▬  ▼ │
    │                                         │
    └─────────────────────────────────────────┘
    ```
    
    ### 均等配置
    
    ```css
    .shapes-container {
      display: flex;
      justify-content: space-between;  /* ← 両端に配置、間を均等 */
      gap: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │ ■          ●          ▬          ▼   │
    │                                         │
    └─────────────────────────────────────────┘
    ```
    
    ### 均等配置(周囲も含む)
    
    ```css
    .shapes-container {
      display: flex;
      justify-content: space-around;  /* ← 周囲も含めて均等 */
      gap: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │    ■       ●       ▬       ▼         │
    │                                         │
    └─────────────────────────────────────────┘
    ```
    
    ---
    
    ## 2-2. CSS Gridで格子状に配置
    
    ### 3列のグリッド
    
    ```css
    .shapes-container {
      display: grid;
      grid-template-columns: repeat(3, 1fr);  /* 3列 */
      gap: 20px;
      padding: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │    ■         ●         ▬              │
    │    A         B         C               │
    │                                         │
    │    ▼         ★         ◆              │
    │    D         E         F               │
    └─────────────────────────────────────────┘
    ```
    
    ### 4列のグリッド
    
    ```css
    .shapes-container {
      display: grid;
      grid-template-columns: repeat(4, 1fr);  /* 4列 */
      gap: 15px;
      padding: 20px;
    }
    ```
    ```
    ┌─────────────────────────────────────────┐
    │   ■      ●      ▬      ▼             │
    │   A      B      C      D              │
    │                                         │
    │   ★      ◆                            │
    │   E      F                             │
    └─────────────────────────────────────────┘
    ```
    
    ### 列幅を固定
    
    ```css
    .shapes-container {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
      /* ↑ 最小120px、最大1frで自動的に列数を調整 */
      gap: 20px;
    }
    ```
    
    ---
    
    ## 2-3. 絶対位置指定(完全自由配置)
    
    ### 基本設定
    
    ```css
    /* コンテナを基準点にする */
    .shapes-container {
      position: relative;  /* ★重要★ 子要素の基準点になる */
      width: 100%;
      height: 400px;       /* 高さを固定 */
      background-color: #f5f5f5 ;
      border: 1px solid #ddd ;
    }
    
    /* 各図形アイテムを絶対位置指定 */
    .shape-item {
      position: absolute;  /* ★重要★ 親要素を基準に配置 */
    }
    ```
    
    ### 個別の位置指定(CSS)
    
    ```css
    /* 1番目の図形:左上 */
    .shape-item:nth-child(1) {
      top: 20px;
      left: 30px;
    }
    
    /* 2番目の図形:右上 */
    .shape-item:nth-child(2) {
      top: 20px;
      right: 30px;
    }
    
    /* 3番目の図形:中央 */
    .shape-item:nth-child(3) {
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);  /* 中心に配置 */
    }
    
    /* 4番目の図形:左下 */
    .shape-item:nth-child(4) {
      bottom: 20px;
      left: 30px;
    }
    ```
    
    ```
    ┌─────────────────────────────────────────┐
    │ ■                                  ●   │
    │ A                                  B   │
    │                                         │
    │               ▬                        │
    │               C                        │
    │                                         │
    │ ▼                                      │
    │ D                                      │
    └─────────────────────────────────────────┘
    ```
    
    ### JavaScriptで動的に位置指定
    
    ```javascript
    // データに位置情報を含める方法
    | makeresults count=4
    | streamstats count as id
    | eval device_name = case(id==1,"A", id==2,"B", id==3,"C", id==4,"D")
    | eval status = case(id==1,"正常", id==2,"警告", id==3,"異常", id==4,"停止")
    | eval status_code = case(status=="正常",0, status=="警告",1, status=="異常",2, status=="停止",3)
    | eval pos_top = case(id==1,"20px", id==2,"20px", id==3,"150px", id==4,"150px")
    | eval pos_left = case(id==1,"30px", id==2,"250px", id==3,"30px", id==4,"250px")
    | table device_name, status, status_code, pos_top, pos_left
    ```
    
    ```javascript
    // JavaScript側で位置を適用
    var topIndex = fields.indexOf('pos_top');
    var leftIndex = fields.indexOf('pos_left');
    
    var posTop = row[topIndex];
    var posLeft = row[leftIndex];
    
    // スタイル属性で位置を指定
    var itemHtml = 
      '<div class="shape-item" style="top: ' + posTop + '; left: ' + posLeft + ';">' +
        shapeHtml +
        '<div class="device-label">' + deviceName + '</div>' +
      '</div>';
    ```
    
    ---
    
    # 🔷 Part 3: 色の変更方法
    
    ## 3-1. 新しいステータス色を追加
    
    ### CSS定義
    
    ```css
    /* ===== 既存のステータス色 ===== */
    .status-normal  { background-color: #28a745 ; }  /* 緑:正常 */
    .status-warning { background-color: #ffc107 ; }  /* 黄:警告 */
    .status-error   { background-color: #dc3545 ; }  /* 赤:異常 */
    .status-stop    { background-color: #6c757d ; }  /* 灰:停止 */
    
    /* ===== ★新規追加★ ===== */
    .status-info    { background-color: #17a2b8 ; }  /* 水色:情報 */
    .status-success { background-color: #20c997 ; }  /* ティール:成功 */
    .status-danger  { background-color: #e83e8c ; }  /* ピンク:危険 */
    .status-dark    { background-color: #343a40 ; }  /* 濃灰:不明 */
    .status-primary { background-color: #007bff ; }  /* 青:メイン */
    .status-secondary { background-color: #6610f2 ; } /* 紫:サブ */
    ```
    
    ### SPL側でステータスコードを追加
    
    ```spl
    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3,
        status=="情報", 4,      /* ★追加★ */
        status=="成功", 5,      /* ★追加★ */
        status=="危険", 6,      /* ★追加★ */
        status=="不明", 7       /* ★追加★ */
      )
    ```
    
    ### JavaScript側でswitch文を追加
    
    ```javascript
    var statusClass = '';
    switch(statusCode) {
      case 0: statusClass = 'status-normal'; break;
      case 1: statusClass = 'status-warning'; break;
      case 2: statusClass = 'status-error'; break;
      case 3: statusClass = 'status-stop'; break;
      // ★追加★
      case 4: statusClass = 'status-info'; break;
      case 5: statusClass = 'status-success'; break;
      case 6: statusClass = 'status-danger'; break;
      case 7: statusClass = 'status-dark'; break;
      default: statusClass = 'status-stop';
    }
    ```
    
    ---
    
    ## 3-2. 数値範囲で色を変える
    
    ### SPLで数値に応じたステータスを設定
    
    ```spl
    | makeresults count=5
    | streamstats count as id
    | eval value = case(id==1, 95, id==2, 75, id==3, 50, id==4, 25, id==5, 10)
    | eval status = case(
        value >= 80, "正常",      /* 80以上:緑 */
        value >= 60, "注意",      /* 60-79:黄緑 */
        value >= 40, "警告",      /* 40-59:黄 */
        value >= 20, "異常",      /* 20-39:オレンジ */
        value < 20, "危険"        /* 20未満:赤 */
      )
    | eval status_code = case(
        status=="正常", 0,
        status=="注意", 1,
        status=="警告", 2,
        status=="異常", 3,
        status=="危険", 4
      )
    | table id, value, status, status_code
    ```
    
    ### 対応するCSS
    
    ```css
    .status-0 { background-color: #28a745 ; }  /* 緑:80以上 */
    .status-1 { background-color: #7cb342 ; }  /* 黄緑:60-79 */
    .status-2 { background-color: #ffc107 ; }  /* 黄:40-59 */
    .status-3 { background-color: #ff9800 ; }  /* オレンジ:20-39 */
    .status-4 { background-color: #dc3545 ; }  /* 赤:20未満 */
    ```
    
    ---
    
    ## 3-3. グラデーション色
    
    ```css
    /* グラデーション背景 */
    .status-gradient-good {
      background: linear-gradient(135deg, #28a745  0%, #20c997  100%);
    }
    
    .status-gradient-warning {
      background: linear-gradient(135deg, #ffc107  0%, #ff9800  100%);
    }
    
    .status-gradient-bad {
      background: linear-gradient(135deg, #dc3545  0%, #c82333  100%);
    }
    ```
    
    ---
    
    ## 3-4. 点滅アニメーション(異常時)
    
    ```css
    /* 点滅アニメーション定義 */
    @keyframes blink {
      0%   { opacity: 1; }
      50%  { opacity: 0.3; }
      100% { opacity: 1; }
    }
    
    /* 異常ステータスに点滅を適用 */
    .status-error {
      background-color: #dc3545 ;
      animation: blink 1s ease-in-out infinite;
      /* ↑ 1秒間隔で無限に点滅 */
    }
    
    /* 危険ステータスは高速点滅 */
    .status-danger {
      background-color: #e83e8c ;
      animation: blink 0.5s ease-in-out infinite;
      /* ↑ 0.5秒間隔で高速点滅 */
    }
    ```
    
    ---
    
    # 🔷 Part 4: サイズ変更
    
    ## 4-1. 図形サイズの変更
    
    ### 小サイズ
    
    ```css
    .shape-small {
      transform: scale(0.7);  /* 70%に縮小 */
    }
    
    /* または個別に指定 */
    .rectangle-small {
      width: 70px;   /* 100px → 70px */
      height: 42px;  /* 60px → 42px */
    }
    
    .circle-small {
      width: 50px;   /* 70px → 50px */
      height: 50px;
    }
    ```
    
    ### 大サイズ
    
    ```css
    .shape-large {
      transform: scale(1.5);  /* 150%に拡大 */
    }
    
    /* または個別に指定 */
    .rectangle-large {
      width: 150px;  /* 100px → 150px */
      height: 90px;  /* 60px → 90px */
    }
    
    .circle-large {
      width: 100px;  /* 70px → 100px */
      height: 100px;
    }
    ```
    
    ### SPLでサイズを指定
    
    ```spl
    | eval size_class = case(
        status=="正常", "shape-small",
        status=="警告", "",              /* 通常サイズ */
        status=="異常", "shape-large",   /* 異常は大きく目立たせる */
        status=="停止", "shape-small"
      )
    | table device_name, status, status_code, size_class
    ```
    
    ### JavaScript側でサイズクラスを適用
    
    ```javascript
    var sizeIndex = fields.indexOf('size_class');
    var sizeClass = row[sizeIndex] || '';  // nullの場合は空文字
    
    // 図形にサイズクラスを追加
    shapeHtml = '<div class="rectangle ' + statusClass + ' ' + sizeClass + '">' + status + '</div>';
    ```
    
    ---
    
    # 🔷 Part 5: 図形のグループ化
    
    ## 5-1. カテゴリ別にグループ化
    
    ### SPLでカテゴリを追加
    
    ```spl
    | makeresults count=6
    | streamstats count as id
    | eval device_name = case(
        id==1, "Webサーバー1",
        id==2, "Webサーバー2",
        id==3, "DBサーバー1",
        id==4, "DBサーバー2",
        id==5, "ネットワーク1",
        id==6, "ネットワーク2"
      )
    | eval category = case(
        id<=2, "Web層",
        id<=4, "DB層",
        id<=6, "NW層"
      )
    | eval status = case(id==1,"正常", id==2,"警告", id==3,"正常", id==4,"異常", id==5,"正常", id==6,"停止")
    | eval status_code = case(status=="正常",0, status=="警告",1, status=="異常",2, status=="停止",3)
    | table category, device_name, status, status_code
    ```
    
    ### CSS:グループスタイル
    
    ```css
    /* グループコンテナ */
    .category-group {
      border: 2px solid #ddd ;
      border-radius: 10px;
      padding: 15px;
      margin-bottom: 20px;
      background-color: #f9f9f9 ;
    }
    
    /* グループタイトル */
    .category-title {
      font-size: 16px;
      font-weight: bold;
      color: #333 ;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 2px solid #007bff ;
    }
    
    /* グループ内の図形コンテナ */
    .category-shapes {
      display: flex;
      gap: 15px;
      flex-wrap: wrap;
    }
    ```
    
    ### JavaScript:グループ別に描画
    
    ```javascript
    results.on("data", function() {
      if (results.hasData()) {
        var rows = results.data().rows;
        var fields = results.data().fields;
        
        var container = document.getElementById('shapesContainer');
        container.innerHTML = '';
        
        // カテゴリごとにデータをグループ化
        var groups = {};
        
        rows.forEach(function(row) {
          var categoryIndex = fields.indexOf('category');
          var category = row[categoryIndex];
          
          // グループが存在しなければ作成
          if (!groups[category]) {
            groups[category] = [];
          }
          
          // グループにデータを追加
          groups[category].push(row);
        });
        
        // グループごとにHTMLを生成
        Object.keys(groups).forEach(function(category) {
          // グループコンテナ開始
          var groupHtml = '<div class="category-group">';
          groupHtml += '<div class="category-title">' + category + '</div>';
          groupHtml += '<div class="category-shapes">';
          
          // グループ内の各図形を生成
          groups[category].forEach(function(row, index) {
            var deviceIndex = fields.indexOf('device_name');
            var statusIndex = fields.indexOf('status');
            var codeIndex = fields.indexOf('status_code');
            
            var deviceName = row[deviceIndex];
            var status = row[statusIndex];
            var statusCode = parseInt(row[codeIndex]);
            
            var statusClass = '';
            switch(statusCode) {
              case 0: statusClass = 'status-normal'; break;
              case 1: statusClass = 'status-warning'; break;
              case 2: statusClass = 'status-error'; break;
              case 3: statusClass = 'status-stop'; break;
            }
            
            // 図形HTML
            groupHtml += '<div class="shape-item">';
            groupHtml += '<div class="circle ' + statusClass + '">' + status + '</div>';
            groupHtml += '<div class="device-label">' + deviceName + '</div>';
            groupHtml += '</div>';
          });
          
          groupHtml += '</div>';  // category-shapes終了
          groupHtml += '</div>';  // category-group終了
          
          container.innerHTML += groupHtml;
        });
      }
    });
    ```
    
    ### 表示結果
    
    ```
    ┌─────────────────────────────────────────────────────┐
    │  Web層                                              │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 警告  │                           │
    │  │  Web1     Web2    │                            │
    │  └────────────────────┘                            │
    ├─────────────────────────────────────────────────────┤
    │  DB層                                               │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 異常  │                           │
    │  │  DB1      DB2     │                            │
    │  └────────────────────┘                            │
    ├─────────────────────────────────────────────────────┤
    │  NW層                                               │
    │  ─────────────────────────────────                  │
    │  │  ● 正常    ● 停止  │                           │
    │  │  NW1      NW2     │                            │
    │  └────────────────────┘                            │
    └─────────────────────────────────────────────────────┘
    ```
    
    ---
    
    # 🔷 Part 6: 既存コードの修正チェックリスト
    
    ## 6-1. 図形を追加するときのチェックリスト
    
    ```
    □ 1. SPL修正
      □ makeresults count の数を増やした
      □ eval の case文に新しいデータを追加した
      
    □ 2. CSS修正
      □ 新しい図形のクラスを定義した
      □ 新しいステータス色を定義した(必要な場合)
      □ 三角形など特殊図形の場合、border-colorも定義した
      
    □ 3. JavaScript修正
      □ shapeTypes配列に新しい図形タイプを追加した
      □ if-else文に新しい図形の生成コードを追加した
      □ switch文に新しいステータスコードを追加した(必要な場合)
    ```
    
    ## 6-2. よくある修正パターン
    
    ### パターン1: 図形の色だけ変えたい
    
    ```
    修正箇所: CSSのみ
    
    .status-normal { background-color: #28a745 ; }
             ↓ 変更
    .status-normal { background-color: #00ff00 ; }  /* より明るい緑に */
    ```
    
    ### パターン2: 図形のサイズだけ変えたい
    
    ```
    修正箇所: CSSのみ
    
    .rectangle {
      width: 100px;   →  width: 150px;
      height: 60px;   →  height: 90px;
    }
    ```
    
    ### パターン3: 図形の配置を変えたい
    
    ```
    修正箇所: CSSのみ
    
    .shapes-container {
      justify-content: flex-start;  →  justify-content: center;
    }
    ```
    
    ### パターン4: 新しいデバイスを追加したい
    
    ```
    修正箇所: SPLのみ
    
    count=4  →  count=5
    case文に id==5 の条件を追加
    ```
    
    ### パターン5: 新しい図形タイプを追加したい
    
    ```
    修正箇所: CSS + JavaScript
    
    1. CSSに .newShape { ... } を追加
    2. JSの shapeTypes に 'newShape' を追加
    3. JSの if-else に newShape の分岐を追加
    ```
    
    ---
    
    # 🔷 Part 7: 完全版テンプレート(カスタマイズ用)
    
    ## 7-1. フル機能版コード
    
    ```xml
    <dashboard>
      <label>カスタマイズ可能図形ダッシュボード</label>
      
      <!-- ========== データ取得サーチ ========== -->
      <search id="mainSearch">
        <query>
          | makeresults count=8
          | streamstats count as id
          
          | eval device_name = case(
              id==1, "Webサーバー1",
              id==2, "Webサーバー2",
              id==3, "APサーバー1",
              id==4, "APサーバー2",
              id==5, "DBサーバー1",
              id==6, "DBサーバー2",
              id==7, "FW1",
              id==8, "FW2"
            )
          
          | eval category = case(
              id&lt;=2, "Web層",
              id&lt;=4, "AP層",
              id&lt;=6, "DB層",
              id&lt;=8, "NW層"
            )
          
          | eval status = case(
              id==1, "正常",
              id==2, "正常",
              id==3, "警告",
              id==4, "正常",
              id==5, "異常",
              id==6, "正常",
              id==7, "停止",
              id==8, "正常"
            )
          
          | eval status_code = case(
              status=="正常", 0,
              status=="警告", 1,
              status=="異常", 2,
              status=="停止", 3
            )
          
          | eval shape_type = case(
              id&lt;=2, "circle",
              id&lt;=4, "rectangle",
              id&lt;=6, "hexagon",
              id&lt;=8, "diamond"
            )
          
          | eval size_class = case(
              status=="異常", "shape-large",
              status=="停止", "shape-large",
              1==1, ""
            )
          
          | table category, device_name, status, status_code, shape_type, size_class
        </query>
        <earliest>-24h@h</earliest>
        <latest>now</latest>
      </search>
      
      <row>
        <panel>
          <title>システム監視ダッシュボード</title>
          <html>
            <style>
              /* ========== メインコンテナ ========== */
              .main-container {
                padding: 20px;
                background-color: #f8f9fa ;
              }
              
              /* ========== グループスタイル ========== */
              .category-group {
                border: 2px solid #dee2e6 ;
                border-radius: 12px;
                padding: 20px;
                margin-bottom: 25px;
                background-color: white;
                box-shadow: 0 2px 8px rgba(0,0,0,0.1);
              }
              
              .category-title {
                font-size: 18px;
                font-weight: bold;
                color: #495057 ;
                margin-bottom: 15px;
                padding-bottom: 10px;
                border-bottom: 3px solid #007bff ;
              }
              
              .category-shapes {
                display: flex;
                gap: 25px;
                flex-wrap: wrap;
                justify-content: flex-start;
              }
              
              /* ========== 図形アイテム共通 ========== */
              .shape-item {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 8px;
                padding: 10px;
                border-radius: 8px;
                transition: transform 0.2s ease, box-shadow 0.2s ease;
              }
              
              .shape-item:hover {
                transform: translateY(-5px);
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
              }
              
              /* ========== 四角形 ========== */
              .rectangle {
                width: 90px;
                height: 55px;
                background-color: #808080 ;
                border-radius: 6px;
                border: 2px solid #333 ;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 13px;
                transition: all 0.3s ease;
              }
              
              /* ========== 丸 ========== */
              .circle {
                width: 65px;
                height: 65px;
                background-color: #808080 ;
                border-radius: 50%;
                border: 2px solid #333 ;
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 12px;
                transition: all 0.3s ease;
              }
              
              /* ========== 六角形 ========== */
              .hexagon {
                width: 70px;
                height: 60px;
                background-color: #808080 ;
                clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
                display: flex;
                justify-content: center;
                align-items: center;
                color: white;
                font-weight: bold;
                font-size: 11px;
                transition: all 0.3s ease;
              }
              
              /* ========== ひし形 ========== */
              .diamond {
                width: 50px;
                height: 50px;
                background-color: #808080 ;
                transform: rotate(45deg);
                display: flex;
                justify-content: center;
                align-items: center;
                transition: all 0.3s ease;
              }
              
              .diamond span {
                transform: rotate(-45deg);
                color: white;
                font-size: 10px;
                font-weight: bold;
              }
              
              /* ========== ラベル ========== */
              .device-label {
                font-size: 12px;
                color: #333 ;
                font-weight: bold;
                text-align: center;
              }
              
              .status-label {
                font-size: 11px;
                color: #666 ;
              }
              
              /* ========== ステータス色 ========== */
              .status-normal  { background-color: #28a745  !important; }
              .status-warning { background-color: #ffc107  !important; color: #333  !important; }
              .status-error   { background-color: #dc3545  !important; }
              .status-stop    { background-color: #6c757d  !important; }
              
              /* ========== サイズ変更 ========== */
              .shape-large { transform: scale(1.3); }
              .shape-small { transform: scale(0.8); }
              
              /* ========== 点滅(異常・停止時) ========== */
              @keyframes pulse {
                0%   { transform: scale(1); opacity: 1; }
                50%  { transform: scale(1.05); opacity: 0.8; }
                100% { transform: scale(1); opacity: 1; }
              }
              
              .status-error, .status-stop {
                animation: pulse 1.5s ease-in-out infinite;
              }
              
              .shape-large.status-error,
              .shape-large.status-stop {
                animation: pulse 1s ease-in-out infinite;
              }
            </style>
            
            <div class="main-container" id="mainContainer">
              <!-- JavaScriptで生成 -->
            </div>
            
            <script>
              require([
                'splunkjs/mvc',
                'splunkjs/mvc/simplexml/ready!'
              ], function(mvc) {
                
                var searchManager = mvc.Components.get("mainSearch");
                var results = searchManager.data("results");
                
                results.on("data", function() {
                  if (results.hasData()) {
                    var rows = results.data().rows;
                    var fields = results.data().fields;
                    
                    var container = document.getElementById('mainContainer');
                    container.innerHTML = '';
                    
                    // カテゴリごとにグループ化
                    var groups = {};
                    
                    rows.forEach(function(row) {
                      var catIndex = fields.indexOf('category');
                      var category = row[catIndex];
                      
                      if (!groups[category]) {
                        groups[category] = [];
                      }
                      groups[category].push(row);
                    });
                    
                    // グループごとに描画
                    Object.keys(groups).forEach(function(category) {
                      var groupHtml = '<div class="category-group">';
                      groupHtml += '<div class="category-title">' + category + '</div>';
                      groupHtml += '<div class="category-shapes">';
                      
                      groups[category].forEach(function(row) {
                        var deviceIndex = fields.indexOf('device_name');
                        var statusIndex = fields.indexOf('status');
                        var codeIndex = fields.indexOf('status_code');
                        var shapeIndex = fields.indexOf('shape_type');
                        var sizeIndex = fields.indexOf('size_class');
                        
                        var deviceName = row[deviceIndex];
                        var status = row[statusIndex];
                        var statusCode = parseInt(row[codeIndex]);
                        var shapeType = row[shapeIndex];
                        var sizeClass = row[sizeIndex] || '';
                        
                        var statusClass = '';
                        switch(statusCode) {
                          case 0: statusClass = 'status-normal'; break;
                          case 1: statusClass = 'status-warning'; break;
                          case 2: statusClass = 'status-error'; break;
                          case 3: statusClass = 'status-stop'; break;
                        }
                        
                        var shapeHtml = '';
                        var allClasses = statusClass + ' ' + sizeClass;
                        
                        if (shapeType === 'rectangle') {
                          shapeHtml = '<div class="rectangle ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'circle') {
                          shapeHtml = '<div class="circle ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'hexagon') {
                          shapeHtml = '<div class="hexagon ' + allClasses + '">' + status + '</div>';
                        } else if (shapeType === 'diamond') {
                          shapeHtml = '<div class="diamond ' + allClasses + '"><span>' + status + '</span></div>';
                        }
                        
                        groupHtml += '<div class="shape-item">';
                        groupHtml += shapeHtml;
                        groupHtml += '<div class="device-label">' + deviceName + '</div>';
                        groupHtml += '</div>';
                      });
                      
                      groupHtml += '</div></div>';
                      container.innerHTML += groupHtml;
                    });
                  }
                });
              });
            </script>
          </html>
        </panel>
      </row>
    </dashboard>
    ```
    
    ---
    
    # 🎯 クイックリファレンス
    
    ## 追加・修正早見表
    
    | やりたいこと | 修正箇所 | 修正内容 |
    |-------------|---------|---------|
    | データを増やす | SPL | `count=N` を増やす、`case`文に追加 |
    | 新しい図形を追加 | CSS + JS | クラス定義 + `shapeTypes`配列 + `if-else` |
    | 色を変える | CSS | `.status-xxx { background-color: #xxx ; }` |
    | サイズを変える | CSS | `width` / `height` または `transform: scale()` |
    | 配置を変える | CSS | `justify-content` / `grid-template-columns` |
    | グループ化する | SPL + JS | `category`フィールド追加 + グループ処理 |
    | 点滅させる | CSS | `@keyframes` + `animation` |
    | ホバー効果を追加 | CSS | `.shape-item:hover { ... }` |
    
    ---
    
    **これで既存ダッシュボードへの図形追加・修正・編集が自由自在です!🎉**

    ~新人エンジニア向け・実務で使えるコード徹底解説~

    # 🎓 Splunk 図形追加・編集コード 一行ずつ完全解説
    ## ~新人エンジニア向け・実務で使えるコード徹底解説~
    
    ---
    
    ## 📚 この記事の目的
    
    | 項目 | 内容 |
    |------|------|
    | 対象者 | Splunk初心者・新人エンジニア |
    | 目標 | コードの意味を完全理解し、実務で修正・追加できるようになる |
    | 構成 | SPL → CSS → HTML → JavaScript の順に解説 |
    
    ---
    
    # 🔷 Part 1: SPL(データ取得)完全解説
    
    ## 1-1. SPL全体コード
    
    ```spl
    | makeresults count=8
    | streamstats count as id
    
    | eval device_name = case(
        id==1, "Webサーバー1",
        id==2, "Webサーバー2",
        id==3, "APサーバー1",
        id==4, "APサーバー2",
        id==5, "DBサーバー1",
        id==6, "DBサーバー2",
        id==7, "FW1",
        id==8, "FW2"
      )
    
    | eval category = case(
        id<=2, "Web層",
        id<=4, "AP層",
        id<=6, "DB層",
        id<=8, "NW層"
      )
    
    | eval status = case(
        id==1, "正常",
        id==2, "正常",
        id==3, "警告",
        id==4, "正常",
        id==5, "異常",
        id==6, "正常",
        id==7, "停止",
        id==8, "正常"
      )
    
    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3
      )
    
    | eval shape_type = case(
        id<=2, "circle",
        id<=4, "rectangle",
        id<=6, "hexagon",
        id<=8, "diamond"
      )
    
    | eval size_class = case(
        status=="異常", "shape-large",
        status=="停止", "shape-large",
        1==1, ""
      )
    
    | table category, device_name, status, status_code, shape_type, size_class
    ```
    
    ---
    
    ## 1-2. SPL一行ずつ解説
    
    ### データ生成部分
    
    ```spl
    | makeresults count=8
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【コマンド】makeresults                                         │
    │ 【役割】テスト用の空データを生成する                            │
    │ 【引数】count=8 → 8行のデータを作成                            │
    │                                                                 │
    │ 【実務での使い方】                                              │
    │ ・テスト時: makeresults で仮データを作成                        │
    │ ・本番時: index=your_index に置き換える                         │
    │                                                                 │
    │ 【修正例:デバイスを増やしたい場合】                            │
    │ count=8 → count=10  (10個に増やす)                           │
    │ count=8 → count=20  (20個に増やす)                           │
    │                                                                 │
    │ 【この時点のデータ】                                            │
    │ ┌──────────────────┐                                           │
    │ │ _time            │ ← 8行の空データ(時刻のみ)              │
    │ ├──────────────────┤                                           │
    │ │ 2024-01-01 00:00 │                                           │
    │ │ 2024-01-01 00:00 │                                           │
    │ │ ... (8行)        │                                           │
    │ └──────────────────┘                                           │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ```spl
    | streamstats count as id
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【コマンド】streamstats                                         │
    │ 【役割】各行に連番を振る                                        │
    │ 【引数】count as id → カウント結果を「id」フィールドに保存     │
    │                                                                 │
    │ 【stats vs streamstats の違い】                                 │
    │ ・stats: 全データを集計して1行にまとめる                        │
    │ ・streamstats: 各行ごとに累計を計算(行数は変わらない)         │
    │                                                                 │
    │ 【この時点のデータ】                                            │
    │ ┌──────────────────┬─────┐                                     │
    │ │ _time            │ id  │                                     │
    │ ├──────────────────┼─────┤                                     │
    │ │ 2024-01-01 00:00 │  1  │ ← 連番が振られた                    │
    │ │ 2024-01-01 00:00 │  2  │                                     │
    │ │ 2024-01-01 00:00 │  3  │                                     │
    │ │ ... (8行)        │ ... │                                     │
    │ └──────────────────┴─────┘                                     │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### デバイス名の設定
    
    ```spl
    | eval device_name = case(
        id==1, "Webサーバー1",
        id==2, "Webサーバー2",
        id==3, "APサーバー1",
        id==4, "APサーバー2",
        id==5, "DBサーバー1",
        id==6, "DBサーバー2",
        id==7, "FW1",
        id==8, "FW2"
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【コマンド】eval                                                │
    │ 【役割】新しいフィールドを作成し、値を計算・設定する            │
    │ 【書式】eval フィールド名 = 値または関数                        │
    │                                                                 │
    │ 【関数】case(条件1, 値1, 条件2, 値2, ...)                       │
    │ 【役割】条件に応じた値を返す(if-else文のようなもの)           │
    │                                                                 │
    │ 【書き方のルール】                                              │
    │ case(                                                           │
    │   条件,    結果,     ← カンマで区切る                          │
    │   条件,    結果,                                                │
    │   ...                                                           │
    │ )                                                               │
    │                                                                 │
    │ 【演算子】                                                      │
    │ ==  等しい(比較)                                              │
    │ =   代入(evalの左辺で使う)                                    │
    │ !=  等しくない                                                  │
    │ <   より小さい                                                  │
    │ <=  以下                                                        │
    │ >   より大きい                                                  │
    │ >=  以上                                                        │
    │                                                                 │
    │ 【実務での修正例:デバイスを追加】                              │
    │ id==9, "ロードバランサー1",                                     │
    │ id==10, "ロードバランサー2"                                     │
    │ ↑ case文の最後に追加(カンマを忘れずに!)                      │
    │                                                                 │
    │ 【この時点のデータ】                                            │
    │ ┌─────┬─────────────────┐                                      │
    │ │ id  │ device_name     │                                      │
    │ ├─────┼─────────────────┤                                      │
    │ │  1  │ Webサーバー1    │                                      │
    │ │  2  │ Webサーバー2    │                                      │
    │ │  3  │ APサーバー1     │                                      │
    │ │ ... │ ...             │                                      │
    │ └─────┴─────────────────┘                                      │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### カテゴリの設定
    
    ```spl
    | eval category = case(
        id<=2, "Web層",
        id<=4, "AP層",
        id<=6, "DB層",
        id<=8, "NW層"
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】デバイスをグループ(カテゴリ)に分類する                │
    │                                                                 │
    │ 【条件の評価順序】★重要★                                      │
    │ case文は上から順に評価され、最初にマッチした条件の値を返す     │
    │                                                                 │
    │ id=1 の場合:                                                    │
    │   id<=2 (1<=2) → true → "Web層" を返す → 終了                  │
    │                                                                 │
    │ id=3 の場合:                                                    │
    │   id<=2 (3<=2) → false → 次へ                                  │
    │   id<=4 (3<=4) → true → "AP層" を返す → 終了                   │
    │                                                                 │
    │ 【カテゴリの対応表】                                            │
    │ id 1〜2 → Web層                                                │
    │ id 3〜4 → AP層                                                 │
    │ id 5〜6 → DB層                                                 │
    │ id 7〜8 → NW層                                                 │
    │                                                                 │
    │ 【実務での修正例:カテゴリを追加】                              │
    │ id<=10, "LB層"   ← 新しいカテゴリを追加                        │
    │                                                                 │
    │ 【注意】XMLでは < を &lt; と書く必要がある                      │
    │ id<=2  →  id&lt;=2                                              │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### ステータスの設定
    
    ```spl
    | eval status = case(
        id==1, "正常",
        id==2, "正常",
        id==3, "警告",
        id==4, "正常",
        id==5, "異常",
        id==6, "正常",
        id==7, "停止",
        id==8, "正常"
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】各デバイスのステータス(状態)を設定                    │
    │                                                                 │
    │ 【ステータスの種類】                                            │
    │ "正常" → 問題なく稼働中(緑色で表示)                          │
    │ "警告" → 注意が必要(黄色で表示)                              │
    │ "異常" → 問題発生中(赤色で表示)                              │
    │ "停止" → 稼働していない(灰色で表示)                          │
    │                                                                 │
    │ 【実務での本番データ取得例】                                    │
    │ テストではなく実際のデータを使う場合:                           │
    │                                                                 │
    │ index=monitoring sourcetype=server_status                       │
    │ | stats latest(status) as status by device_name                 │
    │ | eval status = case(                                           │
    │     status_code==0, "正常",                                     │
    │     status_code==1, "警告",                                     │
    │     status_code==2, "異常",                                     │
    │     1==1, "不明"                                                │
    │   )                                                             │
    │                                                                 │
    │ 【1==1 の意味】                                                 │
    │ 「常に true」= else(その他すべて)の役割                       │
    │ どの条件にも当てはまらない場合のデフォルト値を設定             │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### ステータスコードの設定
    
    ```spl
    | eval status_code = case(
        status=="正常", 0,
        status=="警告", 1,
        status=="異常", 2,
        status=="停止", 3
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】ステータス文字列を数値コードに変換                      │
    │                                                                 │
    │ 【なぜ数値コードが必要?】                                      │
    │ 1. JavaScriptのswitch文で使いやすい                             │
    │ 2. 文字列より数値の方が比較が高速                               │
    │ 3. ソートや計算がしやすい                                       │
    │                                                                 │
    │ 【対応表】                                                      │
    │ ┌─────────┬─────────────┬────────────────┐                     │
    │ │ status  │ status_code │ 後で使う色     │                     │
    │ ├─────────┼─────────────┼────────────────┤                     │
    │ │ 正常    │      0      │ 緑 (#28a745)   │                     │
    │ │ 警告    │      1      │ 黄 (#ffc107)   │                     │
    │ │ 異常    │      2      │ 赤 (#dc3545)   │                     │
    │ │ 停止    │      3      │ 灰 (#6c757d)   │                     │
    │ └─────────┴─────────────┴────────────────┘                     │
    │                                                                 │
    │ 【実務での修正例:新しいステータスを追加】                      │
    │ status=="情報", 4,                                              │
    │ status=="メンテナンス", 5                                       │
    │ ↑ JavaScriptのswitch文にも対応するcaseを追加すること           │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### 図形タイプの設定
    
    ```spl
    | eval shape_type = case(
        id<=2, "circle",
        id<=4, "rectangle",
        id<=6, "hexagon",
        id<=8, "diamond"
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】各デバイスに表示する図形の種類を指定                    │
    │                                                                 │
    │ 【図形の種類と見た目】                                          │
    │ "circle"    → ● 丸                                             │
    │ "rectangle" → ■ 四角形                                         │
    │ "hexagon"   → ⬡ 六角形                                         │
    │ "diamond"   → ◆ ひし形                                         │
    │                                                                 │
    │ 【カテゴリと図形の対応】                                        │
    │ Web層(id 1〜2) → 丸                                          │
    │ AP層(id 3〜4)  → 四角形                                      │
    │ DB層(id 5〜6)  → 六角形                                      │
    │ NW層(id 7〜8)  → ひし形                                      │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ ・カテゴリごとに図形を変える(現在の方式)                      │
    │ ・ステータスごとに図形を変える:                                 │
    │   | eval shape_type = case(                                     │
    │       status=="正常", "circle",                                 │
    │       status=="警告", "triangle",                               │
    │       status=="異常", "rectangle",                              │
    │       1==1, "diamond"                                           │
    │     )                                                           │
    │                                                                 │
    │ 【新しい図形を追加する場合】                                    │
    │ 1. SPLに図形タイプを追加                                        │
    │ 2. CSSに .newShape { } を追加                                   │
    │ 3. JavaScriptの if-else に分岐を追加                            │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### サイズクラスの設定
    
    ```spl
    | eval size_class = case(
        status=="異常", "shape-large",
        status=="停止", "shape-large",
        1==1, ""
      )
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】ステータスに応じて図形のサイズを変える                  │
    │                                                                 │
    │ 【設定の意味】                                                  │
    │ 異常 → "shape-large" → 大きく表示(目立たせる)               │
    │ 停止 → "shape-large" → 大きく表示(目立たせる)               │
    │ その他 → "" → 通常サイズ                                       │
    │                                                                 │
    │ 【1==1 の意味】                                                 │
    │ 「1は1に等しい」= 常にtrue                                      │
    │ = else(上のどの条件にも当てはまらない場合)                    │
    │ = デフォルト値を設定するイディオム(慣用句)                    │
    │                                                                 │
    │ 【"" (空文字)の意味】                                         │
    │ CSSクラスを追加しない = 通常のスタイルのまま                    │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ ・警告も少し大きくしたい場合:                                   │
    │   status=="警告", "shape-medium",                               │
    │                                                                 │
    │ ・正常は小さくしたい場合:                                       │
    │   status=="正常", "shape-small",                                │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ### 出力フィールドの指定
    
    ```spl
    | table category, device_name, status, status_code, shape_type, size_class
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【コマンド】table                                               │
    │ 【役割】最終的に出力するフィールドを指定し、表形式で表示        │
    │                                                                 │
    │ 【指定したフィールド】                                          │
    │ category    → グループ名(Web層、AP層など)                    │
    │ device_name → デバイス名                                       │
    │ status      → ステータス文字列(正常、警告など)               │
    │ status_code → ステータス数値(0, 1, 2, 3)                     │
    │ shape_type  → 図形の種類(circle, rectangleなど)              │
    │ size_class  → サイズクラス(shape-large または空)             │
    │                                                                 │
    │ 【最終出力データ】                                              │
    │ ┌──────────┬───────────────┬────────┬─────┬───────────┬────────┐│
    │ │ category │ device_name   │ status │code │shape_type │size    ││
    │ ├──────────┼───────────────┼────────┼─────┼───────────┼────────┤│
    │ │ Web層    │ Webサーバー1  │ 正常   │  0  │ circle    │        ││
    │ │ Web層    │ Webサーバー2  │ 正常   │  0  │ circle    │        ││
    │ │ AP層     │ APサーバー1   │ 警告   │  1  │ rectangle │        ││
    │ │ AP層     │ APサーバー2   │ 正常   │  0  │ rectangle │        ││
    │ │ DB層     │ DBサーバー1   │ 異常   │  2  │ hexagon   │ large  ││
    │ │ DB層     │ DBサーバー2   │ 正常   │  0  │ hexagon   │        ││
    │ │ NW層     │ FW1           │ 停止   │  3  │ diamond   │ large  ││
    │ │ NW層     │ FW2           │ 正常   │  0  │ diamond   │        ││
    │ └──────────┴───────────────┴────────┴─────┴───────────┴────────┘│
    │                                                                 │
    │ 【実務ポイント】                                                │
    │ ・tableで指定しないフィールドはJavaScriptに渡されない           │
    │ ・必要なフィールドは必ずtableに含める                           │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ---
    
    # 🔷 Part 2: CSS(スタイル)完全解説
    
    ## 2-1. メインコンテナ
    
    ```css
    /* ========== メインコンテナ ========== */
    .main-container {
      padding: 20px;
      background-color: #f8f9fa ;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.main-container                                     │
    │ 【役割】ダッシュボード全体を包む最外側の箱                      │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ padding: 20px;                                                  │
    │ ├─ 意味: 内側の余白を20ピクセルに設定                          │
    │ ├─ 効果: コンテンツが枠にくっつかず、見やすくなる              │
    │ └─ 図解:                                                       │
    │    ┌─────────────────────────┐                                 │
    │    │ ←20px→ コンテンツ ←20px→│                                │
    │    │    ↑                    │                                 │
    │    │  20px                   │                                 │
    │    │    ↓                    │                                 │
    │    └─────────────────────────┘                                 │
    │                                                                 │
    │ background-color: #f8f9fa ;                                      │
    │ ├─ 意味: 背景色を薄いグレーに設定                              │
    │ ├─ #f8f9fa  = Bootstrapのlight色(目に優しい薄グレー)          │
    │ └─ 効果: 白背景よりコンテンツが引き立つ                        │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ padding: 30px;        → もっと余白を広げる                     │
    │ background-color: #fff ; → 白背景に変更                         │
    │ background-color: #343a40 ; → ダークモードに                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-2. グループスタイル
    
    ```css
    /* ========== グループスタイル ========== */
    .category-group {
      border: 2px solid #dee2e6 ;
      border-radius: 12px;
      padding: 20px;
      margin-bottom: 25px;
      background-color: white;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.category-group                                     │
    │ 【役割】各カテゴリ(Web層、AP層など)を囲む箱                   │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ border: 2px solid #dee2e6 ;                                      │
    │ ├─ 2px = 枠線の太さ(2ピクセル)                               │
    │ ├─ solid = 実線(他: dashed=破線, dotted=点線)                │
    │ └─ #dee2e6  = 枠線の色(薄いグレー)                            │
    │                                                                 │
    │ border-radius: 12px;                                            │
    │ ├─ 意味: 角を12ピクセル丸くする                                │
    │ └─ 図解:                                                       │
    │    0px:        12px:                                            │
    │    ┌────┐      ╭────╮  ← 角が丸い                             │
    │    │    │      │    │                                          │
    │    └────┘      ╰────╯                                          │
    │                                                                 │
    │ padding: 20px;                                                  │
    │ └─ 意味: 箱の内側の余白(コンテンツと枠線の間)                │
    │                                                                 │
    │ margin-bottom: 25px;                                            │
    │ ├─ 意味: 箱の下側の外余白                                      │
    │ └─ 効果: 次のグループとの間隔を確保                            │
    │    ┌────────────┐                                              │
    │    │  Web層     │                                              │
    │    └────────────┘                                              │
    │         ↓ 25px ← この間隔                                      │
    │    ┌────────────┐                                              │
    │    │  AP層      │                                              │
    │    └────────────┘                                              │
    │                                                                 │
    │ background-color: white;                                        │
    │ └─ 意味: 背景を白に(メインコンテナの薄グレーとのコントラスト) │
    │                                                                 │
    │ box-shadow: 0 2px 8px rgba(0,0,0,0.1);                          │
    │ ├─ 0 = 横方向のずれ(なし)                                    │
    │ ├─ 2px = 縦方向のずれ(下に2px)                               │
    │ ├─ 8px = ぼかしの範囲                                          │
    │ ├─ rgba(0,0,0,0.1) = 影の色(黒10%透明度)                     │
    │ └─ 効果: カードが浮いているように見える                        │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ border: 3px solid #007bff ; → 青い太枠に                        │
    │ border-radius: 0px;        → 角張ったデザインに                │
    │ box-shadow: none;          → 影をなくす                        │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ```css
    .category-title {
      font-size: 18px;
      font-weight: bold;
      color: #495057 ;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 3px solid #007bff ;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.category-title                                     │
    │ 【役割】カテゴリ名(「Web層」など)の見出しスタイル             │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ font-size: 18px;                                                │
    │ └─ 意味: 文字サイズを18ピクセルに(デフォルト16pxより大きめ)  │
    │                                                                 │
    │ font-weight: bold;                                              │
    │ └─ 意味: 太字にする                                            │
    │                                                                 │
    │ color: #495057 ;                                                 │
    │ └─ 意味: 文字色(濃いめのグレー)                              │
    │                                                                 │
    │ margin-bottom: 15px;                                            │
    │ └─ 意味: タイトルの下の外余白(図形との間隔)                  │
    │                                                                 │
    │ padding-bottom: 10px;                                           │
    │ └─ 意味: タイトルの下の内余白(線との間隔)                    │
    │                                                                 │
    │ border-bottom: 3px solid #007bff ;                               │
    │ ├─ 意味: 下線を引く                                            │
    │ ├─ 3px = 線の太さ                                              │
    │ ├─ solid = 実線                                                │
    │ ├─ #007bff  = 青色                                              │
    │ └─ 図解:                                                       │
    │    Web層                                                        │
    │    ━━━━━━━━━━ ← 青い下線                                      │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ border-bottom: 3px solid #28a745 ; → 緑の下線に                 │
    │ font-size: 24px;                  → より大きな見出しに         │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ```css
    .category-shapes {
      display: flex;
      gap: 25px;
      flex-wrap: wrap;
      justify-content: flex-start;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.category-shapes                                    │
    │ 【役割】グループ内の図形を並べるコンテナ                        │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ display: flex;                                                  │
    │ ├─ 意味: Flexboxレイアウトを有効にする                         │
    │ └─ 効果: 子要素が横並びになる                                  │
    │                                                                 │
    │ gap: 25px;                                                      │
    │ ├─ 意味: 子要素間の余白を25ピクセルに                          │
    │ └─ 図解:                                                       │
    │    ● ←25px→ ● ←25px→ ●                                       │
    │                                                                 │
    │ flex-wrap: wrap;                                                │
    │ ├─ 意味: 子要素がはみ出したら折り返す                          │
    │ └─ 図解:                                                       │
    │    nowrap:              wrap:                                   │
    │    ● ● ● ● ● →はみ出す   ● ● ● ●                            │
    │                         ● ● ← 折り返す                        │
    │                                                                 │
    │ justify-content: flex-start;                                    │
    │ ├─ 意味: 子要素を左寄せにする                                  │
    │ └─ 他の値:                                                     │
    │    flex-start  : 左寄せ    │ ● ● ●           │                │
    │    center      : 中央寄せ  │    ● ● ●        │                │
    │    flex-end    : 右寄せ    │          ● ● ● │                │
    │    space-between: 両端揃え │ ●     ●     ●   │                │
    │    space-around : 均等配置 │   ●   ●   ●     │                │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ justify-content: center;     → 中央揃えに                      │
    │ gap: 40px;                   → もっと間隔を広げる              │
    │ flex-direction: column;      → 縦並びに変更                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-3. 図形アイテム共通スタイル
    
    ```css
    /* ========== 図形アイテム共通 ========== */
    .shape-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 8px;
      padding: 10px;
      border-radius: 8px;
      transition: transform 0.2s ease, box-shadow 0.2s ease;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.shape-item                                         │
    │ 【役割】1つの図形+ラベルをまとめるコンテナ                      │
    │                                                                 │
    │ 【構造イメージ】                                                │
    │ ┌─────────────────┐                                            │
    │ │   .shape-item   │                                            │
    │ │  ┌───────────┐  │                                            │
    │ │  │   ● 図形  │  │                                            │
    │ │  └───────────┘  │                                            │
    │ │    デバイス名   │                                            │
    │ └─────────────────┘                                            │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ display: flex;                                                  │
    │ └─ Flexboxを有効化                                             │
    │                                                                 │
    │ flex-direction: column;                                         │
    │ ├─ 意味: 子要素を縦に並べる                                    │
    │ └─ 図解:                                                       │
    │    row(横):     column(縦):                                 │
    │    ● ラベル       ●                                            │
    │                   ラベル                                        │
    │                                                                 │
    │ align-items: center;                                            │
    │ ├─ 意味: 子要素を中央揃え(横方向)                            │
    │ └─ 図解:                                                       │
    │    flex-start:    center:       flex-end:                       │
    │    │●           │    ●    │           ●│                      │
    │    │ラベル      │  ラベル  │      ラベル│                      │
    │                                                                 │
    │ gap: 8px;                                                       │
    │ └─ 図形とラベルの間の余白                                      │
    │                                                                 │
    │ padding: 10px;                                                  │
    │ └─ 内側の余白(ホバー効果用の余白確保)                        │
    │                                                                 │
    │ border-radius: 8px;                                             │
    │ └─ 角を丸くする(ホバー時の背景用)                            │
    │                                                                 │
    │ transition: transform 0.2s ease, box-shadow 0.2s ease;          │
    │ ├─ 意味: プロパティ変化時にアニメーションする                  │
    │ ├─ transform 0.2s = 変形を0.2秒かけて行う                      │
    │ ├─ box-shadow 0.2s = 影の変化を0.2秒かけて行う                 │
    │ ├─ ease = 最初と最後がゆっくり、中間が速い                     │
    │ └─ 効果: ホバー時に滑らかに変化する                            │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ```css
    .shape-item:hover {
      transform: translateY(-5px);
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.shape-item:hover                                   │
    │ 【役割】マウスを乗せた時のスタイル                              │
    │                                                                 │
    │ 【:hover とは】                                                 │
    │ 疑似クラス(pseudo-class)の一つ                                │
    │ マウスカーソルが要素の上にある時だけ適用される                  │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ transform: translateY(-5px);                                    │
    │ ├─ 意味: Y軸方向(縦)に-5px移動 = 5px上に移動                 │
    │ └─ 図解:                                                       │
    │    通常:        ホバー時:                                       │
    │    ────         ────                                            │
    │    │●│         │●│ ← 5px上に浮く                            │
    │    ────         ────                                            │
    │                   ↑                                             │
    │                  5px                                            │
    │                                                                 │
    │ box-shadow: 0 4px 12px rgba(0,0,0,0.15);                        │
    │ ├─ 0 = 横方向のずれ(なし)                                    │
    │ ├─ 4px = 縦方向のずれ(下に4px)                               │
    │ ├─ 12px = ぼかしの範囲(より広く)                             │
    │ ├─ rgba(0,0,0,0.15) = 影の色(黒15%透明度)                    │
    │ └─ 効果: より大きな影 = より浮いて見える                       │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ transform: scale(1.1);  → 1.1倍に拡大                          │
    │ transform: rotate(5deg); → 5度傾ける                           │
    │ background-color: #f0f0f0 ; → 背景色を変える                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-4. 四角形スタイル
    
    ```css
    /* ========== 四角形 ========== */
    .rectangle {
      width: 90px;
      height: 55px;
      background-color: #808080 ;
      border-radius: 6px;
      border: 2px solid #333 ;
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      font-size: 13px;
      transition: all 0.3s ease;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.rectangle                                          │
    │ 【役割】四角形図形のスタイル                                    │
    │                                                                 │
    │ 【プロパティ解説】                                              │
    │                                                                 │
    │ width: 90px;                                                    │
    │ height: 55px;                                                   │
    │ ├─ 意味: 横90px、縦55pxの四角形                                │
    │ └─ 図解:                                                       │
    │    ┌───────────────────────────┐                               │
    │    │                           │ ← 90px                        │
    │    │           正常            │ ← 55px                        │
    │    │                           │                               │
    │    └───────────────────────────┘                               │
    │                                                                 │
    │ background-color: #808080 ;                                      │
    │ └─ 意味: 初期背景色(グレー)。後でステータス色に上書きされる  │
    │                                                                 │
    │ border-radius: 6px;                                             │
    │ └─ 意味: 角を6px丸める(少しだけ丸い四角形)                   │
    │                                                                 │
    │ border: 2px solid #333 ;                                         │
    │ └─ 意味: 2pxの黒い枠線                                         │
    │                                                                 │
    │ display: flex;                                                  │
    │ justify-content: center;                                        │
    │ align-items: center;                                            │
    │ └─ 意味: 中のテキストを上下左右中央に配置                      │
    │                                                                 │
    │ color: white;                                                   │
    │ └─ 意味: テキストの色を白に                                    │
    │                                                                 │
    │ font-weight: bold;                                              │
    │ └─ 意味: テキストを太字に                                      │
    │                                                                 │
    │ font-size: 13px;                                                │
    │ └─ 意味: テキストサイズを13pxに                                │
    │                                                                 │
    │ transition: all 0.3s ease;                                      │
    │ ├─ all = すべてのプロパティ変化にアニメーション適用            │
    │ ├─ 0.3s = 0.3秒かけて変化                                      │
    │ └─ 効果: 色が変わる時に滑らかに変化                            │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ width: 120px; height: 70px;  → より大きな四角形               │
    │ border-radius: 0px;          → 角張った四角形                  │
    │ border: none;                → 枠線なし                        │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-5. 丸スタイル
    
    ```css
    /* ========== 丸 ========== */
    .circle {
      width: 65px;
      height: 65px;
      background-color: #808080 ;
      border-radius: 50%;
      border: 2px solid #333 ;
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      font-size: 12px;
      transition: all 0.3s ease;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.circle                                             │
    │ 【役割】丸(円)図形のスタイル                                  │
    │                                                                 │
    │ 【丸を作るポイント】★重要★                                    │
    │                                                                 │
    │ width: 65px;                                                    │
    │ height: 65px;                                                   │
    │ border-radius: 50%;                                             │
    │                                                                 │
    │ ┌─ 円を作る条件 ─────────────────────────────────────────────┐ │
    │ │ 1. width と height が同じ値 → 正方形になる                 │ │
    │ │ 2. border-radius: 50% → 正方形の角が最大限丸まる → 円に   │ │
    │ └────────────────────────────────────────────────────────────┘ │
    │                                                                 │
    │ 【図解】                                                        │
    │ border-radius: 0%     border-radius: 25%    border-radius: 50%  │
    │      ┌────┐                ╭────╮               (●)            │
    │      │    │                │    │               完全な円        │
    │      │    │                │    │                               │
    │      └────┘                ╰────╯                               │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ width: 100px; height: 100px; → より大きな円                    │
    │ border: 4px solid #007bff ;   → 青い太枠の円                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-6. 六角形スタイル
    
    ```css
    /* ========== 六角形 ========== */
    .hexagon {
      width: 70px;
      height: 60px;
      background-color: #808080 ;
      clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: bold;
      font-size: 11px;
      transition: all 0.3s ease;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.hexagon                                            │
    │ 【役割】六角形図形のスタイル                                    │
    │                                                                 │
    │ 【clip-pathとは】★新しい技術★                                 │
    │ 要素を指定した形状で切り抜く(クリッピング)CSSプロパティ      │
    │                                                                 │
    │ clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
    │                                                                 │
    │ 【polygon の座標システム】                                      │
    │ ・左上が (0%, 0%)                                               │
    │ ・右下が (100%, 100%)                                           │
    │ ・時計回りに頂点を指定                                          │
    │                                                                 │
    │ 【六角形の頂点】                                                │
    │                                                                 │
    │         (25%,0%)  (75%,0%)                                      │
    │             ●━━━━━━━●                                          │
    │            ╱         ╲                                          │
    │  (0%,50%) ●           ● (100%,50%)                             │
    │            ╲         ╱                                          │
    │             ●━━━━━━━●                                          │
    │       (25%,100%) (75%,100%)                                     │
    │                                                                 │
    │ 【頂点の順番】                                                  │
    │ 1. (25%, 0%)   = 左上                                          │
    │ 2. (75%, 0%)   = 右上                                          │
    │ 3. (100%, 50%) = 右中央                                        │
    │ 4. (75%, 100%) = 右下                                          │
    │ 5. (25%, 100%) = 左下                                          │
    │ 6. (0%, 50%)   = 左中央                                        │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ ・五角形にする:                                                 │
    │   clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
    │                                                                 │
    │ ・八角形にする:                                                 │
    │   clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%,        │
    │                      70% 100%, 30% 100%, 0% 70%, 0% 30%);       │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-7. ひし形スタイル
    
    ```css
    /* ========== ひし形 ========== */
    .diamond {
      width: 50px;
      height: 50px;
      background-color: #808080 ;
      transform: rotate(45deg);
      display: flex;
      justify-content: center;
      align-items: center;
      transition: all 0.3s ease;
    }
    
    .diamond span {
      transform: rotate(-45deg);
      color: white;
      font-size: 10px;
      font-weight: bold;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.diamond                                            │
    │ 【役割】ひし形図形のスタイル                                    │
    │                                                                 │
    │ 【ひし形の作り方】★重要★                                      │
    │ 正方形を45度回転させてひし形にする!                            │
    │                                                                 │
    │ transform: rotate(45deg);                                       │
    │ ├─ 意味: 要素を45度回転させる                                  │
    │ └─ 図解:                                                       │
    │    回転前:        回転後:                                       │
    │    ┌────┐           ◇                                          │
    │    │    │    →    ◇   ◇                                       │
    │    └────┘           ◇                                          │
    │                                                                 │
    │ 【問題点と解決】                                                │
    │ 問題: 外側を回転すると、中のテキストも回転してしまう           │
    │                                                                 │
    │ そのままだと:    修正後:                                        │
    │      ◇              ◇                                          │
    │    ◇正◇    →    ◇正常◇  ← テキストは水平                    │
    │      ◇常            ◇                                          │
    │      ↑                                                          │
    │    読めない!                                                   │
    │                                                                 │
    │ 【解決方法】.diamond span                                       │
    │                                                                 │
    │ .diamond span {                                                 │
    │   transform: rotate(-45deg);                                    │
    │ }                                                               │
    │ ├─ 意味: テキストを-45度回転(元に戻す)                       │
    │ └─ 45deg + (-45deg) = 0deg → 水平になる!                      │
    │                                                                 │
    │ 【HTML構造】                                                    │
    │ <div class="diamond">                                           │
    │   <span>正常</span>   ← spanで囲む必要がある                   │
    │ </div>                                                          │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ transform: rotate(30deg);  → 30度傾けたひし形                  │
    │ width: 70px; height: 70px; → より大きなひし形                  │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-8. ラベルスタイル
    
    ```css
    /* ========== ラベル ========== */
    .device-label {
      font-size: 12px;
      color: #333 ;
      font-weight: bold;
      text-align: center;
    }
    
    .status-label {
      font-size: 11px;
      color: #666 ;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【セレクタ】.device-label / .status-label                       │
    │ 【役割】図形の下に表示するテキストラベルのスタイル              │
    │                                                                 │
    │ 【表示イメージ】                                                │
    │      ● 正常         ← 図形                                     │
    │    Webサーバー1     ← .device-label(デバイス名)              │
    │        正常         ← .status-label(ステータス)※今回は未使用│
    │                                                                 │
    │ 【.device-label】                                               │
    │ font-size: 12px;    → 少し小さめの文字                         │
    │ color: #333 ;        → 濃いグレー(読みやすい)                 │
    │ font-weight: bold;  → 太字                                     │
    │ text-align: center; → 中央揃え                                 │
    │                                                                 │
    │ 【.status-label】                                               │
    │ font-size: 11px;    → さらに小さい(サブ情報)                 │
    │ color: #666 ;        → 中間グレー(控えめ)                     │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ font-size: 14px;    → もっと大きな文字に                       │
    │ color: #007bff ;     → 青色の文字に                             │
    │ max-width: 100px;   → 最大幅を指定して折り返し制御             │
    │ overflow: hidden;   → はみ出した部分を隠す                     │
    │ text-overflow: ellipsis; → はみ出し部分を「...」で表示        │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-9. ステータス色
    
    ```css
    /* ========== ステータス色 ========== */
    .status-normal  { background-color: #28a745  !important; }
    .status-warning { background-color: #ffc107  !important; color: #333  !important; }
    .status-error   { background-color: #dc3545  !important; }
    .status-stop    { background-color: #6c757d  !important; }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】ステータスに応じた背景色を定義                          │
    │                                                                 │
    │ 【カラーコード一覧】                                            │
    │ ┌───────────────┬───────────┬────────┬─────────────────────┐   │
    │ │ クラス名      │ カラー    │ 色     │ 用途                │   │
    │ ├───────────────┼───────────┼────────┼─────────────────────┤   │
    │ │ status-normal │ #28a745    │ 緑     │ 正常(問題なし)    │   │
    │ │ status-warning│ #ffc107    │ 黄     │ 警告(注意が必要)  │   │
    │ │ status-error  │ #dc3545    │ 赤     │ 異常(問題発生)    │   │
    │ │ status-stop   │ #6c757d    │ 灰     │ 停止(稼働していない)│  │
    │ └───────────────┴───────────┴────────┴─────────────────────┘   │
    │                                                                 │
    │ 【!important とは】                                             │
    │ ├─ 意味: このスタイルを最優先で適用する                        │
    │ ├─ 理由: .rectangle や .circle の background-color より        │
    │ │        優先して適用したいため                                │
    │ └─ 注意: 乱用は避ける(デバッグが難しくなる)                  │
    │                                                                 │
    │ 【status-warning の color: #333 】                               │
    │ └─ 理由: 黄色背景に白文字は読みにくいため、黒文字にする        │
    │                                                                 │
    │ 【実務での修正例:新しいステータスを追加】                      │
    │ .status-info    { background-color: #17a2b8  !important; }       │
    │ .status-success { background-color: #20c997  !important; }       │
    │ .status-danger  { background-color: #e83e8c  !important; }       │
    │                                                                 │
    │ 【実務での修正例:色を変更】                                    │
    │ .status-normal { background-color: #00ff00  !important; }        │
    │                 ↑ より明るい緑に変更                            │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-10. サイズ変更
    
    ```css
    /* ========== サイズ変更 ========== */
    .shape-large { transform: scale(1.3); }
    .shape-small { transform: scale(0.8); }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】図形のサイズを変更するクラス                            │
    │                                                                 │
    │ 【transform: scale() とは】                                     │
    │ ├─ 意味: 要素を拡大・縮小する                                  │
    │ ├─ scale(1) = 100% = 元のサイズ                                │
    │ ├─ scale(1.3) = 130% = 1.3倍に拡大                             │
    │ └─ scale(0.8) = 80% = 0.8倍に縮小                              │
    │                                                                 │
    │ 【図解】                                                        │
    │ scale(0.8):     scale(1):      scale(1.3):                      │
    │     ●             ●               ●                             │
    │   (小さい)      (通常)         (大きい)                        │
    │                                                                 │
    │ 【scaleのメリット】                                             │
    │ ・width/heightを個別に変えなくていい                            │
    │ ・縦横比を維持したまま拡大縮小できる                            │
    │ ・アニメーションしやすい                                        │
    │                                                                 │
    │ 【実務での使い方】                                              │
    │ SPLで size_class フィールドに "shape-large" を設定              │
    │ → JavaScriptでクラスに追加                                      │
    │ → 自動的に図形が大きくなる                                      │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ .shape-xlarge { transform: scale(1.5); }  → 1.5倍              │
    │ .shape-medium { transform: scale(1.1); }  → 1.1倍              │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 2-11. 点滅アニメーション
    
    ```css
    /* ========== 点滅(異常・停止時) ========== */
    @keyframes pulse {
      0%   { transform: scale(1); opacity: 1; }
      50%  { transform: scale(1.05); opacity: 0.8; }
      100% { transform: scale(1); opacity: 1; }
    }
    
    .status-error, .status-stop {
      animation: pulse 1.5s ease-in-out infinite;
    }
    
    .shape-large.status-error,
    .shape-large.status-stop {
      animation: pulse 1s ease-in-out infinite;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【@keyframes とは】                                             │
    │ CSSアニメーションの動きを定義するルール                         │
    │                                                                 │
    │ @keyframes pulse {                                              │
    │   0%   { transform: scale(1); opacity: 1; }                     │
    │   50%  { transform: scale(1.05); opacity: 0.8; }                │
    │   100% { transform: scale(1); opacity: 1; }                     │
    │ }                                                               │
    │                                                                 │
    │ 【各キーフレームの意味】                                        │
    │ 0%(開始):                                                     │
    │   scale(1)    → 元のサイズ                                     │
    │   opacity: 1  → 完全に不透明                                   │
    │                                                                 │
    │ 50%(中間):                                                    │
    │   scale(1.05) → 1.05倍に少し拡大                               │
    │   opacity: 0.8 → 少し透明に(80%不透明)                       │
    │                                                                 │
    │ 100%(終了):                                                   │
    │   scale(1)    → 元のサイズに戻る                               │
    │   opacity: 1  → 完全に不透明に戻る                             │
    │                                                                 │
    │ 【図解:アニメーションの流れ】                                  │
    │ 0%        50%         100%      0%(繰り返し)                  │
    │  ●    →   ◯    →     ●    →   ◯    → ...                    │
    │ 通常     少し大きく   通常     少し大きく                       │
    │         少し薄く              少し薄く                         │
    │                                                                 │
    │ 【animation プロパティ】                                        │
    │ animation: pulse 1.5s ease-in-out infinite;                     │
    │            ↑     ↑    ↑           ↑                            │
    │            │     │    │           └─ 無限に繰り返す            │
    │            │     │    └─ 速度曲線(滑らかに)                  │
    │            │     └─ 1回のアニメーション時間(1.5秒)           │
    │            └─ アニメーション名(@keyframesで定義した名前)      │
    │                                                                 │
    │ 【セレクタの意味】                                              │
    │ .status-error, .status-stop                                     │
    │ └─ 異常と停止の両方にアニメーションを適用(カンマで複数指定)  │
    │                                                                 │
    │ .shape-large.status-error                                       │
    │ └─ 複合セレクタ: shape-large AND status-error の両方を持つ要素 │
    │ └─ 大きい図形で異常 → 1秒間隔(より速く)                      │
    │                                                                 │
    │ 【実務での修正例】                                              │
    │ ・もっと速く点滅:                                               │
    │   animation: pulse 0.5s ease-in-out infinite;                   │
    │                                                                 │
    │ ・点滅ではなく明滅(消える):                                   │
    │   @keyframes blink {                                            │
    │     0%   { opacity: 1; }                                        │
    │     50%  { opacity: 0; }  ← 完全に透明                         │
    │     100% { opacity: 1; }                                        │
    │   }                                                             │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ---
    
    # 🔷 Part 3: HTML構造の解説
    
    ```html
    <div class="main-container" id="mainContainer">
      <!-- JavaScriptで生成 -->
    </div>
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】JavaScriptが図形を追加する先のコンテナ                  │
    │                                                                 │
    │ <div                                                            │
    │   class="main-container"  ← CSSスタイルを適用                  │
    │   id="mainContainer"      ← JavaScriptから参照するためのID     │
    │ >                                                               │
    │                                                                 │
    │ 【classとidの違い】                                             │
    │ class:                                                          │
    │ ・複数の要素に同じクラスを付けられる                            │
    │ ・CSSでスタイルを適用するために使う                             │
    │ ・セレクタは .クラス名                                          │
    │                                                                 │
    │ id:                                                             │
    │ ・ページ内で一意(1つだけ)                                     │
    │ ・JavaScriptから getElementById() で取得するために使う          │
    │ ・セレクタは #id名                                               │
    │                                                                 │
    │ 【JavaScript実行後の構造】                                      │
    │ <div class="main-container" id="mainContainer">                 │
    │   <div class="category-group">                                  │
    │     <div class="category-title">Web層</div>                     │
    │     <div class="category-shapes">                               │
    │       <div class="shape-item">                                  │
    │         <div class="circle status-normal">正常</div>            │
    │         <div class="device-label">Webサーバー1</div>            │
    │       </div>                                                    │
    │       <div class="shape-item">...</div>                         │
    │     </div>                                                      │
    │   </div>                                                        │
    │   <div class="category-group">...</div>                         │
    │ </div>                                                          │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ---
    
    # 🔷 Part 4: JavaScript完全解説
    
    ## 4-1. モジュール読み込み
    
    ```javascript
    require([
      'splunkjs/mvc',
      'splunkjs/mvc/simplexml/ready!'
    ], function(mvc) {
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】Splunk用のモジュールを読み込んで使えるようにする        │
    │                                                                 │
    │ require([モジュール1, モジュール2, ...], function(引数) {       │
    │   // モジュールが読み込まれたら実行されるコード                 │
    │ });                                                             │
    │                                                                 │
    │ 【各モジュールの役割】                                          │
    │                                                                 │
    │ 'splunkjs/mvc'                                                  │
    │ ├─ SplunkのMVCフレームワーク                                   │
    │ └─ ダッシュボードのコンポーネントを操作するために必要          │
    │                                                                 │
    │ 'splunkjs/mvc/simplexml/ready!'                                 │
    │ ├─ ダッシュボードの準備完了を待つ                              │
    │ ├─ 末尾の「!」が「待つ」という意味                             │
    │ └─ これがないと、まだ読み込み中のコンポーネントにアクセスして │
    │    エラーになる可能性がある                                     │
    │                                                                 │
    │ function(mvc)                                                   │
    │ ├─ 読み込んだモジュールを「mvc」という名前で受け取る           │
    │ └─ この中でSplunkの機能を使える                                │
    │                                                                 │
    │ 【実務ポイント】                                                │
    │ このrequire文は定型文として覚えておく                           │
    │ 基本的にこの形で書けばOK                                        │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-2. サーチマネージャーと結果の取得
    
    ```javascript
    var searchManager = mvc.Components.get("mainSearch");
    var results = searchManager.data("results");
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【1行目】サーチマネージャーの取得                               │
    │                                                                 │
    │ var searchManager = mvc.Components.get("mainSearch");           │
    │     ↑              ↑                    ↑                       │
    │     │              │                    └─ XMLのsearch id      │
    │     │              └─ Splunkコンポーネントを名前で取得          │
    │     └─ 変数に保存                                              │
    │                                                                 │
    │ 【対応関係】                                                    │
    │ XML:  <search id="mainSearch">                                  │
    │                     ↑                                           │
    │ JS:   mvc.Components.get("mainSearch")                          │
    │                           ↑                                     │
    │ ★この名前が一致していないとエラーになる★                      │
    │                                                                 │
    │ 【2行目】結果データの取得                                       │
    │                                                                 │
    │ var results = searchManager.data("results");                    │
    │     ↑                        ↑                                  │
    │     │                        └─ 完了した結果を取得              │
    │     └─ 変数に保存                                              │
    │                                                                 │
    │ 【data()の引数】                                                │
    │ "results" → 完了した最終結果(通常はこれを使う)               │
    │ "preview" → 途中経過の結果(リアルタイム監視用)               │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-3. イベントリスナーの設定
    
    ```javascript
    results.on("data", function() {
      if (results.hasData()) {
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【results.on("data", function() {...})】                        │
    │                                                                 │
    │ 【役割】データが更新されたら自動的に実行される関数を登録        │
    │                                                                 │
    │ results.on("data", function() {                                 │
    │          ↑        ↑                                             │
    │          │        └─ データ更新時に実行する関数                │
    │          └─ イベント名("data" = データ更新)                  │
    │                                                                 │
    │ 【いつ実行される?】                                            │
    │ 1. ページ読み込み時(最初のサーチ完了時)                       │
    │ 2. 自動更新時(<refresh>タグがある場合)                        │
    │ 3. 手動でサーチを再実行した時                                   │
    │                                                                 │
    │ 【results.hasData()】                                           │
    │                                                                 │
    │ if (results.hasData()) {                                        │
    │     ↑                                                           │
    │     └─ 結果データがあるかチェック(true/false)                │
    │                                                                 │
    │ 【なぜチェックが必要?】                                        │
    │ ・サーチがエラーの場合、データがない                            │
    │ ・結果が0件の場合、データがない                                 │
    │ ・データがない状態で処理するとエラーになる                      │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-4. データ取得とコンテナ初期化
    
    ```javascript
    var rows = results.data().rows;
    var fields = results.data().fields;
    
    var container = document.getElementById('mainContainer');
    container.innerHTML = '';
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【results.data()】                                              │
    │ サーチ結果のデータオブジェクトを取得                            │
    │                                                                 │
    │ 返り値の構造:                                                   │
    │ {                                                               │
    │   rows: [                                                       │
    │     ["Web層", "Webサーバー1", "正常", "0", "circle", ""],       │
    │     ["Web層", "Webサーバー2", "正常", "0", "circle", ""],       │
    │     ...                                                         │
    │   ],                                                            │
    │   fields: ["category", "device_name", "status", "status_code",  │
    │            "shape_type", "size_class"]                          │
    │ }                                                               │
    │                                                                 │
    │ 【rows と fields の関係】                                       │
    │                                                                 │
    │ fields[0]="category"  fields[1]="device_name"  ...              │
    │        ↓                    ↓                                   │
    │ rows[0][0]="Web層"   rows[0][1]="Webサーバー1"  ...             │
    │ rows[1][0]="Web層"   rows[1][1]="Webサーバー2"  ...             │
    │                                                                 │
    │ 【document.getElementById()】                                   │
    │                                                                 │
    │ var container = document.getElementById('mainContainer');       │
    │                 ↑                         ↑                     │
    │                 │                         └─ HTML要素のid       │
    │                 └─ HTMLからID指定で要素を取得                  │
    │                                                                 │
    │ 【container.innerHTML = ''】                                    │
    │                                                                 │
    │ container.innerHTML = '';                                       │
    │           ↑            ↑                                        │
    │           │            └─ 空文字(中身を空にする)             │
    │           └─ 要素の中のHTMLを取得/設定するプロパティ           │
    │                                                                 │
    │ 【なぜ空にする?】                                              │
    │ データ更新のたびに図形を追加すると、前の図形が残ったまま       │
    │ 新しい図形が追加されてしまう。毎回リセットして再描画する。     │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-5. グループ化処理
    
    ```javascript
    // カテゴリごとにグループ化
    var groups = {};
    
    rows.forEach(function(row) {
      var catIndex = fields.indexOf('category');
      var category = row[catIndex];
      
      if (!groups[category]) {
        groups[category] = [];
      }
      groups[category].push(row);
    });
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【目的】データをカテゴリ(Web層、AP層など)ごとにまとめる       │
    │                                                                 │
    │ 【var groups = {}】                                             │
    │ 空のオブジェクト(連想配列)を作成                              │
    │ ここにカテゴリごとのデータを格納していく                        │
    │                                                                 │
    │ 【rows.forEach(function(row) {...})】                           │
    │ 配列の各要素に対して関数を実行(ループ処理)                    │
    │                                                                 │
    │ rows = [                                                        │
    │   ["Web層", "Webサーバー1", ...],  ← 1回目: row = この行       │
    │   ["Web層", "Webサーバー2", ...],  ← 2回目: row = この行       │
    │   ["AP層", "APサーバー1", ...],    ← 3回目: row = この行       │
    │   ...                                                           │
    │ ]                                                               │
    │                                                                 │
    │ 【fields.indexOf('category')】                                  │
    │ fieldsの中から'category'の位置を探す                            │
    │                                                                 │
    │ fields = ["category", "device_name", "status", ...]             │
    │              ↑                                                  │
    │           インデックス0                                         │
    │                                                                 │
    │ 【row[catIndex]】                                               │
    │ row = ["Web層", "Webサーバー1", ...]                            │
    │          ↑                                                      │
    │       row[0] = "Web層"                                          │
    │                                                                 │
    │ 【if (!groups[category])】                                      │
    │ そのカテゴリがまだgroupsに存在しないなら                        │
    │                                                                 │
    │ !groups["Web層"]                                                │
    │ ├─ groups["Web層"] が undefined(存在しない)の場合             │
    │ │  → !undefined → true → if文の中に入る                        │
    │ └─ groups["Web層"] がすでに存在する場合                         │
    │    → ![] → false → if文をスキップ                              │
    │                                                                 │
    │ 【groups[category] = []】                                       │
    │ そのカテゴリ用の空配列を作成                                    │
    │                                                                 │
    │ 【groups[category].push(row)】                                  │
    │ そのカテゴリの配列にデータ行を追加                              │
    │                                                                 │
    │ 【処理後のgroupsの中身】                                        │
    │ groups = {                                                      │
    │   "Web層": [                                                    │
    │     ["Web層", "Webサーバー1", ...],                             │
    │     ["Web層", "Webサーバー2", ...]                              │
    │   ],                                                            │
    │   "AP層": [                                                     │
    │     ["AP層", "APサーバー1", ...],                               │
    │     ["AP層", "APサーバー2", ...]                                │
    │   ],                                                            │
    │   ...                                                           │
    │ }                                                               │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-6. グループごとの描画処理
    
    ```javascript
    // グループごとに描画
    Object.keys(groups).forEach(function(category) {
      var groupHtml = '<div class="category-group">';
      groupHtml += '<div class="category-title">' + category + '</div>';
      groupHtml += '<div class="category-shapes">';
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【Object.keys(groups)】                                         │
    │ オブジェクトのキー(カテゴリ名)を配列として取得                │
    │                                                                 │
    │ groups = { "Web層": [...], "AP層": [...], "DB層": [...], ... }  │
    │                ↓                                                │
    │ Object.keys(groups) = ["Web層", "AP層", "DB層", "NW層"]         │
    │                                                                 │
    │ 【.forEach(function(category) {...})】                          │
    │ 各カテゴリ名に対してループ処理                                  │
    │                                                                 │
    │ 1回目: category = "Web層"                                       │
    │ 2回目: category = "AP層"                                        │
    │ 3回目: category = "DB層"                                        │
    │ 4回目: category = "NW層"                                        │
    │                                                                 │
    │ 【HTML文字列の組み立て】                                        │
    │                                                                 │
    │ var groupHtml = '<div class="category-group">';                 │
    │ ├─ グループ全体を囲むdivの開始タグ                             │
    │                                                                 │
    │ groupHtml += '<div class="category-title">' + category + '</div>';
    │ ├─ +=  → 既存の文字列に追加                                    │
    │ ├─ category = "Web層" の場合                                   │
    │ └─ 結果: '<div class="category-title">Web層</div>'             │
    │                                                                 │
    │ groupHtml += '<div class="category-shapes">';                   │
    │ └─ 図形を並べるコンテナの開始タグ                              │
    │                                                                 │
    │ 【この時点のgroupHtml】                                         │
    │ '<div class="category-group">' +                                │
    │ '<div class="category-title">Web層</div>' +                     │
    │ '<div class="category-shapes">'                                 │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-7. 各図形のデータ取得
    
    ```javascript
    groups[category].forEach(function(row) {
      var deviceIndex = fields.indexOf('device_name');
      var statusIndex = fields.indexOf('status');
      var codeIndex = fields.indexOf('status_code');
      var shapeIndex = fields.indexOf('shape_type');
      var sizeIndex = fields.indexOf('size_class');
      
      var deviceName = row[deviceIndex];
      var status = row[statusIndex];
      var statusCode = parseInt(row[codeIndex]);
      var shapeType = row[shapeIndex];
      var sizeClass = row[sizeIndex] || '';
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【groups[category].forEach(function(row) {...})】               │
    │ そのカテゴリに属する各デバイスデータをループ処理                │
    │                                                                 │
    │ category = "Web層" の場合:                                      │
    │ groups["Web層"] = [                                             │
    │   ["Web層", "Webサーバー1", "正常", "0", "circle", ""],         │
    │   ["Web層", "Webサーバー2", "正常", "0", "circle", ""]          │
    │ ]                                                               │
    │                                                                 │
    │ 1回目: row = ["Web層", "Webサーバー1", "正常", "0", "circle", ""]
    │ 2回目: row = ["Web層", "Webサーバー2", "正常", "0", "circle", ""]
    │                                                                 │
    │ 【fields.indexOf() で各フィールドの位置を取得】                 │
    │                                                                 │
    │ fields = ["category", "device_name", "status", "status_code",   │
    │           "shape_type", "size_class"]                           │
    │              0            1           2           3             │
    │              4            5                                     │
    │                                                                 │
    │ deviceIndex = 1  (device_nameの位置)                            │
    │ statusIndex = 2  (statusの位置)                                 │
    │ codeIndex   = 3  (status_codeの位置)                            │
    │ shapeIndex  = 4  (shape_typeの位置)                             │
    │ sizeIndex   = 5  (size_classの位置)                             │
    │                                                                 │
    │ 【値の取得】                                                    │
    │ row = ["Web層", "Webサーバー1", "正常", "0", "circle", ""]      │
    │                                                                 │
    │ deviceName = row[1] = "Webサーバー1"                            │
    │ status     = row[2] = "正常"                                    │
    │ statusCode = parseInt(row[3]) = 0  ← 文字列"0"を数値0に変換    │
    │ shapeType  = row[4] = "circle"                                  │
    │ sizeClass  = row[5] || '' = ""  ← 空の場合は空文字             │
    │                                                                 │
    │ 【parseInt() が必要な理由】                                     │
    │ Splunkのサーチ結果は全て文字列として返される                    │
    │ switch文で数値と比較するために変換が必要                        │
    │                                                                 │
    │ "0" === 0  → false(型が違う)                                 │
    │  0  === 0  → true(正しく比較できる)                          │
    │                                                                 │
    │ 【|| '' の意味】                                                │
    │ row[sizeIndex] が null や undefined の場合、空文字''を使う     │
    │ これを「デフォルト値」という                                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-8. ステータスクラスの決定
    
    ```javascript
    var statusClass = '';
    switch(statusCode) {
      case 0: statusClass = 'status-normal'; break;
      case 1: statusClass = 'status-warning'; break;
      case 2: statusClass = 'status-error'; break;
      case 3: statusClass = 'status-stop'; break;
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【役割】ステータスコードに応じたCSSクラス名を決定               │
    │                                                                 │
    │ 【switch文の構造】                                              │
    │                                                                 │
    │ switch(statusCode) {     ← 評価する値                          │
    │   case 0:                ← statusCodeが0の場合                 │
    │     statusClass = 'status-normal';                              │
    │     break;               ← switch文を抜ける                    │
    │   case 1:                ← statusCodeが1の場合                 │
    │     statusClass = 'status-warning';                             │
    │     break;                                                      │
    │   ...                                                           │
    │ }                                                               │
    │                                                                 │
    │ 【対応表】                                                      │
    │ statusCode │ statusClass     │ CSSの色                         │
    │ ───────────┼─────────────────┼───────────────────────────────  │
    │     0      │ status-normal   │ 緑 (#28a745)                    │
    │     1      │ status-warning  │ 黄 (#ffc107)                    │
    │     2      │ status-error    │ 赤 (#dc3545)                    │
    │     3      │ status-stop     │ 灰 (#6c757d)                    │
    │                                                                 │
    │ 【break の重要性】★超重要★                                    │
    │                                                                 │
    │ breakがない場合(フォールスルー):                              │
    │ case 0: statusClass = 'status-normal';  ← 実行                 │
    │ case 1: statusClass = 'status-warning'; ← これも実行される!   │
    │ case 2: statusClass = 'status-error';   ← これも実行される!   │
    │ → 最終的にstatus-errorになってしまう                            │
    │                                                                 │
    │ breakがある場合(正しい動作):                                  │
    │ case 0: statusClass = 'status-normal'; break; ← ここで終了     │
    │                                                                 │
    │ 【実務での修正例:新しいステータスを追加】                      │
    │ case 4: statusClass = 'status-info'; break;                     │
    │ case 5: statusClass = 'status-success'; break;                  │
    │                                                                 │
    │ 【default の追加(推奨)】                                      │
    │ default: statusClass = 'status-stop'; break;                    │
    │ ↑ どのcaseにも当てはまらない場合のデフォルト値                 │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-9. 図形HTMLの生成
    
    ```javascript
    var shapeHtml = '';
    var allClasses = statusClass + ' ' + sizeClass;
    
    if (shapeType === 'rectangle') {
      shapeHtml = '<div class="rectangle ' + allClasses + '">' + status + '</div>';
    } else if (shapeType === 'circle') {
      shapeHtml = '<div class="circle ' + allClasses + '">' + status + '</div>';
    } else if (shapeType === 'hexagon') {
      shapeHtml = '<div class="hexagon ' + allClasses + '">' + status + '</div>';
    } else if (shapeType === 'diamond') {
      shapeHtml = '<div class="diamond ' + allClasses + '"><span>' + status + '</span></div>';
    }
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【目的】図形タイプに応じたHTML文字列を生成                      │
    │                                                                 │
    │ 【var allClasses = statusClass + ' ' + sizeClass】              │
    │                                                                 │
    │ 例: statusClass = "status-normal"                               │
    │     sizeClass = "" (空)                                       │
    │     → allClasses = "status-normal "                             │
    │                                                                 │
    │ 例: statusClass = "status-error"                                │
    │     sizeClass = "shape-large"                                   │
    │     → allClasses = "status-error shape-large"                   │
    │                                                                 │
    │ 【if-else による図形タイプ分岐】                                │
    │                                                                 │
    │ shapeType = 'rectangle' の場合:                                 │
    │ shapeHtml = '<div class="rectangle status-normal">正常</div>'   │
    │                                                                 │
    │ shapeType = 'circle' の場合:                                    │
    │ shapeHtml = '<div class="circle status-normal">正常</div>'      │
    │                                                                 │
    │ shapeType = 'hexagon' の場合:                                   │
    │ shapeHtml = '<div class="hexagon status-normal">正常</div>'     │
    │                                                                 │
    │ shapeType = 'diamond' の場合:                                   │
    │ shapeHtml = '<div class="diamond status-normal"><span>正常</span></div>'
    │             ↑ ひし形は<span>で囲む(テキスト回転のため)        │
    │                                                                 │
    │ 【複数クラスの適用】                                            │
    │ class="rectangle status-normal shape-large"                     │
    │        ↑          ↑             ↑                               │
    │        │          │             └─ サイズクラス(大きく)      │
    │        │          └─ 色クラス(緑)                            │
    │        └─ 図形クラス(四角形)                                 │
    │                                                                 │
    │ → 3つのCSSルールが全て適用される                                │
    │                                                                 │
    │ 【実務での修正例:新しい図形を追加】                            │
    │ else if (shapeType === 'star') {                                │
    │   shapeHtml = '<div class="star ' + allClasses + '"></div>';    │
    │ }                                                               │
    │ else if (shapeType === 'triangle') {                            │
    │   shapeHtml = '<div class="triangle ' + allClasses + '"></div>';│
    │ }                                                               │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-10. 図形アイテムの組み立てとコンテナへの追加
    
    ```javascript
    groupHtml += '<div class="shape-item">';
    groupHtml += shapeHtml;
    groupHtml += '<div class="device-label">' + deviceName + '</div>';
    groupHtml += '</div>';
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【目的】図形とラベルを組み合わせて1つのアイテムを作成           │
    │                                                                 │
    │ 【生成されるHTML構造】                                          │
    │                                                                 │
    │ <div class="shape-item">                  ← アイテム開始       │
    │   <div class="circle status-normal">正常</div>  ← 図形         │
    │   <div class="device-label">Webサーバー1</div>  ← ラベル       │
    │ </div>                                    ← アイテム終了       │
    │                                                                 │
    │ 【各行の意味】                                                  │
    │                                                                 │
    │ groupHtml += '<div class="shape-item">';                        │
    │ └─ shape-itemコンテナの開始タグを追加                          │
    │                                                                 │
    │ groupHtml += shapeHtml;                                         │
    │ └─ 前のステップで生成した図形HTMLを追加                        │
    │                                                                 │
    │ groupHtml += '<div class="device-label">' + deviceName + '</div>';
    │ └─ デバイス名ラベルを追加                                      │
    │    deviceName = "Webサーバー1" の場合                           │
    │    → '<div class="device-label">Webサーバー1</div>'             │
    │                                                                 │
    │ groupHtml += '</div>';                                          │
    │ └─ shape-itemコンテナの終了タグを追加                          │
    │                                                                 │
    │ 【表示イメージ】                                                │
    │ ┌─────────────────┐                                            │
    │ │  ● 正常         │  ← 図形                                   │
    │ │  Webサーバー1   │  ← ラベル                                 │
    │ └─────────────────┘                                            │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ## 4-11. グループ完成とコンテナへの追加
    
    ```javascript
      });  // groups[category].forEach 終了
      
      groupHtml += '</div></div>';
      container.innerHTML += groupHtml;
    });  // Object.keys(groups).forEach 終了
    ```
    ```
    ┌─────────────────────────────────────────────────────────────────┐
    │ 【groupHtml += '</div></div>'】                                 │
    │                                                                 │
    │ 1つ目の </div> → category-shapes の終了タグ                    │
    │ 2つ目の </div> → category-group の終了タグ                     │
    │                                                                 │
    │ 【完成したgroupHtmlの構造】                                     │
    │                                                                 │
    │ <div class="category-group">                 ← グループ開始    │
    │   <div class="category-title">Web層</div>    ← タイトル        │
    │   <div class="category-shapes">              ← 図形コンテナ開始│
    │     <div class="shape-item">                 ← アイテム1       │
    │       <div class="circle status-normal">正常</div>              │
    │       <div class="device-label">Webサーバー1</div>              │
    │     </div>                                                      │
    │     <div class="shape-item">                 ← アイテム2       │
    │       <div class="circle status-normal">正常</div>              │
    │       <div class="device-label">Webサーバー2</div>              │
    │     </div>                                                      │
    │   </div>                                     ← 図形コンテナ終了│
    │ </div>                                       ← グループ終了    │
    │                                                                 │
    │ 【container.innerHTML += groupHtml】                            │
    │                                                                 │
    │ += → 既存の内容に追加(上書きではない)                        │
    │                                                                 │
    │ 【処理の流れ】                                                  │
    │ 1. Web層のgroupHtmlを追加                                       │
    │ 2. AP層のgroupHtmlを追加                                        │
    │ 3. DB層のgroupHtmlを追加                                        │
    │ 4. NW層のgroupHtmlを追加                                        │
    │                                                                 │
    │ 【最終的なcontainer.innerHTML】                                 │
    │ <div class="category-group">...Web層...</div>                   │
    │ <div class="category-group">...AP層...</div>                    │
    │ <div class="category-group">...DB層...</div>                    │
    │ <div class="category-group">...NW層...</div>                    │
    └─────────────────────────────────────────────────────────────────┘
    ```
    
    ---
    
    # 🎯 Part 5: 実務での修正チートシート
    
    ## 5-1. よくある修正パターン早見表
    
    | やりたいこと | 修正箇所 | 修正内容 |
    |-------------|---------|---------|
    | デバイスを増やす | SPL | `count=N`増やす + `case`文追加 |
    | カテゴリを増やす | SPL | `category`の`case`文に追加 |
    | 新しいステータスを追加 | SPL + CSS + JS | 全部に追加が必要 |
    | 色を変える | CSS | `.status-xxx { background-color: }` |
    | サイズを変える | CSS | `width` / `height` または `scale()` |
    | 配置を変える | CSS | `justify-content` / `gap` |
    | 新しい図形を追加 | SPL + CSS + JS | 全部に追加が必要 |
    | 点滅を変える | CSS | `@keyframes` / `animation` |
    
    ## 5-2. 修正箇所の対応表
    
    ```
    【新しいステータス「メンテナンス」を追加する場合】
    
    ┌─ SPL ──────────────────────────────────────────────────────────┐
    │ | eval status_code = case(                                     │
    │     status=="正常", 0,                                         │
    │     status=="警告", 1,                                         │
    │     status=="異常", 2,                                         │
    │     status=="停止", 3,                                         │
    │     status=="メンテナンス", 4    ← 追加                        │
    │   )                                                            │
    └────────────────────────────────────────────────────────────────┘
    
    ┌─ CSS ──────────────────────────────────────────────────────────┐
    │ .status-maintenance {                                          │
    │   background-color: #17a2b8  !important;   ← 水色              │
    │ }                                                              │
    └────────────────────────────────────────────────────────────────┘
    
    ┌─ JavaScript ───────────────────────────────────────────────────┐
    │ switch(statusCode) {                                           │
    │   case 0: statusClass = 'status-normal'; break;                │
    │   case 1: statusClass = 'status-warning'; break;               │
    │   case 2: statusClass = 'status-error'; break;                 │
    │   case 3: statusClass = 'status-stop'; break;                  │
    │   case 4: statusClass = 'status-maintenance'; break; ← 追加   │
    │ }                                                              │
    └────────────────────────────────────────────────────────────────┘
    ```
    
    ---
    
    **お疲れ様でした!🎊**
    
    これで全コードの意味を完全に理解し、実務で修正・追加ができるようになりました!

    あなたへのおすすめ