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
13 changes: 9 additions & 4 deletions apps/web/src/page/integrations/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Message } from "./message"
import type { Model } from "./model"
import { init } from "./update"
import { cardView, connectButton } from "./view"
import { awsDisconnectDialog } from "./view-aws"
import { cloudflareReconnectButton } from "./view-cloudflare"
import { gcpDialogs } from "./view-gcp-dialogs"

Expand Down Expand Up @@ -42,7 +43,7 @@ export const cardEmbed = Submodel.defineView<Model, Message, EmbedInputs>((model

/**
* A source's header: the Docs link, Connect while available, Cloudflare's Reconnect, and Google
* Cloud's removal dialogs (the source's Delete opens them). The source's own Delete disconnects.
* Cloud's and AWS's removal dialogs (the source's Delete opens them, on any tab).
*/
export const actionsEmbed = Submodel.defineView<Model, Message, EmbedInputs>((model, inputs, h) => {
const entry = catalogEntry(inputs.integration)
Expand All @@ -52,10 +53,14 @@ export const actionsEmbed = Submodel.defineView<Model, Message, EmbedInputs>((mo
overview?.kind === "connected" && inputs.integration === "cloudflare"
? [cloudflareReconnectButton(h, model)]
: []
const dialogs =
inputs.integration === "gcp" && (inputs.focusId ?? null) !== null
const isFocused = (inputs.focusId ?? null) !== null
const dialogs = !isFocused
? []
: inputs.integration === "gcp"
? gcpDialogs(h, model, inputs.timeZone)
: []
: inputs.integration === "aws"
? [awsDisconnectDialog(h, model)]
: []
return h.div(
[h.Class("flex shrink-0 items-center gap-3")],
[
Expand Down
8 changes: 5 additions & 3 deletions apps/web/src/page/integrations/view-aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,10 @@ const removedPanel = (h: H, model: Model, focusId: string | null): ReadonlyArray
),
)

const disconnectConfirm = (h: H, model: Model, status: V2AwsIntegration): Html => {
const connection = status.connections.find((entry) => entry.id === model.awsCard.disconnectId)
/** Mounted once: by the card on the integrations page, by the header on a source page. */
export const awsDisconnectDialog = (h: H, model: Model): Html => {
const status = Option.getOrNull(AsyncData.getData(model.statuses.aws.data))
const connection = status?.connections.find((entry) => entry.id === model.awsCard.disconnectId)
return confirmDialog(h, {
slotId: "aws-disconnect",
model: model.awsCard.disconnectDialog,
Expand Down Expand Up @@ -487,7 +489,7 @@ const connectedView = (
],
),
]),
disconnectConfirm(h, model, status),
...(focusId === null ? [awsDisconnectDialog(h, model)] : []),
],
)
}
Expand Down
55 changes: 54 additions & 1 deletion apps/web/src/page/sources/detail/detail.scene.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@ import {
V2SourceUsage,
} from "@maple/domain/http/v2"
import { Option, Result, Schema } from "effect"
import { expect as expectView, given, Mount, role, scene, text, withViewInputs } from "foldkit/scene"
import {
all,
expect as expectView,
expectAll,
given,
Mount,
role,
scene,
text,
withViewInputs,
} from "foldkit/scene"
import { describe, expect, test } from "vitest"
import { ResolveThemeColors, ThemeMessage } from "../../../charts/theme-colors"
import { toLoadFailure } from "../../../data/load-failure"
Expand Down Expand Up @@ -728,6 +738,49 @@ describe("integration focus", () => {
expect(clicked.model.connection?.awsCard.disconnectDialog.isOpen).toBe(true)
})

test("an AWS source's Delete shows the disconnect dialog from the Overview tab too", () => {
const connection = Schema.decodeSync(AwsConnectionId)("018f2b3c-4d5e-4f70-8192-a3b4c5d6e7f8")
const model = loaded(
source({
platform: "aws",
kind: "integration",
key_prefix: null,
integration_id: encodePublicId(PublicIdPrefixes.awsConnection, connection),
}),
)
const withStatus = pageUpdate(
model,
Message.GotConnectionMessage({
message: IntegrationMessage.CompletedLoadAws({
key: model.connection?.statuses.aws.key ?? "",
result: Result.succeed({
object: "aws_integration",
available: true,
modules: [],
connections: [awsConnection(connection)],
}),
}),
}),
).model
const name = "Disconnect AWS account 123456789012"
const mountDialog = Mount.resolve(Dialog.MountDialog, Dialog.Message.CompletedMountDialog())
scene(
{ update: pageUpdate, view },
given(pageUpdate(withStatus, Message.ClickedDelete()).model),
resolveTheme,
mountDialog,
expectView(role("alertdialog", { name })).toExist(),
)
// The Connection tab mounts it once, not once per card and header.
scene(
{ update: pageUpdate, view },
given(pageUpdate({ ...withStatus, tab: "connection" }, Message.ClickedDelete()).model),
resolveTheme,
mountDialog,
expectAll(all.role("alertdialog", { name })).toHaveCount(1),
)
})

test("disconnecting its own AWS connection leaves for the AWS page, where the cleanup waits", () => {
const connection = Schema.decodeSync(AwsConnectionId)("018f2b3c-4d5e-4f70-8192-a3b4c5d6e7f8")
const model = loaded(
Expand Down
Loading