api-workflow-builder / index.html
kenken999's picture
Upload folder using huggingface_hub
69996c8 verified
<!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>
<!-- GrapeJS CSS -->
<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 カスタムスタイル */
.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()">&times;</span>
<h2>📂 保存されたページ</h2>
<div id="pageList"></div>
</div>
</div>
<!-- Supabase JS SDK -->
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
<!-- GrapeJS -->
<script src="https://unpkg.com/grapesjs"></script>
<script src="https://unpkg.com/grapesjs-blocks-basic"></script>
<!-- Supabase Config -->
<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>