/* components/FilterModal.jsx */ (() => { const METRIC_LABELS = { cpo: "CPO", ctr: "CTR", cvr: "CVR", cvCount: "CV数", cost: "広告費", click: "クリック", imp: "IMP", }; const METRIC_UNITS = { cpo: "円", ctr: "%", cvr: "%", cvCount: "件", cost: "円", click: "", imp: "", }; const MetricRow = ({ k, v, onChange }) => { const label = METRIC_LABELS[k] || k; const unit = METRIC_UNITS[k] || ""; return (
{label}
範囲で絞り込み
最小
onChange(k, { ...v, min: e.target.value })} inputMode="decimal" className="w-full px-3 py-2 rounded-lg bg-white/5 border border-white/10 focus:outline-none focus:ring-2 focus:ring-emerald-600 text-sm" placeholder="例:100" />
最大
onChange(k, { ...v, max: e.target.value })} inputMode="decimal" className="w-full px-3 py-2 rounded-lg bg-white/5 border border-white/10 focus:outline-none focus:ring-2 focus:ring-emerald-600 text-sm" placeholder="例:500" />
{unit}
); }; function FilterModal(props) { const { open, onClose, filters, setFilters, matchedCount, totalCount } = props; if (!open) return null; const onRowChange = (k, next) => { setFilters((prev) => ({ ...prev, [k]: next })); }; const clearAll = () => { const cleared = {}; Object.keys(filters).forEach((k) => (cleared[k] = { min: "", max: "" })); setFilters(cleared); }; // UX: クリエイティブ担当がよく使う「まずCVありに寄せる」ショートカット const setHasCV = () => { setFilters((prev) => ({ ...prev, cvCount: { ...(prev.cvCount || { min: "", max: "" }), min: "1" } })); }; return (
フィルター
{totalCount}件中 {matchedCount}件 を表示
メモ:空欄は無視されます(その指標では絞り込みません)。
{/* よく使う項目を上に */} {["cpo", "ctr", "cvr", "cvCount"].map((k) => ( ))}
その他(任意)
{["cost", "click", "imp"].map((k) => (
))}
); } window.Cmp = window.Cmp || {}; window.Cmp.FilterModal = FilterModal; })();