Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
import { ChevronRight, Info } from "lucide-react";
|
|
import { Link } from "react-router";
|
|
import { useEpisodeMappingPreview } from "~/api/episode-mapping.ts";
|
|
import { useEntityDetail } from "~/api/library.ts";
|
|
import { consolePath } from "~/components/console/nav.ts";
|
|
import { StatusBadge } from "~/components/console/status-badge.tsx";
|
|
import { Panel } from "~/components/library/parts.tsx";
|
|
import { absoluteGroups, groupMethodsText, mappingHref } from "./model.ts";
|
|
|
|
// 订阅详情的集数映射摘要 + 订阅表单的绝对集号提示(批次 4)。数据来自模拟预览(不带草稿 = 当前规则)。
|
|
|
|
/** 订阅的季:有季单元取它的季号,否则不按季过滤 */
|
|
function useSubscriptionSeason(entityId: string, seasonUnitId: string | null | undefined): number | null {
|
|
const entity = useEntityDetail(seasonUnitId ? entityId : undefined);
|
|
if (!seasonUnitId) return null;
|
|
return entity.data?.contentUnits.find((u) => u.id === seasonUnitId)?.seasonNumber ?? null;
|
|
}
|
|
|
|
export function SubscriptionMappingSummary({
|
|
entityId,
|
|
seasonUnitId,
|
|
}: {
|
|
entityId: string;
|
|
seasonUnitId?: string | null;
|
|
}) {
|
|
const season = useSubscriptionSeason(entityId, seasonUnitId);
|
|
const preview = useEpisodeMappingPreview(entityId, season != null ? { season } : {});
|
|
const p = preview.data;
|
|
if (!p?.applicable || (p.groups.length === 0 && p.seasons.seasons.length === 0)) return null;
|
|
const pending = p.groups.reduce((n, g) => n + g.pending, 0);
|
|
return (
|
|
<Panel
|
|
title="集数映射"
|
|
description={`${season != null ? `第 ${season} 季` : "全部季"}最近资源按发布组的识别方式`}
|
|
actions={
|
|
<Link
|
|
to={mappingHref(consolePath("library", entityId))}
|
|
className="inline-flex items-center text-meta text-muted-foreground hover:text-foreground hover:underline"
|
|
>
|
|
到作品页调整
|
|
<ChevronRight className="h-3 w-3" />
|
|
</Link>
|
|
}
|
|
>
|
|
{p.groups.length ? (
|
|
<ul className="space-y-1 text-caption text-foreground">
|
|
{p.groups.map((g) => (
|
|
<li key={g.releaseGroup ?? "-"}>{groupMethodsText(g)}</li>
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<p className="text-caption text-muted-foreground">还没有绑定到这一季的资源。</p>
|
|
)}
|
|
{pending ? (
|
|
<div className="mt-2">
|
|
<StatusBadge tone="warning">待确认 {pending} 条</StatusBadge>
|
|
</div>
|
|
) : null}
|
|
</Panel>
|
|
);
|
|
}
|
|
|
|
/** 订阅表单:作品的近期资源里有发布组用绝对集号时提示一句,不阻塞 */
|
|
export function AbsoluteNumberingHint({ entityId }: { entityId: string | null }) {
|
|
const preview = useEpisodeMappingPreview(entityId, {});
|
|
const groups = absoluteGroups(preview.data);
|
|
if (!entityId || !groups.length) return null;
|
|
return (
|
|
<p className="flex items-start gap-1.5 text-meta text-muted-foreground">
|
|
<Info aria-hidden className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
|
<span>
|
|
{groups.map((g) => `「${g}」`).join("")}用的是绝对集号,已自动换算成季内集号;不对的话到
|
|
<Link className="mx-0.5 underline-offset-4 hover:underline" to={mappingHref(consolePath("library", entityId))}>
|
|
作品页的集数映射
|
|
</Link>
|
|
调整。
|
|
</span>
|
|
</p>
|
|
);
|
|
}
|