Add kitchen sink plugin example
This commit is contained in:
@@ -24,6 +24,8 @@ import { StatusIcon } from "../components/StatusIcon";
|
||||
import { PriorityIcon } from "../components/PriorityIcon";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { Identity } from "../components/Identity";
|
||||
import { PluginSlotMount, PluginSlotOutlet, usePluginSlots } from "@/plugins/slots";
|
||||
import { PluginLauncherOutlet } from "@/plugins/launchers";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -168,6 +170,7 @@ export function IssueDetail() {
|
||||
queryFn: () => issuesApi.get(issueId!),
|
||||
enabled: !!issueId,
|
||||
});
|
||||
const resolvedCompanyId = issue?.companyId ?? selectedCompanyId;
|
||||
|
||||
const { data: comments } = useQuery({
|
||||
queryKey: queryKeys.issues.comments(issueId!),
|
||||
@@ -257,6 +260,21 @@ export function IssueDetail() {
|
||||
companyId: selectedCompanyId,
|
||||
userId: currentUserId,
|
||||
});
|
||||
const { slots: issuePluginDetailSlots } = usePluginSlots({
|
||||
slotTypes: ["detailTab"],
|
||||
entityType: "issue",
|
||||
companyId: resolvedCompanyId,
|
||||
enabled: !!resolvedCompanyId,
|
||||
});
|
||||
const issuePluginTabItems = useMemo(
|
||||
() => issuePluginDetailSlots.map((slot) => ({
|
||||
value: `plugin:${slot.pluginKey}:${slot.id}`,
|
||||
label: slot.displayName,
|
||||
slot,
|
||||
})),
|
||||
[issuePluginDetailSlots],
|
||||
);
|
||||
const activePluginTab = issuePluginTabItems.find((item) => item.value === detailTab) ?? null;
|
||||
|
||||
const agentMap = useMemo(() => {
|
||||
const map = new Map<string, Agent>();
|
||||
@@ -678,6 +696,47 @@ export function IssueDetail() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PluginSlotOutlet
|
||||
slotTypes={["toolbarButton", "contextMenuItem"]}
|
||||
entityType="issue"
|
||||
context={{
|
||||
companyId: issue.companyId,
|
||||
projectId: issue.projectId ?? null,
|
||||
entityId: issue.id,
|
||||
entityType: "issue",
|
||||
}}
|
||||
className="flex flex-wrap gap-2"
|
||||
itemClassName="inline-flex"
|
||||
missingBehavior="placeholder"
|
||||
/>
|
||||
|
||||
<PluginLauncherOutlet
|
||||
placementZones={["toolbarButton"]}
|
||||
entityType="issue"
|
||||
context={{
|
||||
companyId: issue.companyId,
|
||||
projectId: issue.projectId ?? null,
|
||||
entityId: issue.id,
|
||||
entityType: "issue",
|
||||
}}
|
||||
className="flex flex-wrap gap-2"
|
||||
itemClassName="inline-flex"
|
||||
/>
|
||||
|
||||
<PluginSlotOutlet
|
||||
slotTypes={["taskDetailView"]}
|
||||
entityType="issue"
|
||||
context={{
|
||||
companyId: issue.companyId,
|
||||
projectId: issue.projectId ?? null,
|
||||
entityId: issue.id,
|
||||
entityType: "issue",
|
||||
}}
|
||||
className="space-y-3"
|
||||
itemClassName="rounded-lg border border-border p-3"
|
||||
missingBehavior="placeholder"
|
||||
/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">Attachments</h3>
|
||||
@@ -766,12 +825,19 @@ export function IssueDetail() {
|
||||
<ActivityIcon className="h-3.5 w-3.5" />
|
||||
Activity
|
||||
</TabsTrigger>
|
||||
{issuePluginTabItems.map((item) => (
|
||||
<TabsTrigger key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="comments">
|
||||
<CommentThread
|
||||
comments={commentsWithRunMeta}
|
||||
linkedRuns={timelineRuns}
|
||||
companyId={issue.companyId}
|
||||
projectId={issue.projectId}
|
||||
issueStatus={issue.status}
|
||||
agentMap={agentMap}
|
||||
draftKey={`paperclip:issue-comment-draft:${issue.id}`}
|
||||
@@ -844,6 +910,21 @@ export function IssueDetail() {
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
{activePluginTab && (
|
||||
<TabsContent value={activePluginTab.value}>
|
||||
<PluginSlotMount
|
||||
slot={activePluginTab.slot}
|
||||
context={{
|
||||
companyId: issue.companyId,
|
||||
projectId: issue.projectId ?? null,
|
||||
entityId: issue.id,
|
||||
entityType: "issue",
|
||||
}}
|
||||
missingBehavior="placeholder"
|
||||
/>
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
|
||||
{linkedApprovals && linkedApprovals.length > 0 && (
|
||||
|
||||
@@ -19,7 +19,8 @@ import { PageSkeleton } from "../components/PageSkeleton";
|
||||
import { PageTabBar } from "../components/PageTabBar";
|
||||
import { projectRouteRef, cn } from "../lib/utils";
|
||||
import { Tabs } from "@/components/ui/tabs";
|
||||
import { PluginSlotMount, usePluginSlots } from "@/plugins/slots";
|
||||
import { PluginLauncherOutlet } from "@/plugins/launchers";
|
||||
import { PluginSlotMount, PluginSlotOutlet, usePluginSlots } from "@/plugins/slots";
|
||||
|
||||
/* ── Top-level tab types ── */
|
||||
|
||||
@@ -405,6 +406,37 @@ export function ProjectDetail() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PluginSlotOutlet
|
||||
slotTypes={["toolbarButton", "contextMenuItem"]}
|
||||
entityType="project"
|
||||
context={{
|
||||
companyId: resolvedCompanyId ?? null,
|
||||
companyPrefix: companyPrefix ?? null,
|
||||
projectId: project.id,
|
||||
projectRef: canonicalProjectRef,
|
||||
entityId: project.id,
|
||||
entityType: "project",
|
||||
}}
|
||||
className="flex flex-wrap gap-2"
|
||||
itemClassName="inline-flex"
|
||||
missingBehavior="placeholder"
|
||||
/>
|
||||
|
||||
<PluginLauncherOutlet
|
||||
placementZones={["toolbarButton"]}
|
||||
entityType="project"
|
||||
context={{
|
||||
companyId: resolvedCompanyId ?? null,
|
||||
companyPrefix: companyPrefix ?? null,
|
||||
projectId: project.id,
|
||||
projectRef: canonicalProjectRef,
|
||||
entityId: project.id,
|
||||
entityType: "project",
|
||||
}}
|
||||
className="flex flex-wrap gap-2"
|
||||
itemClassName="inline-flex"
|
||||
/>
|
||||
|
||||
<Tabs value={activeTab ?? "list"} onValueChange={(value) => handleTabChange(value as ProjectTab)}>
|
||||
<PageTabBar
|
||||
items={[
|
||||
|
||||
Reference in New Issue
Block a user