fix: UI fixes in alert list and DateTime selection (#4449)

fix #4427 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new event handler for managing component state when
hiding.
	- Enhanced styling for the alert list component with a new CSS class.
- Added a new reactive variable for handling function error messages in
the TenstackTable component.

- **Improvements**
- Streamlined logic for setting absolute and relative time in the
DateTime component.
- Improved error handling and notifications for alert duplication in the
AlertList component.
	- Adjusted layout and spacing for better user interface consistency.
- Enhanced functionality to dynamically update start and end times based
on relative datetime selection.
- Improved robustness of the QueryEditor and SearchBar components with
optional chaining.
- Simplified loading strategy for the SearchBar and QueryEditor
components to enhance performance.

- **Bug Fixes**
- Refined handling of selected date and time to prevent unnecessary
overwriting of values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Bhargav <BJP232004@GMAIL.COM>
This commit is contained in:
Omkar Kesarkhane
2024-09-04 14:43:44 +05:30
committed by GitHub
co-authored by Bhargav
parent 7680952cdd
commit 304a0f7895
8 changed files with 283 additions and 297 deletions
+26 -66
View File
@@ -36,6 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
self="top left"
no-route-dismiss
@before-show="onBeforeShow"
@before-hide="onBeforeHide"
>
<div class="flex justify-evenly q-py-sm">
<q-btn
@@ -320,6 +321,7 @@ import {
watch,
nextTick,
onActivated,
onBeforeUnmount,
} from "vue";
import {
getImageURL,
@@ -500,13 +502,9 @@ export default defineComponent({
selectedType.value = props.defaultType;
// if type is absolute
if (selectedType.value == "absolute") {
setAbsoluteTime(startTime, endTime);
} else {
// else type is relative
setRelativeTime(props.defaultRelativeTime);
}
setAbsoluteTime(startTime, endTime);
setRelativeTime(props.defaultRelativeTime);
if (props.queryRangeRestrictionInHour) computeRelativePeriod();
// displayValue.value = getDisplayValue();
@@ -536,60 +534,6 @@ export default defineComponent({
{ deep: true },
);
watch(
() => {
router.currentRoute.value.query?.from;
},
() => {
if (
router.currentRoute.value.query.hasOwnProperty("from") &&
router.currentRoute.value.query.hasOwnProperty("to")
) {
selectedType.value = "absolute";
selectedTime.value.startTime = timestampToTimezoneDate(
router.currentRoute.value.query?.from / 1000,
store.state.timezone,
"HH:mm",
);
selectedTime.value.endTime = timestampToTimezoneDate(
router.currentRoute.value.query?.to / 1000,
store.state.timezone,
"HH:mm",
);
selectedDate.value.from = timestampToTimezoneDate(
router.currentRoute.value.query?.from / 1000,
store.state.timezone,
"yyyy/MM/dd",
);
selectedDate.value.to = timestampToTimezoneDate(
router.currentRoute.value.query?.to / 1000,
store.state.timezone,
"yyyy/MM/dd",
);
saveDate("absolute");
}
},
{ deep: true },
);
// watch(
// () => props.defaultAbsoluteTime,
// (value) => {
// console.log("defaultAbsoluteTime", value);
// if (
// (value.startTime !== datePayload.value.startTime ||
// value.endTime !== datePayload.value.endTime) &&
// store.state.savedViewFlag == false
// ) {
// selectedType.value = props.defaultType;
// setAbsoluteTime(value.startTime, value.endTime);
// }
// },
// {
// deep: true,
// }
// );
const setRelativeDate = (period, value) => {
selectedType.value = "relative";
relativePeriod.value = period;
@@ -629,19 +573,28 @@ export default defineComponent({
if (periodValue) {
relativeValue.value = parseInt(periodValue);
}
selectedType.value = "relative";
}
};
const resetTime = (startTime, endTime) => {
if (!startTime || !endTime) {
var dateString = new Date().toLocaleDateString("en-ZA");
selectedDate.value.from = dateString;
selectedDate.value.to = dateString;
if (!selectedDate.value.from) selectedDate.value.from = dateString;
if (!selectedDate.value.to) selectedDate.value.to = dateString;
if (!startTime) selectedTime.value.startTime = "00:00";
if (!endTime) selectedTime.value.endTime = "23:59";
if (!endTime) {
const endDateTime = convertUnixTime(new Date().getTime() * 1000);
// If the selected date is today, set the end time to the current time
if (selectedDate.value.to === endDateTime.date) {
selectedTime.value.endTime = endDateTime.time;
} else selectedTime.value.endTime = "23:59";
}
return;
}
return;
@@ -731,6 +684,11 @@ export default defineComponent({
// if (props.modelValue) selectedDate.value = cloneDeep(props.modelValue);
};
const onBeforeHide = () => {
if (selectedType.value === "absolute")
resetTime(selectedTime.value.startTime, selectedTime.value.endTime);
};
const getPeriodLabel = computed(() => {
const periodMapping = {
m: "Minutes",
@@ -828,6 +786,7 @@ export default defineComponent({
if (dateobj.type === "relative") {
setRelativeTime(dateobj.relativeTimePeriod);
selectedType.value = "relative";
} else {
if (
dateobj.hasOwnProperty("selectedDate") &&
@@ -1030,6 +989,7 @@ export default defineComponent({
relativePeriodsMaxValue,
relativePeriodsSelect,
computeRelativePeriod,
onBeforeHide,
};
},
computed: {
+4 -4
View File
@@ -209,12 +209,12 @@ export default defineComponent({
});
window.addEventListener("click", () => {
editorObj.layout();
editorObj?.layout();
});
window.addEventListener("resize", async () => {
await nextTick();
editorObj.layout();
editorObj?.layout();
// queryEditorRef.value.resetEditorLayout();
});
};
@@ -307,7 +307,7 @@ export default defineComponent({
const setValue = (value: string) => {
if (editorObj?.setValue) {
editorObj.setValue(value);
editorObj.layout();
editorObj?.layout();
}
};
@@ -361,7 +361,7 @@ export default defineComponent({
};
const resetEditorLayout = () => {
editorObj.layout();
editorObj?.layout();
};
const triggerAutoComplete = async (value: string) => {
+176 -170
View File
@@ -21,8 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="alert-list-page"
class="q-pa-none flex"
style="height: calc(100vh - 57px)"
:class="store.state.theme === 'dark' ? 'dark-theme' : 'light-theme'"
>
<div v-if="!showAddAlertDialog" class="full-width">
<div v-if="!showAddAlertDialog" class="full-width alert-list-table">
<q-table
data-test="alert-list-table"
ref="qTable"
@@ -130,17 +131,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@click="showAddUpdateFn(props)"
></q-btn>
<q-btn
icon="content_copy"
:title="t('alerts.clone')"
class="q-ml-xs"
padding="sm"
unelevated
size="sm"
round
flat
@click.stop="duplicateAlert(props.row)"
data-test="alert-clone"
></q-btn>
icon="content_copy"
:title="t('alerts.clone')"
class="q-ml-xs"
padding="sm"
unelevated
size="sm"
round
flat
@click.stop="duplicateAlert(props.row)"
data-test="alert-clone"
></q-btn>
<q-btn
:data-test="`alert-list-${props.row.name}-delete-alert`"
:icon="outlinedDelete"
@@ -164,6 +165,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<pre style="white-space: break-spaces">{{ props.row.sql }}</pre>
</q-td>
</template>
<template #top="scope">
<div class="q-table__title" data-test="alerts-list-title">
{{ t("alerts.header") }}
@@ -231,76 +233,73 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-model="confirmDelete"
/>
<template>
<q-dialog class="q-pa-md " v-model="showForm" persistent>
<q-card class="clone-alert-popup">
<div class="row items-center no-wrap q-mx-md q-my-sm">
<div class="flex items-center">
<div
data-test="add-alert-back-btn"
class="flex justify-center items-center q-mr-md cursor-pointer"
style="
border: 1.5px solid;
border-radius: 50%;
width: 22px;
height: 22px;
"
title="Go Back"
@click="showForm = false"
>
<q-icon name="arrow_back_ios_new" size="14px" />
</div>
<div class="text-h6" data-test="clone-alert-title">
{{ t("alerts.cloneTitle") }}
</div>
</div>
</div>
<q-card-section>
<q-form @submit="submitForm">
<q-input v-model="toBeCloneAlertName" label="Alert Name" />
<q-select
v-model="toBeClonestreamType"
label="Stream Type"
:options="streamTypes"
@update:model-value="updateStreams()"
/>
<q-select
v-model="toBeClonestreamName"
:loading="isFetchingStreams"
:disable="!toBeClonestreamType"
label="Stream Name"
:options="streamNames"
@change="updateStreamName"
@filter="filterStreams"
/>
<div class="flex justify-center q-mt-lg">
<q-btn
data-test="add-alert-cancel-btn"
v-close-popup="true"
class="q-mb-md text-bold"
:label="t('alerts.cancel')"
text-color="light-text"
padding="sm md"
no-caps
/>
<q-btn
data-test="add-alert-submit-btn"
:label="t('alerts.save')"
class="q-mb-md text-bold no-border q-ml-md"
color="secondary"
padding="sm xl"
type="submit"
:disable="isSubmitting"
no-caps
/>
<q-dialog class="q-pa-md" v-model="showForm" persistent>
<q-card class="clone-alert-popup">
<div class="row items-center no-wrap q-mx-md q-my-sm">
<div class="flex items-center">
<div
data-test="add-alert-back-btn"
class="flex justify-center items-center q-mr-md cursor-pointer"
style="
border: 1.5px solid;
border-radius: 50%;
width: 22px;
height: 22px;
"
title="Go Back"
@click="showForm = false"
>
<q-icon name="arrow_back_ios_new" size="14px" />
</div>
<div class="text-h6" data-test="clone-alert-title">
{{ t("alerts.cloneTitle") }}
</div>
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</template>
</div>
<q-card-section>
<q-form @submit="submitForm">
<q-input v-model="toBeCloneAlertName" label="Alert Name" />
<q-select
v-model="toBeClonestreamType"
label="Stream Type"
:options="streamTypes"
@update:model-value="updateStreams()"
/>
<q-select
v-model="toBeClonestreamName"
:loading="isFetchingStreams"
:disable="!toBeClonestreamType"
label="Stream Name"
:options="streamNames"
@change="updateStreamName"
@filter="filterStreams"
/>
<div class="flex justify-center q-mt-lg">
<q-btn
data-test="add-alert-cancel-btn"
v-close-popup="true"
class="q-mb-md text-bold"
:label="t('alerts.cancel')"
text-color="light-text"
padding="sm md"
no-caps
/>
<q-btn
data-test="add-alert-submit-btn"
:label="t('alerts.save')"
class="q-mb-md text-bold no-border q-ml-md"
color="secondary"
padding="sm xl"
type="submit"
:disable="isSubmitting"
no-caps
/>
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</template>
</div>
</template>
@@ -346,7 +345,7 @@ export default defineComponent({
components: {
QTablePagination,
AddAlert: defineAsyncComponent(
() => import("@/components/alerts/AddAlert.vue")
() => import("@/components/alerts/AddAlert.vue"),
),
NoData,
ConfirmDialog,
@@ -371,21 +370,21 @@ export default defineComponent({
const isUpdated: any = ref(false);
const confirmDelete = ref<boolean>(false);
const splitterModel = ref(220);
const showForm = ref(false);
const showForm = ref(false);
const indexOptions = ref([]);
const schemaList = ref([]);
const streams: any = ref({});
const isFetchingStreams = ref(false);
const isSubmitting = ref(false);
const isSubmitting = ref(false);
const { getStreams } = useStreams();
const toBeCloneAlertName = ref ("");
const toBeCloneUUID = ref("");
const toBeClonestreamType = ref('');
const toBeClonestreamName = ref('');
const streamTypes = ref(['logs', 'metrics', 'traces']);
const streamNames : Ref<string[]> = ref([]);
const toBeCloneAlertName = ref("");
const toBeCloneUUID = ref("");
const toBeClonestreamType = ref("");
const toBeClonestreamName = ref("");
const streamTypes = ref(["logs", "metrics", "traces"]);
const streamNames: Ref<string[]> = ref([]);
const alertStateLoadingMap: Ref<{ [key: string]: boolean }> = ref({});
const folders = ref([
{
@@ -409,27 +408,6 @@ export default defineComponent({
align: "left",
sortable: true,
},
{
name: "alert_type",
field: "alert_type",
label: t("alerts.alertType"),
align: "left",
sortable: true,
},
{
name: "stream_type",
field: "stream_type",
label: t("alerts.streamType"),
align: "left",
sortable: true,
},
{
name: "stream_name",
field: "stream_name",
label: t("alerts.stream_name"),
align: "left",
sortable: true,
},
{
name: "owner",
field: "owner",
@@ -488,7 +466,7 @@ export default defineComponent({
"name",
false,
"",
store.state.selectedOrganization.identifier
store.state.selectedOrganization.identifier,
)
.then((res) => {
var counter = 1;
@@ -526,8 +504,12 @@ export default defineComponent({
description: data.description,
uuid: data.uuid,
owner: data.owner,
last_triggered_at:convertUnixToQuasarFormat(data.last_triggered_at),
last_satisfied_at:convertUnixToQuasarFormat(data.last_satisfied_at),
last_triggered_at: convertUnixToQuasarFormat(
data.last_triggered_at,
),
last_satisfied_at: convertUnixToQuasarFormat(
data.last_satisfied_at,
),
};
});
alertsRows.value.forEach((alert: AlertListItem) => {
@@ -569,7 +551,7 @@ export default defineComponent({
() => router.currentRoute.value.query.action,
(action) => {
if (!action) showAddAlertDialog.value = false;
}
},
);
const getDestinations = () => {
destinationService
@@ -584,7 +566,7 @@ export default defineComponent({
type: "negative",
message: "Error while fetching destinations.",
timeout: 3000,
})
}),
);
};
@@ -601,7 +583,7 @@ export default defineComponent({
type: "negative",
message: "Error while fetching templates.",
timeout: 3000,
})
}),
);
};
const perPageOptions: any = [
@@ -626,35 +608,37 @@ export default defineComponent({
const changeMaxRecordToReturn = (val: any) => {
maxRecordToReturn.value = val;
};
function convertUnixToQuasarFormat(unixMicroseconds : any) {
if(!unixMicroseconds) return "";
const unixSeconds = unixMicroseconds / 1e6;
const dateToFormat = new Date(unixSeconds * 1000);
const formattedDate = dateToFormat.toISOString();
return date.formatDate(formattedDate, "YYYY-MM-DDTHH:mm:ssZ");
}
function convertUnixToQuasarFormat(unixMicroseconds: any) {
if (!unixMicroseconds) return "";
const unixSeconds = unixMicroseconds / 1e6;
const dateToFormat = new Date(unixSeconds * 1000);
const formattedDate = dateToFormat.toISOString();
return date.formatDate(formattedDate, "YYYY-MM-DDTHH:mm:ssZ");
}
const addAlert = () => {
showAddAlertDialog.value = true;
};
const duplicateAlert = (row : any) =>{
toBeCloneUUID.value = row.uuid
const duplicateAlert = (row: any) => {
toBeCloneUUID.value = row.uuid;
toBeCloneAlertName.value = row.name;
toBeClonestreamName.value = "";
toBeClonestreamType.value = "";
showForm.value = true;
}
const submitForm = async () =>{
const alertToBeCloned = alerts.value.find((alert) => alert.uuid === toBeCloneUUID.value) as Alert;
};
const submitForm = async () => {
const alertToBeCloned = alerts.value.find(
(alert) => alert.uuid === toBeCloneUUID.value,
) as Alert;
const dismiss = $q.notify({
spinner: true,
message: "Please wait...",
timeout: 2000,
});
spinner: true,
message: "Please wait...",
timeout: 2000,
});
if (!alertToBeCloned) {
$q.notify({
@@ -664,7 +648,7 @@ export default defineComponent({
});
return;
}
if(!toBeClonestreamType.value){
if (!toBeClonestreamType.value) {
$q.notify({
type: "negative",
message: "Please select stream type ",
@@ -672,7 +656,7 @@ export default defineComponent({
});
return;
}
if(!toBeClonestreamName.value){
if (!toBeClonestreamName.value) {
$q.notify({
type: "negative",
message: "Please select stream name",
@@ -682,22 +666,24 @@ export default defineComponent({
}
isSubmitting.value = true;
alertToBeCloned.name = toBeCloneAlertName.value;
alertToBeCloned.stream_name = toBeClonestreamName.value;
alertToBeCloned.stream_type = toBeClonestreamType.value;
try{
alertsService.create(
alertToBeCloned.name = toBeCloneAlertName.value;
alertToBeCloned.stream_name = toBeClonestreamName.value;
alertToBeCloned.stream_type = toBeClonestreamType.value;
try {
alertsService
.create(
store.state.selectedOrganization.identifier,
alertToBeCloned.stream_name,
alertToBeCloned.stream_type,
alertToBeCloned,
).then((res) => {
)
.then((res) => {
dismiss();
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: "Alert Cloned Successfully",
message: "Alert Cloned Successfully",
timeout: 2000,
});
showForm.value = false;
@@ -716,27 +702,24 @@ export default defineComponent({
type: "negative",
message: e.response.data.message,
timeout: 2000,
});
}).finally(() => {
isSubmitting.value = false;
});
})
}
catch(e : any) {
.finally(() => {
isSubmitting.value = false;
});
} catch (e: any) {
showForm.value = true;
isSubmitting.value = false;
$q.notify({
type: "negative",
message: e.data.message,
timeout: 2000,
});
$q.notify({
type: "negative",
message: e.data.message,
timeout: 2000,
});
}
}
};
const showAddUpdateFn = (props: any) => {
formData.value = alerts.value.find(
(alert: any) => alert.uuid === props.row?.uuid
(alert: any) => alert.uuid === props.row?.uuid,
) as Alert;
let action;
if (!props.row) {
@@ -790,7 +773,7 @@ export default defineComponent({
store.state.selectedOrganization.identifier,
selectedDelete.value.stream_name,
selectedDelete.value.name,
selectedDelete.value.stream_type
selectedDelete.value.stream_type,
)
.then((res: any) => {
if (res.data.code == 200) {
@@ -845,9 +828,9 @@ export default defineComponent({
});
return filteredOptions;
};
const updateStreamName = (selectedOption : any)=> {
toBeClonestreamName.value = selectedOption;
}
const updateStreamName = (selectedOption: any) => {
toBeClonestreamName.value = selectedOption;
};
const updateStreams = (resetStream = true) => {
if (resetStream) toBeClonestreamName.value = "";
if (streams.value[toBeClonestreamType.value]) {
@@ -865,7 +848,7 @@ export default defineComponent({
if (!toBeClonestreamType) return Promise.resolve();
isFetchingStreams.value = true;
return getStreams (toBeClonestreamType.value, false)
return getStreams(toBeClonestreamType.value, false)
.then((res: any) => {
streams.value[toBeClonestreamType.value] = res.list;
schemaList.value = res.list;
@@ -879,14 +862,13 @@ export default defineComponent({
.finally(() => (isFetchingStreams.value = false));
};
const filterStreams = (val: string, update: any) => {
streamNames.value = filterColumns(indexOptions.value, val, update)
streamNames.value = filterColumns(indexOptions.value, val, update);
};
const toggleAlertState = (row: any) => {
alertStateLoadingMap.value[row.uuid] = true;
const alert: Alert = alerts.value.find(
(alert) => alert.uuid === row.uuid
(alert) => alert.uuid === row.uuid,
) as Alert;
alertsService
.toggleState(
@@ -894,7 +876,7 @@ export default defineComponent({
alert.stream_name,
alert.name,
!alert?.enabled,
alert.stream_type
alert.stream_type,
)
.then(() => {
alert.enabled = !alert.enabled;
@@ -999,7 +981,31 @@ export default defineComponent({
}
}
.alert-list-table {
th:last-child,
td:last-child {
position: sticky;
right: 0;
z-index: 1;
background: #ffffff;
box-shadow: -4px 0px 4px 0 rgba(0, 0, 0, 0.1);
}
}
.dark-theme {
th:last-child,
td:last-child {
background: var(--q-dark);
box-shadow: -4px 0px 4px 0 rgba(144, 144, 144, 0.1);
}
}
.light-theme {
th:last-child,
td:last-child {
background: #ffffff;
}
}
.alerts-tabs {
.q-tabs {
&--vertical {
@@ -1027,7 +1033,7 @@ export default defineComponent({
}
}
}
.clone-alert-popup{
.clone-alert-popup {
width: 400px;
}
</style>
+5
View File
@@ -707,6 +707,11 @@ const useLogs = () => {
)
: cloneDeep(searchObj.data.datetime);
if (searchObj.data.datetime.type === "relative") {
searchObj.data.datetime.startTime = timestamps.startTime;
searchObj.data.datetime.endTime = timestamps.endTime;
}
if (
timestamps.startTime != "Invalid Date" &&
timestamps.endTime != "Invalid Date"
+2 -3
View File
@@ -287,13 +287,12 @@ import { getConsumableRelativeTime } from "@/utils/date";
import { cloneDeep } from "lodash-es";
import { buildSqlQuery, getFieldsFromQuery } from "@/utils/query/sqlUtils";
import useNotifications from "@/composables/useNotifications";
import SearchBar from "@/plugins/logs/SearchBar.vue";
export default defineComponent({
name: "PageSearch",
components: {
SearchBar: defineAsyncComponent(
() => import("@/plugins/logs/SearchBar.vue"),
),
SearchBar,
IndexList: defineAsyncComponent(
() => import("@/plugins/logs/IndexList.vue"),
),
+5 -6
View File
@@ -920,6 +920,7 @@ import ConfirmDialog from "@/components/ConfirmDialog.vue";
import { cloneDeep } from "lodash-es";
import useDashboardPanelData from "@/composables/useDashboardPanel";
import { inject } from "vue";
import QueryEditor from "@/components/QueryEditor.vue";
const defaultValue: any = () => {
return {
@@ -934,9 +935,7 @@ export default defineComponent({
name: "ComponentSearchSearchBar",
components: {
DateTime,
QueryEditor: defineAsyncComponent(
() => import("@/components/QueryEditor.vue"),
),
QueryEditor,
SyntaxGuide,
AutoRefreshInterval,
ConfirmDialog,
@@ -1155,10 +1154,10 @@ export default defineComponent({
const updateAutoComplete = (value) => {
autoCompleteData.value.query = value;
autoCompleteData.value.cursorIndex =
queryEditorRef.value.getCursorIndex();
queryEditorRef?.value?.getCursorIndex();
autoCompleteData.value.fieldValues = props.fieldValues;
autoCompleteData.value.popup.open =
queryEditorRef.value.triggerAutoComplete;
queryEditorRef?.value?.triggerAutoComplete;
getSuggestions();
};
@@ -2350,7 +2349,7 @@ export default defineComponent({
searchObj.data.query = "";
}
searchObj.data.editorValue = "";
queryEditorRef.value.setValue(searchObj.data.query);
queryEditorRef.value?.setValue(searchObj.data.query);
if (store.state.zoConfig.query_on_stream_selection == false) {
handleRunQueryFn();
}
+53 -37
View File
@@ -162,46 +162,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</div>
</td>
</tr>
</thead>
<tr
data-test="log-search-result-function-error"
v-if="functionErrorMsg != ''"
>
<td
:colspan="columnOrder.length"
class="text-bold"
style="opacity: 0.6"
<tr
data-test="log-search-result-function-error"
v-if="functionErrorMsg != ''"
>
<div class="text-subtitle2 text-weight-bold bg-warning">
<q-btn
:icon="
expandedRowIndices.includes(-1)
? 'expand_more'
: 'chevron_right'
<td
:colspan="columnOrder.length"
class="text-bold"
style="opacity: 0.6"
>
<div
class="text-subtitle2 text-weight-bold q-pl-sm"
:class="
store.state.theme === 'dark'
? 'tw-bg-yellow-600'
: 'tw-bg-amber-300'
"
dense
size="xs"
flat
class="q-mr-xs"
data-test="table-row-expand-menu"
@click.self.stop="expandRow(-1)"
></q-btn
><b>
<q-icon name="warning" size="15px"></q-icon>
{{ t("search.functionErrorLabel") }}</b
>
</div>
</td>
</tr>
<tr v-if="expandedRowIndices.includes(-1)">
<td
:colspan="columnOrder.length"
class="bg-warning"
style="opacity: 0.7"
>
<pre>{{ functionErrorMsg }}</pre>
</td>
</tr>
<q-btn
:icon="isFunctionErrorOpen ? 'expand_more' : 'chevron_right'"
dense
size="xs"
flat
class="q-mr-xs"
data-test="table-row-expand-menu"
@click.capture.stop="expandFunctionError"
></q-btn
><b>
<q-icon name="warning" size="15px"></q-icon>
{{ t("search.functionErrorLabel") }}</b
>
</div>
</td>
</tr>
<tr v-if="functionErrorMsg != '' && isFunctionErrorOpen">
<td
:colspan="columnOrder.length"
style="opacity: 0.7"
class="q-px-sm"
:class="
store.state.theme === 'dark'
? 'tw-bg-yellow-600'
: 'tw-bg-amber-300'
"
>
<pre>{{ functionErrorMsg }}</pre>
</td>
</tr>
</thead>
<tbody
data-test="logs-search-result-table-body"
ref="tableBodyRef"
@@ -347,6 +355,7 @@ import {
useVueTable,
getCoreRowModel,
getSortedRowModel,
isFunction,
} from "@tanstack/vue-table";
import JsonPreview from "./JsonPreview.vue";
import { useStore } from "vuex";
@@ -435,6 +444,8 @@ const columnOrder = ref<any>([]);
const tableRows = ref(props.rows);
const isFunctionErrorOpen = ref(false);
watch(
() => props.columns,
async (newVal) => {
@@ -652,6 +663,7 @@ const expandRow = async (index: number) => {
isCollapseOperation = true;
} else {
expandedRowIndices.value.push(index);
tableRows.value.splice(index + 1, 0, {
isExpandedRow: true,
...(props.rows[index] as {}),
@@ -694,6 +706,10 @@ const handleDataRowClick = (row: any, index: number) => {
}
};
const expandFunctionError = () => {
isFunctionErrorOpen.value = !isFunctionErrorOpen.value;
};
defineExpose({
parentRef,
});
+12 -11
View File
@@ -46,7 +46,7 @@ const enterpriseResolverPlugin = {
const enterprisePath = path.resolve(
__dirname,
`./src/enterprise/${fileName}`
`./src/enterprise/${fileName}`,
);
const defaultPath = path.resolve(__dirname, `./src/${fileName}`);
@@ -119,7 +119,7 @@ export default defineConfig({
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
"@enterprise": fileURLToPath(
new URL("./src/enterprise", import.meta.url)
new URL("./src/enterprise", import.meta.url),
),
stream: "rollup-plugin-node-polyfills/polyfills/stream",
events: "rollup-plugin-node-polyfills/polyfills/events",
@@ -142,20 +142,21 @@ export default defineConfig({
brotliSize: true,
}),
],
manualChunks: {
"o2cs-analytics": ["rudder-sdk-js"],
"o2cs-oo-rum": [
"@openobserve/browser-logs",
"@openobserve/browser-rum",
],
"o2cs-date-fns": ["date-fns", "date-fns-tz"],
},
output: {
manualChunks: {
"o2cs-analytics": ["rudder-sdk-js"],
"o2cs-oo-rum": [
"@openobserve/browser-logs",
"@openobserve/browser-rum",
],
"o2cs-date-fns": ["date-fns", "date-fns-tz"],
"editor.api": ["monaco-editor"],
},
chunkFileNames: ({ name }) => {
if (name.startsWith("o2cs-")) {
return `assets/vendor/${name}.[hash].js`;
}
if (name.includes("editor.api")) {
return `assets/${name}.v1.js`;
}