0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

地域密着型の“その場で描く”生成AIダッシュボード

0
Posted at

“その場で描く”生成AIダッシュボードを作ったよ

- Chrome拡張 × Google Custom Search API × Gemini 2.5 -

 
まずは動画を見てください
その場で様々なデザインのダッシュボードが作成されてデジタルサイネージになっています

はじめに

このプロジェクトは、検索クエリで決めたテーマの最新情報を集め、プロンプトに合成し、Gemini 2.5(Flash/Pro)がその場で単一HTMLを出力する仕組みです。ダッシュボードは毎回新しい紙面が生まれるため、同じテーマでも視覚表現が変化します。サイネージ運用で起こりやすい「飽き」を構造的に避けられます。さらに、コードに地域名や特定分野の語を埋め込まず、{{search_query}} へ責務を集約することで、任意の地域とテーマへ横展開できます。
要点は、生成の自由度を保ちながら、画面崩れ・処理失敗・情報鮮度といった運用の落とし穴を設計段階で塞ぐことです。

 
全体像:

# 観点 要旨
1 価値 その場で1枚HTMLが誕生し、毎回ちがう紙面が出力される。
2 汎用性 コードから地域語を排し、{{search_query}} で指示する。
3 安定性 レート制御、指数バックオフ、ログ可視化で運用を止めない

生成AIは強力ですが、プロンプトに固定語が混ざると出力が偏ります。そこで地域語や具体名をコードから除外し、外部入力の変数に集約しました。これにより、同じロジックを保ったまま用途を横展開できます。運用では、失敗時に素早く復旧できることが価値になります。リトライ戦略とログ可視化は、**“生成がうまくいかなかった時の責任の所在”**を明確にしてくれます。

生成の自由と運用の確実性を両立する設計が、この仕組みを日常のサイネージに耐えるものへ押し上げます。


処理フロー

ユーザーに響く価値の流れを図で示し、内部の検証系(疎通テスト、ログ作成)はあえて省きました。見る人は「いつ、何が、どう生まれるのか」に集中できます。

image.png

図の核心は 「その場で1枚HTMLが誕生」 という一点です。ユーザーは“変わり続ける紙面”に価値を感じます。ここに至るまでの CSE → 合成 → 生成の流れを一筆書きで示すと、失敗時の復帰点も自然に見えてきます。再実行の矢印が必ずスタートに戻るため、更新サイクルが止まりません。

価値の瞬間を中心にフローを描くと、実装の判断が揺れなくなります。


画面構成

後述の開発プロンプトで開発物を作ると、以下のような画面が出てきます。
設定画面
image.png

ダッシュボード/デジタルサイネージ画面
image.png

ログ画面
image.png

生成AIに指示をしながら開発すると失敗も増えますので、ログや実機画面を見ながら支持をし直して修正をしてくのでログ画面があった方がいいです。


アーキテクチャ

MV3 の Service Worker(background.js)を中枢に据え、取得・生成・保存を集約しました。表示制御は dashboard.*、設定と疎通検証は options.*、取得と生成の履歴は logs.* に分割しています。生成HTMLは iframe(srcdoc) または Shadow DOM に描画し、拡張全体の CSS と衝突しないようにしました。

 
構成

# 階層 ファイル 役割
1 定義 manifest.json MV3 定義。host_permissionsbackground を宣言する。
2 中枢 background.js CSE 検索 → プロンプト合成 → Gemini 生成 → 保存。レート制御指数バックオフを実装する。
3 表示 dashboard.html/js パネル切替、全画面、即時更新、停止、進捗表示を担う。
4 設定 options.html/js/css API キー保存、AIza 形式検証、接続テスト(1/3→3/3)、クエリ編集、Ctrl+S 保存。
5 ログ logs.html/js API/生成ログを可視化し、head/tail100JSON エクスポートを提供する。
6 共通 style.css 拡張全体の UI。生成HTMLは iframe/Shadow DOM で分離する。

Service Worker に処理を集約したことで、ブラウザアクティビティに依存せず、安定して定期ジョブを走らせられます。生成HTMLを分離描画する方針は、動的に変わる紙面を安全に載せ替える前提になります。ここを共通 CSS と混在させると、思わぬ崩れが連鎖します。“混ぜない”ことが最大の安定化策です。

