Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions apps/web/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -4052,6 +4052,88 @@ tr.insights-row-clickable:hover {
overflow-y: auto;
padding: 20px;
}
.drawer-sec-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.drawer-sec-head h3 {
margin: 0;
font-size: 12.5px;
font-weight: 800;
color: var(--ink-2);
}
.drawer-sec-head a {
font-size: 12.5px;
color: var(--ink-3);
text-decoration: none;
}
.drawer-sec-head a:hover {
color: var(--ink);
}
.drawer-list {
display: grid;
gap: 2px;
}
.drawer-li {
display: flex;
align-items: center;
gap: 10px;
min-height: 40px;
padding: 6px 8px;
margin: 0 -8px;
border-radius: var(--r-md);
color: inherit;
text-decoration: none;
transition: background-color 120ms ease;
}
a.drawer-li:hover,
.drawer-li:hover {
background: var(--hover);
}
.drawer-li > svg {
color: var(--ink-3);
flex: 0 0 auto;
}
.drawer-li-t {
flex: 1;
min-width: 0;
color: inherit;
text-decoration: none;
}
.drawer-li-t b,
.drawer-li-t span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.drawer-li-t b {
font-size: 13.5px;
font-weight: 600;
}
.drawer-li-t span {
font-size: 12px;
color: var(--ink-3);
}
.drawer-li-m {
font-size: 12px;
color: var(--ink-3);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.drawer-file-ic {
width: 32px;
height: 32px;
flex: 0 0 32px;
display: grid;
place-items: center;
border-radius: var(--r-md);
background: var(--surface-sunk);
box-shadow: inset 0 0 0 1px var(--line);
color: var(--ink-3);
}
@media (max-width: 860px) {
.wb[data-drawer="open"] {
grid-template-columns: minmax(0, 1fr) 0px;
Expand Down
47 changes: 47 additions & 0 deletions apps/web/src/bench/artifacts-tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Skeleton, formatRelativeTime } from "@corbits/react-ui";

import { ArtifactListPageSchema, useAPIQuery } from "@/api";
import { libraryArtifactPath } from "@/library";
import { FileText } from "@/lib/icons";
import { Link } from "@/navigation";
import { ARTIFACTS_PATH_PREFIX } from "@/path-ids";
import { benchLink } from "../shell/page-crumbs";

/** The files saved in this bench, newest first. */
export function ArtifactsTab({ workbenchTenantId }: { readonly workbenchTenantId: string }) {
const page = useAPIQuery(`/api/tenants/${workbenchTenantId}/artifacts`, ArtifactListPageSchema);
const rows =
page.kind === "ready"
? [...page.data.artifacts].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
: [];

return (
<section className="drawer-sec">
<div className="drawer-sec-head">
<h3>Files saved here</h3>
<Link to={benchLink(ARTIFACTS_PATH_PREFIX, workbenchTenantId)}>See all</Link>
</div>
{page.kind === "loading" ? <Skeleton className="h-16 w-full" /> : null}
{page.kind === "error" ? <p className="workbench-info-empty-note">{page.message}</p> : null}
{page.kind === "ready" && rows.length === 0 ? (
<p className="workbench-info-empty-note">Nothing saved here yet.</p>
) : null}
{rows.length > 0 ? (
<div className="drawer-list">
{rows.map((artifact) => (
<Link key={artifact.id} to={libraryArtifactPath(artifact.id)} className="drawer-li">
<span className="drawer-file-ic">
<FileText size={16} aria-hidden="true" />
</span>
<span className="drawer-li-t">
<b>{artifact.title}</b>
<span>{artifact.kind}</span>
</span>
<span className="drawer-li-m">{formatRelativeTime(artifact.updatedAt)}</span>
</Link>
))}
</div>
) : null}
</section>
);
}
68 changes: 68 additions & 0 deletions apps/web/src/bench/workflows-tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { RunNowButton, Skeleton } from "@corbits/react-ui";
import { useQuery } from "@tanstack/react-query";

import { routineDetailPath, useRoutineActions } from "@/global-routines";
import { scheduleSentence } from "@/pages/routines-page";
import { describeApiError } from "@/lib/api-query";
import { FlowArrow } from "@/lib/icons";
import { Link } from "@/navigation";
import { WORKFLOWS_PATH_PREFIX } from "@/path-ids";
import { tenantKeys } from "@/query-client";
import { listScheduledWorkflows } from "@/routines-api";
import { benchLink } from "../shell/page-crumbs";

/** The workflows deployed in this bench with their schedule and state. */
export function WorkflowsTab({ workbenchTenantId }: { readonly workbenchTenantId: string }) {
const actions = useRoutineActions();
const flows = useQuery({
queryKey: tenantKeys.routines(workbenchTenantId),
queryFn: () => listScheduledWorkflows(workbenchTenantId),
});
const rows = flows.data ?? [];

return (
<section className="drawer-sec">
<div className="drawer-sec-head">
<h3>Scheduled here</h3>
<Link to={benchLink(WORKFLOWS_PATH_PREFIX, workbenchTenantId)}>See all</Link>
</div>
{flows.isLoading ? <Skeleton className="h-16 w-full" /> : null}
{flows.isError ? (
<p className="workbench-info-empty-note">
{describeApiError(flows.error, "loading workflows")}
</p>
) : null}
{flows.isSuccess && rows.length === 0 ? (
<p className="workbench-info-empty-note">No workflows deployed here yet.</p>
) : null}
{rows.length > 0 ? (
<div className="drawer-list">
{rows.map((definition) => (
<div key={definition.definitionId} className="drawer-li">
<FlowArrow size={16} aria-hidden="true" />
<Link to={routineDetailPath(definition.definitionId)} className="drawer-li-t">
<b>{definition.name}</b>
<span>{scheduleSentence(definition.schedule)}</span>
</Link>
{definition.status === "deployed" ? (
<RunNowButton
variant="outline"
size="sm"
onRun={() =>
actions.runNow({
definition,
tenantId: workbenchTenantId,
tenantName: "",
})
}
/>
) : (
<span className="drawer-li-m">Paused</span>
)}
</div>
))}
</div>
) : null}
</section>
);
}
4 changes: 4 additions & 0 deletions apps/web/src/pages/workbench-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ import {
import { VoiceOverlay } from "../voice/voice-overlay";
import { BenchDrawer } from "../bench/bench-drawer";
import { BenchPill } from "../bench/bench-pill";
import { ArtifactsTab } from "../bench/artifacts-tab";
import { useWorkerStatus } from "../worker-status";
import { GrantsTab } from "../bench/grants-tab";
import { InformationTab } from "../bench/information-tab";
import { InsightsTab } from "../bench/insights-tab";
import { MembersTab } from "../bench/members-tab";
import { ToolsTab } from "../bench/tools-tab";
import { WorkflowsTab } from "../bench/workflows-tab";
import { useBench } from "../bench-context";
import { createFetchStockHub } from "../needs-converge";
import { workbenchKeys } from "../chat-path";
Expand Down Expand Up @@ -333,6 +335,7 @@ function Workbench({ workbenchTenantId }: { readonly workbenchTenantId: string }
participants={participants.data ?? []}
/>
),
Artifacts: <ArtifactsTab workbenchTenantId={workbenchTenantId} />,
Tools: <ToolsTab workbenchTenantId={workbenchTenantId} />,
Grants: <GrantsTab workbenchTenantId={workbenchTenantId} />,
Insights: <InsightsTab workbenchTenantId={workbenchTenantId} />,
Expand All @@ -342,6 +345,7 @@ function Workbench({ workbenchTenantId }: { readonly workbenchTenantId: string }
participants={participants.data ?? []}
/>
),
Workflows: <WorkflowsTab workbenchTenantId={workbenchTenantId} />,
}}
/>
</div>
Expand Down
Loading