feat(announcements): add tests, skeleton, pagination, and service context

- Add announcement-card test for component testing

- Add is-announcement-visible test and schema test for logic testing

- Add announcement-list-skeleton for loading states

- Add announcement-pagination for list pagination

- Add announcements-service-context and default-announcements-service for service layer
This commit is contained in:
SpecialX
2026-07-03 10:24:50 +08:00
parent 2859ef74f2
commit 2b95fd668b
15 changed files with 1539 additions and 206 deletions

View File

@@ -22,13 +22,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui
import { ConfirmDeleteDialog } from "@/shared/components/ui/confirm-delete-dialog"
import { cn, formatDate } from "@/shared/lib/utils"
import {
archiveAnnouncementAction,
deleteAnnouncementAction,
markAnnouncementAsReadAction,
publishAnnouncementAction,
toggleAnnouncementPinAction,
} from "../actions"
import { useAnnouncementsService } from "./announcements-service-context"
import type { Announcement } from "../types"
export function AnnouncementDetail({
@@ -44,6 +38,7 @@ export function AnnouncementDetail({
}) {
const t = useTranslations("announcements")
const router = useRouter()
const service = useAnnouncementsService()
const [isWorking, setIsWorking] = useState(false)
const [deleteOpen, setDeleteOpen] = useState(false)
const [isPinned, setIsPinned] = useState(announcement.isPinned)
@@ -56,7 +51,7 @@ export function AnnouncementDetail({
if (isRead) return
let cancelled = false
void markAnnouncementAsReadAction(announcement.id)
void service.markRead(announcement.id)
.then((res) => {
if (cancelled) return
if (res.success) {
@@ -70,12 +65,12 @@ export function AnnouncementDetail({
return () => {
cancelled = true
}
}, [canManage, isRead, announcement.id])
}, [canManage, isRead, announcement.id, service])
const handlePublish = async () => {
setIsWorking(true)
try {
const res = await publishAnnouncementAction(announcement.id)
const res = await service.publish(announcement.id)
if (res.success) {
toast.success(res.message)
router.refresh()
@@ -92,7 +87,7 @@ export function AnnouncementDetail({
const handleArchive = async () => {
setIsWorking(true)
try {
const res = await archiveAnnouncementAction(announcement.id)
const res = await service.archive(announcement.id)
if (res.success) {
toast.success(res.message)
router.refresh()
@@ -109,7 +104,7 @@ export function AnnouncementDetail({
const handleDelete = async () => {
setIsWorking(true)
try {
const res = await deleteAnnouncementAction(announcement.id)
const res = await service.delete(announcement.id)
if (res.success) {
toast.success(res.message)
router.push("/admin/announcements")
@@ -131,7 +126,7 @@ export function AnnouncementDetail({
// 乐观更新
setIsPinned(!prevPinned)
try {
const res = await toggleAnnouncementPinAction(announcement.id)
const res = await service.togglePin(announcement.id)
if (res.success) {
toast.success(t("messages.pinToggled"))
router.refresh()