// Dashboard mockups for hero and features
const { useEffect, useRef, useState } = React;
// === Animated bar chart ===
function MockBarChart({ values = [42, 58, 51, 67, 74, 68, 82], color = '#22c55e' }) {
const max = Math.max(...values);
return (
{values.map((v, i) => (
{['月','火','水','木','金','土','日'][i]}
))}
);
}
// === Animated counter ===
function Counter({ to, duration = 1400, suffix = '', decimals = 0, prefix = '' }) {
const [val, setVal] = useState(0);
const ref = useRef(null);
const started = useRef(false);
useEffect(() => {
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting && !started.current) {
started.current = true;
const start = performance.now();
const tick = (now) => {
const t = Math.min(1, (now - start)/duration);
const eased = 1 - Math.pow(1 - t, 3);
setVal(eased * to);
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}
}, { threshold: 0.3 });
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, [to, duration]);
return {prefix}{val.toFixed(decimals)}{suffix};
}
// === Hero dashboard mockup ===
function HeroDashboard() {
return (
{/* window chrome */}
{/* header row */}
RETENTION RATE
▲ +12.3% vs. 先月
{['7D','30D','90D'].map((t,i) => (
{t}
))}
{/* chart */}
{/* meta cards */}
離脱予兆アラート
{[1,1,1,1,1,1,1,0,0,0].map((on,i) => (
))}
{/* Live tail / AI activity */}
✓ 田中様の問診票を解析 · 2s
↻ フォロー文面を生成中 · 5s
✓ 山本様にLINE送信完了 · 12s
);
}
// === LINE message mockup ===
function LineChatMock() {
return (
{/* received bubble */}
田中さま、先日はご来院ありがとうございました。
腰の張り、その後いかがでしょうか?
今週は施術頻度を少し上げると回復が早まる時期です🌱
14:32
ありがとうございます!
明日18時で予約お願いします🙇
14:34 · 既読
);
}
// === AI Intake analysis mockup ===
function IntakeMock() {
const tags = [
{ label: '主訴: 腰痛', conf: 0.98, c: '#22c55e' },
{ label: '部位: 腰椎L4-L5', conf: 0.92, c: '#06b6d4' },
{ label: 'デスクワーク要因', conf: 0.87, c: '#7c3aed' },
{ label: '再発リスク: 中', conf: 0.71, c: '#fb7847' },
];
return (
「3週間前からデスクワーク中に左腰が張る感じ。前屈すると痛い。朝起きた時が一番つらく…」
{tags.map((t, i) => (
{t.label}
{Math.round(t.conf*100)}%
))}
);
}
// === AI Trend chart mockup ===
function TrendMock() {
const points = [25, 32, 28, 41, 38, 52, 58, 49, 65, 71, 68, 82];
const max = 100;
const w = 280, h = 120;
const step = w / (points.length - 1);
const path = points.map((p, i) => `${i===0?'M':'L'} ${i*step} ${h - (p/max)*h}`).join(' ');
const area = path + ` L ${w} ${h} L 0 ${h} Z`;
return (
3回目までの来院転換率
82%
● 腰痛: 41%
● 肩こり: 32%
● 膝痛: 18%
);
}
// === AI follow draft mockup ===
function FollowDraftMock() {
return (
FOR: 田中 健一 様 · 来院3回目
田中さま、こんにちは。あおぞら整骨院です。
先日の施術お疲れさまでした。
腰の張り、デスクワーク中に少し気をつけていただくと、回復のスピードが上がります。
来週、3回目の調整をおすすめする時期です🌱
);
}
Object.assign(window, { HeroDashboard, LineChatMock, IntakeMock, TrendMock, FollowDraftMock, MockBarChart, Counter });