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
84 changes: 79 additions & 5 deletions apps/azure/src/components/editor/FrameView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* only reads app/store state and wires the app-specific Analysis navigation.
*/
import React from 'react';
import { CanvasWorkspace, type ContextLinkGroup, type ContextLinkItem } from '@variscout/ui';
import {
CanvasWorkspace,
type ContextLinkGroup,
type ContextLinkItem,
type LogActionPayload,
} from '@variscout/ui';
import {
useImprovementProjectStore,
useInvestigationStore,
Expand All @@ -18,11 +23,23 @@ import type {
StepCapabilityStamp,
WorkflowReadinessSignals,
} from '@variscout/core';
import { createActionItem, type ActionItem } from '@variscout/core/findings';
import { azureHubRepository } from '../../persistence';
import { usePanelsStore } from '../../features/panels/panelsStore';
import { useInvestigationFeatureStore } from '../../features/investigation/investigationStore';

const EMPTY_PRIOR_STEP_STATS: ReadonlyMap<string, StepCapabilityStamp> = new Map();
const EMPTY_ACTION_ITEMS: ActionItem[] = [];

function mergeActionItems(
current: readonly ActionItem[],
next: readonly ActionItem[]
): ActionItem[] {
const byId = new Map<string, ActionItem>();
for (const item of current) byId.set(item.id, item);
for (const item of next) byId.set(item.id, item);
return Array.from(byId.values());
}

function priorStepStatsFromSnapshots(
snapshots: readonly EvidenceSnapshot[]
Expand Down Expand Up @@ -54,6 +71,12 @@ const FrameView: React.FC = () => {
const projectsByHub = useImprovementProjectStore(s => s.projectsByHub);
const [priorStepStats, setPriorStepStats] =
React.useState<ReadonlyMap<string, StepCapabilityStamp>>(EMPTY_PRIOR_STEP_STATS);
const [actionItems, setActionItems] = React.useState<ActionItem[]>(EMPTY_ACTION_ITEMS);
const activeHubIdRef = React.useRef<string | null>(activeHubId);

React.useEffect(() => {
activeHubIdRef.current = activeHubId;
}, [activeHubId]);

React.useEffect(() => {
if (!activeHubId) {
Expand All @@ -76,6 +99,28 @@ const FrameView: React.FC = () => {
};
}, [activeHubId]);

React.useEffect(() => {
setActionItems(EMPTY_ACTION_ITEMS);

if (!activeHubId) {
return;
}

let cancelled = false;
void (async () => {
try {
const items = await azureHubRepository.actionItems.listByHub(activeHubId);
if (!cancelled) setActionItems(items);
} catch {
// Keep any in-memory quick actions if the local repository is unavailable.
}
})();

return () => {
cancelled = true;
};
}, [activeHubId]);

const signals: WorkflowReadinessSignals = React.useMemo(
() => ({ hasIntervention: false, sustainmentConfirmed: false }),
[]
Expand Down Expand Up @@ -113,9 +158,37 @@ const FrameView: React.FC = () => {
usePanelsStore.getState().showAnalysis();
}, []);

const handleQuickAction = React.useCallback(() => {
usePanelsStore.getState().showImprovement();
}, []);
const handleLogQuickAction = React.useCallback(
(stepId: string, payload: LogActionPayload) => {
if (!activeHubId) return;
const actionItem = createActionItem(payload.text, {
stepId,
parentImprovementProjectId: null,
parentImprovementIdeaId: null,
assignedTo: payload.status === 'open' ? payload.assignedTo : null,
dueAt: payload.status === 'open' ? (payload.dueAt ?? null) : null,
status: payload.status,
doneAt: payload.status === 'done' ? new Date().toISOString() : null,
doneBy: null,
createdBy: { displayName: 'Local browser' },
});
setActionItems(current => mergeActionItems(current, [actionItem]));
void (async () => {
try {
await azureHubRepository.dispatch({
kind: 'ACTION_ITEM_ADD',
hubId: activeHubId,
actionItem,
});
const items = await azureHubRepository.actionItems.listByHub(activeHubId);
if (activeHubIdRef.current === activeHubId) setActionItems(items);
} catch {
// Keep the local quick action visible even when persistence is unavailable.
}
})();
},
[activeHubId]
);

const handleFocusedInvestigation = React.useCallback(() => {
usePanelsStore.getState().showInvestigation();
Expand Down Expand Up @@ -199,7 +272,7 @@ const FrameView: React.FC = () => {
processContext={processContext}
setProcessContext={setProcessContext}
onSeeData={handleSeeData}
onQuickAction={handleQuickAction}
onLogQuickAction={handleLogQuickAction}
onFocusedInvestigation={handleFocusedInvestigation}
findings={findings}
questions={questions}
Expand All @@ -216,6 +289,7 @@ const FrameView: React.FC = () => {
contextLinkGroups={contextLinkGroups}
onNavigateContextLink={handleNavigateContextLink}
priorStepStats={priorStepStats}
actionItems={actionItems}
/>
);
};
Expand Down
Loading