mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 08:09:27 +00:00
696 lines
24 KiB
Vue
696 lines
24 KiB
Vue
<!-- Copyright 2026 OpenObserve Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-->
|
|
|
|
<template>
|
|
<div class="flex h-full flex-col p-0">
|
|
<template v-if="!showImportRegexPatternDialog">
|
|
<!-- Standard section header: title + actions only. Search moved to toolbar. -->
|
|
<OPageLayout
|
|
:title="t('regex_patterns.title')"
|
|
icon="pattern"
|
|
:subtitle="t('settings.regexPatternList.subtitle')"
|
|
bleed
|
|
>
|
|
<template #actions>
|
|
<OButton
|
|
variant="outline"
|
|
size="sm"
|
|
@click="importRegexPattern"
|
|
data-test="regex-pattern-list-import"
|
|
>{{ t("regex_patterns.import") }}</OButton
|
|
>
|
|
<OButton
|
|
data-test="regex-pattern-list-add-pattern-btn"
|
|
variant="primary"
|
|
size="sm"
|
|
@click="createRegexPattern"
|
|
>{{ t("regex_patterns.create_pattern") }}</OButton
|
|
>
|
|
</template>
|
|
<div class="bg-card-glass-bg min-h-0 flex-1 overflow-hidden">
|
|
<OTable
|
|
ref="oTableRef"
|
|
:frame="false"
|
|
data-test="regex-pattern-list-table"
|
|
:data="visibleRows"
|
|
:columns="columns"
|
|
row-key="id"
|
|
:selected-ids="selectedPatternIds"
|
|
selection="multiple"
|
|
pagination="client"
|
|
:page-size="pageSize"
|
|
:page-size-options="[10, 20, 50, 100]"
|
|
:current-page="currentPage"
|
|
:sort-by="sortBy"
|
|
:sort-order="sortOrder"
|
|
@update:page-size="pageSize = $event"
|
|
@update:current-page="currentPage = $event"
|
|
@update:sort-by="sortBy = $event"
|
|
@update:sort-order="sortOrder = $event"
|
|
sorting="client"
|
|
filter-mode="client"
|
|
:default-columns="false"
|
|
show-index
|
|
:enable-column-resize="true"
|
|
:persist-columns="true"
|
|
table-id="settings-regex-patterns"
|
|
:show-global-filter="false"
|
|
:loading="listLoading"
|
|
:forbidden="forbidden"
|
|
@update:selected-ids="handleSelectedIdsUpdate"
|
|
>
|
|
<template #toolbar>
|
|
<OSearchInput
|
|
v-model="filterQuery"
|
|
class="flex-1"
|
|
:placeholder="t('regex_patterns.search')"
|
|
/>
|
|
</template>
|
|
<template #toolbar-trailing>
|
|
<ORefreshButton
|
|
layout="inline"
|
|
variant="outline"
|
|
:last-run-at="lastUpdatedAt"
|
|
:loading="fetching"
|
|
shortcut-id="regexPatternsRefresh"
|
|
data-test="regex-pattern-list-refresh-btn"
|
|
@click="refreshRegexPatterns"
|
|
/>
|
|
</template>
|
|
<template #empty>
|
|
<OEmptyState
|
|
v-if="!listLoading"
|
|
size="hero"
|
|
preset="no-regex-patterns"
|
|
:filtered="filterQuery !== ''"
|
|
@action="
|
|
(id) =>
|
|
id === 'clear-filters'
|
|
? (filterQuery = '')
|
|
: id === 'import'
|
|
? importRegexPattern()
|
|
: createRegexPattern()
|
|
"
|
|
/>
|
|
</template>
|
|
<template #cell-pattern="{ row }">
|
|
<OCodeCell :value="row.pattern" />
|
|
</template>
|
|
<template #cell-created_at="{ row }">
|
|
<OTimeCell :value="row.created_at" unit="iso" :timezone="store.state.timezone" />
|
|
</template>
|
|
<template #cell-updated_at="{ row }">
|
|
<OTimeCell :value="row.updated_at" unit="iso" :timezone="store.state.timezone" />
|
|
</template>
|
|
<template #cell-actions="{ row }">
|
|
<div class="flex items-center justify-center gap-1">
|
|
<OButton
|
|
:data-test="`regex-pattern-list-${row.id}-export-regex-pattern`"
|
|
data-row-action="export"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
class="max-md:hidden"
|
|
:title="t('settings.regexPatternList.exportTitle')"
|
|
@click.stop="exportRegexPattern(row)"
|
|
icon-left="download"
|
|
/>
|
|
<OButton
|
|
:data-test="`regex-pattern-list-${row.id}-update-regex-pattern`"
|
|
data-row-action="edit"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
class="max-md:hidden"
|
|
:title="t('regex_patterns.edit')"
|
|
@click.stop="editRegexPattern(row)"
|
|
icon-left="edit"
|
|
/>
|
|
<OButton
|
|
:data-test="`regex-pattern-list-${row.id}-delete-regex-pattern`"
|
|
data-row-action="delete"
|
|
variant="ghost-destructive"
|
|
size="icon-sm"
|
|
class="max-md:hidden"
|
|
:title="t('regex_patterns.delete')"
|
|
@click.stop="confirmDeleteRegexPattern(row)"
|
|
icon-left="delete"
|
|
/>
|
|
<ODropdown side="bottom" align="end">
|
|
<template #trigger>
|
|
<OButton
|
|
icon-left="more-vert"
|
|
variant="ghost"
|
|
size="icon-xs-sq"
|
|
class="md:hidden"
|
|
data-test="regex-pattern-list-row-more-actions"
|
|
@click.stop
|
|
/>
|
|
</template>
|
|
<ODropdownItem
|
|
icon-left="download"
|
|
class="md:hidden"
|
|
:data-test="`regex-pattern-list-${row.id}-export-regex-pattern-menu`"
|
|
@select="exportRegexPattern(row)"
|
|
>
|
|
<span>{{ t("settings.regexPatternList.exportTitle") }}</span>
|
|
</ODropdownItem>
|
|
<ODropdownItem
|
|
icon-left="edit"
|
|
class="md:hidden"
|
|
:data-test="`regex-pattern-list-${row.id}-update-regex-pattern-menu`"
|
|
@select="editRegexPattern(row)"
|
|
>
|
|
<span>{{ t("regex_patterns.edit") }}</span>
|
|
</ODropdownItem>
|
|
<ODropdownItem
|
|
icon-left="delete"
|
|
variant="destructive"
|
|
class="md:hidden"
|
|
:data-test="`regex-pattern-list-${row.id}-delete-regex-pattern-menu`"
|
|
@select="confirmDeleteRegexPattern(row)"
|
|
>
|
|
<span>{{ t("regex_patterns.delete") }}</span>
|
|
</ODropdownItem>
|
|
</ODropdown>
|
|
</div>
|
|
</template>
|
|
<template #selection-actions>
|
|
<OButton
|
|
data-test="regex-pattern-list-delete-patterns-btn"
|
|
variant="outline-destructive"
|
|
size="sm"
|
|
icon-left="delete"
|
|
:loading="bulkDeleteLoading"
|
|
@click="openBulkDeleteDialog"
|
|
>
|
|
{{ t("settings.regexPatternList.delete") }}
|
|
</OButton>
|
|
</template>
|
|
</OTable>
|
|
</div>
|
|
</OPageLayout>
|
|
</template>
|
|
<ImportRegexPattern
|
|
v-else-if="showImportRegexPatternDialog"
|
|
@cancel:hideform="showImportRegexPatternDialog = false"
|
|
@update:list="refreshRegexPatterns"
|
|
:regex-patterns="regexPatterns.map((pattern: any) => pattern.name)"
|
|
/>
|
|
<ConfirmDialog
|
|
v-model="deleteDialog.show"
|
|
:title="deleteDialog.title"
|
|
:message="deleteDialog.message"
|
|
@update:ok="deleteRegexPattern"
|
|
@update:cancel="deleteDialog.show = false"
|
|
/>
|
|
|
|
<ConfirmDialog
|
|
:title="t('settings.regexPatternList.bulkDeleteTitle')"
|
|
:message="t('settings.regexPatternList.bulkDeleteMessage', { n: selectedPatterns.length })"
|
|
@update:ok="bulkDeleteRegexPatterns"
|
|
@update:cancel="confirmBulkDelete = false"
|
|
v-model="confirmBulkDelete"
|
|
/>
|
|
<AddRegexPattern
|
|
data-test="regex-pattern-list-add-regex-pattern-drawer"
|
|
v-model:open="showAddRegexPatternDialog.show"
|
|
:data="showAddRegexPatternDialog.data"
|
|
:is-edit="showAddRegexPatternDialog.isEdit"
|
|
@update:list="refreshRegexPatterns"
|
|
@close="closeAddRegexPatternDialog"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { useOrgId } from "@/composables/query/useOrgId";
|
|
import { useQuery } from "@tanstack/vue-query";
|
|
import { regexPatternsQuery } from "@/services/regex_pattern.queries";
|
|
import { regexPatternKeys } from "@/services/regex_pattern.querykeys";
|
|
import { queryClient } from "@/composables/query/queryClient";
|
|
import { ref, onMounted, defineComponent, computed, watch } from "vue";
|
|
import type { Ref } from "vue";
|
|
import { useI18nTyped } from "@/types/i18n";
|
|
import { convertUnixToDateFormat } from "@/utils/zincutils";
|
|
import ConfirmDialog from "../ConfirmDialog.vue";
|
|
import { useRouter } from "vue-router";
|
|
import { useStore } from "vuex";
|
|
import regexPatternsService from "@/services/regex_pattern";
|
|
import AddRegexPattern from "./AddRegexPattern.vue";
|
|
import ImportRegexPattern from "./ImportRegexPattern.vue";
|
|
import config from "@/aws-exports";
|
|
import OEmptyState from "@/lib/core/EmptyState/OEmptyState.vue";
|
|
import OButton from "@/lib/core/Button/OButton.vue";
|
|
import ODropdown from "@/lib/overlay/Dropdown/ODropdown.vue";
|
|
import ODropdownItem from "@/lib/overlay/Dropdown/ODropdownItem.vue";
|
|
import ORefreshButton from "@/lib/core/RefreshButton/ORefreshButton.vue";
|
|
import OSearchInput from "@/lib/forms/SearchInput/OSearchInput.vue";
|
|
import OTable from "@/lib/core/Table/OTable.vue";
|
|
import OCodeCell from "@/lib/core/Table/cells/OCodeCell.vue";
|
|
import OTimeCell from "@/lib/core/Table/cells/OTimeCell.vue";
|
|
import type { OTableColumnDef } from "@/lib/core/Table/OTable.types";
|
|
import { toast } from "@/lib/feedback/Toast/useToast";
|
|
import OPageLayout from "@/lib/core/PageLayout/OPageLayout.vue";
|
|
import { COL } from "@/lib/core/Table/OTable.types";
|
|
import { useShortcuts } from "@/lib/vue-shortcut-manager";
|
|
import { isInputFocused } from "@/utils/keyboardShortcuts";
|
|
import { useListViewState } from "@/composables/useListViewState";
|
|
|
|
export default defineComponent({
|
|
name: "RegexPatternList",
|
|
components: {
|
|
OPageLayout,
|
|
ConfirmDialog,
|
|
AddRegexPattern,
|
|
ImportRegexPattern,
|
|
OEmptyState,
|
|
OButton,
|
|
ODropdown,
|
|
ODropdownItem,
|
|
ORefreshButton,
|
|
OSearchInput,
|
|
OTable,
|
|
OCodeCell,
|
|
OTimeCell,
|
|
},
|
|
setup() {
|
|
const viewState = useListViewState("regex-patterns", {
|
|
filterQuery: "",
|
|
pageSize: 20,
|
|
currentPage: 1,
|
|
sortBy: "",
|
|
sortOrder: "asc" as "asc" | "desc",
|
|
});
|
|
const filterQuery = ref(viewState.filterQuery);
|
|
const pageSize = ref(viewState.pageSize);
|
|
const currentPage = ref(viewState.currentPage);
|
|
const sortBy = ref(viewState.sortBy);
|
|
const sortOrder = ref(viewState.sortOrder);
|
|
const oTableRef = ref<any>(null);
|
|
|
|
watch([filterQuery, pageSize, currentPage, sortBy, sortOrder], () => {
|
|
Object.assign(viewState, {
|
|
filterQuery: filterQuery.value,
|
|
pageSize: pageSize.value,
|
|
currentPage: currentPage.value,
|
|
sortBy: sortBy.value,
|
|
sortOrder: sortOrder.value,
|
|
});
|
|
});
|
|
const { t } = useI18nTyped();
|
|
const store = useStore();
|
|
const router = useRouter();
|
|
|
|
const columns: OTableColumnDef[] = [
|
|
{
|
|
id: "name",
|
|
header: t("regex_patterns.name"),
|
|
accessorKey: "name",
|
|
sortable: true,
|
|
resizable: true,
|
|
hideable: true,
|
|
size: COL.name,
|
|
minSize: 270,
|
|
meta: { align: "left", flex: true },
|
|
},
|
|
{
|
|
id: "pattern",
|
|
header: t("regex_patterns.pattern"),
|
|
accessorKey: "pattern",
|
|
resizable: true,
|
|
hideable: true,
|
|
size: 400,
|
|
minSize: 200,
|
|
meta: { align: "left" },
|
|
},
|
|
{
|
|
id: "created_at",
|
|
header: t("regex_patterns.created_at"),
|
|
accessorKey: "created_at",
|
|
resizable: true,
|
|
hideable: true,
|
|
size: COL.createdAt,
|
|
meta: { align: "left" },
|
|
},
|
|
{
|
|
id: "updated_at",
|
|
header: t("regex_patterns.updated_at"),
|
|
accessorKey: "updated_at",
|
|
sortable: true,
|
|
resizable: true,
|
|
hideable: true,
|
|
size: COL.updatedAt,
|
|
meta: { align: "left" },
|
|
},
|
|
{
|
|
id: "actions",
|
|
header: t("regex_patterns.actions"),
|
|
isAction: true,
|
|
pinned: "right",
|
|
meta: { align: "center", actionCount: 3 },
|
|
},
|
|
];
|
|
|
|
const deleteDialog = ref({
|
|
show: false,
|
|
title: t("settings.regexPatternList.deleteTitle"),
|
|
message: t("settings.regexPatternList.deleteMessage"),
|
|
data: "" as any,
|
|
});
|
|
|
|
const orgIdForList = useOrgId();
|
|
const regexPatternsList = useQuery(() =>
|
|
Object.assign(regexPatternsQuery(orgIdForList.value), { enabled: !!orgIdForList.value }),
|
|
);
|
|
|
|
// The list is the query, not a copy of it: any invalidation of the scope
|
|
// repaints these rows with no wiring here.
|
|
const regexPatterns = computed(() =>
|
|
(regexPatternsList.data.value ?? []).map((pattern: any) => ({
|
|
...pattern,
|
|
created_at: convertUnixToDateFormat(pattern.created_at),
|
|
updated_at: convertUnixToDateFormat(pattern.updated_at),
|
|
})),
|
|
);
|
|
const selectedPatterns: Ref<any[]> = ref([]);
|
|
const confirmBulkDelete = ref(false);
|
|
const bulkDeleteLoading = ref(false);
|
|
|
|
const listLoading = regexPatternsList.isPending;
|
|
// setTimeout(0) runs after TanStack's deferred auto-reset-on-data-change, so the restored page wins.
|
|
watch(
|
|
listLoading,
|
|
(v) => {
|
|
if (v) return;
|
|
setTimeout(() => oTableRef.value?.restorePage?.(currentPage.value), 0);
|
|
},
|
|
{ once: true, immediate: true },
|
|
);
|
|
// A 403 lands in the query's error rather than a loader's catch, so derive the no-access state from it.
|
|
const forbidden = computed(() => {
|
|
const e: any = regexPatternsList.error.value;
|
|
return e?.status === 403 || e?.response?.status === 403;
|
|
});
|
|
// Request in flight, with rows still on screen — the refresh button's
|
|
// spinner. `listLoading` stays for the skeleton, which only a cold read wants.
|
|
const fetching = regexPatternsList.isFetching;
|
|
// Epoch ms of the last successful read — drives the button's "1m ago" label.
|
|
const lastUpdatedAt = regexPatternsList.dataUpdatedAt;
|
|
|
|
const showImportRegexPatternDialog = ref(false);
|
|
|
|
const showAddRegexPatternDialog = ref({
|
|
show: false,
|
|
data: {} as any,
|
|
isEdit: false,
|
|
});
|
|
|
|
const selectedPatternIds = computed(() => selectedPatterns.value.map((p: any) => p.id));
|
|
|
|
const handleSelectedIdsUpdate = (ids: string[]) => {
|
|
const map = new Map(regexPatterns.value.map((r: any) => [r.id, r]));
|
|
selectedPatterns.value = ids.map((id: any) => map.get(id)).filter(Boolean);
|
|
};
|
|
|
|
onMounted(async () => {
|
|
// Unconditional: the cache paints what it has straight away and only
|
|
// reaches the server when the entry is stale. Reading Vuex instead
|
|
// short-circuited that, so the list never revalidated after the first load.
|
|
await getRegexPatterns();
|
|
if (router.currentRoute.value.query.from == "logs" && config.isEnterprise == "true") {
|
|
createRegexPattern();
|
|
}
|
|
if (router.currentRoute.value.query.action == "import" && config.isEnterprise == "true") {
|
|
importRegexPattern();
|
|
}
|
|
});
|
|
|
|
const filterData = (rows: any, terms: any) => {
|
|
var filtered = [];
|
|
terms = terms.toLowerCase();
|
|
for (var i = 0; i < rows.length; i++) {
|
|
if (rows[i]["name"].toLowerCase().includes(terms)) {
|
|
filtered.push(rows[i]);
|
|
}
|
|
}
|
|
return filtered;
|
|
};
|
|
|
|
const createRegexPattern = () => {
|
|
showAddRegexPatternDialog.value.show = true;
|
|
showAddRegexPatternDialog.value.isEdit = false;
|
|
showAddRegexPatternDialog.value.data = {};
|
|
};
|
|
|
|
// `force` for the refresh shortcut and post-mutation reloads — those must
|
|
// reach the server; a plain call is a cache hit while the list is fresh.
|
|
// Bound to refresh / "list changed" events: always hits the server.
|
|
const refreshRegexPatterns = () => getRegexPatterns(true);
|
|
|
|
// Bridge for consumers still reading the Vuex copy.
|
|
watch(
|
|
regexPatterns,
|
|
(rows: any[]) => {
|
|
store.dispatch("setRegexPatterns", rows);
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
// The query owns its failure now, so this reports it once per error however
|
|
// the read was triggered.
|
|
watch(regexPatternsList.error, (error: any) => {
|
|
// The grouped access toast already reports a 403; a second red toast adds nothing.
|
|
if (!error || forbidden.value) return;
|
|
toast({
|
|
message: error.data?.message || t("settings.regexPatternList.errorFetching"),
|
|
variant: "error",
|
|
});
|
|
});
|
|
|
|
// `force` is only meaningful for an explicit refresh now: a write that
|
|
// invalidates the scope repaints these rows on its own.
|
|
const getRegexPatterns = async (force = false) => {
|
|
if (force) await regexPatternsList.refetch();
|
|
};
|
|
|
|
const editRegexPattern = (row: any) => {
|
|
showAddRegexPatternDialog.value.show = true;
|
|
showAddRegexPatternDialog.value.isEdit = true;
|
|
showAddRegexPatternDialog.value.data = row;
|
|
};
|
|
|
|
const confirmDeleteRegexPattern = (row: any) => {
|
|
deleteDialog.value.show = true;
|
|
deleteDialog.value.data = row.id;
|
|
};
|
|
|
|
const deleteRegexPattern = async () => {
|
|
try {
|
|
await regexPatternsService.delete(
|
|
store.state.selectedOrganization.identifier,
|
|
deleteDialog.value.data,
|
|
);
|
|
// Drop the row from the cache first so it disappears now, not when the
|
|
// refetch lands; the forced reload re-persists the corrected list.
|
|
const deletedId = deleteDialog.value.data;
|
|
queryClient.setQueriesData(
|
|
{ queryKey: regexPatternKeys.all(store.state.selectedOrganization.identifier) },
|
|
(list: any) =>
|
|
Array.isArray(list)
|
|
? list.filter((p: any) => p.id !== deletedId && p.name !== deletedId)
|
|
: list,
|
|
);
|
|
getRegexPatterns(true);
|
|
toast({
|
|
message: t("settings.regexPatternList.deletedSuccess"),
|
|
variant: "success",
|
|
});
|
|
} catch (error: any) {
|
|
toast({
|
|
message:
|
|
error?.data?.message ||
|
|
error?.response?.data?.message ||
|
|
t("settings.regexPatternList.errorDeleting"),
|
|
variant: "error",
|
|
});
|
|
}
|
|
};
|
|
|
|
const importRegexPattern = () => {
|
|
showImportRegexPatternDialog.value = true;
|
|
router.push({
|
|
path: "/settings/regex_patterns",
|
|
query: {
|
|
org_identifier: store.state.selectedOrganization.identifier,
|
|
action: "import",
|
|
},
|
|
});
|
|
};
|
|
|
|
const exportRegexPattern = (row: any) => {
|
|
let url = "";
|
|
try {
|
|
const regexPatternToBeExported = {
|
|
name: row.name,
|
|
pattern: row.pattern,
|
|
description: row.description,
|
|
};
|
|
|
|
const regexPatternJson = JSON.stringify(regexPatternToBeExported, null, 2);
|
|
const blob = new Blob([regexPatternJson], {
|
|
type: "application/json",
|
|
});
|
|
url = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
link.download = `${row.name || "regex_pattern"}.json`;
|
|
link.click();
|
|
toast({
|
|
message: t("settings.regexPatternList.exportedSuccess"),
|
|
variant: "success",
|
|
});
|
|
} catch (error: any) {
|
|
toast({
|
|
message: error.data.message || t("settings.regexPatternList.errorExporting"),
|
|
variant: "error",
|
|
});
|
|
} finally {
|
|
if (url) {
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
}
|
|
};
|
|
|
|
const closeAddRegexPatternDialog = () => {
|
|
showAddRegexPatternDialog.value.show = false;
|
|
store.state.organizationData.regexPatternPrompt = "";
|
|
store.state.organizationData.regexPatternTestValue = "";
|
|
router.push({
|
|
path: "/settings/regex_patterns",
|
|
query: {
|
|
org_identifier: store.state.selectedOrganization.identifier,
|
|
},
|
|
});
|
|
};
|
|
|
|
const visibleRows = computed(() => {
|
|
return filterData(regexPatterns.value, filterQuery.value);
|
|
});
|
|
|
|
const openBulkDeleteDialog = () => {
|
|
confirmBulkDelete.value = true;
|
|
};
|
|
|
|
const bulkDeleteRegexPatterns = async () => {
|
|
bulkDeleteLoading.value = true;
|
|
const patternIds = selectedPatterns.value.map((pattern: any) => pattern.id);
|
|
|
|
try {
|
|
const res = await regexPatternsService.bulkDelete(
|
|
store.state.selectedOrganization.identifier,
|
|
{ ids: patternIds },
|
|
);
|
|
const { successful, unsuccessful } = res.data;
|
|
|
|
if (successful.length > 0 && unsuccessful.length === 0) {
|
|
toast({
|
|
message: t("settings.regexPatternList.bulkDeleteSuccess", { n: successful.length }),
|
|
variant: "success",
|
|
});
|
|
} else if (successful.length > 0 && unsuccessful.length > 0) {
|
|
toast({
|
|
message: t("settings.regexPatternList.bulkDeletePartial", {
|
|
count: successful.length,
|
|
unsuccessful: unsuccessful.length,
|
|
}),
|
|
variant: "warning",
|
|
});
|
|
} else if (unsuccessful.length > 0) {
|
|
toast({
|
|
message: t("settings.regexPatternList.bulkDeleteFailed", { n: unsuccessful.length }),
|
|
variant: "error",
|
|
});
|
|
}
|
|
|
|
selectedPatterns.value = [];
|
|
confirmBulkDelete.value = false;
|
|
await getRegexPatterns(true);
|
|
} catch (error: any) {
|
|
const errorMessage =
|
|
error?.data?.message ||
|
|
error?.message ||
|
|
t("settings.regexPatternList.errorBulkDeleting");
|
|
if (error.response?.status != 403 || error?.status != 403) {
|
|
toast({
|
|
message: errorMessage,
|
|
variant: "error",
|
|
});
|
|
}
|
|
} finally {
|
|
bulkDeleteLoading.value = false;
|
|
}
|
|
};
|
|
|
|
useShortcuts([
|
|
{
|
|
id: "regexPatternsRefresh",
|
|
handler: () => {
|
|
if (!isInputFocused()) getRegexPatterns(true);
|
|
},
|
|
},
|
|
]);
|
|
|
|
return {
|
|
refreshRegexPatterns,
|
|
t,
|
|
store,
|
|
router,
|
|
filterQuery,
|
|
pageSize,
|
|
currentPage,
|
|
sortBy,
|
|
sortOrder,
|
|
oTableRef,
|
|
columns,
|
|
regexPatterns,
|
|
filterData,
|
|
createRegexPattern,
|
|
listLoading,
|
|
fetching,
|
|
lastUpdatedAt,
|
|
forbidden,
|
|
editRegexPattern,
|
|
deleteRegexPattern,
|
|
deleteDialog,
|
|
confirmDeleteRegexPattern,
|
|
showAddRegexPatternDialog,
|
|
getRegexPatterns,
|
|
showImportRegexPatternDialog,
|
|
importRegexPattern,
|
|
exportRegexPattern,
|
|
closeAddRegexPatternDialog,
|
|
visibleRows,
|
|
selectedPatterns,
|
|
selectedPatternIds,
|
|
handleSelectedIdsUpdate,
|
|
confirmBulkDelete,
|
|
openBulkDeleteDialog,
|
|
bulkDeleteRegexPatterns,
|
|
bulkDeleteLoading,
|
|
};
|
|
},
|
|
});
|
|
</script>
|