(function(wp){ if(!wp || !wp.element){ console.error('wp.element not found'); return; } const { useState, useEffect, createElement: h, Fragment } = wp.element; const { Button, CheckboxControl, TextControl, SelectControl, Notice, Spinner, Card, CardBody, __experimentalInputControl:InputControl } = wp.components; const apiFetch = wp.apiFetch; function App(){ const [cats, setCats] = useState([]); const [cat, setCat] = useState('all'); const [query, setQuery] = useState(''); const [loading, setLoading] = useState(false); const [posts, setPosts] = useState([]); const [checked, setChecked] = useState({}); const [selectAll, setSelectAll] = useState(false); const [noti, setNoti] = useState({type:'',msg:''}); const [previewHTML, setPreviewHTML] = useState(''); const [previewFor, setPreviewFor] = useState(null); // タブ切替(設定の表示/非表示) useEffect(()=>{ const tabs = document.querySelectorAll('.nav-tab-wrapper .nav-tab'); tabs.forEach(el=>{ el.addEventListener('click',(e)=>{ e.preventDefault(); tabs.forEach(t=>t.classList.remove('nav-tab-active')); el.classList.add('nav-tab-active'); document.getElementById('pbbu-settings').style.display = (el.getAttribute('href')==='#tab-settings') ? '' : 'none'; }); }); },[]); // カテゴリ読み込み useEffect(()=>{ apiFetch({ path: '/wp/v2/categories?per_page=100' }) .then(d=>setCats(Array.isArray(d)?d:[])) .catch(()=>setCats([])); },[]); // 投稿読み込み useEffect(()=>{ fetchPosts(); },[cat,query]); function fetchPosts(){ setLoading(true); let path = '/wp/v2/posts?per_page=100&orderby=date&order=desc&_fields=id,title,status'; if(cat!=='all') path += '&categories='+cat; if(query) path += '&search='+encodeURIComponent(query); apiFetch({ path }) .then(async list=>{ const ids = (Array.isArray(list)?list:[]).map(p=>p.id); const meta = ids.length ? await apiFetch({ path: '/paybridge/v1/status?ids='+ids.join(',') }) : {}; const merged = (Array.isArray(list)?list:[]).map(p=>{ const m = meta[String(p.id)] || {}; return {...p, _updated: !!m.updated, _updated_at: m.updated_at || '', _keywords: m.keywords || [], _intent: m.intent || '', _analyzed_at: m.analyzed_at || '' }; }); setPosts(merged); setChecked({}); setSelectAll(false); }) .finally(()=>setLoading(false)); } function toggleAll(val){ setSelectAll(val); const m = {}; posts.forEach(p=>m[p.id]=val); setChecked(m); } function toggleOne(id,val){ const m = {...checked,[id]:val}; setChecked(m); if(!val) setSelectAll(false); } function onKeywordChange(id, text){ setPosts(posts.map(p=> p.id===id ? {...p, _keywords:text.split(',').map(s=>s.trim()).filter(Boolean)} : p )); } async function saveKeywords(ids){ if(!ids.length) return; await apiFetch({ path:'/paybridge/v1/save_keywords', method:'POST', data:{ items: posts.filter(p=>ids.includes(p.id)).map(p=>({id:p.id, keywords:p._keywords, intent:p._intent})) }}); } async function analyze(){ const ids = Object.keys(checked).filter(id=>checked[id]).map(n=>parseInt(n,10)); if(!ids.length){ setNoti({type:'warning',msg:'解析する記事を選択してください。'}); return; } setLoading(true); setNoti({type:'',msg:''}); try{ const data = await apiFetch({ path:'/paybridge/v1/analyze', method:'POST', data:{ ids } }); if(data.error) setNoti({type:'error',msg:data.error}); else{ const map = data.items || []; setPosts(posts.map(p=>{ const found = map.find(x=>x.id===p.id); return found ? {...p, _keywords:found.keywords, _intent:found.intent, _analyzed_at:found.analyzed_at} : p; })); setNoti({type:'success',msg:'キーワードを抽出しました。必要に応じて編集して保存してください。'}); } }catch(e){ setNoti({type:'error',msg:String(e)}); } finally{ setLoading(false); } } async function doPreview(){ const ids = Object.keys(checked).filter(id=>checked[id]).map(n=>parseInt(n,10)); if(!ids.length){ setNoti({type:'warning',msg:'プレビューする記事を選択してください。'}); return; } await saveKeywords([ids[0]]); setLoading(true); setPreviewHTML(''); setPreviewFor(null); try{ const data = await apiFetch({ path:'/paybridge/v1/preview', method:'POST', data:{ ids:[ids[0]] } }); if(data.error) setNoti({type:'error',msg:data.error}); else { setPreviewHTML(data.html||''); setPreviewFor(data.post_id||null); } }catch(e){ setNoti({type:'error',msg:String(e)}); } finally{ setLoading(false); } } async function doExecute(){ const ids = Object.keys(checked).filter(id=>checked[id]).map(n=>parseInt(n,10)); if(!ids.length){ setNoti({type:'warning',msg:'更新する記事を選択してください。'}); return; } if(!confirm('選択した '+ids.length+' 件を上書き更新します。よろしいですか?')) return; await saveKeywords(ids); setLoading(true); setNoti({type:'',msg:''}); try{ const data = await apiFetch({ path:'/paybridge/v1/execute', method:'POST', data:{ ids } }); if(data.error) setNoti({type:'error',msg:data.error}); else { setNoti({type:'success',msg:`更新完了:${data.updated_count} 件`}); fetchPosts(); } }catch(e){ setNoti({type:'error',msg:String(e)}); } finally{ setLoading(false); } } function link(id){ return PBBU.wp_root.replace('wp-json/','')+'wp-admin/post.php?post='+id+'&action=edit'; } return h(Fragment, {}, noti.msg ? h(Notice,{status:noti.type||'info',isDismissible:true,onRemove:()=>setNoti({type:'',msg:''})}, noti.msg):null, h(Card,{}, h(CardBody,{}, h('div',{style:{display:'flex',gap:'16px',alignItems:'flex-end',flexWrap:'wrap'}}, h(SelectControl,{label:'カテゴリ',value:cat, options:[{label:'全て',value:'all'}].concat(cats.map(c=>({label:c.name,value:String(c.id)}))), onChange:setCat}), h(TextControl,{label:'キーワード検索',value:query,onChange:setQuery,placeholder:'タイトル/本文'}), h(Button,{variant:'secondary',onClick:()=>window.location.reload()},'再読み込み') ), h('div',{style:{marginTop:'10px'}}, h(CheckboxControl,{label:'全選択',checked:selectAll,onChange:toggleAll})), h('table',{className:'widefat striped',style:{marginTop:'8px'}}, h('thead',{},h('tr',{}, h('th',{},''),h('th',{},'ID'),h('th',{},'タイトル'),h('th',{},'キーワード(編集可:カンマ区切り)'),h('th',{},'検索意図'),h('th',{},'更新状況'))), h('tbody',{}, loading ? h('tr',{},h('td',{colSpan:6},h(Spinner,{}),' 読み込み中…')) : (posts.length? posts.map(p=>h('tr',{key:p.id}, h('td',{}, h(CheckboxControl,{checked:!!checked[p.id],onChange:v=>toggleOne(p.id,v)})), h('td',{}, p.id), h('td',{}, h('a',{href:link(p.id),target:'_blank',rel:'noopener'}, p.title.rendered||'(無題)')), h('td',{}, h((InputControl||TextControl),{value:(p._keywords||[]).join(', '),onChange:val=>onKeywordChange(p.id, val)})), h('td',{}, h((InputControl||TextControl),{value:p._intent||'',onChange:val=>setPosts(posts.map(x=>x.id===p.id?{...x,_intent:val}:x))})), h('td',{}, p._updated ? h('span',{className:'components-badge is-success'}, '更新済み'+(p._updated_at? ' ('+p._updated_at+')':'')) : (p._analyzed_at? h('span',{className:'components-badge is-primary'}, '解析済み '+p._analyzed_at): '') ) )) : h('tr',{},h('td',{colSpan:6},'該当なし'))) ) ), h('div',{style:{display:'flex',gap:'8px',marginTop:'12px',flexWrap:'wrap'}}, h(Button,{variant:'secondary',onClick:analyze},'キーワード抽出(選択分)'), h(Button,{variant:'primary',onClick:doPreview},'プレビュー(差分)'), h(Button,{variant:'primary',onClick:doExecute},'実行(上書き更新)') ), previewHTML ? h('div',{style:{marginTop:'16px'}}, h('h2',{},'差分プレビュー(ID: '+previewFor+')'), h('div',{dangerouslySetInnerHTML:{__html:previewHTML}}) ) : null ))) ); } // レンダリング function mount(){ const root = document.getElementById('pbbu-app'); if(!root){ return; } if(!wp.apiFetch){ root.innerHTML = '

wp.apiFetch が読み込まれていません。最適化プラグインの「管理画面でのJS最適化」を無効にしてください。

'; return; } wp.element.render( wp.element.createElement(App), root ); } document.addEventListener('DOMContentLoaded', mount); })(window.wp);
Warning: Cannot modify header information - headers already sent by (output started at /home/hybridsalon/factoringnavi.jp/public_html/wp-content/plugins/article-updrater/article-updrater.php:1) in /home/hybridsalon/factoringnavi.jp/public_html/wp-content/themes/cocoon-child-master/functions.php on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/hybridsalon/factoringnavi.jp/public_html/wp-content/plugins/article-updrater/article-updrater.php:1) in /home/hybridsalon/factoringnavi.jp/public_html/wp-includes/pluggable.php on line 1450

Warning: Cannot modify header information - headers already sent by (output started at /home/hybridsalon/factoringnavi.jp/public_html/wp-content/plugins/article-updrater/article-updrater.php:1) in /home/hybridsalon/factoringnavi.jp/public_html/wp-includes/pluggable.php on line 1453