役割が一望できる構成に分割し、描画は分離する。これだけで運用時のトラブルの多くを未然に防げます。


プロンプト設計

プロンプトは HTML/CSS コンパイラとしての役割を固定し、崩れやすい要素を明示的に抑えます。変数は {{search_query}}{{windowsize}}{{current_time}}{{search_results}} の四つに限定し、地域語や特定分野の語をコードに混在させません。

設計方針

# 観点 指針
1 役割固定 “HTML/CSS だけを返す” と明記し、不要な説明を排する。
2 レイアウト 横型モニター、はみ出し禁止、左右ランダムのヒーロー、高コントラストを必須にする。
3 変数化 地域語やテーマ語は {{search_query}} に集約し、コードへ埋め込まない。
4 互換性 単一HTMLを要求し、外部リソースを禁止して移植性を担保する。

生成AIは自由度が高いほど画面が崩れやすくなります。そこで、自由度は紙面の造形に残し、出力形式と可読性に関わる部分は強く縛る方針を取りました。結果として、表現は毎回変わるのに、読みやすさは変わらない状態を作れます。

自由に変わる余地と、決して変えない約束を分けることが、生成物の信頼性を上げます。


開発をするためのプロンプト文(GPT-5なら9割完成します)

コードをすべて書くとすごい量になってしまいますので、今回は開発をするためのプロンプト文を公開します。GPT-5で作るとあっという間にChrome拡張が出来上がります✨

私はコードは一切書けませんが、めっちゃ国語が得意なのですごい指示しまくって開発をする好例です

//
# 相模原市南区AIダッシュボード 詳細要件仕様書(地域性の記載箇所は変更するか削除する)

## 🎯 プロジェクト概要
**目的**: 相模原市南区の地域住民向けに、AIが自動生成するリアルタイム情報ダッシュボードを提供
**対象**: デジタルサイネージ、公共スペース、地域拠点での情報表示
**技術**: Chrome拡張機能 + Google Custom Search API + Gemini AI API
**表示環境**: 横型モニター専用設計

## 📋 全体アーキテクチャ

### システム構成

Chrome拡張機能
├── background.js (サービスワーカー)
├── dashboard.html/js/css (表示UI)
├── options.html/js/css (設定UI)
├── manifest.json (拡張機能定義)
└── style.css 


*UI全体に適用するstyle.cssとの競合防止のためGemini生成部分を <iframe> または Shadow DOM 内で描画する


### データフロー
1. **設定管理**: options.js → Chrome Storage API
2. **データ取得**: background.js → Google Custom Search API
3. **AI処理**: background.js → Gemini API → HTMLパネル生成
4. **表示制御**: dashboard.js → パネル切り替え表示
5. **更新管理**: Chrome Alarms API → 定期実行

## 🔧 機能詳細仕様

### 1. バックグラウンド処理 (`background.js`)

#### 1.1 データ取得・AI処理エンジン
- **Google Custom Search API統合**
  - 検索クエリ: 設定画面で定義された複数クエリを順次実行
  - 検索制限: 過去1週間の結果、日本語コンテンツ優先
  - レート制限対策: 2秒間隔でのリクエスト実行
  - エラーハンドリング: タイムアウト180秒、詳細エラーログ出力
  - Google Custom Search API と Gemini API の両方に動的レート制限を入れる。
   例: APIから429 Too Many Requestsや503 Service Unavailableが返ったら次の実行間隔を倍にする。

- **Gemini AI統合**
  - **対応モデル**: 
    - `gemini-2.5-flash` (高速処理)※確認済みモデル。変更禁止。デフォルト選択
    - `gemini-2.5-pro` (高品質生成)※確認済みモデル。変更禁止
  - **並行処理**:  Geminiへのアクセスは最大3つ並行可能とする。
  - **リトライ機能**: タイムアウト180秒、リトライ最大3回、5秒→10秒→15秒の指数バックオフ
  - **出力制御**: HTMLのみ出力、横型モニターに最適化済み

