import { n as __exportAll } from "./chunk-B-1-B7_t.js";
import { n as Button_default } from "./button-B9CnnuTu.js";
import { t as Input_default } from "./input-CwDp-vE8.js";
import { a as TableCell_default, c as Table_default, n as TableHeader_default, r as TableHead_default, s as TableBody_default, t as TableRow_default } from "./table-d-2vl8Cn.js";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, renderList, toDisplayString, toRef, unref, watch, withCtx } from "vue";
//#region src/components/compositions/cards/useDataTableData.ts
/**
* Source-of-rows composable for `DataTableCard`.
*
* Resolves the row array from one of three sources, in priority order:
*
*   1. `searchRoute` — remote fetch. Currently a STUB that logs a debug
*      message and returns `[]`. Real HTTP integration (axios) lands in
*      a follow-up task.
*   2. `listKey` — nested array read off `item[listKey]`.
*   3. `listData` — inline static rows on the card.
*
* Falls back to an empty array when no source is configured.
*
* Exposed as a composable so tests can intercept the row source
* without mocking the network layer.
*/
function useDataTableData(opts) {
	return { rows: computed(() => {
		const i = opts.item.value;
		const o = opts.options.value;
		const listKey = opts.listKey.value;
		const listData = opts.listData.value;
		if (o?.searchRoute) {
			console.debug("[DataTableCard] searchRoute fetch is stubbed; returning empty rows.", o.searchRoute);
			return [];
		}
		if (listKey) {
			const raw = i[listKey];
			if (Array.isArray(raw)) return raw;
			return [];
		}
		if (Array.isArray(listData)) return listData;
		return [];
	}) };
}
//#endregion
//#region src/components/compositions/cards/DataTableCard.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = {
	key: 0,
	class: "flex flex-col gap-0.5"
};
var _hoisted_2 = {
	key: 0,
	class: "text-base font-semibold"
};
var _hoisted_3 = {
	key: 1,
	class: "text-sm text-muted-foreground"
};
var _hoisted_4 = {
	key: 1,
	class: "flex flex-wrap items-center justify-between gap-2"
};
var _hoisted_5 = {
	key: 0,
	class: "flex items-center gap-2",
	"data-testid": "datatable-exports"
};
var _hoisted_6 = {
	key: 1,
	class: "ml-auto w-full max-w-xs"
};
var _hoisted_7 = ["data-testid", "onClick"];
var _hoisted_8 = { class: "text-xs" };
var _hoisted_9 = { key: 1 };
var _hoisted_10 = {
	key: 0,
	"data-testid": "cell-timestamp"
};
var _hoisted_11 = ["aria-label"];
var _hoisted_12 = ["src", "alt"];
var _hoisted_13 = {
	key: 3,
	class: "inline-flex items-center gap-2"
};
var _hoisted_14 = { key: 4 };
var _hoisted_15 = { class: "inline-flex items-center justify-end gap-1" };
var _hoisted_16 = { "data-testid": "datatable-empty" };
var _hoisted_17 = {
	key: 2,
	class: "flex items-center justify-between gap-2",
	"data-testid": "datatable-pagination"
};
var _hoisted_18 = { class: "text-sm text-muted-foreground" };
var _hoisted_19 = { class: "flex items-center gap-2" };
var DataTableCard_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
	__name: "DataTableCard",
	props: {
		tableTitle: {},
		title: {},
		subtitle: {},
		showCard: {
			type: Boolean,
			default: true
		},
		classList: {},
		headers: { default: () => [] },
		options: {},
		listKey: {},
		listData: {},
		actions: {},
		selectable: {
			type: Boolean,
			default: false
		},
		bulkActions: {},
		emptyMessage: {},
		item: { default: () => ({}) },
		createConfig: {},
		updateConfig: {},
		deleteConfig: {}
	},
	emits: ["action", "export"],
	setup(__props, { emit: __emit }) {
		/**
		* Props-driven `datatable` composition.
		*
		* Renders a paginated, searchable, sortable table from `headers` + a
		* row source determined in priority order:
		*
		*   1. `options.searchRoute` — remote fetch (stubbed; future HTTP).
		*   2. `listKey` — read sub-array from `item[listKey]` (nested).
		*   3. `listData` — inline static rows.
		*   4. Otherwise: empty.
		*
		* Sorting, search, and pagination all run client-side over the
		* resolved rows. Column rendering branches on `header.type`.
		*
		* Self-contained — directly mountable in any Vue app:
		*
		*   <DataTableCard
		*     :headers="[{ key: 'name', sortable: true }]"
		*     :list-data="rows"
		*     @action="onRowAction"
		*   />
		*/
		const props = __props;
		const emit = __emit;
		const sortState = ref(props.options?.defaultSortBy ? {
			key: props.options.defaultSortBy,
			direction: props.options.defaultSortDirection ?? "asc"
		} : null);
		const searchQuery = ref("");
		const currentPage = ref(1);
		const perPage = computed(() => {
			const opts = props.options;
			return opts?.perPage ?? opts?.defaultItemsPerPage ?? 25;
		});
		const paginated = computed(() => props.options?.paginated === true);
		const visibleHeaders = computed(() => (props.headers ?? []).filter((h) => h.visible !== false));
		const emptyMessageText = computed(() => {
			return props.emptyMessage ?? props.options?.emptyText ?? "No results";
		});
		const exports = computed(() => props.options?.exports ?? []);
		const tableActions = computed(() => {
			const base = props.actions ?? [];
			const additional = props.options?.additionalActions ?? [];
			return [...base, ...additional];
		});
		const showSearch = computed(() => {
			const sr = props.options?.searchRoute;
			if (sr && sr.hide) return false;
			return true;
		});
		/** Underlying row source — composable that can be mocked in tests. */
		const { rows: sourceRows } = useDataTableData({
			listKey: toRef(props, "listKey"),
			listData: toRef(props, "listData"),
			options: toRef(props, "options"),
			item: toRef(props, "item")
		});
		/** Read a dot-notation path on a row, returning unknown. */
		function readPath(row, path) {
			if (!path) return;
			const segments = path.split(".");
			let value = row;
			for (const seg of segments) {
				if (value == null || typeof value !== "object") return;
				value = value[seg];
			}
			return value;
		}
		function toComparable(value) {
			if (value == null) return "";
			if (typeof value === "number") return value;
			if (typeof value === "boolean") return value ? 1 : 0;
			if (value instanceof Date) return value.getTime();
			return String(value).toLowerCase();
		}
		const filteredRows = computed(() => {
			const query = searchQuery.value.trim().toLowerCase();
			if (!query) return sourceRows.value;
			return sourceRows.value.filter((row) => {
				return visibleHeaders.value.some((h) => {
					const v = readPath(row, h.key);
					if (v == null) return false;
					return String(v).toLowerCase().includes(query);
				});
			});
		});
		const sortedRows = computed(() => {
			const state = sortState.value;
			if (!state) return filteredRows.value;
			const dir = state.direction === "asc" ? 1 : -1;
			return [...filteredRows.value].sort((a, b) => {
				const av = toComparable(readPath(a, state.key));
				const bv = toComparable(readPath(b, state.key));
				if (av < bv) return -1 * dir;
				if (av > bv) return 1 * dir;
				return 0;
			});
		});
		const pageCount = computed(() => {
			if (!paginated.value) return 1;
			return Math.max(1, Math.ceil(sortedRows.value.length / perPage.value));
		});
		const pagedRows = computed(() => {
			if (!paginated.value) return sortedRows.value;
			const start = (currentPage.value - 1) * perPage.value;
			return sortedRows.value.slice(start, start + perPage.value);
		});
		watch([filteredRows, perPage], () => {
			if (currentPage.value > pageCount.value) currentPage.value = 1;
		});
		function toggleSort(header) {
			if (!header.sortable) return;
			const state = sortState.value;
			if (!state || state.key !== header.key) {
				sortState.value = {
					key: header.key,
					direction: "asc"
				};
				return;
			}
			if (state.direction === "asc") {
				sortState.value = {
					key: header.key,
					direction: "desc"
				};
				return;
			}
			sortState.value = null;
		}
		function sortIndicator(header) {
			const state = sortState.value;
			if (!state || state.key !== header.key) return "";
			return state.direction === "asc" ? "↑" : "↓";
		}
		function alignClass(header) {
			const a = header.align;
			if (a === "center") return "text-center";
			if (a === "end" || a === "right") return "text-right";
			return "text-left";
		}
		function previousPage() {
			if (currentPage.value > 1) currentPage.value -= 1;
		}
		function nextPage() {
			if (currentPage.value < pageCount.value) currentPage.value += 1;
		}
		function formatTimestamp(value) {
			if (value == null) return "";
			const date = value instanceof Date ? value : new Date(typeof value === "number" ? value : String(value));
			if (Number.isNaN(date.getTime())) return String(value);
			return new Intl.DateTimeFormat(void 0, {
				year: "numeric",
				month: "short",
				day: "numeric",
				hour: "numeric",
				minute: "numeric"
			}).format(date);
		}
		function statusColor(value) {
			const s = String(value ?? "").toLowerCase();
			if (s === "success" || s === "active" || s === "ok" || s === "paid") return "bg-green-500";
			if (s === "warning" || s === "pending") return "bg-yellow-500";
			if (s === "danger" || s === "error" || s === "failed" || s === "unpaid") return "bg-red-500";
			if (s === "info") return "bg-blue-500";
			return "bg-gray-400";
		}
		function onAction(action, row) {
			emit("action", {
				action,
				row
			});
		}
		function onExport(config) {
			emit("export", { export: config });
		}
		return (_ctx, _cache) => {
			return openBlock(), createElementBlock("section", {
				class: normalizeClass(["gxp-datatable-card flex flex-col gap-3", __props.showCard !== false ? "rounded-lg border bg-card text-card-foreground shadow-sm p-4" : ""]),
				"data-testid": "datatable-card"
			}, [
				__props.tableTitle || __props.title || __props.subtitle ? (openBlock(), createElementBlock("header", _hoisted_1, [__props.tableTitle || __props.title ? (openBlock(), createElementBlock("h3", _hoisted_2, toDisplayString(__props.tableTitle ?? __props.title), 1)) : createCommentVNode("", true), __props.subtitle ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)])) : createCommentVNode("", true),
				exports.value.length > 0 || showSearch.value ? (openBlock(), createElementBlock("div", _hoisted_4, [exports.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_5, [(openBlock(true), createElementBlock(Fragment, null, renderList(exports.value, (ex, i) => {
					return openBlock(), createBlock(unref(Button_default), {
						key: i,
						variant: "outline",
						size: "sm",
						type: "button",
						onClick: ($event) => onExport(ex)
					}, {
						default: withCtx(() => [createTextVNode(toDisplayString(ex.label ?? "Export"), 1)]),
						_: 2
					}, 1032, ["onClick"]);
				}), 128))])) : createCommentVNode("", true), showSearch.value ? (openBlock(), createElementBlock("div", _hoisted_6, [createVNode(unref(Input_default), {
					modelValue: searchQuery.value,
					"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
					type: "search",
					placeholder: "Search...",
					"data-testid": "datatable-search"
				}, null, 8, ["modelValue"])])) : createCommentVNode("", true)])) : createCommentVNode("", true),
				createVNode(unref(Table_default), null, {
					default: withCtx(() => [createVNode(unref(TableHeader_default), null, {
						default: withCtx(() => [createVNode(unref(TableRow_default), null, {
							default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(visibleHeaders.value, (h) => {
								return openBlock(), createBlock(unref(TableHead_default), {
									key: h.key,
									class: normalizeClass(alignClass(h)),
									style: normalizeStyle(h.width ? { width: h.width } : void 0)
								}, {
									default: withCtx(() => [h.sortable ? (openBlock(), createElementBlock("button", {
										key: 0,
										type: "button",
										class: "inline-flex items-center gap-1 hover:text-foreground",
										"data-testid": `sort-${h.key}`,
										onClick: ($event) => toggleSort(h)
									}, [createElementVNode("span", null, toDisplayString(h.title ?? h.key), 1), createElementVNode("span", _hoisted_8, toDisplayString(sortIndicator(h)), 1)], 8, _hoisted_7)) : (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString(h.title ?? h.key), 1))]),
									_: 2
								}, 1032, ["class", "style"]);
							}), 128)), tableActions.value.length > 0 ? (openBlock(), createBlock(unref(TableHead_default), {
								key: 0,
								class: "text-right"
							}, {
								default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode(" Actions ", -1)])]),
								_: 1
							})) : createCommentVNode("", true)]),
							_: 1
						})]),
						_: 1
					}), createVNode(unref(TableBody_default), null, {
						default: withCtx(() => [pagedRows.value.length > 0 ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(pagedRows.value, (row, ri) => {
							return openBlock(), createBlock(unref(TableRow_default), {
								key: ri,
								"data-testid": `datatable-row-${ri}`
							}, {
								default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(visibleHeaders.value, (h) => {
									return openBlock(), createBlock(unref(TableCell_default), {
										key: h.key,
										class: normalizeClass(alignClass(h))
									}, {
										default: withCtx(() => [h.type === "timestamp" || h.type === "laravel-timestamp" ? (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString(formatTimestamp(readPath(row, h.key))), 1)) : h.type === "boolean" ? (openBlock(), createElementBlock("span", {
											key: 1,
											"data-testid": "cell-boolean",
											class: normalizeClass(readPath(row, h.key) ? "text-green-600" : "text-red-600"),
											"aria-label": readPath(row, h.key) ? "true" : "false"
										}, toDisplayString(readPath(row, h.key) ? "✓" : "✗"), 11, _hoisted_11)) : h.type === "image" ? (openBlock(), createElementBlock("img", {
											key: 2,
											"data-testid": "cell-image",
											src: String(readPath(row, h.key) ?? ""),
											alt: h.title ?? h.key,
											class: "h-8 w-8 rounded object-cover"
										}, null, 8, _hoisted_12)) : h.type === "status_circle" ? (openBlock(), createElementBlock("span", _hoisted_13, [createElementVNode("span", {
											"data-testid": "cell-status-circle",
											class: normalizeClass(["inline-block h-2.5 w-2.5 rounded-full", statusColor(readPath(row, h.key))])
										}, null, 2), createElementVNode("span", null, toDisplayString(readPath(row, h.key)), 1)])) : (openBlock(), createElementBlock("span", _hoisted_14, [
											h.prepend ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(h.prepend), 1)], 64)) : createCommentVNode("", true),
											createTextVNode(" " + toDisplayString(readPath(row, h.key) ?? "") + " ", 1),
											h.append ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(h.append), 1)], 64)) : createCommentVNode("", true)
										]))]),
										_: 2
									}, 1032, ["class"]);
								}), 128)), tableActions.value.length > 0 ? (openBlock(), createBlock(unref(TableCell_default), {
									key: 0,
									class: "text-right"
								}, {
									default: withCtx(() => [createElementVNode("div", _hoisted_15, [(openBlock(true), createElementBlock(Fragment, null, renderList(tableActions.value, (action, ai) => {
										return openBlock(), createBlock(unref(Button_default), {
											key: ai,
											variant: "ghost",
											size: "sm",
											type: "button",
											"data-testid": `datatable-action-${action.slug ?? ai}`,
											onClick: ($event) => onAction(action, row)
										}, {
											default: withCtx(() => [createTextVNode(toDisplayString(action.label ?? action.tooltip ?? action.slug ?? "Action"), 1)]),
											_: 2
										}, 1032, ["data-testid", "onClick"]);
									}), 128))])]),
									_: 2
								}, 1024)) : createCommentVNode("", true)]),
								_: 2
							}, 1032, ["data-testid"]);
						}), 128)) : (openBlock(), createBlock(unref(TableRow_default), { key: 1 }, {
							default: withCtx(() => [createVNode(unref(TableCell_default), {
								colspan: visibleHeaders.value.length + (tableActions.value.length > 0 ? 1 : 0),
								class: "text-center text-muted-foreground"
							}, {
								default: withCtx(() => [createElementVNode("span", _hoisted_16, toDisplayString(emptyMessageText.value), 1)]),
								_: 1
							}, 8, ["colspan"])]),
							_: 1
						}))]),
						_: 1
					})]),
					_: 1
				}),
				paginated.value && sortedRows.value.length > 0 ? (openBlock(), createElementBlock("footer", _hoisted_17, [createElementVNode("span", _hoisted_18, " Page " + toDisplayString(currentPage.value) + " of " + toDisplayString(pageCount.value), 1), createElementVNode("div", _hoisted_19, [createVNode(unref(Button_default), {
					variant: "outline",
					size: "sm",
					type: "button",
					disabled: currentPage.value <= 1,
					"data-testid": "datatable-prev",
					onClick: previousPage
				}, {
					default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode(" Previous ", -1)])]),
					_: 1
				}, 8, ["disabled"]), createVNode(unref(Button_default), {
					variant: "outline",
					size: "sm",
					type: "button",
					disabled: currentPage.value >= pageCount.value,
					"data-testid": "datatable-next",
					onClick: nextPage
				}, {
					default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode(" Next ", -1)])]),
					_: 1
				}, 8, ["disabled"])])])) : createCommentVNode("", true)
			], 2);
		};
	}
});
//#endregion
//#region src/components/compositions/cards/DataTableCard.vue
var DataTableCard_exports = /* @__PURE__ */ __exportAll({ default: () => DataTableCard_default });
var DataTableCard_default = DataTableCard_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { DataTableCard_exports as n, DataTableCard_default as t };

//# sourceMappingURL=DataTableCard-T4g6bRiT.js.map