// showcase.jsx — interactive tool showcase with category filter and detail expand
const { useState: useStateSC, useEffect: useEffectSC } = React;
function ToolShowcase() {
const [cat, setCat] = useStateSC("All");
const [active, setActive] = useStateSC(null);
const tools = cat === "All" ? TOOLS : TOOLS.filter(t => t.cat === cat);
return (
);
}
// Modal overlay for tool detail
function ToolModal({ t, accent, onClose }) {
useEffectSC(() => {
const onKey = (e) => { if (e.key === "Escape") onClose(); };
document.addEventListener("keydown", onKey);
const prevOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", onKey);
document.body.style.overflow = prevOverflow;
};
}, [onClose]);
return (
e.stopPropagation()} style={{
width: "min(1040px, 100%)",
maxHeight: "min(82vh, 720px)",
background: "var(--surface)",
border: "1px solid var(--line-2)",
borderRadius: 22,
boxShadow: "0 30px 80px rgba(11,14,19,0.18), 0 0 0 1px rgba(11,14,19,0.02)",
position: "relative",
overflow: "hidden",
display: "flex",
flexDirection: "column",
animation: "tmRise .28s cubic-bezier(.2,.7,.2,1) both"
}}>
{t.cat.toUpperCase()}
{t.en.toUpperCase()}
{t.name}
{t.blurb}
{t.bullets.map((b, i) => (
-
{b}
))}
);
}
function ToolCard({ t, active, onClick, delay }) {
const accent = ACC[t.accent];
return (
);
}
// Per-tool stylized preview (no fake screenshots — abstracted UI moments)
function ToolPreview({ t, accent }) {
// a different mini-mock per tool id, falling back to a generic
const mono = {fontFamily:"var(--mono)",fontSize:11.5,color:"var(--muted)",letterSpacing:"0.06em"};
switch (t.id) {
case "shipping":
return (
SHIPPING SLIP / 2026.05.14
{[["市場A 出荷","142件"],["市場B 出荷","87件"],["返品処理","3件"]].map(([k,v],i)=>(
{k}
{v}
))}
✓ 232 件 / 印刷キュー送信完了
);
case "excel-dict":
case "img-to-list":
return (
{t.en.toUpperCase()}
· 辞書ルール: 17 件
· 処理レコード: 3,412 行
· 経過: 1.8 s
);
case "streak":
return (
STREAK · MAY
27days
{Array.from({length:28}).map((_,i)=>(
))}
{["🥇","🔥","⭐"].map((e,i)=>(
{e}
))}
);
case "newtab":
return (
NEW TAB / WORK
{["Drive","Sheets","Gmail","Slack","Notion","Cal","Stripe","GA","Figma"].map((l,i)=>(
{l}
))}
);
case "sns-viewer":
return (
FEED · FILTERED
{[1,2,3].map(i=>(
@user_{i}2{i}m
{["業務改善のヒント。", "新しいツールを試した結果。", "今日のオペレーション進捗。"][i-1]}
))}
);
case "course-site":
case "members-msg":
return (
{t.en.toUpperCase()}
{(t.id==="course-site"
? [["Chapter 01 · イントロ","✓"],["Chapter 02 · 基礎編","✓"],["Chapter 03 · 応用編","45%"],["Chapter 04 · 実践編","—"]]
: [["田中 さん","2件の未読"],["山田 さん","既読"],["全体配信","12人着"]]
).map(([k,v],i)=>(
{k}
{v}
))}
);
case "house-task":
return (
HOUSEHOLD · THIS WEEK
{[["皿洗い","パートナー A"],["ごみ出し","パートナー B"],["買い物","パートナー A"],["子供のお迎え","パートナー B"]].map(([k,v],i)=>(
{k}
{v}
))}
);
case "split":
return (
SPLIT · MAY
¥ 12,840
A → B (50:50 折半 / 立替分差額)
);
case "pdf-edit":
case "pdf-compress":
return (
{t.id==="pdf-edit"?"PDF EDITOR":"PDF COMPRESS"}
{t.id==="pdf-compress" ? (
<>
12.4MB
→
1.1MB
SAVED 91%
>
) : (
{Array.from({length:8}).map((_,i)=>(
))}
)}
);
default:
return (
{t.en.toUpperCase()}
業務ヒアリングをもとに、必要十分な構成で実装した小〜中規模ツール。
);
}
}
Object.assign(window, { ToolShowcase });