#### 1.2 リアルタイム更新システム
- **段階的更新**: 1パネル完成毎に即座にStorage保存・UI更新
- **進捗表示**: 「処理中: 3/12 完了」形式でリアルタイム状況通知
- **自動更新**: Chrome Alarms APIで設定間隔での定期実行
- **手動更新**: ダッシュボードからの即時更新リクエスト対応(押したら進行中の処理を停止して、再度やり直しができるようにする)

#### 1.3 エラー管理・復旧
- **階層的エラーハンドリング**: 個別クエリエラー、システムエラーの分離
- **自動復旧**: 一時的API制限での自動リトライ実装
- **フォールバック表示**: エラー時の代替コンテンツ自動生成
- 複数パネルを並行取得する場合、AbortControllerで中断可能にし、ユーザーが「即時更新」押下時に全リクエストを停止。
- 進行中の処理を止めずに更新をかけるとUIのフリッカーやStorage不整合が発生する可能性があるため、更新キューを導入。
- ログファイルに以下を含める
  Resultコード
  検索した文字の先頭100文字、末尾100文字
  検索結果の先頭100文字、末尾100文字
  Geminiとのやり取り送信したメッセージの先頭100文字、末尾100文字
  Geminiとのやり取り受信したメッセージの先頭100文字、末尾100文字


### 2. ダッシュボード表示 (`dashboard.html`, `dashboard.js`, `style.css`)

#### 2.1 UI/UXデザイン
- **横型モニター最適化**
  - レスポンシブ対応: 複数解像度での適切表示
  - ウインドウサイズを取得し、プロンプト文に反映する。

- **モダンデザイン**
  - マテリアルデザイン風のカード表示
  - グラデーション効果、シャドウ、アニメーション
  - ダークモード対応準備済み
  - アクセシビリティ対応(フォーカス表示、キーボード操作)

#### 2.2 表示制御システム
- **パネル切り替え**
  - アニメーション付き切り替え(フェードイン/アウト)
  - 設定可能切り替え間隔(3-60秒)
  - 手動切り替え対応(矢印キー、ボタン操作)
  - 進捗表示(現在パネル/総パネル数)
  - 情報が取得できたページからデジタルサイネージの表示を開始する。
  - 情報取得やページ作成に失敗したダッシュボードは成功するまで切り替え表示の対象から外してスキップする
  - 全画面表示したときは、ヘッダやボタンはすべて非表示にしてコンテンツのみ表示する。
  - Geminiキュー表示、停止ボタン


- **リアルタイム機能**
  - 日時表示: 1秒間隔での更新
  - データ更新: Storage変更の即座反映
  - 状況通知: プログレスバー、ローディング表示
  


#### 2.4 キーボードショートカット
- **F11**: 全画面表示切り替え
- **F5/Ctrl+R**: 手動更新実行
- **左右矢印**: パネル手動切り替え
- **ESC**: 全画面解除

### 3. 設定管理画面 (`options.html`, `options.js`, `options.css`)

#### 3.1 API設定管理
- **セキュアな入力**
  - パスワード表示/非表示切り替え機能
  - APIキー形式バリデーション("AIza"プレフィックス確認)
  - リアルタイム入力検証とエラー表示

- **接続テスト機能**
  - Google Custom Search API疎通確認
  - Gemini API疎通確認と応答テスト(「おはよう」と聞いたら返事が来るか確認する)
  - 段階的テスト進捗表示(1/3, 2/3, 3/3)

#### 3.2 高度な設定オプション
- **Geminiモデル選択**
  - 利用可能モデル自動取得・表示機能
  - モデル別特徴説明(速度vs品質)
  - クォータ制限対応のモデル推奨

- **動作パラメータ調整**
  - 更新間隔: 5-180分(APIレート制限考慮)
  - パネル切替: 3-60秒
  - 検索クエリ: 20行以内(API制限対応)

#### 3.3 コンテンツ制御
- **検索クエリ管理**
  - デフォルト12種類のクエリテンプレート
  - 相模原市南区特化の情報源設定
  - 1行1クエリ形式での直感的編集

- **AIプロンプト最適化**
  - 横型モニター最適化プロンプト
  - 文字はみ出し厳禁指示の明記
  - 地域住民向け情報優先指示

