doctor-mail/apps/web/src/components/models/post/detail/PostHeader.tsx

167 lines
4.3 KiB
TypeScript
Raw Normal View History

2025-01-24 00:19:02 +08:00
import { useContext } from "react";
import { PostDetailContext } from "./context/PostDetailContext";
import { motion } from "framer-motion";
import {
CalendarIcon,
UserCircleIcon,
LockClosedIcon,
LockOpenIcon,
StarIcon,
ClockIcon,
EyeIcon,
ChatBubbleLeftIcon,
} from "@heroicons/react/24/outline";
2025-01-25 00:46:59 +08:00
import { Button, Typography, Space, Tooltip } from "antd";
2025-01-24 00:19:02 +08:00
import { useVisitor } from "@nice/client";
2025-01-24 17:39:41 +08:00
import { PostState, VisitType } from "@nice/common";
import {
2025-01-25 00:46:59 +08:00
CalendarOutlined,
ClockCircleOutlined,
2025-01-24 17:39:41 +08:00
CommentOutlined,
EyeOutlined,
2025-01-25 00:46:59 +08:00
FileTextOutlined,
FolderOutlined,
2025-01-24 17:39:41 +08:00
LikeFilled,
LikeOutlined,
2025-01-25 00:46:59 +08:00
LockOutlined,
UnlockOutlined,
UserOutlined,
2025-01-24 17:39:41 +08:00
} from "@ant-design/icons";
import dayjs from "dayjs";
import { TitleSection } from "./PostHeader/TitleSection";
import {
AuthorBadge,
DateBadge,
TermBadge,
UpdatedBadge,
VisibilityBadge,
} from "./PostHeader/InfoBadge";
import { StatsSection } from "./PostHeader/StatsSection";
2025-01-25 00:46:59 +08:00
import { PostBadge } from "./badge/PostBadge";
2025-01-24 00:19:02 +08:00
2025-01-25 00:46:59 +08:00
const { Title, Paragraph, Text } = Typography;
2025-01-24 00:19:02 +08:00
export default function PostHeader() {
const { post, user } = useContext(PostDetailContext);
2025-01-24 17:39:41 +08:00
const { like, unLike } = useVisitor();
2025-01-24 00:19:02 +08:00
function likeThisPost() {
if (!post?.liked) {
2025-01-24 17:39:41 +08:00
post.likes += 1;
post.liked = true;
2025-01-24 00:19:02 +08:00
like.mutateAsync({
data: {
visitorId: user?.id || null,
postId: post.id,
type: VisitType.LIKE,
},
});
2025-01-24 17:39:41 +08:00
} else {
post.likes -= 1;
post.liked = false;
unLike.mutateAsync({
where: {
visitorId: user?.id || null,
postId: post.id,
type: VisitType.LIKE,
},
});
2025-01-24 00:19:02 +08:00
}
}
return (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
2025-01-25 00:46:59 +08:00
className="relative bg-gradient-to-br from-primary-250 via-primary-150 to--primary-350 rounded-lg p-6 shadow-lg border border-[#97A9C4]/30">
2025-01-24 00:19:02 +08:00
{/* Corner Decorations */}
2025-01-25 00:46:59 +08:00
<div className="absolute top-0 left-0 w-5 h-5 border-t-4 border-l-4 border-primary rounded-tl-lg" />
<div className="absolute bottom-0 right-0 w-5 h-5 border-b-4 border-r-4 border-primary rounded-br-lg" />
2025-01-24 00:19:02 +08:00
{/* Title Section */}
2025-01-24 17:39:41 +08:00
2025-01-25 00:46:59 +08:00
<TitleSection></TitleSection>
2025-01-24 00:19:02 +08:00
<div className="space-y-4">
2025-01-25 00:46:59 +08:00
{/* 收件人信息行 */}
<Space>
<UserOutlined className="text-secondary-400" />
<span className="text-secondary-400"></span>
<Text strong>
{post?.receivers?.map((receiver) => receiver?.showname)}
</Text>
</Space>
2025-01-24 00:19:02 +08:00
{/* First Row - Basic Info */}
2025-01-25 00:46:59 +08:00
<div className="flex flex-wrap items-center gap-1">
2025-01-24 00:19:02 +08:00
{/* Author Info Badge */}
2025-01-25 00:46:59 +08:00
<Space>
<UserOutlined className="text-secondary-400" />
<span className="text-secondary-400"></span>
<Text strong>
{post?.author?.showname || "匿名用户"}
</Text>
</Space>
<Text type="secondary">|</Text>
2025-01-24 00:19:02 +08:00
{/* Date Info Badge */}
2025-01-25 00:46:59 +08:00
<Space>
<CalendarOutlined className="text-secondary-400" />
<Text>
:
{dayjs(post?.createdAt).format("YYYY-MM-DD")}
</Text>
</Space>
<Text type="secondary">|</Text>
2025-01-24 00:19:02 +08:00
{/* Last Updated Badge */}
2025-01-25 00:46:59 +08:00
<Space>
<ClockCircleOutlined className="text-secondary-400" />
<Text>
:
{dayjs(post?.updatedAt).format("YYYY-MM-DD")}
</Text>
</Space>
<Text type="secondary">|</Text>
2025-01-24 00:19:02 +08:00
{/* Visibility Status Badge */}
2025-01-25 00:46:59 +08:00
<Space>
{post?.isPublic ? (
<UnlockOutlined className="text-secondary-400" />
) : (
<LockOutlined className="text-secondary-400" />
)}
<Text>{post?.isPublic ? "公开" : "私信"}</Text>
</Space>
2025-01-24 00:19:02 +08:00
</div>
{/* Second Row - Term and Tags */}
2025-01-25 00:46:59 +08:00
<div className="flex flex-wrap gap-1">
2025-01-24 00:19:02 +08:00
{/* Tags Badges */}
2025-01-24 17:39:41 +08:00
{post?.meta?.tags &&
post.meta.tags.length > 0 &&
post.meta.tags.map((tag, index) => (
2025-01-25 00:46:59 +08:00
<Space key={index}>
<PostBadge
type="tag"
value={`#${tag}`}></PostBadge>
</Space>
2025-01-24 17:39:41 +08:00
))}
2025-01-24 00:19:02 +08:00
</div>
</div>
{/* Content Section */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
2025-01-25 00:46:59 +08:00
className="mt-6 text-secondary-700">
2025-01-24 00:19:02 +08:00
<div
2025-01-25 00:46:59 +08:00
className="ql-editor p-0 space-y-4 leading-relaxed duration-300"
2025-01-24 00:19:02 +08:00
dangerouslySetInnerHTML={{ __html: post?.content || "" }}
/>
</motion.div>
{/* Stats Section */}
2025-01-25 00:46:59 +08:00
<StatsSection></StatsSection>
2025-01-24 00:19:02 +08:00
</motion.div>
);
}