import { n as __exportAll } from "./chunk-B-1-B7_t.js";
import { n as Badge_default } from "./badge-BbQrSqE-.js";
import { n as Button_default } from "./button-B9CnnuTu.js";
import { c as ComboboxAnchor_default, i as ComboboxInput_default, l as Combobox_default, n as ComboboxItemIndicator_default, o as ComboboxEmpty_default, r as ComboboxItem_default, s as ComboboxContent_default } from "./combobox-D0Svg8GU.js";
import { t as FieldFrame_default } from "./field-frame-DhCOWcMP.js";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, shallowRef, toDisplayString, unref, useModel, watch, withCtx, withModifiers } from "vue";
import { ChevronsUpDown, Loader2, Plug, Search } from "@lucide/vue";
//#region src/composables/useConnectorLoader.ts
/**
* Shared loader for `integration-connector` compositions. Wraps a
* Promise-returning `onLoad` callback (and an optional per-row
* `onConnect`) with the in-flight token / error / loading state every
* connector composition needs.
*
*   const {
*     connectors, isLoading, error, refresh,
*     isConnecting, connect,
*   } = useConnectorLoader(() => fetch("/connectors").then((r) => r.json()))
*
* `refresh()` re-invokes `onLoad` and replaces the cached list. `connect`
* forwards to a caller-supplied `onConnect`; when that resolves the list
* is refreshed automatically so the new status surfaces.
*/
function useConnectorLoader(onLoad) {
	const connectors = shallowRef([]);
	const isLoading = ref(false);
	const error = ref(null);
	const isConnecting = ref({});
	let token = 0;
	let disposed = false;
	async function refresh() {
		const myToken = ++token;
		isLoading.value = true;
		error.value = null;
		try {
			const next = await onLoad();
			if (disposed || myToken !== token) return;
			connectors.value = Array.isArray(next) ? next : [];
		} catch (err) {
			if (disposed || myToken !== token) return;
			connectors.value = [];
			error.value = err instanceof Error && err.message ? err.message : "Load failed";
		} finally {
			if (!disposed && myToken === token) isLoading.value = false;
		}
	}
	async function connect(id, onConnect) {
		if (!onConnect) return;
		isConnecting.value = {
			...isConnecting.value,
			[id]: true
		};
		try {
			await onConnect(id);
			await refresh();
		} finally {
			const next = { ...isConnecting.value };
			delete next[id];
			isConnecting.value = next;
		}
	}
	onMounted(() => {
		refresh();
	});
	onBeforeUnmount(() => {
		disposed = true;
		token++;
	});
	return {
		connectors,
		isLoading,
		error,
		refresh,
		isConnecting,
		connect
	};
}
//#endregion
//#region src/components/compositions/elements/picker/IntegrationConnectorPicker.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = { class: "flex min-w-0 flex-1 items-center gap-2" };
var _hoisted_2 = {
	key: 2,
	class: "truncate text-sm",
	"data-testid": "integration-connector-picker-label"
};
var _hoisted_3 = {
	key: 3,
	class: "truncate text-sm text-muted-foreground",
	"data-testid": "integration-connector-picker-placeholder"
};
var _hoisted_4 = ["data-status"];
var _hoisted_5 = { class: "flex items-center border-b px-2 py-1.5" };
var _hoisted_6 = {
	key: 0,
	class: "px-2 py-2 text-sm text-muted-foreground",
	"data-testid": "integration-connector-picker-loading-text"
};
var _hoisted_7 = {
	key: 1,
	class: "flex flex-col gap-2 px-2 py-2",
	"data-testid": "integration-connector-picker-error"
};
var _hoisted_8 = { class: "text-sm text-destructive" };
var _hoisted_9 = { class: "flex flex-1 items-center gap-2" };
var _hoisted_10 = { class: "truncate text-sm" };
var _hoisted_11 = ["data-status", "data-testid"];
var IntegrationConnectorPicker_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
	__name: "IntegrationConnectorPicker",
	props: /* @__PURE__ */ mergeModels({
		modelValue: {},
		id: {},
		name: {},
		label: {},
		hideLabel: { type: Boolean },
		placeholder: {},
		required: { type: Boolean },
		readonly: { type: Boolean },
		disabled: { type: Boolean },
		helpText: {},
		errors: { default: () => [] },
		onLoadConnectors: {},
		onConnect: {},
		loadingText: { default: "Loading connectors..." },
		emptyText: { default: "No connectors available" },
		errorText: { default: "Could not load connectors" }
	}, {
		"modelValue": {},
		"modelModifiers": {}
	}),
	emits: ["update:modelValue"],
	setup(__props, { expose: __expose }) {
		/**
		* Props-driven integration-connector picker. presented as a button-style
		* picker: the trigger renders the selected connector's icon + label +
		* status dot, and clicking opens a searchable Combobox panel.
		*
		*   <IntegrationConnectorPicker
		*     v-model="connectorId"
		*     label="Connector"
		*     :on-load-connectors="() => fetch('/connectors').then((r) => r.json())"
		*     :on-connect="(id) => beginOAuth(id)"
		*   />
		*/
		const props = __props;
		const model = useModel(__props, "modelValue");
		const { connectors, isLoading, error, refresh, isConnecting, connect } = useConnectorLoader(() => props.onLoadConnectors());
		const STATUS_DOT_CLASS = {
			connected: "bg-success",
			disconnected: "bg-muted-foreground/40",
			error: "bg-destructive"
		};
		function statusDotClass(status) {
			if (status && STATUS_DOT_CLASS[status]) return STATUS_DOT_CLASS[status];
			return "bg-muted-foreground/40";
		}
		function toPascalCase(input) {
			return input.replace(/[-_\s]+(.)?/g, (_, ch) => ch ? ch.toUpperCase() : "").replace(/^(.)/, (m) => m.toUpperCase());
		}
		const iconRegistry = ref({});
		async function ensureIcon(name) {
			if (!name) return;
			const pascal = toPascalCase(name);
			if (pascal in iconRegistry.value) return;
			const registry = await import("@lucide/vue");
			iconRegistry.value = {
				...iconRegistry.value,
				[pascal]: registry[pascal] ?? null
			};
		}
		watch(connectors, (next) => {
			for (const c of next) if (c.icon) ensureIcon(c.icon);
		}, { immediate: true });
		function iconFor(name) {
			if (!name) return null;
			return iconRegistry.value[toPascalCase(name)] ?? null;
		}
		const query = ref("");
		const filteredConnectors = computed(() => {
			const q = query.value.trim().toLowerCase();
			if (!q) return connectors.value;
			return connectors.value.filter((c) => [
				c.label,
				c.type ?? "",
				c.id
			].some((field) => field.toLowerCase().includes(q)));
		});
		const selectedConnector = computed(() => {
			if (model.value === void 0 || model.value === null) return null;
			return connectors.value.find((c) => c.id === model.value) ?? null;
		});
		const currentValue = computed(() => {
			if (model.value === void 0 || model.value === null) return;
			return model.value;
		});
		function onInput(event) {
			query.value = event.target.value;
		}
		function onUpdate(next) {
			if (Array.isArray(next)) {
				model.value = next[0] === void 0 ? null : next[0];
				return;
			}
			model.value = next === void 0 ? null : next;
		}
		async function handleConnect(id, event) {
			event?.stopPropagation();
			event?.preventDefault();
			await connect(id, props.onConnect);
		}
		__expose({
			refresh,
			handleConnect
		});
		return (_ctx, _cache) => {
			return openBlock(), createBlock(unref(FieldFrame_default), {
				label: __props.label,
				required: __props.required,
				"hide-label": __props.hideLabel,
				for: __props.id,
				"help-text": __props.helpText,
				errors: __props.errors
			}, {
				default: withCtx(() => [createVNode(unref(Combobox_default), {
					"model-value": currentValue.value,
					disabled: __props.disabled || __props.readonly,
					"ignore-filter": "",
					"onUpdate:modelValue": onUpdate
				}, {
					default: withCtx(() => [createVNode(unref(ComboboxAnchor_default), {
						"data-element-name": __props.name ?? void 0,
						"data-testid": "integration-connector-picker-anchor",
						class: normalizeClass(["justify-between text-left", __props.errors.length ? "border-destructive ring-destructive" : ""])
					}, {
						default: withCtx(() => [createElementVNode("div", _hoisted_1, [
							selectedConnector.value && iconFor(selectedConnector.value.icon) ? (openBlock(), createBlock(resolveDynamicComponent(iconFor(selectedConnector.value?.icon)), {
								key: 0,
								class: "size-4 shrink-0"
							})) : !selectedConnector.value ? (openBlock(), createBlock(unref(Plug), {
								key: 1,
								class: "size-4 shrink-0 text-muted-foreground/60"
							})) : createCommentVNode("", true),
							selectedConnector.value ? (openBlock(), createElementBlock("span", _hoisted_2, toDisplayString(selectedConnector.value.label), 1)) : (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(__props.placeholder ?? "Select connector…"), 1)),
							selectedConnector.value ? (openBlock(), createElementBlock("span", {
								key: 4,
								class: normalizeClass(["inline-block size-2 shrink-0 rounded-full", statusDotClass(selectedConnector.value.status)]),
								"data-status": selectedConnector.value.status ?? "unknown",
								"data-testid": "integration-connector-picker-status"
							}, null, 10, _hoisted_4)) : createCommentVNode("", true)
						]), createVNode(unref(ChevronsUpDown), { class: "ml-2 h-4 w-4 shrink-0 opacity-50" })]),
						_: 1
					}, 8, ["data-element-name", "class"]), createVNode(unref(ComboboxContent_default), null, {
						default: withCtx(() => [
							createElementVNode("div", _hoisted_5, [
								createVNode(unref(Search), { class: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
								createVNode(unref(ComboboxInput_default), {
									id: __props.id,
									"model-value": query.value,
									placeholder: "Search connectors…",
									class: "h-8",
									"data-testid": "integration-connector-picker-input",
									onInput
								}, null, 8, ["id", "model-value"]),
								unref(isLoading) ? (openBlock(), createBlock(unref(Loader2), {
									key: 0,
									"data-testid": "integration-connector-picker-loader",
									class: "ml-2 h-4 w-4 shrink-0 animate-spin text-muted-foreground"
								})) : createCommentVNode("", true)
							]),
							unref(isLoading) ? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(__props.loadingText), 1)) : unref(error) ? (openBlock(), createElementBlock("div", _hoisted_7, [createElementVNode("p", _hoisted_8, toDisplayString(__props.errorText), 1), createVNode(unref(Button_default), {
								type: "button",
								size: "sm",
								variant: "outline",
								"data-testid": "integration-connector-picker-retry",
								onClick: unref(refresh)
							}, {
								default: withCtx(() => [..._cache[0] || (_cache[0] = [createTextVNode(" Retry ", -1)])]),
								_: 1
							}, 8, ["onClick"])])) : filteredConnectors.value.length === 0 ? (openBlock(), createBlock(unref(ComboboxEmpty_default), { key: 2 }, {
								default: withCtx(() => [createTextVNode(toDisplayString(__props.emptyText), 1)]),
								_: 1
							})) : createCommentVNode("", true),
							(openBlock(true), createElementBlock(Fragment, null, renderList(filteredConnectors.value, (conn) => {
								return openBlock(), createBlock(unref(ComboboxItem_default), {
									key: conn.id,
									value: conn.id,
									"data-testid": `integration-connector-picker-item-${conn.id}`
								}, {
									default: withCtx(() => [createVNode(unref(ComboboxItemIndicator_default)), createElementVNode("div", _hoisted_9, [
										iconFor(conn.icon) ? (openBlock(), createBlock(resolveDynamicComponent(iconFor(conn.icon)), {
											key: 0,
											class: "size-4 shrink-0"
										})) : createCommentVNode("", true),
										createElementVNode("span", _hoisted_10, toDisplayString(conn.label), 1),
										conn.type ? (openBlock(), createBlock(unref(Badge_default), {
											key: 1,
											variant: "outline",
											size: "sm"
										}, {
											default: withCtx(() => [createTextVNode(toDisplayString(conn.type), 1)]),
											_: 2
										}, 1024)) : createCommentVNode("", true),
										createElementVNode("span", {
											class: normalizeClass(["ml-auto inline-block size-2 rounded-full", statusDotClass(conn.status)]),
											"data-status": conn.status ?? "unknown",
											"data-testid": `integration-connector-picker-status-${conn.id}`
										}, null, 10, _hoisted_11),
										__props.onConnect && conn.status !== "connected" ? (openBlock(), createBlock(unref(Button_default), {
											key: 2,
											type: "button",
											size: "sm",
											variant: "outline",
											class: "ml-2",
											disabled: Boolean(unref(isConnecting)[conn.id]),
											"data-testid": `integration-connector-picker-connect-${conn.id}`,
											onClick: withModifiers(($event) => handleConnect(conn.id, $event), ["stop"])
										}, {
											default: withCtx(() => [unref(isConnecting)[conn.id] ? (openBlock(), createBlock(unref(Loader2), {
												key: 0,
												class: "size-3 animate-spin"
											})) : createCommentVNode("", true), createElementVNode("span", null, toDisplayString(conn.status === "error" ? "Reconnect" : "Connect"), 1)]),
											_: 2
										}, 1032, [
											"disabled",
											"data-testid",
											"onClick"
										])) : createCommentVNode("", true)
									])]),
									_: 2
								}, 1032, ["value", "data-testid"]);
							}), 128))
						]),
						_: 1
					})]),
					_: 1
				}, 8, ["model-value", "disabled"])]),
				_: 1
			}, 8, [
				"label",
				"required",
				"hide-label",
				"for",
				"help-text",
				"errors"
			]);
		};
	}
});
//#endregion
//#region src/components/compositions/elements/picker/IntegrationConnectorPicker.vue
var IntegrationConnectorPicker_exports = /* @__PURE__ */ __exportAll({ default: () => IntegrationConnectorPicker_default });
var IntegrationConnectorPicker_default = IntegrationConnectorPicker_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { IntegrationConnectorPicker_exports as n, IntegrationConnectorPicker_default as t };

//# sourceMappingURL=IntegrationConnectorPicker-DNdZpNXU.js.map