#### 3.4 UI/UX設計
- **モダンフォームデザイン**
  - カテゴリ別セクション分割(🔧API設定、⚙️動作設定)
  - アニメーション効果(フェードイン、ホバー効果)
  - 統一されたカラースキーム

- **ユーザビリティ向上**
  - テキストエリア自動リサイズ
  - Ctrl+S保存ショートカット
  - 未保存変更警告機能
  - 詳細なヘルプテキストとリンク


### デプロイ・運用考慮
- **設定バックアップ**: Chrome Sync Storage活用
- **ログ管理**: 詳細なコンソールログ出力
- **デバッグ支援**: 開発者向け詳細情報表示

## 🎨 デザインガイドライン
### カラーパレット
```css
--primary-color: #2c5aa0 (メインブルー)
--secondary-color: #4a90e2 (アクセントブルー)  
--success-color: #28a745 (成功グリーン)
--error-color: #dc3545 (エラーレッド)
--warning-color: #ffc107 (警告イエロー)


### タイポグラフィ
- **フォントファミリー**: モダンなフォントを採用
- **フォントサイズ**: clamp()によるレスポンシブ設計

### アニメーション
- **切り替え**: 800msの滑らかなトランジション
- **フェードイン**: 0.5秒のコンテンツ表示
- **ホバー効果**: 0.3秒の即座反応

### 生成AI指示用プロンプト要件
作成要件:
あなたは厳密なテンプレートに従うHTML/CSSコンパイラです。
今回は横型モニターで表示する{{search_query}}に関するデジタルサイネージのダッシュボードを表示するコードを作成してください。

作成要件:
- 横型モニターで表示するデジタルサイネージのダッシュボードを表示するHTML/CSSのコードを作成する。
- レスポンシブデザインで任意の画面サイズに対応
- 見出しは大きく、本文は読みやすいサイズで表示
- 白い背景に黒い文字で高コントラスト
- 余白を十分に取って見やすくする
- 現在時刻を大きく表示
- 検索結果から重要な情報のみを抽出して表示
- 単一HTMLファイルにCSSを含める
- 外部ファイルは使用しない
- 横型モニターに最適化したレイアウト。文字がはみ出る場合はウインドウサイズに合わせて調整する機能を持たせてください。利用者ははみ出した文字のデジタルサイネージを見たくないです。
- 全体的に映画広告風、雑誌風、アイソメトリックデザイン、インフォグラフィック、イベントのチラシ風など内容からテーマを決めてかっこよく仕上げてください
- 背景色と文字の色が似ていると見づらくなりますので、気を付けてください
- 画面のスクロールが不要になるようなコンテンツにしてください。(縮小などの調整をする)
- ヒーローエリアと情報エリアを画面の右側と左側で分ける
  各エリアの比率は自由に決めてください。
  文字やアイコンがはみ出ないことが重要です。
  情報の数が偶数の場合ヒーローエリアは右。奇数の場合はヒーローエリアを左にしてください。
- ヒーローエリア:
  背景SVGヒーローエリア、メッセージ性のあるキーワードと文章。
  時刻は特に大きく表示し、文字やアイコンも視認性重視で大きく
  内容に沿ったヒーローエリア、背景画像を採用してください。(CSS/SVG必須)
- 情報エリアの掲示(以下から必要なものを採用する。windowsサイズをよく確認し、画面からはみ出ないようにする)
  整理された複数のカード型表示、グラフ表示、表形式など、見ごたえのある情報を追加してください。
  主要情報(時刻・天気・アイコン・メッセージ)は3m離れても視認できる大きな文字サイズ
  検索結果のトピックに集中し、関連性の高い情報のみ抽出
  地域住民にとって実用的で分かりやすい言葉を使用
  広告や不要なナビゲーションリンクは完全に除外

インプット:
windowsize: {{windowsize}}
current time: {{current_time}}
search results:
{{search_results}}

出力要件:
- 説明文や前後のテキストを一切出力しません。返答は完全な単一HTMLのみです。
- 出力は必ず `<!DOCTYPE html>` から開始し、`</html>` で終了します。




### サイネージのページ要件(この数だけダッシュボードを作り、定期的に情報更新する)※コードが安定するまで3つ程度にしておいてください。あとで増やします。
相模原市南区 天気
相模原市南区 ニュース
相模原市 地震情報
相模原市南区 イベント情報
相模原市南区 交通情報
相模原市 防災情報
相模原市南区の飲食店・カフェ情報
相模原市南区の観光・名所案内
交通機関の運行状況(神奈川の渋滞、神奈川中央交通・小田急線)
相模原市南区の病院・クリニック案内

### オプション画面設計
 各種API設定(消さない限りは保持する)
 API接続テスト
 AI モデル設定
 自動更新間隔(分)
 パネル切り替え間隔(秒)
 検索クエリ設定
 プロンプト設定
 データ管理(キャッシュクリア、設定リセット)
 設定保存ボタン
 ダッシュボード表示ボタン(ダッシュボードに遷移する)
 ログファイル表示ボタン(logs.htmlへ遷移する
 
 
 ### モデルアクセス方法
 manifest.json
json
{
  "manifest_version": 3,
  "name": "相模原市南区AIダッシュボード",
  "version": "1.0",
  "description": "相模原市南区の地域住民向けに、AIが自動生成するリアルタイム情報ダッシュボードを提供します。",
  "permissions": [
    "storage",
    "alarms",
    "tabs"
  ],
  "host_permissions": [
    "https://www.googleapis.com/",
    "https://generativelanguage.googleapis.com/"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_title": "ダッシュボードを開く"
  },
  "options_page": "options.html",
  "icons": {
  }
}


xxxx.html
html
コピーする
編集する
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Gemini 2.5 Flash</title>
<style>
  body { font-family: sans-serif; width: 300px; padding: 10px; }
  textarea { width: 100%; height: 80px; }
  button { margin-top: 5px; width: 100%; }
  pre { white-space: pre-wrap; margin-top: 10px; background: #f5f5f5; padding: 5px; }
</style>
</head>
<body>
  <h3>Gemini 2.5 Flash</h3>
  <textarea id="prompt" placeholder="プロンプトを入力..."></textarea>
  <button id="sendBtn">送信</button>
  <pre id="result"></pre>
  <script src="popup.js"></script>
</body>
</html>


xxxxx.js
javascript
コピーする
編集する
// ====== ユーザー編集可能パラメータ ======
const API_KEY = "YOUR_API_KEY"; // Google AI Studio で取得
const MODEL_NAME = "gemini-2.5-flash";
const MAX_RETRIES = 3;
// =======================================

async function queryGemini(prompt) {
  let attempt = 0;
  while (attempt < MAX_RETRIES) {
    try {
      const res = await fetch(
        `https://generativelanguage.googleapis.com/v1beta/models/${MODEL_NAME}:generateContent?key=${API_KEY}`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            contents: [{ parts: [{ text: prompt }] }]
          })
        }
      );

      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.json();

      return data?.candidates?.[0]?.content?.parts?.[0]?.text || "No response.";
    } catch (err) {
      console.error(`Error (attempt ${attempt + 1}):`, err);
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
      attempt++;
    }
  }
  return `Error after ${MAX_RETRIES} retries.`;
}

