feat(FN-3240): move drag handle into provider actions and enhance planning

The merge brings several FN-3240 UI refinements that move and anchor drag handles into provider actions, alongside FN-3256 styling improvements to the planning mode modal and quick chat FAB. Key supporting changes include a durable insight lifecycle for FN-3243, a fix for list view detail callback t

Fusion-Task-Id: FN-3240
This commit is contained in:
Fusion
2026-05-03 03:44:22 -07:00
committed by gsxdsm
parent 5d2436df8c
commit 9d1954a124
3 changed files with 25 additions and 6 deletions

View File

@@ -140,6 +140,10 @@
flex-shrink: 0; flex-shrink: 0;
} }
.usage-provider-actions .usage-provider-drag-handle {
margin-left: var(--space-xs);
}
.usage-provider-drag-handle:hover { .usage-provider-drag-handle:hover {
color: var(--text-muted); color: var(--text-muted);
} }
@@ -161,6 +165,7 @@
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
gap: var(--space-sm); gap: var(--space-sm);
margin-left: auto;
flex-shrink: 0; flex-shrink: 0;
} }

View File

@@ -439,9 +439,6 @@ function ProviderCard({
onDragEnd={isTouchReorderMode ? undefined : onDragEnd} onDragEnd={isTouchReorderMode ? undefined : onDragEnd}
> >
<div className="usage-provider-header"> <div className="usage-provider-header">
<div className="usage-provider-drag-handle" aria-hidden="true">
<GripVertical size={16} />
</div>
<div className="usage-provider-info"> <div className="usage-provider-info">
<ProviderIcon provider={getProviderIconKey(provider.name)} size="md" /> <ProviderIcon provider={getProviderIconKey(provider.name)} size="md" />
<span className="usage-provider-name">{provider.name}</span> <span className="usage-provider-name">{provider.name}</span>
@@ -479,6 +476,9 @@ function ProviderCard({
</div> </div>
)} )}
{getStatusBadge()} {getStatusBadge()}
<div className="usage-provider-drag-handle" aria-hidden="true">
<GripVertical size={16} />
</div>
</div> </div>
</div> </div>

View File

@@ -151,7 +151,7 @@ describe("UsageIndicator", () => {
expect(screen.getByText("Hourly")).toBeInTheDocument(); expect(screen.getByText("Hourly")).toBeInTheDocument();
}); });
it("renders drag handle for each provider card", () => { it("renders drag handle in the right-side actions cluster for each provider card", () => {
mockUseUsageData.mockReturnValue({ mockUseUsageData.mockReturnValue({
providers: mockProviders, providers: mockProviders,
loading: false, loading: false,
@@ -162,8 +162,22 @@ describe("UsageIndicator", () => {
render(<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />); render(<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />);
const handles = document.querySelectorAll(".usage-provider-drag-handle"); const cards = Array.from(document.querySelectorAll(".usage-provider"));
expect(handles).toHaveLength(3); expect(cards).toHaveLength(3);
cards.forEach((card) => {
const info = card.querySelector(".usage-provider-info");
const actions = card.querySelector(".usage-provider-actions");
const handle = card.querySelector(".usage-provider-drag-handle");
const headerChildren = Array.from(card.querySelectorAll(":scope > .usage-provider-header > *"));
expect(info).toBeInTheDocument();
expect(actions).toBeInTheDocument();
expect(handle).toBeInTheDocument();
expect(actions).toContainElement(handle);
expect(info?.contains(handle)).toBe(false);
expect(headerChildren[0]).toBe(info);
});
}); });
it("reorders providers on drag and drop and persists order", () => { it("reorders providers on drag and drop and persists order", () => {