@codemation/next-host 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/app/(shell)/credentials/page.tsx +5 -0
- package/app/(shell)/dashboard/page.tsx +14 -0
- package/app/(shell)/layout.tsx +11 -0
- package/app/(shell)/page.tsx +5 -0
- package/app/(shell)/users/page.tsx +5 -0
- package/app/(shell)/workflows/[workflowId]/page.tsx +19 -0
- package/app/(shell)/workflows/page.tsx +5 -0
- package/app/api/[[...path]]/route.ts +40 -0
- package/app/api/auth/[...nextauth]/route.ts +3 -0
- package/app/globals.css +997 -0
- package/app/invite/[token]/page.tsx +10 -0
- package/app/layout.tsx +65 -0
- package/app/login/layout.tsx +25 -0
- package/app/login/page.tsx +22 -0
- package/components.json +21 -0
- package/docs/FORMS.md +46 -0
- package/docs/TAILWIND_SHADCN_MIGRATION.md +89 -0
- package/eslint.config.mjs +56 -0
- package/middleware.ts +29 -0
- package/next-env.d.ts +6 -0
- package/next.config.ts +34 -0
- package/package.json +76 -0
- package/postcss.config.mjs +7 -0
- package/public/canvas-icons/builtin/openai.svg +5 -0
- package/src/api/CodemationApiClient.ts +107 -0
- package/src/api/CodemationApiHttpError.ts +17 -0
- package/src/auth/CodemationNextAuthConfigResolver.ts +14 -0
- package/src/auth/CodemationNextAuthOAuthProviderDescriptorMapper.ts +30 -0
- package/src/auth/CodemationNextAuthOAuthProviderSnapshotResolver.ts +17 -0
- package/src/auth/CodemationNextAuthProviderCatalog.ts +107 -0
- package/src/auth/codemationEdgeAuth.ts +25 -0
- package/src/auth/codemationNextAuth.ts +32 -0
- package/src/components/Codemation.tsx +6 -0
- package/src/components/CodemationDataTable.tsx +37 -0
- package/src/components/CodemationDialog.tsx +137 -0
- package/src/components/CodemationFormattedDateTime.tsx +46 -0
- package/src/components/GoogleColorGIcon.tsx +39 -0
- package/src/components/OauthProviderIcon.tsx +33 -0
- package/src/components/PasswordStrengthMeter.tsx +59 -0
- package/src/components/forms/index.ts +28 -0
- package/src/components/json/JsonMonacoEditor.tsx +75 -0
- package/src/components/oauthProviderIconData.ts +17 -0
- package/src/components/ui/alert.tsx +56 -0
- package/src/components/ui/badge.tsx +40 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +70 -0
- package/src/components/ui/collapsible.tsx +26 -0
- package/src/components/ui/dialog.tsx +137 -0
- package/src/components/ui/dropdown-menu.tsx +238 -0
- package/src/components/ui/form.tsx +147 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/scroll-area.tsx +47 -0
- package/src/components/ui/select.tsx +169 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +76 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle.tsx +41 -0
- package/src/features/credentials/components/CredentialConfirmDialog.tsx +58 -0
- package/src/features/credentials/components/CredentialDialog.tsx +252 -0
- package/src/features/credentials/components/CredentialDialogFeedback.tsx +36 -0
- package/src/features/credentials/components/CredentialDialogFieldRows.tsx +257 -0
- package/src/features/credentials/components/CredentialDialogFormSections.tsx +230 -0
- package/src/features/credentials/components/CredentialEnvFieldStatusRow.tsx +64 -0
- package/src/features/credentials/components/CredentialFieldCopyButton.tsx +48 -0
- package/src/features/credentials/components/CredentialsScreenHealthBadge.tsx +21 -0
- package/src/features/credentials/components/CredentialsScreenInstancesTable.tsx +108 -0
- package/src/features/credentials/components/CredentialsScreenTestFailureAlert.tsx +33 -0
- package/src/features/credentials/hooks/useCredentialCreateDialog.ts +33 -0
- package/src/features/credentials/hooks/useCredentialDialogSession.ts +616 -0
- package/src/features/credentials/hooks/useCredentialsScreen.ts +213 -0
- package/src/features/credentials/lib/credentialFieldHelpers.ts +35 -0
- package/src/features/credentials/lib/credentialFormTypes.ts +1 -0
- package/src/features/credentials/lib/credentialInstanceTestPayloadParser.ts +10 -0
- package/src/features/credentials/screens/CredentialsScreen.tsx +187 -0
- package/src/features/invite/screens/InviteAcceptScreen.tsx +190 -0
- package/src/features/users/components/UsersInviteDialog.tsx +121 -0
- package/src/features/users/components/UsersRegenerateDialog.tsx +81 -0
- package/src/features/users/components/UsersScreenUserStatusBadge.tsx +19 -0
- package/src/features/users/schemas/usersInviteFormSchema.ts +7 -0
- package/src/features/users/screens/UsersScreen.tsx +240 -0
- package/src/features/workflows/components/WorkflowListFolderSection.tsx +91 -0
- package/src/features/workflows/components/WorkflowListItemCard.tsx +67 -0
- package/src/features/workflows/components/WorkflowListRoot.tsx +39 -0
- package/src/features/workflows/components/WorkflowsListTree.tsx +28 -0
- package/src/features/workflows/components/canvas/CanvasNodeChromeTooltip.tsx +96 -0
- package/src/features/workflows/components/canvas/CanvasNodeIconSlot.tsx +25 -0
- package/src/features/workflows/components/canvas/VisibleNodeStatusResolver.tsx +84 -0
- package/src/features/workflows/components/canvas/WorkflowCanvas.tsx +248 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNode.tsx +182 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAccents.tsx +73 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentBottomSourceHandles.tsx +43 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentLabels.tsx +47 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeCard.tsx +202 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeHandles.tsx +77 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeLabelBelow.tsx +51 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeMainGlyph.tsx +64 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeToolbar.tsx +95 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasLoadingPlaceholder.tsx +69 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasNodeIcon.tsx +102 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasSimpleIconGlyph.tsx +21 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasStraightCountEdge.tsx +33 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasStructureSignature.tsx +7 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasSymmetricForkEdge.tsx +32 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasToolbarIconButton.tsx +95 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasBuiltinIconRegistry.ts +26 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeCountResolver.ts +51 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeStyleResolver.ts +35 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasLabelLayoutEstimator.ts +42 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasOverlapResolver.ts +78 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasPortOrderResolver.ts +25 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasRoundedOrthogonalPathPlanner.ts +56 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasSiIconRegistry.ts +18 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasSymmetricForkPathPlanner.ts +43 -0
- package/src/features/workflows/components/canvas/lib/layoutWorkflow.ts +315 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasEdgeGeometry.ts +3 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasEmbeddedStyles.ts +62 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasFlowTypes.ts +10 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasNodeData.ts +41 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasNodeGeometry.ts +99 -0
- package/src/features/workflows/components/canvas/workflowCanvasNodeChrome.tsx +46 -0
- package/src/features/workflows/components/realtime/RealtimeContext.tsx +14 -0
- package/src/features/workflows/components/realtime/WorkflowRealtimeProvider.tsx +15 -0
- package/src/features/workflows/components/workflowDetail/NodeCredentialBindingRow.tsx +209 -0
- package/src/features/workflows/components/workflowDetail/NodeCredentialBindingsSection.tsx +227 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesConfigSection.tsx +51 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesPanelHeader.tsx +50 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesSlidePanel.tsx +134 -0
- package/src/features/workflows/components/workflowDetail/WorkflowActivationErrorDialog.tsx +71 -0
- package/src/features/workflows/components/workflowDetail/WorkflowActivationHeaderControl.tsx +64 -0
- package/src/features/workflows/components/workflowDetail/WorkflowDetailIcons.tsx +52 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspector.tsx +110 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorDetailBody.tsx +213 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorPanes.tsx +239 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorSidebarResizer.tsx +31 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorTreePanel.tsx +133 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentGroupingPresenter.tsx +31 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentList.tsx +118 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorBinaryView.tsx +15 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorErrorView.tsx +107 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorJsonView.tsx +114 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreePresenter.tsx +132 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreeViewRenderer.tsx +147 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyView.tsx +65 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorViews.tsx +5 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryAttachmentRow.tsx +74 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryUploadRow.tsx +69 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorDialog.tsx +254 -0
- package/src/features/workflows/components/workflowDetail/WorkflowRunsList.tsx +89 -0
- package/src/features/workflows/components/workflowDetail/WorkflowRunsSidebar.tsx +50 -0
- package/src/features/workflows/hooks/canvas/useWorkflowCanvasVisibleNodeStatuses.ts +14 -0
- package/src/features/workflows/hooks/realtime/realtime.tsx +271 -0
- package/src/features/workflows/hooks/realtime/runQueryPolling.ts +34 -0
- package/src/features/workflows/hooks/realtime/useWorkflowRealtimeInfrastructure.ts +541 -0
- package/src/features/workflows/hooks/realtime/useWorkflowRealtimeShowDisconnectedBadge.ts +9 -0
- package/src/features/workflows/hooks/workflowDetail/useWorkflowDetailController.tsx +1300 -0
- package/src/features/workflows/lib/realtime/realtimeApi.ts +78 -0
- package/src/features/workflows/lib/realtime/realtimeClientBridge.ts +52 -0
- package/src/features/workflows/lib/realtime/realtimeDomainTypes.ts +191 -0
- package/src/features/workflows/lib/realtime/realtimeQueryKeys.ts +15 -0
- package/src/features/workflows/lib/realtime/realtimeRunMutations.ts +167 -0
- package/src/features/workflows/lib/realtime/workflowTypes.ts +5 -0
- package/src/features/workflows/lib/workflowDetail/PersistedWorkflowSnapshotMapper.ts +205 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowActivationHttpErrorFormat.ts +32 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowDetailPresenter.ts +1017 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowDetailUrlCodec.ts +70 -0
- package/src/features/workflows/lib/workflowDetail/workflowDetailTypes.ts +152 -0
- package/src/features/workflows/lib/workflowDetailTreeStyles.ts +65 -0
- package/src/features/workflows/screens/WorkflowDetailScreen.tsx +236 -0
- package/src/features/workflows/screens/WorkflowDetailScreenInspectorPanel.tsx +55 -0
- package/src/features/workflows/screens/WorkflowsList.tsx +35 -0
- package/src/features/workflows/screens/WorkflowsScreen.tsx +31 -0
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
- package/src/middleware/CodemationNextHostMiddlewarePathRules.ts +31 -0
- package/src/providers/CodemationSessionProvider.tsx +23 -0
- package/src/providers/Providers.tsx +36 -0
- package/src/providers/RealtimeBoundary.tsx +17 -0
- package/src/providers/WhitelabelProvider.tsx +22 -0
- package/src/server/CodemationAuthPrismaClient.ts +21 -0
- package/src/server/CodemationNextHost.ts +379 -0
- package/src/shell/AppLayout.tsx +141 -0
- package/src/shell/AppLayoutNavItems.tsx +129 -0
- package/src/shell/AppLayoutPageHeader.tsx +79 -0
- package/src/shell/AppLayoutSidebarBrand.tsx +33 -0
- package/src/shell/AppMainContent.tsx +17 -0
- package/src/shell/AppShellHeaderActions.tsx +12 -0
- package/src/shell/AppShellHeaderActionsAuthenticated.tsx +51 -0
- package/src/shell/CodemationNextClientShell.tsx +17 -0
- package/src/shell/CredentialsSignInRedirectResolver.ts +21 -0
- package/src/shell/LoginPageClient.tsx +231 -0
- package/src/shell/WorkflowDetailChromeContext.tsx +42 -0
- package/src/shell/WorkflowFolderTreeBuilder.ts +62 -0
- package/src/shell/WorkflowFolderUi.ts +42 -0
- package/src/shell/WorkflowSidebarNavFolder.tsx +112 -0
- package/src/shell/WorkflowSidebarNavTree.tsx +68 -0
- package/src/shell/appLayoutPageTitle.ts +16 -0
- package/src/shell/appLayoutSidebarIcons.tsx +108 -0
- package/src/whitelabel/CodemationWhitelabelSnapshot.ts +4 -0
- package/src/whitelabel/CodemationWhitelabelSnapshotFactory.ts +18 -0
- package/tsconfig.json +40 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +34 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
|
|
2
|
+
|
|
3
|
+
export type WorkflowFolderTreeNode = Readonly<{
|
|
4
|
+
segment: string;
|
|
5
|
+
children: ReadonlyArray<WorkflowFolderTreeNode>;
|
|
6
|
+
workflows: ReadonlyArray<WorkflowSummary>;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
type MutableNode = {
|
|
10
|
+
segment: string;
|
|
11
|
+
childMap: Map<string, MutableNode>;
|
|
12
|
+
workflows: WorkflowSummary[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class WorkflowFolderTreeBuilder {
|
|
16
|
+
build(workflows: ReadonlyArray<WorkflowSummary>): WorkflowFolderTreeNode {
|
|
17
|
+
const root: MutableNode = { segment: "", childMap: new Map(), workflows: [] };
|
|
18
|
+
const sorted = [...workflows].sort((left, right) => {
|
|
19
|
+
const leftKey = (left.discoveryPathSegments ?? []).join("/");
|
|
20
|
+
const rightKey = (right.discoveryPathSegments ?? []).join("/");
|
|
21
|
+
const keyCompare = leftKey.localeCompare(rightKey);
|
|
22
|
+
if (keyCompare !== 0) {
|
|
23
|
+
return keyCompare;
|
|
24
|
+
}
|
|
25
|
+
return left.name.localeCompare(right.name);
|
|
26
|
+
});
|
|
27
|
+
for (const workflow of sorted) {
|
|
28
|
+
this.insertWorkflow(root, workflow);
|
|
29
|
+
}
|
|
30
|
+
return this.freezeNode(root);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private insertWorkflow(root: MutableNode, workflow: WorkflowSummary): void {
|
|
34
|
+
const segments = workflow.discoveryPathSegments ?? [];
|
|
35
|
+
if (segments.length === 0) {
|
|
36
|
+
root.workflows.push(workflow);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const folderParts = segments.length > 1 ? segments.slice(0, -1) : [];
|
|
40
|
+
let node = root;
|
|
41
|
+
for (const part of folderParts) {
|
|
42
|
+
let next = node.childMap.get(part);
|
|
43
|
+
if (!next) {
|
|
44
|
+
next = { segment: part, childMap: new Map(), workflows: [] };
|
|
45
|
+
node.childMap.set(part, next);
|
|
46
|
+
}
|
|
47
|
+
node = next;
|
|
48
|
+
}
|
|
49
|
+
node.workflows.push(workflow);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private freezeNode(node: MutableNode): WorkflowFolderTreeNode {
|
|
53
|
+
const children = [...node.childMap.values()]
|
|
54
|
+
.sort((left, right) => left.segment.localeCompare(right.segment))
|
|
55
|
+
.map((child) => this.freezeNode(child));
|
|
56
|
+
return {
|
|
57
|
+
segment: node.segment,
|
|
58
|
+
children,
|
|
59
|
+
workflows: [...node.workflows],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
|
|
2
|
+
|
|
3
|
+
import type { WorkflowFolderTreeNode } from "./WorkflowFolderTreeBuilder";
|
|
4
|
+
|
|
5
|
+
export class WorkflowFolderUi {
|
|
6
|
+
/** Expand folders that lie on the path to the workflow open in the shell. */
|
|
7
|
+
static computeDefaultFolderOpen(
|
|
8
|
+
folderPath: readonly string[],
|
|
9
|
+
pathname: string,
|
|
10
|
+
workflows: ReadonlyArray<WorkflowSummary>,
|
|
11
|
+
): boolean {
|
|
12
|
+
if (folderPath.length === 0) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const match = pathname.match(/^\/workflows\/([^/]+)/);
|
|
16
|
+
if (!match) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const active = workflows.find((w) => w.id === decodeURIComponent(match[1]!));
|
|
20
|
+
if (!active) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const segs = active.discoveryPathSegments;
|
|
24
|
+
if (segs.length < folderPath.length) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
for (let i = 0; i < folderPath.length; i += 1) {
|
|
28
|
+
if (segs[i] !== folderPath[i]) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static countWorkflowsInSubtree(node: WorkflowFolderTreeNode): number {
|
|
36
|
+
let total = node.workflows.length;
|
|
37
|
+
for (const child of node.children) {
|
|
38
|
+
total += WorkflowFolderUi.countWorkflowsInSubtree(child);
|
|
39
|
+
}
|
|
40
|
+
return total;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
|
|
5
|
+
import { ChevronRight, Folder } from "lucide-react";
|
|
6
|
+
import type { ReactNode } from "react";
|
|
7
|
+
|
|
8
|
+
import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
|
|
9
|
+
|
|
10
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
|
|
13
|
+
import type { WorkflowFolderTreeNode } from "./WorkflowFolderTreeBuilder";
|
|
14
|
+
import { WorkflowFolderUi } from "./WorkflowFolderUi";
|
|
15
|
+
|
|
16
|
+
import { IconWorkflow } from "./appLayoutSidebarIcons";
|
|
17
|
+
|
|
18
|
+
function folderTestId(path: ReadonlyArray<string>): string {
|
|
19
|
+
return `nav-workflow-folder-${path.join("__")}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function WorkflowSidebarNavFolder(
|
|
23
|
+
args: Readonly<{
|
|
24
|
+
node: WorkflowFolderTreeNode;
|
|
25
|
+
pathPrefix: ReadonlyArray<string>;
|
|
26
|
+
pathname: string;
|
|
27
|
+
workflows: ReadonlyArray<WorkflowSummary>;
|
|
28
|
+
workflowLinkClass: (isActive: boolean) => string;
|
|
29
|
+
depth: number;
|
|
30
|
+
collapsed?: boolean;
|
|
31
|
+
}>,
|
|
32
|
+
): ReactNode {
|
|
33
|
+
const { node, pathPrefix, pathname, workflows, workflowLinkClass, depth } = args;
|
|
34
|
+
const collapsed = args.collapsed === true;
|
|
35
|
+
const folderPath = [...pathPrefix, node.segment];
|
|
36
|
+
const defaultOpen = WorkflowFolderUi.computeDefaultFolderOpen(folderPath, pathname, workflows);
|
|
37
|
+
const totalInTree = WorkflowFolderUi.countWorkflowsInSubtree(node);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Collapsible
|
|
41
|
+
defaultOpen={defaultOpen}
|
|
42
|
+
className={cn("rounded-lg", !collapsed && depth > 0 && "ml-0.5 border-l border-sidebar-border/60 pl-2")}
|
|
43
|
+
>
|
|
44
|
+
<CollapsibleTrigger
|
|
45
|
+
type="button"
|
|
46
|
+
data-testid={folderTestId(folderPath)}
|
|
47
|
+
aria-label={collapsed ? folderPath.join(" / ") : undefined}
|
|
48
|
+
className={cn(
|
|
49
|
+
"group/trigger flex w-full min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm outline-none transition-colors",
|
|
50
|
+
collapsed && "justify-center px-1 py-2",
|
|
51
|
+
"text-sidebar-foreground/90 hover:bg-sidebar-accent/70",
|
|
52
|
+
"focus-visible:ring-2 focus-visible:ring-sidebar-ring/40",
|
|
53
|
+
"[&[data-state=open]>svg:first-child]:rotate-90",
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
<ChevronRight
|
|
57
|
+
className="size-3.5 shrink-0 text-muted-foreground transition-transform duration-200 ease-out"
|
|
58
|
+
aria-hidden
|
|
59
|
+
/>
|
|
60
|
+
<Folder className="size-3.5 shrink-0 text-primary/70" aria-hidden />
|
|
61
|
+
{!collapsed && (
|
|
62
|
+
<>
|
|
63
|
+
<span className="min-w-0 flex-1 truncate font-medium">{node.segment}</span>
|
|
64
|
+
<span className="shrink-0 rounded-md bg-sidebar-accent/80 px-1.5 py-0.5 text-[0.65rem] font-semibold tabular-nums text-muted-foreground">
|
|
65
|
+
{totalInTree}
|
|
66
|
+
</span>
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
</CollapsibleTrigger>
|
|
70
|
+
<CollapsibleContent>
|
|
71
|
+
<div className={cn("space-y-0.5 pb-1 pt-0.5", collapsed ? "pl-0" : "pl-1")}>
|
|
72
|
+
{node.workflows.map((w) => {
|
|
73
|
+
const href = `/workflows/${encodeURIComponent(w.id)}`;
|
|
74
|
+
const isActive = pathname === href;
|
|
75
|
+
return (
|
|
76
|
+
<Link
|
|
77
|
+
key={w.id}
|
|
78
|
+
href={href}
|
|
79
|
+
className={workflowLinkClass(isActive)}
|
|
80
|
+
data-testid={`nav-workflow-${w.id}`}
|
|
81
|
+
aria-label={
|
|
82
|
+
collapsed
|
|
83
|
+
? w.discoveryPathSegments.length > 0
|
|
84
|
+
? w.discoveryPathSegments.join(" / ")
|
|
85
|
+
: w.name
|
|
86
|
+
: undefined
|
|
87
|
+
}
|
|
88
|
+
>
|
|
89
|
+
<span className="flex shrink-0 opacity-70" aria-hidden>
|
|
90
|
+
<IconWorkflow />
|
|
91
|
+
</span>
|
|
92
|
+
{!collapsed && <span className="truncate text-sm">{w.name}</span>}
|
|
93
|
+
</Link>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
{node.children.map((child) => (
|
|
97
|
+
<WorkflowSidebarNavFolder
|
|
98
|
+
key={child.segment}
|
|
99
|
+
node={child}
|
|
100
|
+
pathPrefix={folderPath}
|
|
101
|
+
pathname={pathname}
|
|
102
|
+
workflows={workflows}
|
|
103
|
+
workflowLinkClass={workflowLinkClass}
|
|
104
|
+
depth={depth + 1}
|
|
105
|
+
collapsed={collapsed}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
</CollapsibleContent>
|
|
110
|
+
</Collapsible>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
|
|
7
|
+
import type { WorkflowSummary } from "../features/workflows/hooks/realtime/realtime";
|
|
8
|
+
|
|
9
|
+
import { WorkflowFolderTreeBuilder } from "./WorkflowFolderTreeBuilder";
|
|
10
|
+
|
|
11
|
+
import { WorkflowSidebarNavFolder } from "./WorkflowSidebarNavFolder";
|
|
12
|
+
|
|
13
|
+
import { IconWorkflow } from "./appLayoutSidebarIcons";
|
|
14
|
+
|
|
15
|
+
const treeBuilder = new WorkflowFolderTreeBuilder();
|
|
16
|
+
|
|
17
|
+
export function WorkflowSidebarNavTree(
|
|
18
|
+
args: Readonly<{
|
|
19
|
+
workflows: ReadonlyArray<WorkflowSummary>;
|
|
20
|
+
pathname: string;
|
|
21
|
+
workflowLinkClass: (isActive: boolean) => string;
|
|
22
|
+
/** Icon-only / compact layout (collapsed shell sidebar). */
|
|
23
|
+
collapsed?: boolean;
|
|
24
|
+
}>,
|
|
25
|
+
): ReactNode {
|
|
26
|
+
const tree = treeBuilder.build(args.workflows);
|
|
27
|
+
const collapsed = args.collapsed === true;
|
|
28
|
+
return (
|
|
29
|
+
<div className="space-y-0.5" data-testid="workflow-sidebar-nav-tree">
|
|
30
|
+
{tree.workflows.map((w) => {
|
|
31
|
+
const href = `/workflows/${encodeURIComponent(w.id)}`;
|
|
32
|
+
const isActive = args.pathname === href;
|
|
33
|
+
return (
|
|
34
|
+
<Link
|
|
35
|
+
key={w.id}
|
|
36
|
+
href={href}
|
|
37
|
+
className={args.workflowLinkClass(isActive)}
|
|
38
|
+
data-testid={`nav-workflow-${w.id}`}
|
|
39
|
+
aria-label={
|
|
40
|
+
collapsed
|
|
41
|
+
? w.discoveryPathSegments.length > 0
|
|
42
|
+
? w.discoveryPathSegments.join(" / ")
|
|
43
|
+
: w.name
|
|
44
|
+
: undefined
|
|
45
|
+
}
|
|
46
|
+
>
|
|
47
|
+
<span className="flex shrink-0 opacity-70" aria-hidden>
|
|
48
|
+
<IconWorkflow />
|
|
49
|
+
</span>
|
|
50
|
+
{!collapsed && <span className="truncate text-sm">{w.name}</span>}
|
|
51
|
+
</Link>
|
|
52
|
+
);
|
|
53
|
+
})}
|
|
54
|
+
{tree.children.map((child) => (
|
|
55
|
+
<WorkflowSidebarNavFolder
|
|
56
|
+
key={child.segment}
|
|
57
|
+
node={child}
|
|
58
|
+
pathPrefix={[]}
|
|
59
|
+
pathname={args.pathname}
|
|
60
|
+
workflows={args.workflows}
|
|
61
|
+
workflowLinkClass={args.workflowLinkClass}
|
|
62
|
+
depth={0}
|
|
63
|
+
collapsed={collapsed}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function getPageTitle(
|
|
2
|
+
pathname: string,
|
|
3
|
+
workflows: ReadonlyArray<{ id: string; name: string }>,
|
|
4
|
+
shellDefaultTitle: string,
|
|
5
|
+
): string {
|
|
6
|
+
if (pathname === "/dashboard") return "Dashboard";
|
|
7
|
+
if (pathname === "/credentials") return "Credentials";
|
|
8
|
+
if (pathname === "/users") return "Users";
|
|
9
|
+
if (pathname === "/workflows") return "Workflows";
|
|
10
|
+
const workflowMatch = pathname.match(/^\/workflows\/([^/]+)/);
|
|
11
|
+
if (workflowMatch) {
|
|
12
|
+
const w = workflows.find((x) => x.id === decodeURIComponent(workflowMatch[1]));
|
|
13
|
+
return w?.name ?? "Workflow";
|
|
14
|
+
}
|
|
15
|
+
return shellDefaultTitle;
|
|
16
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export const IconDashboard = () => (
|
|
2
|
+
<svg
|
|
3
|
+
width="20"
|
|
4
|
+
height="20"
|
|
5
|
+
viewBox="0 0 24 24"
|
|
6
|
+
fill="none"
|
|
7
|
+
stroke="currentColor"
|
|
8
|
+
strokeWidth="2"
|
|
9
|
+
strokeLinecap="round"
|
|
10
|
+
strokeLinejoin="round"
|
|
11
|
+
aria-hidden
|
|
12
|
+
>
|
|
13
|
+
<rect width="7" height="9" x="3" y="3" rx="1" />
|
|
14
|
+
<rect width="7" height="5" x="14" y="3" rx="1" />
|
|
15
|
+
<rect width="7" height="9" x="14" y="12" rx="1" />
|
|
16
|
+
<rect width="7" height="5" x="3" y="16" rx="1" />
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const IconCredentials = () => (
|
|
21
|
+
<svg
|
|
22
|
+
width="20"
|
|
23
|
+
height="20"
|
|
24
|
+
viewBox="0 0 24 24"
|
|
25
|
+
fill="none"
|
|
26
|
+
stroke="currentColor"
|
|
27
|
+
strokeWidth="2"
|
|
28
|
+
strokeLinecap="round"
|
|
29
|
+
strokeLinejoin="round"
|
|
30
|
+
aria-hidden
|
|
31
|
+
>
|
|
32
|
+
<circle cx="8" cy="15" r="4" />
|
|
33
|
+
<path d="M10.85 12.15L19 4" />
|
|
34
|
+
<path d="m18 5 2 2" />
|
|
35
|
+
<path d="m15 8 2 2" />
|
|
36
|
+
</svg>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const IconChevronLeft = () => (
|
|
40
|
+
<svg
|
|
41
|
+
width="18"
|
|
42
|
+
height="18"
|
|
43
|
+
viewBox="0 0 24 24"
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
strokeWidth="2"
|
|
47
|
+
strokeLinecap="round"
|
|
48
|
+
strokeLinejoin="round"
|
|
49
|
+
aria-hidden
|
|
50
|
+
>
|
|
51
|
+
<path d="m15 18-6-6 6-6" />
|
|
52
|
+
</svg>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const IconChevronRight = () => (
|
|
56
|
+
<svg
|
|
57
|
+
width="18"
|
|
58
|
+
height="18"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
fill="none"
|
|
61
|
+
stroke="currentColor"
|
|
62
|
+
strokeWidth="2"
|
|
63
|
+
strokeLinecap="round"
|
|
64
|
+
strokeLinejoin="round"
|
|
65
|
+
aria-hidden
|
|
66
|
+
>
|
|
67
|
+
<path d="m9 18 6-6-6-6" />
|
|
68
|
+
</svg>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
export const IconWorkflow = () => (
|
|
72
|
+
<svg
|
|
73
|
+
width="16"
|
|
74
|
+
height="16"
|
|
75
|
+
viewBox="0 0 24 24"
|
|
76
|
+
fill="none"
|
|
77
|
+
stroke="currentColor"
|
|
78
|
+
strokeWidth="2"
|
|
79
|
+
strokeLinecap="round"
|
|
80
|
+
strokeLinejoin="round"
|
|
81
|
+
aria-hidden
|
|
82
|
+
>
|
|
83
|
+
<rect width="8" height="8" x="3" y="3" rx="1" />
|
|
84
|
+
<path d="M7 11v4a2 2 0 0 0 2 2h4" />
|
|
85
|
+
<path d="M15 7h4a2 2 0 0 1 2 2v4" />
|
|
86
|
+
<path d="M3 11h4" />
|
|
87
|
+
<path d="M11 3h4" />
|
|
88
|
+
</svg>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export const IconUsers = () => (
|
|
92
|
+
<svg
|
|
93
|
+
width="20"
|
|
94
|
+
height="20"
|
|
95
|
+
viewBox="0 0 24 24"
|
|
96
|
+
fill="none"
|
|
97
|
+
stroke="currentColor"
|
|
98
|
+
strokeWidth="2"
|
|
99
|
+
strokeLinecap="round"
|
|
100
|
+
strokeLinejoin="round"
|
|
101
|
+
aria-hidden
|
|
102
|
+
>
|
|
103
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
|
104
|
+
<circle cx="9" cy="7" r="4" />
|
|
105
|
+
<path d="M22 21v-2a4 4 0 0 0-3-3.87" />
|
|
106
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
107
|
+
</svg>
|
|
108
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CodemationConfig } from "@codemation/host";
|
|
2
|
+
import { ApiPaths } from "@codemation/host-src/presentation/http/ApiPaths";
|
|
3
|
+
|
|
4
|
+
import type { CodemationWhitelabelSnapshot } from "./CodemationWhitelabelSnapshot";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds shell branding from the consumer `codemation.config` object (same source as {@link CodemationApplication.useConfig}).
|
|
8
|
+
*/
|
|
9
|
+
export class CodemationWhitelabelSnapshotFactory {
|
|
10
|
+
static fromConsumerConfig(config: CodemationConfig): CodemationWhitelabelSnapshot {
|
|
11
|
+
const w = config.whitelabel;
|
|
12
|
+
const rawName = w?.productName?.trim();
|
|
13
|
+
const productName = rawName !== undefined && rawName.length > 0 ? rawName : "Codemation";
|
|
14
|
+
const logoPath = w?.logoPath?.trim();
|
|
15
|
+
const logoUrl = logoPath !== undefined && logoPath.length > 0 ? ApiPaths.whitelabelLogo() : null;
|
|
16
|
+
return { productName, logoUrl };
|
|
17
|
+
}
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"types": ["node"],
|
|
5
|
+
"jsx": "preserve",
|
|
6
|
+
"noEmit": true,
|
|
7
|
+
"incremental": true,
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "next"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"baseUrl": ".",
|
|
14
|
+
"paths": {
|
|
15
|
+
"@/*": ["./src/*"],
|
|
16
|
+
"@codemation/core": ["../../packages/core/src/index.ts"],
|
|
17
|
+
"@codemation/core/bootstrap": ["../../packages/core/src/bootstrap/index.ts"],
|
|
18
|
+
"@codemation/core/testing": ["../../packages/core/src/testing.ts"],
|
|
19
|
+
"@codemation/cli": ["../../packages/cli/src/index.ts"],
|
|
20
|
+
"@codemation/core-nodes": ["../../packages/core-nodes/src/index.ts"],
|
|
21
|
+
"@codemation/core-nodes-gmail": ["../../packages/core-nodes-gmail/src/index.ts"],
|
|
22
|
+
"@codemation/eventbus-redis": ["../../packages/eventbus-redis/src/index.ts"],
|
|
23
|
+
"@codemation/host": ["../../packages/host/src/index.ts"],
|
|
24
|
+
"@codemation/host/client": ["../../packages/host/src/client.ts"],
|
|
25
|
+
"@codemation/host/credentials": ["../../packages/host/src/credentials.ts"],
|
|
26
|
+
"@codemation/host/consumer": ["../../packages/host/src/consumer.ts"],
|
|
27
|
+
"@codemation/host/next/server": ["../../packages/host/src/nextServer.ts"],
|
|
28
|
+
"@codemation/host/server": ["../../packages/host/src/server.ts"],
|
|
29
|
+
"@codemation/host/dev-server-sidecar": ["../../packages/host/src/devServerSidecar.ts"],
|
|
30
|
+
"@codemation/host/persistence": ["../../packages/host/src/persistenceServer.ts"],
|
|
31
|
+
"@codemation/host-src/*": ["../../packages/host/src/*"],
|
|
32
|
+
"@codemation/next-host": ["./src/index.ts"],
|
|
33
|
+
"@codemation/next-host/src/*": ["./src/*"],
|
|
34
|
+
"@codemation/node-example": ["../../packages/node-example/src/index.ts"],
|
|
35
|
+
"@codemation/queue-bullmq": ["../../packages/queue-bullmq/src/index.ts"]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"include": ["next-env.d.ts", "app/**/*.ts", "app/**/*.tsx", "src/**/*.ts", "src/**/*.tsx", ".next/types/**/*.ts"],
|
|
39
|
+
"exclude": ["node_modules"]
|
|
40
|
+
}
|