document.getElementById("sendBtn").addEventListener("click", async () => {
  const prompt = document.getElementById("prompt").value;
  const resultEl = document.getElementById("result");
  resultEl.textContent = "送信中...";
  const response = await queryGemini(prompt);
  resultEl.textContent = response;
});

データ取得と安定化

CSE と Gemini の双方で失敗を想定した制御を入れ、止まらない実行を実現しました。

制御仕様

# 項目 仕様
1 CSE レート制御 リクエスト間隔 2 秒。429/503 を受けたら間隔を倍々に伸ばす。
2 収集期間 直近 1 週間を優先(クエリで日本語優先を表現)。
3 Gemini タイムアウト 180 秒で中断。
4 Gemini リトライ 最大 3 回(5→10→15 秒) の指数バックオフ。
5 並列実行 最大 3 並列で効率化しつつ、全停止のAbortに対応する。
6 描画分離 生成HTMLは iframe(srcdoc)/Shadow DOM に描画する。

レート制御と指数バックオフは、単に API を守るだけでなく、ユーザー体験の一貫性を守ります。失敗が連鎖しても、待ち時間が“理由のある遅延”に変わるため、UI の挙動が読みやすくなります。描画分離は、自由な紙面と既存UIの共存を可能にします。

エラーは起き得ます。だからこそ、起きた時にユーザーの信頼を失わない設計が必要です。


