diff --git a/CHANGELOG.md b/CHANGELOG.md
index d5a2806ee0..b771b11b47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,8 @@ and this project adheres to
### Fixed
+- 500 error when navigating from collaborative editor to full history page
+ [#3941](https://github.com/OpenFn/lightning/pull/3941)
- Duplicate `isReadOnly` declaration in TriggerForm that was blocking asset
builds [#3976](https://github.com/OpenFn/lightning/issues/3976)
- Run duration and status alignment drift in history view
diff --git a/assets/js/collaborative-editor/components/diagram/CollaborativeWorkflowDiagram.tsx b/assets/js/collaborative-editor/components/diagram/CollaborativeWorkflowDiagram.tsx
index 7fc8bc94b5..ced33d7cfe 100644
--- a/assets/js/collaborative-editor/components/diagram/CollaborativeWorkflowDiagram.tsx
+++ b/assets/js/collaborative-editor/components/diagram/CollaborativeWorkflowDiagram.tsx
@@ -18,10 +18,13 @@ import {
useRunSteps,
} from '../../hooks/useHistory';
import { useIsNewWorkflow } from '../../hooks/useSessionContext';
+import { useVersionMismatch } from '../../hooks/useVersionMismatch';
+import { useVersionSelect } from '../../hooks/useVersionSelect';
import { useNodeSelection } from '../../hooks/useWorkflow';
import type { Run } from '../../types/history';
import MiniHistory from './MiniHistory';
+import { VersionMismatchBanner } from './VersionMismatchBanner';
import CollaborativeWorkflowDiagramImpl from './WorkflowDiagram';
interface CollaborativeWorkflowDiagramProps {
@@ -72,14 +75,31 @@ export function CollaborativeWorkflowDiagram({
// Use hook to get run steps with automatic subscription management
const currentRunSteps = useRunSteps(selectedRunId);
- // Update URL when run selection changes
- const handleRunSelect = useCallback((run: Run) => {
- setSelectedRunId(run.id);
+ // Detect version mismatch for warning banner
+ const versionMismatch = useVersionMismatch(selectedRunId);
- const url = new URL(window.location.href);
- url.searchParams.set('run', run.id);
- window.history.pushState({}, '', url.toString());
- }, []);
+ // Get version selection handler
+ const handleVersionSelect = useVersionSelect();
+
+ // Update URL when run selection changes
+ const handleRunSelect = useCallback(
+ (run: Run) => {
+ setSelectedRunId(run.id);
+
+ // Find the workorder that contains this run
+ const workorder = history.find(wo => wo.runs.some(r => r.id === run.id));
+
+ // Switch to the version this run was executed on
+ if (workorder) {
+ handleVersionSelect(workorder.version);
+ }
+
+ const url = new URL(window.location.href);
+ url.searchParams.set('run', run.id);
+ window.history.pushState({}, '', url.toString());
+ },
+ [history, handleVersionSelect]
+ );
// Clear URL parameter when deselecting run
const handleDeselectRun = useCallback(() => {
@@ -133,6 +153,15 @@ export function CollaborativeWorkflowDiagram({
return (
+ {/* Version mismatch warning when viewing latest but run used older version */}
+ {versionMismatch && (
+
+ )}
+
state.workflow);
+
// Clear expanded work order when panel collapses
React.useEffect(() => {
if (collapsed) {
@@ -131,49 +142,30 @@ export default function MiniHistory({
const gotoHistory = (e: React.MouseEvent | React.KeyboardEvent) => {
e.preventDefault();
e.stopPropagation();
- const currentUrl = new URL(window.location.href);
- const nextUrl = new URL(currentUrl);
- const paths = nextUrl.pathname.split('/');
- const wIdx = paths.indexOf('w');
- const workflowPaths = paths.splice(wIdx, paths.length - wIdx);
- nextUrl.pathname = paths.join('/') + `/history`;
- nextUrl.search = `?filters[workflow_id]=${
- workflowPaths[workflowPaths.length - 1]
- }`;
- window.location.assign(nextUrl.toString());
+
+ if (project?.id && workflow?.id) {
+ navigateToWorkflowHistory(project.id, workflow.id);
+ }
};
- const navigateToWorkorderHistory = (
+ const handleNavigateToWorkorderHistory = (
e: React.MouseEvent,
workorderId: string
) => {
e.preventDefault();
e.stopPropagation();
- const currentUrl = new URL(window.location.href);
- const nextUrl = new URL(currentUrl);
- const paths = nextUrl.pathname.split('/');
- const projectIndex = paths.indexOf('projects');
- const projectId = projectIndex !== -1 ? paths[projectIndex + 1] : null;
- if (projectId) {
- nextUrl.pathname = `/projects/${projectId}/history`;
- nextUrl.search = `?filters[workorder_id]=${workorderId}`;
- window.location.assign(nextUrl.toString());
+ if (project?.id) {
+ navigateToWorkOrderHistory(project.id, workorderId);
}
};
- const navigateToRunView = (e: React.MouseEvent, runId: string) => {
+ const handleNavigateToRunView = (e: React.MouseEvent, runId: string) => {
e.preventDefault();
e.stopPropagation();
- const currentUrl = new URL(window.location.href);
- const nextUrl = new URL(currentUrl);
- const paths = nextUrl.pathname.split('/');
- const projectIndex = paths.indexOf('projects');
- const projectId = projectIndex !== -1 ? paths[projectIndex + 1] : null;
- if (projectId) {
- nextUrl.pathname = `/projects/${projectId}/runs/${runId}`;
- window.location.assign(nextUrl.toString());
+ if (project?.id) {
+ navigateToRun(project.id, runId);
}
};
@@ -342,7 +334,7 @@ export default function MiniHistory({