diff --git a/client/src/components/Common/GTable.vue b/client/src/components/Common/GTable.vue index 244a9125495..921b0e4718d 100644 --- a/client/src/components/Common/GTable.vue +++ b/client/src/components/Common/GTable.vue @@ -61,12 +61,24 @@ interface Props { */ fields?: TableField[]; + /** + * Filter string to filter items + * @default "" + */ + filter?: string; + /** * Whether to show hover effect on rows * @default true */ hover?: boolean; + /** + * Whether to hide the table header + * @default false + */ + hideHeader?: boolean; + /** * Table data items * @default [] @@ -103,6 +115,12 @@ interface Props { */ overlayLoading?: boolean; + /** + * Whether to use local filtering (client-side) or rely on external filtering (server-side) + * @default true + */ + localFiltering?: boolean; + /** * Whether to use local sorting (client-side) or rely on external sorting (server-side) * @default true @@ -167,12 +185,15 @@ const props = withDefaults(defineProps(), { containerClass: "", emptyState: () => ({ message: "No data available" }), fields: () => [], + filter: "", hover: true, + hideHeader: false, items: () => [], loading: false, loadingMessage: "Loading...", loadMoreLoading: false, loadMoreMessage: "Loading more...", + localFiltering: true, localSorting: true, overlayLoading: false, selectable: false, @@ -189,6 +210,12 @@ const props = withDefaults(defineProps(), { * Events emitted by the GTable component */ const emit = defineEmits<{ + /** + * Emitted when items are filtered + * @event filtered + */ + (e: "filtered", filteredItems: T[]): void; + /** * Emitted when sort changes * @event sort-changed @@ -216,15 +243,33 @@ const emit = defineEmits<{ const sortBy = ref(props.sortBy || "update_time"); const sortDesc = ref(props.sortDesc || true); +const expandedRows = ref>(new Set()); const localItems = computed(() => { - const items = props.items || []; + let items = props.items || []; // If local sorting is disabled, return items as-is if (!props.localSorting) { return items; } + // Apply local filtering if enabled and filter string is provided + if (props.localFiltering && props.filter && props.filter.trim() !== "") { + const filterLower = props.filter.toLowerCase().trim(); + items = items.filter((item) => { + // Search through all field values + return Object.values(item).some((value) => { + if (value == null) { + return false; + } + return String(value).toLowerCase().includes(filterLower); + }); + }); + + // Emit filtered event with the filtered items + emit("filtered", items); + } + // If no sort field is set, return items as-is if (!sortBy.value) { return items; @@ -341,6 +386,26 @@ function onRowSelect(item: T, index: number) { emit("row-select", { item, index, selected: !isSelected }); } +/** + * Check if row details are expanded + */ +function isRowExpanded(index: number) { + return expandedRows.value.has(index); +} + +/** + * Toggle row details expansion + */ +function toggleRowDetails(index: number) { + if (isRowExpanded(index)) { + expandedRows.value.delete(index); + } else { + expandedRows.value.add(index); + } + // Trigger reactivity + expandedRows.value = new Set(expandedRows.value); +} + /** * Get cell value with optional formatter */ @@ -415,7 +480,7 @@ const getCellId = (tableId: string, fieldKey: string, index: number) => `g-table