UI/UX(Dashboard / Options)

Dashboard は成功したパネルから表示を始め、失敗は自動でスキップします。Options は保存の確実さ接続テストの見える化を重視しました。

主要機能

# 画面 機能 注記
1 Dashboard パネル切替(3–60s)、手動切替、全画面、即時更新、停止 全画面時は操作部を自動で隠し、紙面の視認性を優先する。
2 Dashboard 進捗表示、キュー可視化、成功優先表示 空白時間を作らない
3 Options API キー保存、AIza 検証、接続テスト(1/3→3/3) Ctrl+S 保存、未保存警告、クエリは 1 行 = 1 テーマ
4 Logs head/tail100、検索、JSON エクスポート、自動更新 取得・生成の両方を一画面でレビューできる。

ユーザーは結果が見えるまでの待ち時間に敏感です。そこで成功したパネルから出す方針に切り替え、空白を排除しました。設定画面には検証の段階表示を組み込み、「保存したつもり」の不安を潰しました。

“速く見える”ための工夫は、実行速度だけでなく、順序と可視化でも実現できます。


ログと検証

原因に最短でたどり着くために、テキストの 先頭/末尾100文字をそろえ、送受双方を比較できるようにしました。

記録内容

# 区分 記録内容
1 ステータス API/HTTP の結果コード。
2 検索クエリ head100 / tail100
3 検索結果 整形後の head100 / tail100
4 Gemini 送信 プロンプトの head100 / tail100
5 Gemini 受信 応答の head100 / tail100

生成が想定と異なるとき、原因は「検索の揺れ」「合成ミス」「プロンプトのニュアンス」に分かれます。head/tail100 を並べて読めば、どこからズレたのかを自然に辿れます。検索と応答の両端が一致しない場合、クエリ自体を見直すサインです。

計測できるものは改善できます。可視化は開発者だけでなく、運用担当者の安心にもつながります。


セットアップ

Chrome の拡張機能として数分で試せるように準備しました。

導入手順

# 手順 内容
1 追加 chrome://extensions → デベロッパーモード → パッケージ化されていない拡張機能を読み込む。
2 設定 Options で API キーとクエリ、間隔、モデルを入力して保存(Ctrl+S)。
3 検証 接続テストで CSE → Gemini → Snapshot を順に確認する。
4 実行 「ダッシュボードを開く」でスライド開始。

初回導入でつまずきやすいのは保存の失念とキーの形式不一致です。Ctrl+S 保存、AIza 検証、段階テストを備えておけば、導入時の質問はほぼ消えます。

導入時の体験を磨くと、運用フェイズでの信頼も自然に高まります。


応用・展望

この仕組みは、クエリを変えるだけで防災、交通、イベント、ニュース、気候などに即応できます。さらに、どこかのシステムから集約したログをインプットにして**「必要なときに必要なダッシュボードを毎回新しく作る」**使い方も可能です。

活用の方向性

# 方向 期待効果
1 テーマ横展開 {{search_query}} を差し替えるだけで別領域へ広がる。
2 多拠点展開 コードを共有し、クエリを拠点ごとに変えるだけで現地最適になる。
3 ログ駆動 監視・BI の集約ログを合成し、言語でUIを指示して紙面化する。

モデルの世代更新は、そのままデザインの更新に等しい意味を持ちます。運用者はコードを大きく変えずに、新しい表現力を享受できます。言語がUIを駆動するという方向性は、ダッシュボード設計を「要件の文章化」と「検証の自動化」に近づけます。

場所と目的に合わせて紙面が生まれ続ける——サイネージの理想に一歩近づきます。

総括
このアーキテクチャは、変化を味方につけるダッシュボードを現実の運用へ落とし込むための最短ルートです。プロンプトで設計し、ログで検証し、再実行で循環させる。生成AIの力を安全に引き出すための骨格として活用してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?