| | <!DOCTYPE html>
|
| | <html lang="ja">
|
| | <head>
|
| | <meta charset="UTF-8">
|
| | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| | <title>API Workflow Builder - Visual Page Editor</title>
|
| |
|
| |
|
| | <link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
|
| | <link rel="stylesheet" href="https://unpkg.com/grapesjs-blocks-basic/dist/grapesjs-blocks-basic.min.css">
|
| |
|
| | <style>
|
| | body, html {
|
| | height: 100%;
|
| | margin: 0;
|
| | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| | }
|
| |
|
| | #header {
|
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| | color: white;
|
| | padding: 15px 30px;
|
| | box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
| | display: flex;
|
| | justify-content: space-between;
|
| | align-items: center;
|
| | }
|
| |
|
| | #header h1 {
|
| | margin: 0;
|
| | font-size: 24px;
|
| | font-weight: 600;
|
| | }
|
| |
|
| | #header-buttons {
|
| | display: flex;
|
| | gap: 10px;
|
| | }
|
| |
|
| | #header button {
|
| | padding: 10px 20px;
|
| | border: none;
|
| | border-radius: 6px;
|
| | cursor: pointer;
|
| | font-size: 14px;
|
| | font-weight: 500;
|
| | transition: all 0.3s;
|
| | }
|
| |
|
| | .btn-preview {
|
| | background: #ffffff;
|
| | color: #667eea;
|
| | }
|
| |
|
| | .btn-save {
|
| | background: #48bb78;
|
| | color: white;
|
| | }
|
| |
|
| | .btn-load {
|
| | background: #4299e1;
|
| | color: white;
|
| | }
|
| |
|
| | .btn-clear {
|
| | background: #f56565;
|
| | color: white;
|
| | }
|
| |
|
| | #header button:hover {
|
| | transform: translateY(-2px);
|
| | box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
| | }
|
| |
|
| | #gjs {
|
| | height: calc(100vh - 70px);
|
| | overflow: hidden;
|
| | }
|
| |
|
| |
|
| | .shop11-api-block {
|
| | padding: 20px;
|
| | margin: 15px 0;
|
| | border: 2px solid #e2e8f0;
|
| | border-radius: 8px;
|
| | background: #f7fafc;
|
| | }
|
| |
|
| | .shop11-api-block h3 {
|
| | margin-top: 0;
|
| | color: #2d3748;
|
| | border-bottom: 2px solid #667eea;
|
| | padding-bottom: 10px;
|
| | }
|
| |
|
| | .api-button {
|
| | background: #667eea;
|
| | color: white;
|
| | border: none;
|
| | padding: 10px 20px;
|
| | border-radius: 5px;
|
| | cursor: pointer;
|
| | font-weight: 500;
|
| | transition: background 0.3s;
|
| | }
|
| |
|
| | .api-button:hover {
|
| | background: #5a67d8;
|
| | }
|
| |
|
| | .api-result {
|
| | margin-top: 15px;
|
| | padding: 15px;
|
| | background: white;
|
| | border-radius: 5px;
|
| | border: 1px solid #e2e8f0;
|
| | max-height: 300px;
|
| | overflow-y: auto;
|
| | }
|
| |
|
| |
|
| | .modal {
|
| | display: none;
|
| | position: fixed;
|
| | z-index: 9999;
|
| | left: 0;
|
| | top: 0;
|
| | width: 100%;
|
| | height: 100%;
|
| | background-color: rgba(0,0,0,0.5);
|
| | }
|
| |
|
| | .modal-content {
|
| | background-color: #fefefe;
|
| | margin: 5% auto;
|
| | padding: 30px;
|
| | border-radius: 10px;
|
| | width: 80%;
|
| | max-width: 600px;
|
| | max-height: 80vh;
|
| | overflow-y: auto;
|
| | box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
| | }
|
| |
|
| | .close {
|
| | color: #aaa;
|
| | float: right;
|
| | font-size: 28px;
|
| | font-weight: bold;
|
| | cursor: pointer;
|
| | }
|
| |
|
| | .close:hover {
|
| | color: #000;
|
| | }
|
| |
|
| | .page-item {
|
| | padding: 15px;
|
| | margin: 10px 0;
|
| | background: #f7fafc;
|
| | border-radius: 5px;
|
| | border: 1px solid #e2e8f0;
|
| | cursor: pointer;
|
| | transition: all 0.3s;
|
| | }
|
| |
|
| | .page-item:hover {
|
| | background: #edf2f7;
|
| | border-color: #667eea;
|
| | }
|
| | </style>
|
| | </head>
|
| | <body>
|
| | <div id="header">
|
| | <h1>🚀 API Workflow Builder</h1>
|
| | <div id="header-buttons">
|
| | <button class="btn-preview" onclick="previewPage()">👁️ プレビュー</button>
|
| | <button class="btn-load" onclick="loadPageList()">📂 読込</button>
|
| | <button class="btn-save" onclick="savePage()">💾 保存</button>
|
| | <button class="btn-clear" onclick="clearPage()">🗑️ クリア</button>
|
| | </div>
|
| | </div>
|
| |
|
| | <div id="gjs"></div>
|
| |
|
| |
|
| | <div id="pageListModal" class="modal">
|
| | <div class="modal-content">
|
| | <span class="close" onclick="closeModal()">×</span>
|
| | <h2>📂 保存されたページ</h2>
|
| | <div id="pageList"></div>
|
| | </div>
|
| | </div>
|
| |
|
| |
|
| | <script src="https://unpkg.com/@supabase/supabase-js@2"></script>
|
| |
|
| |
|
| | <script src="https://unpkg.com/grapesjs"></script>
|
| | <script src="https://unpkg.com/grapesjs-blocks-basic"></script>
|
| |
|
| |
|
| | <script src="/shop11/public/page-builder/supabase-config.js"></script>
|
| |
|
| | <script>
|
| | // GrapeJS エディタ初期化
|
| | const editor = grapesjs.init({
|
| | container: '#gjs',
|
| | fromElement: false,
|
| | height: '100%',
|
| | width: 'auto',
|
| | plugins: ['gjs-blocks-basic'],
|
| | storageManager: {
|
| | type: 'local',
|
| | autosave: true,
|
| | autoload: true,
|
| | stepsBeforeSave: 1
|
| | },
|
| | canvas: {
|
| | styles: [
|
| | 'https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/css/bootstrap.min.css'
|
| | ],
|
| | scripts: [
|
| | 'https://code.jquery.com/jquery-3.5.1.min.js',
|
| | '/shop11/public/page-builder/api-functions.js'
|
| | ]
|
| | }
|
| | });
|
| |
|
| | // カスタムブロック登録
|
| | editor.on('load', function() {
|
| | const blockManager = editor.BlockManager;
|
| |
|
| | // 地金チェック削除ブロック
|
| | blockManager.add('gold-check-delete', {
|
| | label: '地金チェック削除',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="gold_check"><h3>🥇 地金チェック削除</h3><div class="form-group"><label>商品ID:</label><input type="text" class="form-control gold-product-id" placeholder="商品IDを入力"></div><button class="btn btn-danger" data-action="goldCheckDelete">削除実行</button><div class="api-result gold-check-result" style="display:none;"></div></div>',
|
| | attributes: { class: 'fa fa-trash' }
|
| | });
|
| |
|
| | // 査定タイトル生成ブロック
|
| | blockManager.add('satei-title', {
|
| | label: '査定タイトル生成',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="satei_title"><h3>📝 査定タイトル生成</h3><div class="form-group"><label>商品ID:</label><input type="text" class="form-control satei-product-id" placeholder="商品IDを入力"></div><button class="btn btn-primary" data-action="createSateiTitle">タイトル生成</button><div class="api-result satei-title-result" style="display:none;"></div></div>',
|
| | attributes: { class: 'fa fa-file-text' }
|
| | });
|
| |
|
| | // メール送信ブロック
|
| | blockManager.add('send-notification', {
|
| | label: 'メール送信',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="notification"><h3>📧 メール送信</h3><div class="form-group"><label>宛先:</label><input type="email" class="form-control mail-to" placeholder="email@example.com"></div><div class="form-group"><label>件名:</label><input type="text" class="form-control mail-subject" placeholder="件名を入力"></div><div class="form-group"><label>本文:</label><textarea class="form-control mail-body" rows="4" placeholder="メッセージを入力"></textarea></div><button class="btn btn-success" data-action="sendNotification">送信</button><div class="api-result mail-result" style="display:none;"></div></div>',
|
| | attributes: { class: 'fa fa-envelope' }
|
| | });
|
| |
|
| | // 商品検索ブロック
|
| | blockManager.add('product-search', {
|
| | label: '商品検索',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="product_search"><h3>🔍 商品検索</h3><div class="form-group"><label>検索キーワード:</label><input type="text" class="form-control search-keyword" placeholder="商品名・ブランドで検索"></div><button class="btn btn-info" data-action="searchProduct">検索</button><div class="api-result search-result" style="display:none;"></div></div>',
|
| | attributes: { class: 'fa fa-search' }
|
| | });
|
| |
|
| | // データテーブルブロック
|
| | blockManager.add('data-table', {
|
| | label: 'データテーブル',
|
| | category: 'Shop11 UI',
|
| | content: '<div class="container my-4"><h3>データ一覧</h3><table class="table table-striped table-bordered"><thead class="thead-dark"><tr><th>ID</th><th>商品名</th><th>ステータス</th><th>金額</th><th>操作</th></tr></thead><tbody><tr><td>1</td><td>サンプル商品A</td><td><span class="badge badge-success">完了</span></td><td>¥10,000</td><td><button class="btn btn-sm btn-primary">編集</button></td></tr><tr><td>2</td><td>サンプル商品B</td><td><span class="badge badge-warning">処理中</span></td><td>¥25,000</td><td><button class="btn btn-sm btn-primary">編集</button></td></tr></tbody></table></div>',
|
| | attributes: { class: 'fa fa-table' }
|
| | });
|
| |
|
| | // 商品データテーブルブロック
|
| | blockManager.add('product-data-table', {
|
| | label: '商品データテーブル',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="product_table"><h3>商品データテーブル</h3><div class="form-group"><label>検索キーワード:</label><div class="input-group"><input type="text" class="form-control product-search-keyword" placeholder="商品名・ブランドで検索"><div class="input-group-append"><button class="btn btn-primary" data-action="loadProductTable">🔍 検索</button><button class="btn btn-secondary ml-2" data-action="clearProductTable">🗑️ クリア</button></div></div></div><div class="product-table-container"><table class="table table-striped table-bordered table-hover"><thead class="thead-dark"><tr><th>商品ID</th><th>商品名</th><th>ブランド</th><th>カテゴリ</th><th>価格</th><th>ステータス</th><th>操作</th></tr></thead><tbody class="product-table-body"><tr><td colspan="7" class="text-center text-muted">検索キーワードを入力して検索してください</td></tr></tbody></table></div><div class="product-table-info mt-2 text-muted"></div></div>',
|
| | attributes: { class: 'fa fa-database' }
|
| | });
|
| |
|
| | // 顧客検索テーブルブロック
|
| | blockManager.add('customer-data-table', {
|
| | label: '顧客検索テーブル',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="customer_table"><h3>👤 顧客検索テーブル</h3><div class="form-group"><label>顧客名・電話番号:</label><div class="input-group"><input type="text" class="form-control customer-search-keyword" placeholder="顧客名・電話番号で検索"><div class="input-group-append"><button class="btn btn-primary" data-action="loadCustomerTable">🔍 検索</button><button class="btn btn-secondary ml-2" data-action="clearCustomerTable">🗑️ クリア</button></div></div></div><div class="customer-table-container"><table class="table table-striped table-bordered table-hover"><thead class="thead-dark"><tr><th>顧客ID</th><th>顧客名</th><th>電話番号</th><th>メール</th><th>郵便番号</th><th>住所</th><th>操作</th></tr></thead><tbody class="customer-table-body"><tr><td colspan="7" class="text-center text-muted">検索キーワードを入力して検索してください</td></tr></tbody></table></div><div class="customer-table-info mt-2 text-muted"></div></div>',
|
| | attributes: { class: 'fa fa-users' }
|
| | });
|
| |
|
| | // 査定検索テーブルブロック
|
| | blockManager.add('satei-data-table', {
|
| | label: '査定検索テーブル',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="satei_table"><h3>📝 査定検索テーブル</h3><div class="form-group"><label>検索条件:</label><div class="input-group"><input type="text" class="form-control satei-search-keyword" placeholder="商品名・顧客名で検索"><div class="input-group-append"><button class="btn btn-primary" data-action="loadSateiTable">🔍 検索</button><button class="btn btn-secondary ml-2" data-action="clearSateiTable">🗑️ クリア</button></div></div></div><div class="satei-table-container"><table class="table table-striped table-bordered table-hover"><thead class="thead-dark"><tr><th>査定ID</th><th>商品名</th><th>顧客名</th><th>査定額</th><th>査定日</th><th>ステータス</th><th>操作</th></tr></thead><tbody class="satei-table-body"><tr><td colspan="7" class="text-center text-muted">検索キーワードを入力して検索してください</td></tr></tbody></table></div><div class="satei-table-info mt-2 text-muted"></div></div>',
|
| | attributes: { class: 'fa fa-clipboard' }
|
| | });
|
| |
|
| | // 商品登録フォームブロック
|
| | blockManager.add('product-register-form', {
|
| | label: '商品登録フォーム',
|
| | category: 'Shop11 API',
|
| | content: '<div class="shop11-api-block" data-api="product_register"><h3>✏️ 商品登録フォーム</h3><form class="product-register-form"><div class="row"><div class="col-md-6"><div class="form-group"><label>商品名 <span class="text-danger">*</span></label><input type="text" class="form-control product-name" placeholder="商品名を入力" required></div></div><div class="col-md-6"><div class="form-group"><label>ブランド</label><input type="text" class="form-control product-brand" placeholder="ブランド名"></div></div></div><div class="row"><div class="col-md-6"><div class="form-group"><label>カテゴリ</label><select class="form-control product-category"><option value="">選択してください</option><option value="1">ジュエリー</option><option value="2">時計</option><option value="3">バッグ</option><option value="4">貴金属</option><option value="5">その他</option></select></div></div><div class="col-md-6"><div class="form-group"><label>価格 <span class="text-danger">*</span></label><input type="number" class="form-control product-price" placeholder="価格を入力" required></div></div></div><div class="form-group"><label>商品説明</label><textarea class="form-control product-description" rows="3" placeholder="商品の説明を入力"></textarea></div><div class="form-group"><label>ステータス</label><select class="form-control product-status"><option value="1" selected>在庫あり</option><option value="2">予約中</option><option value="3">売約済</option><option value="4">出品中</option></select></div><div class="text-right"><button type="button" class="btn btn-success btn-lg" data-action="registerProduct">💾 登録する</button><button type="reset" class="btn btn-secondary btn-lg ml-2">🔄 リセット</button></div></form><div class="register-result mt-3" style="display:none;"></div></div>',
|
| | attributes: { class: 'fa fa-plus-square' }
|
| | });
|
| |
|
| | console.log('✅ API Workflow Builder loaded!');
|
| | console.log('📦 Total Blocks:', blockManager.getAll().length);
|
| | });
|
| |
|
| | // プレビュー機能
|
| | function previewPage() {
|
| | const html = editor.getHtml();
|
| | const css = '<style>' + editor.getCss() + '</style>';
|
| | const previewWindow = window.open('', '_blank');
|
| | previewWindow.document.write(`
|
| | <!DOCTYPE html>
|
| | <html>
|
| | <head>
|
| | <meta charset="UTF-8">
|
| | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| | <title>Preview</title>
|
| | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/css/bootstrap.min.css">
|
| | ${css}
|
| | </head>
|
| | <body>
|
| | ${html}
|
| | <script src="https://code.jquery.com/jquery-3.5.1.min.js"><\/script>
|
| | <script src="/shop11/public/page-builder/api-functions.js"><\/script>
|
| | </body>
|
| | </html>
|
| | `);
|
| | }
|
| |
|
| | // 保存機能
|
| | function savePage() {
|
| | const html = editor.getHtml();
|
| | const css = editor.getCss();
|
| | const components = editor.getComponents();
|
| |
|
| | const pageName = prompt('ページ名を入力してください:', 'page-' + Date.now());
|
| | if (!pageName) return;
|
| |
|
| | savePageToSupabase(pageName, html, css, components).then(result => {
|
| | if (result.success) {
|
| | alert('✅ Supabaseに保存しました!\nページID: ' + result.data[0].id);
|
| | downloadHtmlFile(pageName, html, css);
|
| | } else {
|
| | alert('❌ 保存エラー: ' + result.error);
|
| | }
|
| | });
|
| | }
|
| |
|
| | // HTMLファイルダウンロード
|
| | function downloadHtmlFile(pageName, html, css) {
|
| | const fullHtml = `<!DOCTYPE html>
|
| | <html lang="ja">
|
| | <head>
|
| | <meta charset="UTF-8">
|
| | <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| | <title>${pageName}</title>
|
| | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/css/bootstrap.min.css">
|
| | <style>${css}</style>
|
| | </head>
|
| | <body>
|
| | ${html}
|
| | <script src="https://code.jquery.com/jquery-3.5.1.min.js"><\/script>
|
| | <script src="/shop11/public/page-builder/api-functions.js"><\/script>
|
| | </body>
|
| | </html>`;
|
| |
|
| | const blob = new Blob([fullHtml], { type: 'text/html' });
|
| | const url = URL.createObjectURL(blob);
|
| | const a = document.createElement('a');
|
| | a.href = url;
|
| | a.download = pageName + '.html';
|
| | a.click();
|
| | }
|
| |
|
| | // ページ一覧読込
|
| | function loadPageList() {
|
| | loadPagesFromSupabase().then(result => {
|
| | if (result.success && result.data.length > 0) {
|
| | const pageListDiv = document.getElementById('pageList');
|
| | pageListDiv.innerHTML = '';
|
| |
|
| | result.data.forEach(page => {
|
| | const pageItem = document.createElement('div');
|
| | pageItem.className = 'page-item';
|
| | pageItem.innerHTML = `
|
| | <strong>${page.name}</strong><br>
|
| | <small>作成日: ${new Date(page.created_at).toLocaleString('ja-JP')}</small>
|
| | `;
|
| | pageItem.onclick = () => loadPageFromSupabaseById(page.id);
|
| | pageListDiv.appendChild(pageItem);
|
| | });
|
| |
|
| | document.getElementById('pageListModal').style.display = 'block';
|
| | } else {
|
| | alert('保存されたページがありません');
|
| | }
|
| | });
|
| | }
|
| |
|
| | // ページ読込
|
| | function loadPageFromSupabaseById(pageId) {
|
| | loadPageFromSupabase(pageId).then(result => {
|
| | if (result.success && result.data) {
|
| | const page = result.data;
|
| | editor.setComponents(page.html_content);
|
| | editor.setStyle(page.css_content);
|
| | closeModal();
|
| | alert('✅ ページを読み込みました: ' + page.name);
|
| | } else {
|
| | alert('❌ 読込エラー: ' + result.error);
|
| | }
|
| | });
|
| | }
|
| |
|
| | // モーダルを閉じる
|
| | function closeModal() {
|
| | document.getElementById('pageListModal').style.display = 'none';
|
| | }
|
| |
|
| | // クリア機能
|
| | function clearPage() {
|
| | if (confirm('すべてのコンテンツをクリアしますか?')) {
|
| | editor.setComponents('');
|
| | editor.setStyle('');
|
| | alert('✅ クリアしました');
|
| | }
|
| | }
|
| |
|
| | // モーダル外クリックで閉じる
|
| | window.onclick = function(event) {
|
| | const modal = document.getElementById('pageListModal');
|
| | if (event.target == modal) {
|
| | closeModal();
|
| | }
|
| | }
|
| | </script>
|
| | </body>
|
| | </html>
|
| |
|