genser 活用の提案

タブ切替型 (Explicit Toggle)

AI 検索ボタン誘導型 (Dropdown Nudge)

検索没入型 (Immersive/Focus)

既存の検索欄連携型 (Simple Integration)

最終更新
役に立ちましたか?





最終更新
役に立ちましたか?
役に立ちましたか?
const TabSearchPattern = () => {
const [mode, setMode] = useState('AI'); // 'AI' | 'GENERAL'
const [query, setQuery] = useState('');
const [products, setProducts] = useState([]);
const [requestId, setRequestId] = useState(null); // 検索リクエストIDを保存
// 表示(DI)ログ送信
// 商品一覧(products)が更新されて画面レンダリングが完了した時点で呼び出します。
useEffect(() => {
if (products.length > 0 && requestId) {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: products.map(p => ({ code: p.code, name: p.name })),
requestId: requestId
});
// console.log('DI 送信完了');
}
}, [products, requestId]);
// クリック(CL)ログ送信
const handleProductClick = (product) => {
if (!requestId) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId
});
// その後商品詳細ページへ遷移
console.log('CL 送信完了、ページ移動:', product.name);
};
const handleSearch = (e) => {
e.preventDefault();
if (mode === 'AI') {
// genser AI 検索呼び出し
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success((res) => {
if (res.type === 'SEARCH') {
// 状態更新 -> 再レンダリング発生 -> useEffect 実行 -> DI 送信
setProducts(res.products);
setRequestId(res.requestId);
}
});
} else {
console.log('通常のキーワード検索実行:', query);
}
};
return (
<div>
<div className="tabs">
<button onClick={() => setMode('AI')} className={mode === 'AI' ? 'active' : ''}>AI 検索</button>
<button onClick={() => setMode('GENERAL')} className={mode === 'GENERAL' ? 'active' : ''}>通常検索</button>
</div>
<form onSubmit={handleSearch}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={mode === 'AI' ? "結婚式に着ていく30代女性向けネイビージャケットをおすすめして!" : "検索語を入力してください"}
/>
<button type="submit">検索</button>
</form>
{/* 結果リスト例 */}
<ul>
{products.map(p => (
<li key={p.code} onClick={() => handleProductClick(p)}>
{p.name}
</li>
))}
</ul>
</div>
);
};
<template>
<div class="search-container">
<div class="tabs">
<button :class="{ active: mode === 'AI' }" @click="mode = 'AI'">✨ AI 検索</button>
<button :class="{ active: mode === 'GENERAL' }" @click="mode = 'GENERAL'">通常検索</button>
</div>
<div class="input-wrapper">
<input
v-model="query"
@keyup.enter="handleSearch"
:placeholder="placeholderText"
/>
<button @click="handleSearch">検索</button>
</div>
<!-- 結果リスト -->
<ul>
<li
v-for="p in products"
:key="p.code"
@click="handleProductClick(p)"
>
{{ p.name }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed, nextTick } from 'vue';
const mode = ref('AI');
const query = ref('');
const products = ref([]);
const requestId = ref(null);
const placeholderText = computed(() =>
mode.value === 'AI' ? "結婚式に着ていく30代女性向けネイビージャケットをおすすめして!" : "検索語を入力してください"
);
const handleSearch = () => {
if (mode.value === 'AI') {
// 検索API呼び出し
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query.value
}).success((res) => {
products.value = res.products;
requestId.value = res.requestId;
// DI ログ送信
nextTick(() => {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
});
});
} else {
console.log('通常検索実行');
}
};
// クリック(CL)ログ送信
const handleProductClick = (product) => {
if (!requestId.value) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId.value
});
console.log('CL 送信および移動', product.name);
};
</script><div class="search-wrapper">
<div class="tab-group">
<button onclick="setMode('AI')" id="btn-ai" class="active">✨ AI 検索</button>
<button onclick="setMode('GENERAL')" id="btn-gen">通常検索</button>
</div>
<input type="text" id="search-input" placeholder="結婚式に着ていく30代女性向けネイビージャケットをおすすめして!">
<button onclick="executeSearch()">検索</button>
<!-- 結果リスト領域 -->
<ul id="result-list"></ul>
</div>
<script>
let currentMode = 'AI';
let currentRequestId = null; // グローバル変数や状態で requestId を管理
function setMode(mode) {
// ... (従来と同様)
}
function executeSearch() {
const query = document.getElementById('search-input').value;
if(currentMode === 'AI') {
// AI 検索呼び出し
genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success(function(res) {
currentRequestId = res.requestId; // ID 保存
// 結果レンダリング
renderList(res.products);
// DI ログ送信(レンダリング直後)
genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
});
}
}
function renderList(products) {
const ul = document.getElementById('result-list');
ul.innerHTML = '';
products.forEach(p => {
const li = document.createElement('li');
li.textContent = p.name;
// クリックイベントバインディング
li.onclick = function() {
handleProductClick(p);
};
ul.appendChild(li);
});
}
function handleProductClick(product) {
if (!currentRequestId) return;
genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: currentRequestId
});
console.log('CL 送信および移動', product.name);
}
</script>const DropdownNudgePattern = () => {
const [isFocused, setIsFocused] = useState(false);
const goToAiSearch = () => {
alert("AI 検索モードへ切替!");
// window.location.href = '/ai-search';
};
return (
<div className="relative">
<input
type="text"
placeholder="検索語を入力してください"
onFocus={() => setIsFocused(true)}
// Blur 時にすぐ閉じるとボタンがクリックできない可能性があるため、少し遅延を与えます。
onBlur={() => setTimeout(() => setIsFocused(false), 200)}
/>
{isFocused && (
<div className="dropdown">
{/* AI 検索誘導ボタン */}
<button onClick={goToAiSearch} className="ai-nudge-btn">
<strong>✨ 私が探している服、AIに質問してみてください!</strong>
<span>"今週のゲスト服をおすすめして"</span>
</button>
<ul><li>最近の検索語...</li></ul>
</div>
)}
</div>
);
};<template>
<div class="search-wrap">
<input
@focus="isFocused = true"
@blur="handleBlur"
placeholder="検索語を入力してください"
/>
<div v-if="isFocused" class="dropdown">
<button class="ai-nudge-btn" @click="goToAiSearch">
<strong>✨ 私が探している服、AIに質問してみてください!</strong>
<span>"今週のゲスト服をおすすめして"</span>
</button>
<ul class="history"><li>最近の検索語...</li></ul>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
// ... (以前と同様)
</script><div class="search-box">
<input type="text" id="inp-search" placeholder="検索語を入力してください">
<!-- 基本的に隠れていて Focus 時に表示 -->
<div id="dropdown" class="dropdown-content hidden">
<button class="ai-nudge-btn" onclick="goToAiSearch()">
<strong>✨ 私が探している服、AIに質問してみてください!</strong>
<span>"今週のゲスト服をおすすめして"</span>
</button>
<div class="history">最近の検索語...</div>
</div>
</div>
<script>
const input = document.getElementById('inp-search');
const dropdown = document.getElementById('dropdown');
input.addEventListener('focus', () => {
dropdown.classList.remove('hidden'); // CSS .hidden { display: none; }
});
// blur 時に非表示処理(setTimeoutでクリックイベントの許容時間を確保)
input.addEventListener('blur', () => {
setTimeout(() => dropdown.classList.add('hidden'), 200);
});
function goToAiSearch() {
alert("AI 検索モードへ切替!");
// location.href = '/ai-search';
}
</script>
<style>
.hidden { display: none; }
.dropdown-content { position: absolute; /* スタイル省略 */ }
</style>
const ImmersiveSearch = () => {
const [query, setQuery] = useState('');
const [products, setProducts] = useState([]);
const [requestId, setRequestId] = useState(null);
const textareaRef = useRef(null);
// DI ログ送信
useEffect(() => {
if (products.length > 0 && requestId) {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: products.map(p => ({ code: p.code, name: p.name })),
requestId: requestId
});
}
}, [products, requestId]);
// CL ログ送信
const handleProductClick = (product) => {
if (!requestId) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId
});
};
const handleInput = (e) => {
setQuery(e.target.value);
if (textareaRef.current) {
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
}
};
const executeSearch = () => {
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success((res) => {
if(res.type === 'SEARCH') {
setProducts(res.products);
setRequestId(res.requestId);
}
});
};
return (
<div className="immersive-box">
<textarea
ref={textareaRef}
value={query}
onInput={handleInput}
rows={1}
placeholder="AIになんでも聞いてみてください。"
/>
<button onClick={executeSearch}>Send</button>
{/* 結果リスト */}
<ul>
{products.map(p => (
<li key={p.code} onClick={() => handleProductClick(p)}>{p.name}</li>
))}
</ul>
</div>
);
};
<template>
<div class="immersive-box">
<textarea
ref="textarea"
v-model="query"
@input="resize"
rows="1"
placeholder="AIになんでも聞いてみてください。"
></textarea>
<button @click="executeSearch">Send</button>
<!-- 結果リスト -->
<ul>
<li v-for="p in products" :key="p.code" @click="handleProductClick(p)">
{{ p.name }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, nextTick } from 'vue';
const query = ref('');
const textarea = ref(null);
const products = ref([]);
const requestId = ref(null);
const resize = () => {
const el = textarea.value;
el.style.height = 'auto';
el.style.height = el.scrollHeight + 'px';
};
const executeSearch = () => {
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query.value
}).success((res) => {
if (res.type === 'SEARCH') {
products.value = res.products;
requestId.value = res.requestId;
// DI ログ送信
nextTick(() => {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
});
}
});
};
// CL ログ送信
const handleProductClick = (product) => {
if (!requestId.value) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId.value
});
};
</script><div class="immersive-box">
<textarea id="ai-input" rows="1" placeholder="AIになんでも聞いてみてください。"></textarea>
<button onclick="doSearch()">Send</button>
<ul id="immersive-results"></ul>
</div>
<script>
const tx = document.getElementById('ai-input');
let currentRequestId = null;
tx.addEventListener("input", function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + "px";
});
function doSearch() {
const query = tx.value;
genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success(function(res) {
if (res.type === 'SEARCH') {
currentRequestId = res.requestId;
renderImmersiveList(res.products);
// DI ログ送信
genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
}
});
}
function renderImmersiveList(products) {
const ul = document.getElementById('immersive-results');
ul.innerHTML = '';
products.forEach(p => {
const li = document.createElement('li');
li.textContent = p.name;
// CL ログ送信バインディング
li.onclick = function() {
if (currentRequestId) {
genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: p.code, name: p.name }],
requestId: currentRequestId
});
}
};
ul.appendChild(li);
});
}
</script>const SimpleSearch = () => {
const [query, setQuery] = useState('');
const [products, setProducts] = useState([]);
const [requestId, setRequestId] = useState(null);
// DI ログ送信(レンダリング直後)
useEffect(() => {
if (products.length > 0 && requestId) {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: products.map(p => ({ code: p.code, name: p.name })),
requestId: requestId
});
}
}, [products, requestId]);
// CL ログ送信
const handleProductClick = (product) => {
if (!requestId) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId
});
// その後既存の商品詳細ページ遷移ロジックを実行
};
const handleSearch = (e) => {
e.preventDefault();
if (!query) return;
// 既存API呼び出しの代わりにgenser呼び出し
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success((res) => {
if (res.type === 'SEARCH') {
// 既存の状態更新(変数名のみマッピング)
setProducts(res.products);
setRequestId(res.requestId);
}
});
};
return (
<div>
<form onSubmit={handleSearch}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="結婚式に着ていく30代女性向けネイビージャケットをおすすめして!"
/>
<button type="submit">検索</button>
</form>
{/* 既存の結果リストUIを維持 */}
<ul>
{products.map(p => (
<li key={p.code} onClick={() => handleProductClick(p)}>
{p.name}
</li>
))}
</ul>
</div>
);
};
<template>
<div class="search-container">
<!-- 既存の検索フォーム -->
<form @submit.prevent="handleSearch">
<input v-model="query" placeholder="結婚式に着ていく30代女性向けネイビージャケットをおすすめして!" />
<button type="submit">検索</button>
</form>
<!-- 既存の結果リストUI -->
<ul>
<li v-for="p in products" :key="p.code" @click="handleProductClick(p)">
{{ p.name }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, nextTick } from 'vue';
const query = ref('');
const products = ref([]);
const requestId = ref(null);
const handleSearch = () => {
if (!query.value) return;
// 既存の検索ロジックをgenser呼び出しに変更
window.genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query.value
}).success((res) => {
if (res.type === 'SEARCH') {
products.value = res.products;
requestId.value = res.requestId;
// DI ログ送信(DOM更新直後)
nextTick(() => {
window.genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
});
}
});
};
// CL ログ送信
const handleProductClick = (product) => {
if (!requestId.value) return;
window.genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: requestId.value
});
};
</script><div class="search-box">
<input type="text" id="searchInput" placeholder="結婚式に着ていく30代女性向けネイビージャケットをおすすめして!">
<button onclick="executeSearch()">検索</button>
</div>
<ul id="resultList"></ul>
<script>
let currentRequestId = null;
function executeSearch() {
const query = document.getElementById('searchInput').value;
if (!query) return;
// 既存のAJAX呼び出し部分をgenser呼び出しに置換
genser.call('searchProducts', {
instanceKey: 'YOUR_INSTANCE_KEY',
queryText: query
}).success(function(res) {
if (res.type === 'SEARCH') {
currentRequestId = res.requestId;
renderList(res.products); // 既存のレンダリング関数を再利用
// DI ログ送信(レンダリング直後)
genser.call('DI', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: res.products.map(p => ({ code: p.code, name: p.name })),
requestId: res.requestId
});
}
});
}
function renderList(products) {
const ul = document.getElementById('resultList');
ul.innerHTML = '';
products.forEach(p => {
const li = document.createElement('li');
li.textContent = p.name;
// CL ログ送信バインディング
li.onclick = function() {
handleProductClick(p);
};
ul.appendChild(li);
});
}
function handleProductClick(product) {
if (!currentRequestId) return;
genser.call('CL', {
instance: { key: 'YOUR_INSTANCE_KEY' },
goods: [{ code: product.code, name: product.name }],
requestId: currentRequestId
});
// ページ遷移
}
</script>