Files
HealthCarePregnant/pcm-platform/patient-app/src/pages/HomePage.tsx
T
2026-06-18 09:48:05 +08:00

243 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Apple,
ArrowRight,
BarChart3,
CalendarCheck,
ClipboardCheck,
Droplet,
FileText,
HeartHandshake,
Leaf,
LineChart,
PencilLine,
Sparkles,
Sprout,
Heart,
Smile,
CheckCircle2,
type LucideIcon,
} from 'lucide-react';
import { api } from '../api/client';
import { useToast } from '../components/Toast';
import { usePatient } from '../lib/usePatient';
import { BuildArchive } from '../components/BuildArchive';
import { greeting, riskBadgeClass, riskLabel } from '../lib/format';
import './HomePage.css';
type Tone = 'peach' | 'rose' | 'mint' | 'lilac';
interface Intent {
label: string;
q?: string;
to?: string;
icon: LucideIcon;
tone: Tone;
}
const INTENTS: Intent[] = [
{ label: '我能吃这个吗?', q: '孕期可以吃西瓜吗', icon: Apple, tone: 'peach' },
{ label: '记录今天的血糖', to: '/data', icon: Droplet, tone: 'rose' },
{ label: '今天该注意什么?', q: '孕期日常需要注意什么', icon: Leaf, tone: 'mint' },
{ label: '孕期不适怎么办', q: '孕期恶心想吐怎么办', icon: HeartHandshake, tone: 'lilac' },
];
const QUICK: { label: string; to: string; icon: LucideIcon; tone: Tone }[] = [
{ label: '今日打卡', to: '/data', icon: ClipboardCheck, tone: 'peach' },
{ label: '血糖趋势', to: '/data', icon: LineChart, tone: 'rose' },
{ label: '产检提醒', to: '/tasks', icon: CalendarCheck, tone: 'mint' },
];
export function HomePage(): JSX.Element {
const navigate = useNavigate();
const { patient, loading, reload } = usePatient();
const { show } = useToast();
const [emotionScore, setEmotionScore] = useState<number | null>(null);
const [emotionNote, setEmotionNote] = useState('');
const [hasSubmittedEmotion, setHasSubmittedEmotion] = useState(false);
const [submittingEmotion, setSubmittingEmotion] = useState(false);
async function submitEmotion(): Promise<void> {
if (!emotionScore) {
show('请选择一个情绪分值哦~');
return;
}
setSubmittingEmotion(true);
try {
await api.createEmotion({ score: emotionScore, note: emotionNote || '今日情绪状况良好' });
show('打卡成功!AI 已过滤异常信号并同步至管理师,祝您好心情🌸');
setHasSubmittedEmotion(true);
setEmotionNote('');
} catch (err) {
show('心情记录失败,请稍后重试');
} finally {
setSubmittingEmotion(false);
}
}
function goChat(q?: string): void {
navigate('/chat', q ? { state: { initialQuestion: q } } : undefined);
}
if (!patient && !loading) {
return (
<div className="page">
<h1 className="section-title" style={{ marginTop: 0 }}>
</h1>
<BuildArchive onDone={reload} />
</div>
);
}
return (
<div className="page home">
<header className="home__greet">
<p className="home__hello">
{greeting()}{patient?.name ?? ''}
</p>
{patient && (
<p className="home__week">
{patient.gestationalWeeks}
{patient.gestationalDays ? ` ${patient.gestationalDays}` : ''}
<span className={`badge ${riskBadgeClass(patient.initialRiskLevel)}`} style={{ marginLeft: 8 }}>
{riskLabel(patient.initialRiskLevel)}
</span>
</p>
)}
</header>
{/* 渐变 Hero 聊天主入口(AI-first */}
<button className="home__hero" onClick={() => goChat()} type="button">
<div className="home__hero-glow" aria-hidden />
<div className="home__hero-body">
<span className="home__hero-eyebrow">
<Sparkles size={14} strokeWidth={2} /> AI
</span>
<span className="home__hero-title"></span>
<span className="home__hero-sub"></span>
</div>
<span className="home__hero-cta" aria-hidden>
<ArrowRight size={22} strokeWidth={2.2} />
</span>
</button>
{/* 暖色暖系低焦虑今日心情自评打卡(T-D.9) */}
<div className="home__emotion-card">
<h3 className="emotion-card__title">
<Heart size={16} fill="var(--color-destructive)" stroke="none" />
</h3>
{!hasSubmittedEmotion ? (
<div className="emotion-card__box">
<p className="emotion-card__desc">(1-10)</p>
<div className="emotion-card__selectors">
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((score) => (
<button
key={score}
type="button"
className={`emotion-score-btn ${emotionScore === score ? 'active' : ''}`}
onClick={() => setEmotionScore(score)}
>
{score === 10 ? <Smile size={14} /> : score}
</button>
))}
</div>
<textarea
className="emotion-card__textarea"
placeholder="今天有什么烦心事,或者身体上有什么不舒服吗?我们会实时保护您的私密信息,若有剧烈焦虑,个案管理师将主动为您致电疏导..."
value={emotionNote}
onChange={(e) => setEmotionNote(e.target.value)}
rows={2}
/>
<button
type="button"
className="emotion-card__submit-btn"
onClick={submitEmotion}
disabled={submittingEmotion}
>
{submittingEmotion ? '心情记录中…' : '提交今日自评'}
</button>
</div>
) : (
<div className="emotion-card__result animate-fade-in">
<CheckCircle2 size={18} style={{ color: 'var(--color-calm)' }} />
<span><strong>{emotionScore}</strong> · AI 🌸</span>
</div>
)}
</div>
{/* 今日健康 */}
<h2 className="section-title"></h2>
<div className="home__quick">
{QUICK.map((q) => (
<button
key={q.label}
className="home__quick-card"
data-tone={q.tone}
onClick={() => navigate(q.to)}
type="button"
>
<span className="home__quick-chip">
<q.icon size={22} strokeWidth={1.9} />
</span>
{q.label}
</button>
))}
</div>
{/* 意图卡片 */}
<h2 className="section-title"> / </h2>
<div className="home__intents">
{INTENTS.map((it) => (
<button
key={it.label}
className="home__intent"
data-tone={it.tone}
onClick={() => (it.to ? navigate(it.to) : goChat(it.q))}
type="button"
>
<span className="home__intent-chip">
<it.icon size={20} strokeWidth={1.9} />
</span>
<span className="home__intent-label">{it.label}</span>
</button>
))}
</div>
{/* 更多功能 */}
<h2 className="section-title"></h2>
<div className="home__more">
<button onClick={() => navigate('/data')} type="button">
<span className="home__more-chip">
<PencilLine size={22} strokeWidth={1.8} />
</span>
</button>
<button onClick={() => navigate('/me')} type="button">
<span className="home__more-chip">
<FileText size={22} strokeWidth={1.8} />
</span>
</button>
<button onClick={() => navigate('/data')} type="button">
<span className="home__more-chip">
<BarChart3 size={22} strokeWidth={1.8} />
</span>
</button>
<button onClick={() => goChat('孕期可以做哪些调养')} type="button">
<span className="home__more-chip">
<Sprout size={22} strokeWidth={1.8} />
</span>
</button>
</div>
</div>
);
}