Skip to main content
Glama
AuditLogHeader.vue6.54 kB
<template> <th ref="thRef" v-tooltip="tooltip" :colSpan="header.colSpan" :class=" clsx( 'h-8 sticky top-0', header.id !== 'json' && 'cursor-pointer hover:underline', header.id === 'json' && 'w-8 px-2xs', themeClasses('bg-neutral-400', 'bg-shade-100'), ) " @mousedown="startActive" @mouseup="endActive" @click.stop="onClick" > <div :class=" clsx( header.id !== 'json' && 'w-full p-xs truncate', windowWidth <= 1100 && 'flex flex-row items-center h-8', ) " > <IconButton v-if="header.id === 'json' && anyRowsOpen" icon="collapse-row" iconTone="neutral" size="sm" @click.stop="onClick" /> <template v-else> <template v-if="windowWidth > 1100"> <FlexRender v-if="!header.isPlaceholder" :render="label" :props="header.getContext()" /> <IconButton v-if="icon !== 'none'" ref="iconButtonRef" class="absolute right-xs top-2xs" :icon="icon" iconTone="neutral" @click.stop="onClick" /> </template> <template v-else> <div class="flex-grow">{{ label }}</div> <IconButton v-if="icon !== 'none'" ref="iconButtonRef" class="flex-none" :icon="icon" iconTone="neutral" @click.stop="onClick" /> </template> <DropdownMenu v-if="header.id !== 'timestamp' && header.id !== 'json'" ref="dropdownMenuRef" :items="filteredDropdownMenuItems" :anchorTo="{ $el: thRef }" :search="dropdownMenuItems.length > 0" alignCenter @search="onSearch" > <DropdownMenuItem v-if="filteredDropdownMenuItems.length < 1" header label="No matching filters found" /> </DropdownMenu> </template> </div> </th> </template> <script lang="ts" setup> import { DropdownMenu, DropdownMenuItem, DropdownMenuItemObjectDef, IconButton, themeClasses, } from "@si/vue-lib/design-system"; import { FlexRender, Header } from "@tanstack/vue-table"; import clsx from "clsx"; import { computed, onMounted, onUnmounted, ref } from "vue"; import { AuditLogDisplay, useLogsStore } from "@/store/logs.store"; const thRef = ref(); const iconButtonRef = ref<InstanceType<typeof IconButton>>(); const dropdownMenuRef = ref<InstanceType<typeof DropdownMenu>>(); const props = defineProps<{ anyRowsOpen: boolean; header: Header<AuditLogDisplay, unknown>; }>(); const logsStore = useLogsStore(); const label = computed(() => props.header.column.columnDef.header as string); const icon = computed(() => { if (props.header.id === "timestamp") { if (logsStore.sortAscending) return "chevron--up"; return "chevron--down"; } else if (selectedFilters.value.length > 0) { return "filter"; } return "none"; }); const filterOptions = computed(() => { if (props.header.id === "changeSetName") { return logsStore.headerOptions.changeSet; } else if (props.header.id === "entityName") { return logsStore.headerOptions.entityName; } else if (props.header.id === "entityType") { return logsStore.headerOptions.entityType; } else if (props.header.id === "title") { return logsStore.headerOptions.title; } else if (props.header.id === "userName") { return logsStore.headerOptions.user; } return []; }); const selectedFilters = computed(() => { if (props.header.id === "changeSetName") return logsStore.filters.changeSetFilter; else if (props.header.id === "entityName") return logsStore.filters.entityNameFilter; else if (props.header.id === "entityType") return logsStore.filters.entityTypeFilter; else if (props.header.id === "title") return logsStore.filters.titleFilter; else if (props.header.id === "userName") return logsStore.filters.userFilter; else return []; }); const headerText = computed(() => { if (label.value === "Time") { return `Sorting By Timestamp ${ logsStore.sortAscending ? "(Oldest)" : "(Newest)" }`; } if (selectedFilters.value.length > 0) { return `Filtering by ${selectedFilters.value.length} selection${ selectedFilters.value.length > 1 ? "s" : "" }`; } return `Filter by ${label.value}`; }); const tooltip = computed(() => { if (props.header.id === "json") { if (props.anyRowsOpen) { return { content: "Collapse All", theme: "instant-show", }; } return null; } return { content: headerText.value, theme: "instant-show", }; }); const dropdownMenuItems = computed(() => { const items: DropdownMenuItemObjectDef[] = []; items.push({ label: headerText.value, header: true, centerHeader: true, disableCheckable: true, }); for (const k of filterOptions.value) { items.push({ label: k.label, checkable: true, checked: selectedFilters.value.includes(k.value), onSelect: () => { emit("toggleFilter", k.value); }, }); } if (selectedFilters.value.length > 0) { items.unshift({ label: "Clear Filters", onSelect: () => { emit("clearFilters"); }, }); } return items; }); const searchString = ref(""); const onSearch = (search: string) => { searchString.value = search.trim().toLocaleLowerCase(); }; const filteredDropdownMenuItems = computed(() => { if (!dropdownMenuRef.value || searchString.value === "") return dropdownMenuItems.value; return dropdownMenuItems.value.filter((item) => item.label?.toLocaleLowerCase().includes(searchString.value), ); }); const onClick = () => { dropdownMenuRef.value?.open(); emit("select"); }; const active = ref(false); const startActive = () => { active.value = true; iconButtonRef.value?.startActive(); }; const endActive = () => { active.value = false; iconButtonRef.value?.endActive(); }; const windowWidth = ref(window.innerWidth); const onWidthChange = () => { windowWidth.value = window.innerWidth; }; onMounted(() => window.addEventListener("resize", onWidthChange)); onUnmounted(() => window.removeEventListener("resize", onWidthChange)); const emit = defineEmits<{ (e: "select"): void; (e: "clearFilters"): void; (e: "toggleFilter", v: string): void; }>(); </script>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server