"use client"; import { useCallback, useMemo, useState } from "react"; import { useStore } from "zustand"; import { ListTodo, Search } from "lucide-react"; import type { Issue } from "@multica/core/types"; import { actorIssuesViewStore, type ActorIssuesScope, } from "@multica/core/issues/stores/actor-issues-view-store"; import { Button } from "@multica/ui/components/ui/button"; import { Input } from "@multica/ui/components/ui/input"; import { Tooltip, TooltipContent, TooltipTrigger } from "@multica/ui/components/ui/tooltip"; import { IssueDisplayControls, ViewRefreshIndicator, } from "../issues/components/issues-header"; import { IssueSurface } from "../issues/surface/issue-surface"; import { matchesPinyin } from "../editor/extensions/pinyin-match"; import { useT } from "../i18n"; export type TaskActorType = "member" | "agent"; const SCOPE_VALUES: ActorIssuesScope[] = ["assigned", "created"]; function issueMatchesSearch(issue: Issue, rawQuery: string) { const query = rawQuery.trim().toLowerCase(); if (!query) return true; const title = issue.title ?? ""; return ( title.toLowerCase().includes(query) || issue.identifier.toLowerCase().includes(query) || matchesPinyin(title, query) ); } function ActorIssuesHeader({ issues, search, onSearchChange, scope, onScopeChange, isRefreshing = false, }: { issues: Issue[]; search: string; onSearchChange: (value: string) => void; scope: ActorIssuesScope; onScopeChange: (scope: ActorIssuesScope) => void; isRefreshing?: boolean; }) { const { t } = useT("issues"); return (
onSearchChange(e.target.value)} placeholder={t(($) => $.actor_issues.search_placeholder)} className="h-8 w-64 pl-8 text-sm" />
{SCOPE_VALUES.map((value) => ( onScopeChange(value)} > {t(($) => $.actor_issues.scope[value].label)} } /> {t(($) => $.actor_issues.scope[value].description)} ))}
); } export function ActorIssuesPanel({ actorType, actorId, }: { actorType: TaskActorType; actorId: string; }) { const { t } = useT("issues"); const scope = useStore(actorIssuesViewStore, (s) => s.scope); const setScope = useStore(actorIssuesViewStore, (s) => s.setScope); const [search, setSearch] = useState(""); const clientFilter = useCallback( (issue: Issue) => issueMatchesSearch(issue, search), [search], ); const surfaceScope = useMemo( () => ({ type: "actor" as const, actorType, actorId, relation: scope, }), [actorId, actorType, scope], ); return ( search.trim() !== ""} renderHeader={({ controller }) => ( )} renderEmpty={({ controller }) => controller.surfaceIssues.length === 0 ? (

{t(($) => $.actor_issues.empty[scope].title)}

{t(($) => $.actor_issues.empty[scope].description)}

) : (

{t(($) => $.actor_issues.search_empty)}

) } /> ); }