🐛 fix: announcement manager
This commit is contained in:
parent
22b24041a7
commit
93926d9c99
@ -11,6 +11,12 @@
|
||||
"finished": "Finished",
|
||||
"import": "Import",
|
||||
"latestAnnouncement": "Latest Announcement",
|
||||
"announcements": "Announcements",
|
||||
"all": "All",
|
||||
"pinned": "Pinned",
|
||||
"pinnedOnly": "Pinned Only",
|
||||
"popup": "Popup",
|
||||
"popupOnly": "Popup Only",
|
||||
"manualImportMessage": "Please import manually",
|
||||
"mySubscriptions": "My Subscriptions",
|
||||
"nextResetDays": "Next Reset Days",
|
||||
|
||||
@ -11,6 +11,12 @@
|
||||
"finished": "已完成",
|
||||
"import": "一键导入",
|
||||
"latestAnnouncement": "最新公告",
|
||||
"announcements": "公告管理",
|
||||
"all": "全部",
|
||||
"pinned": "置顶",
|
||||
"pinnedOnly": "仅置顶",
|
||||
"popup": "弹窗",
|
||||
"popupOnly": "仅弹窗",
|
||||
"manualImportMessage": "请手动导入",
|
||||
"mySubscriptions": "我的订阅",
|
||||
"nextResetDays": "距离下次重置天数",
|
||||
|
||||
@ -32,7 +32,7 @@ export default function Announcement({ type }: { type: "popup" | "pinned" }) {
|
||||
skipErrorHandler: true,
|
||||
}
|
||||
);
|
||||
return result.data.data?.announcements.find((item) => item[type]) || null;
|
||||
return result.data.data?.announcements?.[0] || null;
|
||||
},
|
||||
enabled: !!user,
|
||||
});
|
||||
@ -44,13 +44,16 @@ export default function Announcement({ type }: { type: "popup" | "pinned" }) {
|
||||
<Dialog defaultOpen={!!data}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{data?.title}</DialogTitle>
|
||||
<DialogTitle>{data.title}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Markdown>{data?.content}</Markdown>
|
||||
<div className="prose prose-sm max-w-none dark:prose-invert">
|
||||
<Markdown>{data.content}</Markdown>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "pinned") {
|
||||
return (
|
||||
<>
|
||||
@ -59,9 +62,17 @@ export default function Announcement({ type }: { type: "popup" | "pinned" }) {
|
||||
{t("latestAnnouncement", "Latest Announcement")}
|
||||
</h2>
|
||||
<Card className="p-6">
|
||||
{data?.content ? <Markdown>{data?.content}</Markdown> : <Empty />}
|
||||
{data.content ? (
|
||||
<div className="prose prose-sm max-w-none dark:prose-invert">
|
||||
<Markdown>{data.content}</Markdown>
|
||||
</div>
|
||||
) : (
|
||||
<Empty />
|
||||
)}
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,34 +1,133 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Timeline } from "@workspace/ui/components/timeline";
|
||||
import Empty from "@workspace/ui/composed/empty";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@workspace/ui/components/card";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@workspace/ui/components/tabs";
|
||||
import {
|
||||
ProList,
|
||||
type ProListActions,
|
||||
} from "@workspace/ui/composed/pro-list/pro-list";
|
||||
import { Icon } from "@workspace/ui/composed/icon";
|
||||
import { Markdown } from "@workspace/ui/composed/markdown";
|
||||
import { queryAnnouncement } from "@workspace/ui/services/user/announcement";
|
||||
import { formatDate } from "@workspace/ui/utils/formatting";
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function Announcement() {
|
||||
const { data } = useQuery({
|
||||
queryKey: ["queryAnnouncement"],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryAnnouncement({
|
||||
page: 1,
|
||||
size: 99,
|
||||
pinned: false,
|
||||
popup: false,
|
||||
});
|
||||
return data.data?.announcements || [];
|
||||
},
|
||||
});
|
||||
return data && data.length > 0 ? (
|
||||
<Timeline
|
||||
data={
|
||||
data.map((item) => ({
|
||||
title: item.title,
|
||||
content: <Markdown>{item.content}</Markdown>,
|
||||
})) || []
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Empty border />
|
||||
const { t } = useTranslation("dashboard");
|
||||
const [activeTab, setActiveTab] = useState("all");
|
||||
const pinnedRef = useRef<ProListActions>(null);
|
||||
const normalRef = useRef<ProListActions>(null);
|
||||
|
||||
const requestAnnouncements = async (
|
||||
pagination: { page: number; size: number },
|
||||
filter: { pinned?: boolean; popup?: boolean }
|
||||
) => {
|
||||
const response = await queryAnnouncement({
|
||||
...pagination,
|
||||
...filter,
|
||||
});
|
||||
return {
|
||||
list: response.data.data?.announcements || [],
|
||||
total: response.data.data?.total || 0,
|
||||
};
|
||||
};
|
||||
|
||||
const renderAnnouncement = (item: any) => (
|
||||
<Card className="overflow-hidden">
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<CardTitle className="text-lg">{item.title}</CardTitle>
|
||||
{item.pinned && (
|
||||
<span className="flex items-center gap-1 rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary">
|
||||
<Icon className="size-3" icon="uil:pin" />
|
||||
{t("pinned", "Pinned")}
|
||||
</span>
|
||||
)}
|
||||
{item.popup && (
|
||||
<span className="flex items-center gap-1 rounded-full bg-orange-500/10 px-2 py-0.5 text-xs font-medium text-orange-500">
|
||||
<Icon className="size-3" icon="uil:popcorn" />
|
||||
{t("popup", "Popup")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<CardDescription className="text-sm">
|
||||
<time dateTime={item.created_at}>
|
||||
{formatDate(item.created_at)}
|
||||
</time>
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="prose prose-sm max-w-none dark:prose-invert">
|
||||
<Markdown>{item.content}</Markdown>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="flex items-center gap-1.5 font-semibold">
|
||||
<Icon className="size-5" icon="uil:bell" />
|
||||
{t("announcements", "Announcements")}
|
||||
</h2>
|
||||
<Tabs defaultValue="all" onValueChange={setActiveTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="all">
|
||||
{t("all", "All")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="pinned">
|
||||
{t("pinnedOnly", "Pinned Only")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="popup">
|
||||
{t("popupOnly", "Popup Only")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="all">
|
||||
<ProList
|
||||
action={normalRef}
|
||||
renderItem={renderAnnouncement}
|
||||
request={async (pagination) => {
|
||||
return requestAnnouncements(pagination, {});
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="pinned">
|
||||
<ProList
|
||||
action={pinnedRef}
|
||||
renderItem={renderAnnouncement}
|
||||
request={async (pagination) => {
|
||||
return requestAnnouncements(pagination, { pinned: true });
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="popup">
|
||||
<ProList
|
||||
action={normalRef}
|
||||
renderItem={renderAnnouncement}
|
||||
request={async (pagination) => {
|
||||
return requestAnnouncements(pagination, { popup: true });
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user