mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 00:08:55 +00:00
fix: ui improvement (#13106)
This commit is contained in:
@@ -15,39 +15,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div data-test="add-action-script-section">
|
||||
<div class="w-full h-full px-[0.625rem] pb-[0.625rem] pt-1">
|
||||
<div class="card-container">
|
||||
<div
|
||||
class="flex items-center justify-between py-3 pl-4 pr-2 h-[68px]"
|
||||
>
|
||||
<div
|
||||
data-test="add-action-script-back-btn"
|
||||
class="flex justify-center items-center mr-3 cursor-pointer"
|
||||
title="Go Back"
|
||||
@click="router.back()"
|
||||
>
|
||||
<OIcon name="arrow-back-ios-new" size="xs" />
|
||||
<div
|
||||
v-if="isEditingActionScript"
|
||||
class="text-xl font-semibold pl-2"
|
||||
data-test="add-action-script-title"
|
||||
>
|
||||
{{ t("actions.update") }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-xl font-semibold pl-2"
|
||||
data-test="add-action-script-title"
|
||||
>
|
||||
{{ t("actions.add") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-test="add-action-script-section" class="w-full h-full flex flex-col">
|
||||
<AppPageHeader
|
||||
:title="isEditingActionScript ? t('actions.update') : t('actions.add')"
|
||||
:back="{
|
||||
onClick: () => router.back(),
|
||||
dataTest: 'add-action-script-back-btn',
|
||||
}"
|
||||
class="px-4 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="add-action-script-title">{{
|
||||
isEditingActionScript ? t("actions.update") : t("actions.add")
|
||||
}}</span>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<div class="w-full h-full px-[0.625rem] pb-[0.625rem]">
|
||||
<div class="w-full flex-1 min-h-0 px-2.5 pb-2.5 pt-1">
|
||||
<div
|
||||
class="card-container overflow-auto"
|
||||
style="max-height: calc(100vh - var(--navbar-height) - 157px)"
|
||||
@@ -505,6 +489,7 @@ import type { Ref } from "vue";
|
||||
import { DateTime as _DateTime } from "luxon";
|
||||
import actions from "@/services/action_scripts";
|
||||
import ConfirmDialog from "@/components/ConfirmDialog.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import CronExpressionParser from "cron-parser";
|
||||
import { convertDateToTimestamp } from "@/utils/date";
|
||||
import service_accounts from "@/services/service_accounts";
|
||||
|
||||
@@ -14,7 +14,7 @@ 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="rounded-md p-0" style="min-height: inherit">
|
||||
<div class="p-0" style="min-height: inherit">
|
||||
<!-- Header -->
|
||||
<AppPageHeader
|
||||
:title="isEditing ? t('aiToolset.update') : t('aiToolset.add')"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 o2-custom-bg"
|
||||
<div class="p-0 o2-custom-bg"
|
||||
style="
|
||||
height: calc(100vh - 48px);
|
||||
min-height: inherit;
|
||||
@@ -28,7 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
label: t('alert_destinations.header'),
|
||||
onClick: () => emit('cancel:hideform'),
|
||||
}"
|
||||
class="card-container px-3 border-b border-border-default"
|
||||
class="px-3 border-b border-border-default"
|
||||
style="flex-shrink: 0"
|
||||
>
|
||||
<template #title>
|
||||
|
||||
@@ -14,7 +14,7 @@ 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="rounded-md p-0 o2-custom-bg"
|
||||
<div class="p-0 o2-custom-bg"
|
||||
style="height: calc(100vh - 48px); min-height: inherit"
|
||||
>
|
||||
<AppPageHeader
|
||||
@@ -22,7 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
label: t('alert_templates.header'),
|
||||
onClick: () => emit('cancel:hideform'),
|
||||
}"
|
||||
class="card-container px-3 border-b border-border-default"
|
||||
class="px-3 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="add-template-title">
|
||||
|
||||
@@ -17,101 +17,82 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template>
|
||||
<div
|
||||
data-test="alert-history-page"
|
||||
class="p-0 flex"
|
||||
class="w-full h-full flex flex-col min-h-0"
|
||||
>
|
||||
<div class="w-full h-full px-2.5 pt-[0.325rem]">
|
||||
<div class="card-container mb-2.5">
|
||||
<div
|
||||
class="flex justify-between w-full h-17 px-2 py-3"
|
||||
<AppPageHeader
|
||||
:title="t('alerts.history')"
|
||||
:back="{ onClick: goBack, dataTest: 'alert-history-back-btn' }"
|
||||
class="shrink-0 px-4 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="alerts-history-title">{{ t("alerts.history") }}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<DateTime
|
||||
ref="dateTimeRef"
|
||||
auto-apply
|
||||
:default-type="dateTimeType"
|
||||
:default-absolute-time="{
|
||||
startTime: absoluteTime.startTime,
|
||||
endTime: absoluteTime.endTime,
|
||||
}"
|
||||
:default-relative-time="relativeTime"
|
||||
data-test="alert-history-date-picker"
|
||||
@on:date-change="updateDateTime"
|
||||
/>
|
||||
<OSelect
|
||||
v-model="selectedAlert"
|
||||
:options="filteredAlertOptions"
|
||||
labelKey="label"
|
||||
valueKey="value"
|
||||
@update:model-value="onAlertSelected"
|
||||
:placeholder="t(`alerts.searcHistory`) || 'Select or search alert...'"
|
||||
data-test="alert-history-search-select"
|
||||
class="o2-search-input min-w-62.5"
|
||||
clearable
|
||||
@clear="clearSearch"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<OButton
|
||||
padding="xs"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
icon-left="arrow-back-ios-new"
|
||||
@click="goBack"
|
||||
data-test="alert-history-back-btn"
|
||||
<template #icon-left>
|
||||
<OIcon
|
||||
class="o2-search-input-icon"
|
||||
:class="
|
||||
store.state.theme === 'dark'
|
||||
? 'o2-search-input-icon-dark'
|
||||
: 'o2-search-input-icon-light'
|
||||
"
|
||||
name="search" size="sm"
|
||||
/>
|
||||
<div
|
||||
class="text-xl tracking-[0.005em] font-semibold ml-2"
|
||||
data-test="alerts-history-title"
|
||||
>
|
||||
{{ t(`alerts.history`) }}
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="px-3 py-2 text-muted-foreground">
|
||||
No alerts found
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex ml-auto items-center">
|
||||
<div class="mr-2">
|
||||
<DateTime
|
||||
ref="dateTimeRef"
|
||||
auto-apply
|
||||
:default-type="dateTimeType"
|
||||
:default-absolute-time="{
|
||||
startTime: absoluteTime.startTime,
|
||||
endTime: absoluteTime.endTime,
|
||||
}"
|
||||
:default-relative-time="relativeTime"
|
||||
data-test="alert-history-date-picker"
|
||||
@on:date-change="updateDateTime"
|
||||
/>
|
||||
</div>
|
||||
<OSelect
|
||||
v-model="selectedAlert"
|
||||
:options="filteredAlertOptions"
|
||||
labelKey="label"
|
||||
valueKey="value"
|
||||
@update:model-value="onAlertSelected"
|
||||
:placeholder="t(`alerts.searcHistory`) || 'Select or search alert...'"
|
||||
data-test="alert-history-search-select"
|
||||
class="o2-search-input mr-2"
|
||||
style="min-width: 250px"
|
||||
clearable
|
||||
@clear="clearSearch"
|
||||
>
|
||||
<template #icon-left>
|
||||
<OIcon
|
||||
class="o2-search-input-icon"
|
||||
:class="
|
||||
store.state.theme === 'dark'
|
||||
? 'o2-search-input-icon-dark'
|
||||
: 'o2-search-input-icon-light'
|
||||
"
|
||||
name="search" size="sm"
|
||||
/>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="px-3 py-2 text-muted-foreground">
|
||||
No alerts found
|
||||
</div>
|
||||
</template>
|
||||
</OSelect>
|
||||
<OButton
|
||||
variant="ghost"
|
||||
icon-left="search"
|
||||
size="icon-sm"
|
||||
@click="manualSearch"
|
||||
data-test="alert-history-manual-search-btn"
|
||||
:disabled="loading"
|
||||
class="mr-2"
|
||||
>
|
||||
<OTooltip :content="t('common.search') || 'Search'" />
|
||||
</OButton>
|
||||
<OButton
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
icon-left="refresh"
|
||||
@click="refreshData"
|
||||
data-test="alert-history-refresh-btn"
|
||||
:loading="loading"
|
||||
>
|
||||
<OTooltip :content="t('common.refresh') || 'Refresh'" />
|
||||
</OButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full h-full px-2.5">
|
||||
<div class="alert-history-table card-container h-[calc(100vh-130px)]">
|
||||
</template>
|
||||
</OSelect>
|
||||
<OButton
|
||||
variant="ghost"
|
||||
icon-left="search"
|
||||
size="icon-sm"
|
||||
@click="manualSearch"
|
||||
data-test="alert-history-manual-search-btn"
|
||||
:disabled="loading"
|
||||
>
|
||||
<OTooltip :content="t('common.search') || 'Search'" />
|
||||
</OButton>
|
||||
<OButton
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
icon-left="refresh"
|
||||
@click="refreshData"
|
||||
data-test="alert-history-refresh-btn"
|
||||
:loading="loading"
|
||||
>
|
||||
<OTooltip :content="t('common.refresh') || 'Refresh'" />
|
||||
</OButton>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
<div class="flex-1 min-h-0 px-2.5 pt-2">
|
||||
<div class="alert-history-table card-container h-full">
|
||||
<OTable
|
||||
data-test="alert-history-table"
|
||||
:data="rows"
|
||||
@@ -478,6 +459,7 @@ import OTimeCell from "@/lib/core/Table/cells/OTimeCell.vue";
|
||||
import type { OTableColumnDef } from "@/lib/core/Table/OTable.types";
|
||||
import alertsService from "@/services/alerts";
|
||||
import NoData from "@/components/shared/grid/NoData.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import OTag from "@/lib/core/Badge/OTag.vue";
|
||||
import OButton from '@/lib/core/Button/OButton.vue';
|
||||
|
||||
@@ -15,61 +15,45 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="px-[0.625rem] py-[0.1rem]">
|
||||
<div class="card-container mb-[0.625rem]">
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="flex justify-between items-center px-2 py-2"
|
||||
<div class="w-full h-full flex flex-col min-h-0">
|
||||
<AppPageHeader
|
||||
:title="t('alerts.insights.title')"
|
||||
:back="{ onClick: goBack, dataTest: 'alert-insights-back-btn' }"
|
||||
tabs-below
|
||||
class="shrink-0 px-4"
|
||||
>
|
||||
<template #actions>
|
||||
<date-time
|
||||
ref="dateTimeRef"
|
||||
auto-apply
|
||||
:default-type="dateTimeType"
|
||||
:default-absolute-time="{
|
||||
startTime: timeRange.__global.start_time.getTime(),
|
||||
endTime: timeRange.__global.end_time.getTime(),
|
||||
}"
|
||||
:default-relative-time="relativeTime"
|
||||
@on:date-change="updateDateTime"
|
||||
@on:timezone-change="updateTimezone"
|
||||
data-test="alert-insights-datetime"
|
||||
/>
|
||||
|
||||
<OButton
|
||||
@click="refreshDashboard"
|
||||
:loading="isLoading"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
data-test="alert-insights-refresh-btn"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<OButton
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
class="hideOnPrintMode el-border"
|
||||
@click="goBack"
|
||||
data-test="alert-insights-back-btn"
|
||||
>
|
||||
<OIcon name="arrow-back-ios-new" size="sm" />
|
||||
</OButton>
|
||||
<div class="text-xl tracking-[0.005em] font-[600] ml-2">{{ t("alerts.insights.title") }}</div>
|
||||
</div>
|
||||
<OIcon name="refresh" size="sm" />
|
||||
<OTooltip :content="t('common.refresh')" />
|
||||
</OButton>
|
||||
</template>
|
||||
|
||||
<div class="flex items-center">
|
||||
<date-time
|
||||
ref="dateTimeRef"
|
||||
auto-apply
|
||||
:default-type="dateTimeType"
|
||||
:default-absolute-time="{
|
||||
startTime: timeRange.__global.start_time.getTime(),
|
||||
endTime: timeRange.__global.end_time.getTime(),
|
||||
}"
|
||||
:default-relative-time="relativeTime"
|
||||
@on:date-change="updateDateTime"
|
||||
@on:timezone-change="updateTimezone"
|
||||
class="mr-2"
|
||||
data-test="alert-insights-datetime"
|
||||
/>
|
||||
|
||||
<OButton
|
||||
@click="refreshDashboard"
|
||||
:loading="isLoading"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
class="mr-1 el-border"
|
||||
data-test="alert-insights-refresh-btn"
|
||||
>
|
||||
<OIcon name="refresh" size="sm" />
|
||||
<OTooltip :content="t('common.refresh')" />
|
||||
</OButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<template #tabs>
|
||||
<OTabs
|
||||
v-model="currentTab"
|
||||
dense
|
||||
class="alert-insights-tabs ml-2"
|
||||
class="alert-insights-tabs"
|
||||
align="left"
|
||||
data-test="alert-insights-tabs"
|
||||
>
|
||||
@@ -88,12 +72,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
<OTab name="quality" :label="t('alerts.insights.tabs.quality')" data-test="tab-quality" />
|
||||
</OTabs>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div
|
||||
v-if="show"
|
||||
class="flex items-center gap-2 flex-wrap px-4 py-3 border-b border-(--q-border-color)"
|
||||
>
|
||||
<!-- Filters Section -->
|
||||
<div
|
||||
v-if="show"
|
||||
class="flex items-center gap-2 flex-wrap px-4 py-3 border-b border-(--q-border-color) shrink-0"
|
||||
>
|
||||
<span class="text-sm font-semibold relative top-1">{{ t("common.filters") }}:</span>
|
||||
|
||||
<!-- Failed Only Toggle -->
|
||||
@@ -166,14 +152,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
>
|
||||
{{ t('alerts.insights.filters.clearAll') }}
|
||||
</OButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons Row -->
|
||||
<div
|
||||
v-if="selectedAlertForAction"
|
||||
class="bg-primary bg-opacity-10 flex items-center px-4 py-3 gap-3 border-b border-(--q-border-color)"
|
||||
class="bg-primary bg-opacity-10 flex items-center px-4 py-3 gap-3 border-b border-(--q-border-color) shrink-0"
|
||||
data-test="action-buttons-row"
|
||||
>
|
||||
<OIcon name="campaign" size="sm" />
|
||||
@@ -228,8 +212,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Content -->
|
||||
<div class="w-full h-full px-[0.625rem] pb-[0.625rem]">
|
||||
<div class="card-container mb-[0.625rem] h-[calc(100vh-208px)]">
|
||||
<div class="flex-1 min-h-0 px-2.5 pb-2.5">
|
||||
<div class="card-container mb-2.5 h-[calc(100vh-13rem)]">
|
||||
<div
|
||||
@contextmenu="handleNativeContextMenu"
|
||||
>
|
||||
@@ -283,6 +267,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import OTabs from '@/lib/navigation/Tabs/OTabs.vue'
|
||||
import OTab from '@/lib/navigation/Tabs/OTab.vue'
|
||||
import OButton from '@/lib/core/Button/OButton.vue';
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import { ref, computed, onMounted, watch, nextTick, reactive, provide } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
|
||||
<div v-if="!showDestinationEditor && !showImportDestination" class="flex flex-col h-full">
|
||||
<AppPageHeader
|
||||
@@ -106,7 +106,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
|
||||
<template #bottom="{ totalRows }">
|
||||
<span class="o2-table-footer-title text-primary">
|
||||
<span class="o2-table-footer-title">
|
||||
{{ totalRows.toLocaleString() }} {{ t('alert_destinations.header') }}
|
||||
</span>
|
||||
<OButton
|
||||
@@ -232,7 +232,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@get:destinations="getDestinations"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-else class="flex-1 min-h-0">
|
||||
<ImportDestination
|
||||
:destinations="destinations"
|
||||
:templates="templates"
|
||||
|
||||
@@ -19,13 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
ref="baseImportRef"
|
||||
title="Import Alert"
|
||||
test-prefix="alert"
|
||||
class="flex-1 min-h-0"
|
||||
:is-importing="isAlertImporting"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 110px)',
|
||||
errorReport: 'calc(100vh - 192px)',
|
||||
}"
|
||||
@back="router.back()"
|
||||
@cancel="router.back()"
|
||||
@import="importJson"
|
||||
@@ -81,16 +76,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- Output Section with Alert-specific Error Display -->
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="alertErrorsToDisplay.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
Error Validations
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="error-report-container" style="height: calc(100vh - 192px) !important; overflow: auto; resize: none;">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="error-report-container flex-1 min-h-0">
|
||||
<!-- Alert Errors Section -->
|
||||
<div class="p-2.5 mb-2.5" v-if="alertErrorsToDisplay.length > 0">
|
||||
<div class="error-list">
|
||||
|
||||
@@ -21,29 +21,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
test-prefix="destination"
|
||||
:is-importing="isDestinationImporting"
|
||||
container-class=""
|
||||
container-style="height: calc(100vh - 50px);"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 110px)',
|
||||
errorReport: 'calc(100vh - 128px)',
|
||||
}"
|
||||
container-style=""
|
||||
@back="arrowBackFn"
|
||||
@cancel="router.back()"
|
||||
@import="importJson"
|
||||
>
|
||||
<!-- Output Section with Destination-specific Error Display -->
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="destinationErrorsToDisplay.length > 0 || destinationCreators.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
{{ destinationErrorsToDisplay.length > 0 ? 'Error Validations' : 'Output Messages' }}
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="h-[calc(60vh-8px)] overflow-auto [resize:none] w-full min-w-100">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="flex-1 min-h-0 overflow-auto [resize:none] w-full min-w-100">
|
||||
<!-- Destination Errors Section -->
|
||||
<div
|
||||
class="error-section p-2.5 mb-2.5"
|
||||
|
||||
@@ -21,10 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
test-prefix="semantic-groups"
|
||||
:is-importing="isImporting"
|
||||
:show-splitter="false"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 286px)',
|
||||
fileEditor: 'calc(100vh - 308px)',
|
||||
}"
|
||||
container-class=""
|
||||
container-style="height: calc(100vh - var(--navbar-height));"
|
||||
@back="handleBack"
|
||||
@cancel="handleBack"
|
||||
@import="handleImport"
|
||||
@@ -32,7 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
>
|
||||
<!-- Full-width content for diff view -->
|
||||
<template #full-width-content>
|
||||
<div class="w-full p-2 h-[calc(100vh-140px)] flex flex-col overflow-hidden">
|
||||
<div class="w-full p-2 h-full flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||
<!-- Compact Header with File Upload -->
|
||||
<div class="card-container p-2 mb-2">
|
||||
<div class="flex items-center">
|
||||
|
||||
@@ -21,29 +21,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
test-prefix="template"
|
||||
:is-importing="isTemplateImporting"
|
||||
container-class=""
|
||||
container-style="height: calc(100vh - 50px);"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 110px)',
|
||||
errorReport: 'calc(100vh - 130px)',
|
||||
}"
|
||||
container-style=""
|
||||
@back="arrowBackFn"
|
||||
@cancel="router.back()"
|
||||
@import="importJson"
|
||||
>
|
||||
<!-- Output Section with Template-specific Error Display -->
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="templateErrorsToDisplay.length > 0 || tempalteCreators.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
{{ templateErrorsToDisplay.length > 0 ? 'Error Validations' : 'Output Messages' }}
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="h-[calc(60vh-8px)] overflow-auto [resize:none] w-full min-w-[400px]">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="flex-1 min-h-0 overflow-auto [resize:none] w-full min-w-[400px]">
|
||||
<!-- Template Errors Section -->
|
||||
<div
|
||||
class="error-section p-2.5 mb-2.5"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<div v-if="!showDestinationEditor" class="flex flex-col h-full">
|
||||
<!-- Standard section header: title + actions only. Search moved to toolbar. -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<div v-if="!showImportTemplate && !showTemplateEditor" class="flex flex-col h-full">
|
||||
<!-- Standard section header: title + actions only. Search moved to toolbar. -->
|
||||
<AppPageHeader
|
||||
@@ -207,7 +207,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@get:templates="getTemplates"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-else class="flex-1 min-h-0">
|
||||
<ImportTemplate :templates="templates" @update:templates="getTemplates" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ 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="rounded-md p-0" style="min-height: inherit;">
|
||||
<div class="p-0" style="min-height: inherit;">
|
||||
<AppPageHeader
|
||||
:back="{
|
||||
label: t('cipherKey.header'),
|
||||
|
||||
@@ -80,7 +80,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<slot name="back">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center shrink-0 w-9.5 h-9.5 rounded-[0.625rem] text-text-secondary transition-colors hover:bg-surface-subtle hover:text-text-primary outline-none focus-visible:ring-4 focus-visible:ring-primary-500/25 focus-visible:ring-inset"
|
||||
class="inline-flex items-center justify-center shrink-0 w-9.5 h-9.5 rounded-[0.625rem] bg-surface-subtle text-text-primary transition-colors hover:bg-button-ghost-hover-bg outline-none focus-visible:ring-4 focus-visible:ring-primary-500/25 focus-visible:ring-inset"
|
||||
:title="backLabel"
|
||||
:aria-label="backLabel"
|
||||
:data-test="props.back?.dataTest ?? 'app-page-header-back'"
|
||||
|
||||
@@ -325,16 +325,18 @@ describe("BaseImport.vue", () => {
|
||||
expect(style.height).toBe("100%");
|
||||
});
|
||||
|
||||
it("outputContainerStyle uses editorHeights.outputContainer", () => {
|
||||
wrapper = createWrapper({
|
||||
editorHeights: {
|
||||
outputContainer: "500px",
|
||||
urlEditor: "300px",
|
||||
fileEditor: "300px",
|
||||
errorReport: "200px",
|
||||
},
|
||||
});
|
||||
expect((wrapper.vm as any).outputContainerStyle.height).toBe("500px");
|
||||
it("output editor container fills its pane via flex instead of a fixed height", () => {
|
||||
wrapper = createWrapper();
|
||||
const outputEditor = wrapper.find(
|
||||
'[data-test="dashboard-import-output-editor"]',
|
||||
);
|
||||
expect(outputEditor.exists()).toBe(true);
|
||||
// The pane fills the splitter height with flex (no brittle calc(100vh-Npx)).
|
||||
const classes = outputEditor.classes();
|
||||
expect(classes).toContain("h-full");
|
||||
expect(classes).toContain("flex");
|
||||
expect(classes).toContain("flex-col");
|
||||
expect(outputEditor.attributes("style") || "").not.toContain("calc(");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-full"
|
||||
class="w-full h-full flex flex-col overflow-hidden min-h-0"
|
||||
:class="[containerClass]"
|
||||
:style="containerStyle"
|
||||
>
|
||||
@@ -54,20 +54,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<div class="flex" :class="contentWrapperClass">
|
||||
<div class="flex" :style="contentStyle">
|
||||
<div class="flex flex-1 min-h-0" :class="contentWrapperClass">
|
||||
<div class="flex w-full min-h-0" :style="contentStyle">
|
||||
<OSplitter
|
||||
v-if="showSplitter"
|
||||
class="logs-search-splitter"
|
||||
class="logs-search-splitter w-full h-full min-h-0"
|
||||
v-model="splitterModel"
|
||||
:style="splitterStyle"
|
||||
:limits="[30, 60]"
|
||||
:horizontal="false"
|
||||
>
|
||||
<template #before>
|
||||
<div class="w-full h-full border-r border-border-default">
|
||||
<div class="w-full h-full flex flex-col border-r border-border-default">
|
||||
<!-- Tabs Section -->
|
||||
<div class="card-container py-2 px-2 mb-[0.625rem]">
|
||||
<div class="card-container py-2 px-2 mb-1 shrink-0">
|
||||
<div class="app-tabs-container h-[36px] w-fit">
|
||||
<app-tabs
|
||||
:data-test="`${testPrefix}-import-tabs`"
|
||||
@@ -82,12 +82,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- URL Import Tab -->
|
||||
<div
|
||||
v-if="activeTab === 'import_json_url'"
|
||||
class="editor-container-url card-container py-1"
|
||||
class="editor-container-url card-container py-1 flex-1 min-h-0 flex flex-col"
|
||||
>
|
||||
<div class="mx-2 pb-2">
|
||||
<div class="mx-2 mt-1 pb-2 flex flex-col flex-1 min-h-0">
|
||||
<!-- Slot for custom URL input section -->
|
||||
<slot name="url-input-section" :url="url" :updateUrl="updateUrl">
|
||||
<div class="flex mt-3 mb-3">
|
||||
<div class="flex mb-3 shrink-0">
|
||||
<OInput
|
||||
:data-test="`${testPrefix}-import-url-input`"
|
||||
v-model="url"
|
||||
@@ -102,7 +102,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:data-test="`${testPrefix}-import-sql-editor`"
|
||||
ref="queryEditorRef"
|
||||
:editor-id="`${testPrefix}-import-query-editor`"
|
||||
class="import-editor-shell import-url-editor mx-2"
|
||||
class="import-editor-shell import-url-editor mx-2 flex-1 min-h-0"
|
||||
:debounceTime="300"
|
||||
v-model:query="jsonStr"
|
||||
language="json"
|
||||
@@ -111,12 +111,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
<div
|
||||
v-if="activeTab === 'import_json_file'"
|
||||
class="editor-container-json card-container py-1"
|
||||
class="editor-container-json card-container py-1 flex-1 min-h-0 flex flex-col"
|
||||
>
|
||||
<div class="mx-2 mt-3 pb-2">
|
||||
<div class="mx-2 mt-1 pb-2 flex flex-col flex-1 min-h-0">
|
||||
<!-- Slot for custom file input section -->
|
||||
<slot name="file-input-section" :jsonFiles="jsonFiles" :updateFiles="updateFiles">
|
||||
<div style="width: calc(100% - 10px)" class="mb-1 flex">
|
||||
<div style="width: calc(100% - 10px)" class="mb-1 flex shrink-0">
|
||||
<div style="width: 100%" class="pr-2">
|
||||
<OFile
|
||||
:data-test="`${testPrefix}-import-json-file-input`"
|
||||
@@ -147,7 +147,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:data-test="`${testPrefix}-import-sql-editor`"
|
||||
ref="queryEditorRef"
|
||||
:editor-id="`${testPrefix}-import-query-editor`"
|
||||
class="import-editor-shell import-file-editor mx-2"
|
||||
class="import-editor-shell import-file-editor mx-2 flex-1 min-h-0"
|
||||
:debounceTime="300"
|
||||
v-model:query="jsonStr"
|
||||
language="json"
|
||||
@@ -162,16 +162,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template #after>
|
||||
<div
|
||||
:data-test="`${testPrefix}-import-output-editor`"
|
||||
class="card-container w-full"
|
||||
:style="outputContainerStyle"
|
||||
class="card-container w-full h-full flex flex-col min-h-0"
|
||||
>
|
||||
<!-- Slot for complete output section customization -->
|
||||
<slot name="output-section">
|
||||
<!-- Default output section - only shown if slot not used -->
|
||||
<slot name="output-content">
|
||||
<div class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="error-report-container">
|
||||
<div class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="error-report-container flex-1 min-h-0 overflow-auto">
|
||||
<div class="text-center p-3 text-gray-400">
|
||||
No messages to display
|
||||
</div>
|
||||
@@ -365,12 +364,6 @@ export default defineComponent({
|
||||
};
|
||||
});
|
||||
|
||||
const outputContainerStyle = computed(() => {
|
||||
return {
|
||||
height: props.editorHeights.outputContainer,
|
||||
};
|
||||
});
|
||||
|
||||
// Methods
|
||||
const handleBack = () => {
|
||||
emit("back");
|
||||
@@ -542,7 +535,6 @@ export default defineComponent({
|
||||
updateJsonArray,
|
||||
contentStyle,
|
||||
splitterStyle,
|
||||
outputContainerStyle,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -561,23 +553,15 @@ export default defineComponent({
|
||||
box-sizing: border-box;
|
||||
/* w-full (100%) + mx-2 (1rem total) would overflow by 1rem and add a
|
||||
horizontal scrollbar; subtract the margins so the box stays inside and
|
||||
keeps a right-side gap. */
|
||||
keeps a right-side gap. The height comes from flex (flex-1 min-h-0) so the
|
||||
editor grows to fill the pane instead of a brittle calc(100vh - Npx). */
|
||||
width: calc(100% - 1rem);
|
||||
border: 1px solid var(--o2-border-color);
|
||||
border-radius: 0.375rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.import-url-editor {
|
||||
height: v-bind('editorHeights.urlEditor');
|
||||
}
|
||||
|
||||
.import-file-editor {
|
||||
height: v-bind('editorHeights.fileEditor');
|
||||
}
|
||||
|
||||
.error-report-container {
|
||||
height: v-bind('editorHeights.errorReport') !important;
|
||||
overflow: auto;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template #bottom>
|
||||
<div class="flex items-center justify-between w-full py-2">
|
||||
<div class="flex items-center font-bold text-[14px] mr-4">
|
||||
<div class="flex items-center o2-table-footer-title mr-4">
|
||||
{{ resultTotal }} {{ t('function.enrichmentTables') }}
|
||||
</div>
|
||||
<OButton
|
||||
|
||||
@@ -130,7 +130,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template #bottom="scope">
|
||||
<div class="flex items-center justify-between w-full py-2">
|
||||
<div class="flex items-center font-bold text-[14px] mr-4">
|
||||
<div class="flex items-center o2-table-footer-title mr-4">
|
||||
{{ resultTotal }} {{ t('function.header') }}
|
||||
</div>
|
||||
<OButton
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard section header: title + description + Create action. -->
|
||||
<AppPageHeader
|
||||
icon="key"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + actions only. Search moved into the
|
||||
table's own toolbar (built-in global filter). -->
|
||||
<AppPageHeader
|
||||
@@ -116,7 +116,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<span class="o2-table-footer-title text-text-primary">{{ rows.length }} {{ t('iam.groups') }}</span>
|
||||
<span class="o2-table-footer-title">{{ rows.length }} {{ t('iam.groups') }}</span>
|
||||
<OButton
|
||||
v-if="selectedGroups.length > 0"
|
||||
data-test="iam-groups-bulk-delete-btn"
|
||||
|
||||
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<!-- eslint-disable vue/attribute-hyphenation -->
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + actions only. Search moved into the
|
||||
table's own toolbar (built-in global filter). -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<!-- TODO: we need to completely remove the store.state.theme based styling on this page as we have moved it to central place app.scss -->
|
||||
<template>
|
||||
<div class="rounded-md quota-page text-left h-full flex flex-col"
|
||||
<div class="quota-page text-left h-full flex flex-col"
|
||||
:class="
|
||||
store.state.theme === 'dark' ? 'dark-theme-page' : 'light-theme-page'
|
||||
"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + actions only. Search moved into the
|
||||
table's own toolbar (built-in global filter). -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -137,7 +137,7 @@ const columns: OTableColumnDef[] = [
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<span class="o2-table-footer-title text-text-primary">{{ data.length }} {{ t("iam.roles") }}</span>
|
||||
<span class="o2-table-footer-title">{{ data.length }} {{ t("iam.roles") }}</span>
|
||||
<OButton
|
||||
v-if="(selectedIds?.length ?? 0) > 0"
|
||||
data-test="iam-roles-bulk-delete-btn"
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + actions only. Search moved into the
|
||||
table's own toolbar (built-in global filter). -->
|
||||
<AppPageHeader
|
||||
@@ -171,7 +171,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<span class="o2-table-footer-title text-text-primary">{{ serviceAccountsState.service_accounts_users.length }} {{ t('serviceAccounts.header') }}</span>
|
||||
<span class="o2-table-footer-title">{{ serviceAccountsState.service_accounts_users.length }} {{ t('serviceAccounts.header') }}</span>
|
||||
<OButton
|
||||
v-if="selectedAccounts.length > 0"
|
||||
data-test="service-accounts-list-delete-accounts-btn"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + icon + subtitle, matching the Users page. -->
|
||||
<AppPageHeader
|
||||
:title="t('invitation.pendingInvitations')"
|
||||
@@ -108,7 +108,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<span class="o2-table-footer-title text-text-primary">
|
||||
<span class="o2-table-footer-title">
|
||||
{{ resultTotal }} {{ t('invitation.pendingInvitations') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<!-- eslint-disable vue/attribute-hyphenation -->
|
||||
<template>
|
||||
<div class="rounded-md p-0 h-full flex flex-col">
|
||||
<div class="p-0 h-full flex flex-col">
|
||||
<!-- Standard page header: title + actions only. The user search moved into
|
||||
the table's own toolbar (built-in global filter) per the layout system. -->
|
||||
<AppPageHeader
|
||||
@@ -175,7 +175,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</OButton>
|
||||
</template>
|
||||
<template #bottom>
|
||||
<span class="o2-table-footer-title text-text-primary">{{ rows.length }} {{ isEnterpriseOrCloud ? (t('iam.organizationMembers') || 'Organization Members') : t('iam.basicUsers') }}</span>
|
||||
<span class="o2-table-footer-title">{{ rows.length }} {{ isEnterpriseOrCloud ? (t('iam.organizationMembers') || 'Organization Members') : t('iam.basicUsers') }}</span>
|
||||
<OButton
|
||||
v-if="selectedUsers.length > 0"
|
||||
data-test="users-list-delete-users-btn"
|
||||
|
||||
@@ -20,29 +20,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
title="Import Pipeline"
|
||||
test-prefix="pipeline"
|
||||
hide-header
|
||||
container-class=""
|
||||
:is-importing="isPipelineImporting"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 112px)',
|
||||
errorReport: 'calc(100vh - 132px)',
|
||||
}"
|
||||
@back="router.back()"
|
||||
@cancel="router.back()"
|
||||
@import="importJson"
|
||||
>
|
||||
<!-- Output Section with Pipeline-specific Error Display -->
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="pipelineErrorsToDisplay.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
Error Validations
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="error-report-container" style="height: calc(100vh - 128px) !important; overflow: auto; resize: none;">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="error-report-container flex-1 min-h-0">
|
||||
<!-- Pipeline Errors Section -->
|
||||
<div
|
||||
class="p-2.5 mb-2.5"
|
||||
|
||||
@@ -15,14 +15,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0"
|
||||
<div class="p-0"
|
||||
>
|
||||
<AppPageHeader
|
||||
:back="{
|
||||
label: t('pipeline_destinations.header'),
|
||||
onClick: () => emit('cancel'),
|
||||
}"
|
||||
class="rounded-lg px-3 border-b border-border-default"
|
||||
class="px-3 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="pipeline-destination-editor-title">
|
||||
|
||||
@@ -273,7 +273,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="flex items-center justify-between w-full py-1"
|
||||
>
|
||||
<div
|
||||
class="flex items-center text-sm mr-4"
|
||||
class="flex items-center o2-table-footer-title mr-4"
|
||||
>
|
||||
{{ bottomProps.totalRows }} {{ t("pipeline.header") }}
|
||||
</div>
|
||||
|
||||
@@ -108,7 +108,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- Bottom footer -->
|
||||
<template #bottom="{ totalRows }">
|
||||
<div
|
||||
class="flex items-center font-bold text-[14px] mr-4 py-2"
|
||||
class="flex items-center o2-table-footer-title mr-4 py-2"
|
||||
>
|
||||
{{ totalRows }} Backfill Job{{ totalRows === 1 ? "" : "s" }}
|
||||
</div>
|
||||
|
||||
@@ -226,7 +226,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template #bottom="{ totalRows }">
|
||||
<div
|
||||
class="flex items-center font-bold text-[14px] mr-4 py-2"
|
||||
class="flex items-center o2-table-footer-title mr-4 py-2"
|
||||
>
|
||||
{{ totalRows }} {{ t("pipeline.header") }}
|
||||
</div>
|
||||
@@ -627,7 +627,11 @@ const columns = ref([
|
||||
accessorKey: "status",
|
||||
sortable: true,
|
||||
hideable: true,
|
||||
size: 150,
|
||||
// Wide enough for the longest status chip ("Condition Not Satisfied");
|
||||
// minSize stops the column shrinking and clipping the pill on narrow
|
||||
// viewports (the cell clips non-wrapped content by design).
|
||||
size: 200,
|
||||
minSize: 200,
|
||||
meta: { align: "left" as const },
|
||||
},
|
||||
{
|
||||
|
||||
@@ -83,13 +83,13 @@ describe("ErrorHeader", () => {
|
||||
expect(wrapper.find('[data-test="back-button"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("has title attribute Go Back on the back button", async () => {
|
||||
it("has a Back title attribute on the back button", async () => {
|
||||
// Arrange
|
||||
wrapper = mountComponent();
|
||||
await flushPromises();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.find('[data-test="back-button"]').attributes("title")).toBe("Go Back");
|
||||
// Assert — AppPageHeader's back button uses the standard "Back" label.
|
||||
expect(wrapper.find('[data-test="back-button"]').attributes("title")).toBe("Back");
|
||||
});
|
||||
|
||||
it("calls router.back() when the back button is clicked", async () => {
|
||||
|
||||
@@ -17,35 +17,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<div class="pt-2 pb-1 flex justify-start">
|
||||
<div
|
||||
data-test="back-button"
|
||||
class="flex justify-center items-center mr-3 cursor-pointer hover:text-[var(--o2-primary-btn-bg)]"
|
||||
style="
|
||||
border: 1.5px solid;
|
||||
border-radius: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
"
|
||||
title="Go Back"
|
||||
@click="router.back()"
|
||||
>
|
||||
<OIcon name="arrow-back-ios-new" size="xs" />
|
||||
</div>
|
||||
<span class="font-bold">{{ t("rum.eventID") }}:</span>
|
||||
<span
|
||||
data-test="error-id"
|
||||
:title="error.error_id"
|
||||
class="pl-1 cursor-pointer"
|
||||
>{{ error.error_id }}
|
||||
<OIcon
|
||||
size="xs"
|
||||
name="content-copy"
|
||||
class="hover:text-[var(--o2-primary-btn-bg)]"
|
||||
@click="copyErrorId(error.error_id)"
|
||||
/></span>
|
||||
<span class="ml-4">{{ error.timestamp }}</span>
|
||||
</div>
|
||||
<AppPageHeader
|
||||
:back="{ onClick: () => router.back(), dataTest: 'back-button' }"
|
||||
>
|
||||
<template #title>
|
||||
<span class="font-bold">{{ t("rum.eventID") }}:</span>
|
||||
<span
|
||||
data-test="error-id"
|
||||
:title="error.error_id"
|
||||
class="pl-1 cursor-pointer"
|
||||
>{{ error.error_id }}
|
||||
<OIcon
|
||||
size="xs"
|
||||
name="content-copy"
|
||||
class="hover:text-[var(--o2-primary-btn-bg)]"
|
||||
@click="copyErrorId(error.error_id)"
|
||||
/></span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<span>{{ error.timestamp }}</span>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
<div class="flex items-center flex-nowrap my-1">
|
||||
<div
|
||||
data-test="error-header-error-type"
|
||||
@@ -76,6 +68,7 @@ import { useRouter } from "vue-router";
|
||||
import { copyToClipboard } from "@/utils/clipboard";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
||||
@@ -213,10 +213,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
name="testString"
|
||||
data-test="add-regex-test-string-input"
|
||||
class="regex-test-string-input"
|
||||
:class="
|
||||
store.state.theme === 'dark'
|
||||
? 'dark-mode-regex-test-string-input'
|
||||
: 'light-mode-regex-test-string-input bg-white! border-l! border-l-[#e6e6e6]! border-r! border-r-[#e6e6e6]! border-b! border-b-[#e6e6e6]!'"
|
||||
tabindex="0"
|
||||
style="width: 100%; resize: none"
|
||||
placeholder="Eg. 1234567890"
|
||||
@@ -243,10 +239,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:readonly="true"
|
||||
data-test="add-regex-output-string-input"
|
||||
class="regex-test-string-input"
|
||||
:class="
|
||||
store.state.theme === 'dark'
|
||||
? 'dark-mode-regex-test-string-input'
|
||||
: 'light-mode-regex-test-string-input bg-white! border-l! border-l-[#e6e6e6]! border-r! border-r-[#e6e6e6]! border-b! border-b-[#e6e6e6]!'"
|
||||
tabindex="0"
|
||||
style="width: 100%; resize: none"
|
||||
placeholder="Output String"
|
||||
@@ -254,12 +246,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="flex flex-col items-center justify-center h-[111px]"
|
||||
:class="
|
||||
store.state.theme === 'dark'
|
||||
? 'bg-(--o2-primary-background)! border-l-2! border-r-2! border-b-2! border-(--o2-primary-background)!'
|
||||
: 'bg-white! border-l! border-r! border-b! border-[#e6e6e6]!'
|
||||
"
|
||||
class="flex flex-col items-center justify-center h-[111px] rounded-md border border-input-border bg-input-bg"
|
||||
>
|
||||
<div v-if="!testLoading && outputStringValue.length === 0">
|
||||
<OIcon
|
||||
@@ -640,10 +627,12 @@ export default defineComponent({
|
||||
padding-left: 0.5rem !important;
|
||||
}
|
||||
|
||||
.light-mode-regex-test-string-input .monaco-editor-background {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
.dark-mode-regex-test-string-input .monaco-editor-background {
|
||||
background-color: var(--o2-primary-background) !important;
|
||||
/* The section header strips (Write Pattern / Input string / Output) are flat
|
||||
full-width bars. Square the textareas' own border box so each field reads as
|
||||
one connected unit under its strip instead of a detached rounded box. */
|
||||
.regex-pattern-input .rounded-md.border,
|
||||
.regex-test-string-input .rounded-md.border {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- eslint-disable vue/x-invalid-end-tag -->
|
||||
<template>
|
||||
<div class="rounded-md p-0" style="min-height: inherit; height: calc(100vh - 88px);">
|
||||
<div class="p-0" style="min-height: inherit; height: calc(100vh - 88px);">
|
||||
<div v-if="!showAddDialog">
|
||||
<!-- Standard section header: title + actions only. Search moved into the
|
||||
table toolbar below. -->
|
||||
|
||||
@@ -143,8 +143,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template #bottom="bottomProps">
|
||||
<div
|
||||
class="flex items-center gap-4 py-2 px-1 text-xs"
|
||||
style="color: var(--o2-text-muted)"
|
||||
class="flex items-center gap-4 py-2 px-1 o2-table-footer-title"
|
||||
>
|
||||
<span
|
||||
>{{ bottomProps.totalRows }} model{{
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- eslint-disable vue/x-invalid-end-tag -->
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<div v-if="!showAddDialog" class="flex flex-col h-full">
|
||||
<!-- Standard section header: title + actions only. Search moved into the
|
||||
table's own toolbar below. -->
|
||||
|
||||
@@ -19,14 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
:title="t('modelPricing.importTitle')"
|
||||
test-prefix="model-pricing"
|
||||
:is-importing="isImporting"
|
||||
container-class=""
|
||||
container-style="height: calc(100vh - 50px);"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 110px)',
|
||||
errorReport: 'calc(100vh - 128px)',
|
||||
}"
|
||||
container-class="flex-1 min-h-0"
|
||||
container-style=""
|
||||
:tabs="allTabs"
|
||||
@back="arrowBackFn"
|
||||
@cancel="arrowBackFn"
|
||||
@@ -34,16 +28,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@update:active-tab="handleTabChange"
|
||||
>
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="modelPricingErrorsToDisplay.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
{{ t('modelPricing.errorValidations') }}
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">{{ t('modelPricing.outputMessages') }}</div>
|
||||
<OSeparator class="mr-4 mt-4" />
|
||||
<div class="overflow-auto resize-none" style="height: calc(100vh - 200px)">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">{{ t('modelPricing.outputMessages') }}</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="flex-1 min-h-0 overflow-auto resize-none">
|
||||
<!-- Model Pricing Errors Section -->
|
||||
<div
|
||||
class="p-2.5 mb-2.5"
|
||||
|
||||
@@ -14,7 +14,7 @@ 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 flex-col" style="height: calc(100vh - 50px);">
|
||||
<div class="flex flex-col flex-1 min-h-0 h-full">
|
||||
<!-- Standard page header (AppPageHeader), matching Add Panel / Import Dashboard.
|
||||
BaseImport's built-in header is hidden (hide-header) so this single header
|
||||
serves all three tabs. The title preserves the existing per-tab text: the
|
||||
@@ -51,15 +51,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
title="Import Regex Pattern"
|
||||
test-prefix="regex-pattern"
|
||||
hide-header
|
||||
container-class=""
|
||||
container-class="flex-1 min-h-0"
|
||||
container-style=""
|
||||
:is-importing="isImporting"
|
||||
:editor-heights="{
|
||||
urlEditor: 'calc(100vh - 285px)',
|
||||
fileEditor: 'calc(100vh - 282px)',
|
||||
outputContainer: 'calc(100vh - 108px)',
|
||||
errorReport: 'calc(100vh - 128px)',
|
||||
}"
|
||||
:tabs="allTabs"
|
||||
@back="arrowBackFn"
|
||||
@cancel="arrowBackFn"
|
||||
@@ -67,16 +61,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@update:active-tab="handleTabChange"
|
||||
>
|
||||
<template #output-content>
|
||||
<div class="w-full h-full border-l border-border-default" style="min-width: 400px;">
|
||||
<div class="w-full h-full flex flex-col border-l border-border-default" style="min-width: 400px;">
|
||||
<div
|
||||
v-if="regexPatternErrorsToDisplay.length > 0"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
>
|
||||
Error Validations
|
||||
</div>
|
||||
<div v-else class="text-center text-xl font-semibold py-2">Output Messages</div>
|
||||
<OSeparator class="mt-4" />
|
||||
<div class="overflow-auto resize-none" style="height: calc(100vh - 200px)">
|
||||
<div v-else class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Output Messages</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div class="flex-1 min-h-0 overflow-auto resize-none">
|
||||
<!-- Regex Pattern Errors Section -->
|
||||
<div
|
||||
class="p-2.5 mb-2.5"
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<!-- Full-page Import View -->
|
||||
<ImportModelPricing
|
||||
v-if="showImportModelPricingPage"
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- eslint-disable vue/x-invalid-end-tag -->
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full overflow-hidden">
|
||||
<div class="flex flex-col h-full overflow-hidden">
|
||||
<!-- Standard page header on top (full-width). The filter panel (left) + table
|
||||
(right) sit below in the splitter — the standard header + left + right model. -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Standard section header: title only. Search moved into the table toolbar. -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -15,7 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md flex flex-col h-full p-0">
|
||||
<div class="flex flex-col h-full p-0">
|
||||
<template v-if="!showImportRegexPatternDialog">
|
||||
<!-- Standard section header: title + actions only. Search moved to toolbar. -->
|
||||
<AppPageHeader
|
||||
@@ -131,7 +131,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</template>
|
||||
<template #bottom>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-text-primary font-medium">
|
||||
<span class="o2-table-footer-title">
|
||||
{{ resultTotal }} {{ t("regex_patterns.bottom_header") }}
|
||||
</span>
|
||||
<OButton
|
||||
|
||||
@@ -138,7 +138,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="flex items-center justify-between w-full py-2"
|
||||
>
|
||||
<div
|
||||
class="flex items-center font-bold text-[14px] mr-4"
|
||||
class="flex items-center o2-table-footer-title mr-4"
|
||||
>
|
||||
{{ bottomProps.totalRows }} {{ t("evalTemplate.header") }}
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- eslint-disable vue/x-invalid-end-tag -->
|
||||
<template>
|
||||
<div class="rounded-md p-0 pt-1 overflow-hidden h-full flex flex-col">
|
||||
<div class="p-0 pt-1 overflow-hidden h-full flex flex-col">
|
||||
<!-- Standard page header: title + icon. Usage date / data-type controls live
|
||||
in the toolbar row below. -->
|
||||
<AppPageHeader
|
||||
|
||||
@@ -16,12 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template>
|
||||
<div class="rounded-md px-4 pt-3" style="min-height: inherit; overflow: auto">
|
||||
<div v-if="!isChildOrg" class="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold leading-9 text-(--o2-text-heading) mt-0 mr-0 mb-2 ml-0 block">{{ t("billing.title") }}</h1>
|
||||
<p class="text-base font-semibold leading-[1.375rem] text-(--o2-text-secondary) m-0 block">{{ t("billing.subtitle") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page title is supplied by the parent Billing.vue AppPageHeader; no local title here. -->
|
||||
<!-- Managed billing empty state for child orgs -->
|
||||
<div
|
||||
v-if="isChildOrg"
|
||||
|
||||
@@ -18,24 +18,24 @@ the Free Software Foundation, either version 3 of the License, or
|
||||
@import="importJson"
|
||||
>
|
||||
<template #output-content>
|
||||
<div class="w-full" style="min-width: 380px">
|
||||
<div class="w-full h-full flex flex-col" style="min-width: 380px">
|
||||
<div
|
||||
v-if="errors.length"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
data-test="score-config-import-errors-title"
|
||||
>
|
||||
{{ t("onlineEvals.scoreConfig.import.errors.title") }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
data-test="score-config-import-output-title"
|
||||
>
|
||||
{{ t("onlineEvals.scoreConfig.import.outputMessages") }}
|
||||
</div>
|
||||
<OSeparator class="mx-4 mt-4" />
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
|
||||
<div class="overflow-auto">
|
||||
<div class="flex-1 min-h-0 overflow-auto">
|
||||
<div v-if="errors.length" class="p-2.5 mb-2.5">
|
||||
<div class="error-list">
|
||||
<div
|
||||
|
||||
@@ -18,24 +18,24 @@ the Free Software Foundation, either version 3 of the License, or
|
||||
@import="importJson"
|
||||
>
|
||||
<template #output-content>
|
||||
<div class="w-full" style="min-width: 380px">
|
||||
<div class="w-full h-full flex flex-col" style="min-width: 380px">
|
||||
<div
|
||||
v-if="errors.length"
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
data-test="scorer-import-errors-title"
|
||||
>
|
||||
{{ t("onlineEvals.scorer.import.errors.title") }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-center text-xl font-semibold py-2"
|
||||
class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0"
|
||||
data-test="scorer-import-output-title"
|
||||
>
|
||||
{{ t("onlineEvals.scorer.import.outputMessages") }}
|
||||
</div>
|
||||
<OSeparator class="mx-4 mt-4" />
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
|
||||
<div class="overflow-auto">
|
||||
<div class="flex-1 min-h-0 overflow-auto">
|
||||
<div v-if="errors.length" class="p-2.5 mb-2.5">
|
||||
<div class="error-list">
|
||||
<div
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
</template>
|
||||
|
||||
<template #bottom="{ totalRows }">
|
||||
<span class="o2-table-footer-title text-primary">
|
||||
<span class="o2-table-footer-title">
|
||||
{{ totalRows.toLocaleString() }} {{ t("onlineEvals.scoreConfig.listTitle") }}
|
||||
</span>
|
||||
<OButton
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
</template>
|
||||
|
||||
<template #bottom="{ totalRows }">
|
||||
<span class="o2-table-footer-title text-primary">
|
||||
<span class="o2-table-footer-title">
|
||||
{{ totalRows.toLocaleString() }} {{ t("onlineEvals.scorer.listTitle") }}
|
||||
</span>
|
||||
<OButton
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
onClick: () => $emit('cancel'),
|
||||
dataTest: 'job-form-back-btn',
|
||||
}"
|
||||
class="card-container px-3 border-b border-border-default"
|
||||
class="px-3 border-b border-border-default"
|
||||
style="flex-shrink: 0"
|
||||
>
|
||||
<template #title>
|
||||
|
||||
@@ -1,46 +1,21 @@
|
||||
<template>
|
||||
<form class="flex flex-col flex-1 min-h-0 bg-card-bg border border-dialog-header-border rounded-md" @submit.prevent="save">
|
||||
<div class="flex items-center gap-2.5 min-h-12 px-3.5 py-2 border-b border-dialog-header-border shrink-0">
|
||||
<OButton
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
icon-left="arrow-back-ios-new"
|
||||
data-test="provider-form-back-btn"
|
||||
:title="t('onlineEvals.provider.backTo')"
|
||||
@click="$emit('cancel')"
|
||||
/>
|
||||
<div class="m-0 text-[17px] font-semibold text-text-primary tracking-[0.005em] whitespace-nowrap">
|
||||
{{
|
||||
mode === "create"
|
||||
? t("onlineEvals.provider.createTitle")
|
||||
: t("onlineEvals.provider.editTitle")
|
||||
}}
|
||||
</div>
|
||||
<span class="text-text-secondary text-xs overflow-hidden text-ellipsis whitespace-nowrap min-w-0">{{ t("onlineEvals.provider.subtitle") }}</span>
|
||||
<div class="flex-1 min-w-2" />
|
||||
<button
|
||||
type="button"
|
||||
class="provider-form__close inline-flex items-center justify-center w-7 h-7 p-0 text-text-secondary bg-transparent border-0 rounded-md cursor-pointer transition-[background,color] duration-150 hover:bg-[color-mix(in_srgb,var(--color-text-primary)_6%,transparent)] hover:text-[var(--color-primary-600,#3F7994)]"
|
||||
:aria-label="t('onlineEvals.buttons.cancel')"
|
||||
data-test="provider-form-close-btn"
|
||||
@click="$emit('cancel')"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form class="flex flex-col flex-1 min-h-0 bg-card-bg" @submit.prevent="save">
|
||||
<AppPageHeader
|
||||
:subtitle="t('onlineEvals.provider.subtitle')"
|
||||
:back="{
|
||||
label: t('onlineEvals.provider.backTo'),
|
||||
onClick: () => $emit('cancel'),
|
||||
dataTest: 'provider-form-back-btn',
|
||||
}"
|
||||
class="px-3 border-b border-border-default"
|
||||
style="flex-shrink: 0"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="provider-form-title">
|
||||
{{ mode === "create" ? t("onlineEvals.provider.createTitle") : t("onlineEvals.provider.editTitle") }}
|
||||
</span>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-auto px-6 py-4.5 [&_textarea]:max-h-[220px] [&_textarea]:overflow-y-auto [&_textarea]:font-mono">
|
||||
<section class="mb-6">
|
||||
@@ -180,6 +155,7 @@ import OButton from "@/lib/core/Button/OButton.vue";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import OInput from "@/lib/forms/Input/OInput.vue";
|
||||
import OSelect from "@/lib/forms/Select/OSelect.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import { toast } from "@/lib/feedback/Toast/useToast";
|
||||
import onlineEvalsService, { type Provider } from "@/services/online-evals.service";
|
||||
import {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
onClick: () => $emit('cancel'),
|
||||
dataTest: 'scorer-form-back-btn',
|
||||
}"
|
||||
class="card-container px-3 border-b border-border-default"
|
||||
class="px-3 border-b border-border-default"
|
||||
style="flex-shrink: 0"
|
||||
>
|
||||
<template #title>
|
||||
|
||||
@@ -893,6 +893,9 @@ defineExpose({
|
||||
props.horizontalScroll ? 'min-w-max' : ((useComputedWidth && frozen) ? '' : 'w-full'),
|
||||
props.horizontalScroll || props.defaultColumns ? 'table-auto' : 'table-fixed',
|
||||
(props.bordered && !props.columns.some((c) => c.pinned || c.isAction)) ? '' : 'border-separate border-spacing-0',
|
||||
// Without a leading selection/expansion gutter the first column would
|
||||
// hug the table's left edge; inset it so it aligns like gutter tables.
|
||||
(!selection.isEnabled.value && !expansion.isEnabled.value) ? 'o2-table--inset-first' : '',
|
||||
]"
|
||||
:style="{
|
||||
...columnSizeVars,
|
||||
@@ -1163,3 +1166,16 @@ defineExpose({
|
||||
</div> <!-- /bordered wrapper -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* When the table has no leading selection/expansion gutter, the first data
|
||||
column would otherwise sit flush against the left edge with only the default
|
||||
cell padding (0.5rem). Inset the first header + body cell so these tables read
|
||||
with the same comfortable left margin as tables that do have a checkbox
|
||||
gutter. Targeted by data-test prefix so full-width colspan rows (expanded /
|
||||
tree-warning) are left untouched. */
|
||||
.o2-table--inset-first :deep(th[data-test^="o2-table-th-"]:first-child),
|
||||
.o2-table--inset-first :deep(td[data-test^="o2-table-cell-"]:first-child) {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -55,8 +55,11 @@ const pageSizeSelectOptions = computed(() =>
|
||||
:data-test="`o2-table-pagination-${position}`"
|
||||
class="flex items-center flex-wrap justify-between gap-x-3 gap-y-1 py-1 px-3 border-t border-border-default min-h-10"
|
||||
>
|
||||
<!-- Left: bulk actions slot or row count -->
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Left: bulk actions slot or row count.
|
||||
The footer-title typography lives on this wrapper so BOTH the default
|
||||
row count and any custom #bottom (actions) slot content inherit it —
|
||||
consumers don't need to remember to add `o2-table-footer-title`. -->
|
||||
<div class="flex items-center gap-2 o2-table-footer-title">
|
||||
<!-- Loading: always skeleton, regardless of slot/count -->
|
||||
<span
|
||||
v-if="loading"
|
||||
@@ -65,10 +68,7 @@ const pageSizeSelectOptions = computed(() =>
|
||||
data-test="o2-table-pagination-count-skel"
|
||||
/>
|
||||
<slot v-else-if="slots.actions" name="actions" />
|
||||
<span
|
||||
v-else
|
||||
class="o2-table-footer-title text-primary"
|
||||
>
|
||||
<span v-else>
|
||||
{{ totalCount.toLocaleString() }} {{ title }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -697,6 +697,19 @@ const isOpen = computed(() =>
|
||||
listboxModeEnabled.value ? popoverOpen.value : selectOpen.value,
|
||||
);
|
||||
|
||||
// Clicking the field label opens the dropdown — mirroring how clicking an
|
||||
// OInput's label focuses its input. Reka's triggers open on `pointerdown`, so
|
||||
// the `click` a native <label for> forwards never opens them; open explicitly.
|
||||
// `.prevent` (on the template handler) stops the redundant native forwarding.
|
||||
function handleLabelClick() {
|
||||
if (props.disabled) return;
|
||||
if (listboxModeEnabled.value) {
|
||||
popoverOpen.value = true;
|
||||
} else {
|
||||
selectOpen.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// When this OSelect is nested inside an ODropdown, signal its
|
||||
// open/close so the parent ignores the pointer event that closes us.
|
||||
@@ -960,8 +973,9 @@ const fieldWidthClass = computed(() => {
|
||||
:for="inputId"
|
||||
:class="[
|
||||
'o-input-label text-sm font-semibold leading-tight flex items-center gap-1',
|
||||
disabled && 'o-input-label--disabled',
|
||||
disabled ? 'o-input-label--disabled' : 'cursor-pointer',
|
||||
]"
|
||||
@click.prevent="handleLabelClick"
|
||||
>
|
||||
{{ label }}<span v-if="required" aria-hidden="true" class="select-none">*</span>
|
||||
<OIcon
|
||||
@@ -1006,8 +1020,8 @@ const fieldWidthClass = computed(() => {
|
||||
labelPosition === 'inside' && label ? '' : ($slots['icon-left'] ? 'ps-2' : 'ps-3'),
|
||||
'bg-select-bg',
|
||||
hasError
|
||||
? 'border-select-border-error'
|
||||
: 'border-select-border hover:border-select-border-hover focus:border-select-border-focus',
|
||||
? 'border-select-border-error focus:ring-[0.125rem] focus:ring-select-border-error/30 data-[state=open]:ring-[0.125rem] data-[state=open]:ring-select-border-error/30'
|
||||
: 'border-select-border hover:border-select-border-hover focus:border-select-border-focus focus:ring-[0.125rem] focus:ring-primary-500/25 data-[state=open]:border-select-border-focus data-[state=open]:ring-[0.125rem] data-[state=open]:ring-primary-500/25',
|
||||
/* Keep the red error border on focus; focus border color applies only when there's no error. */
|
||||
'focus:outline-none',
|
||||
'transition-[color,background-color,border-color,box-shadow] duration-150',
|
||||
@@ -1538,8 +1552,8 @@ const fieldWidthClass = computed(() => {
|
||||
labelPosition === 'inside' && label ? '' : 'ps-3',
|
||||
'bg-select-bg',
|
||||
hasError
|
||||
? 'border-select-border-error'
|
||||
: 'border-select-border hover:border-select-border-hover focus:border-select-border-focus',
|
||||
? 'border-select-border-error focus:ring-[0.125rem] focus:ring-select-border-error/30 data-[state=open]:ring-[0.125rem] data-[state=open]:ring-select-border-error/30'
|
||||
: 'border-select-border hover:border-select-border-hover focus:border-select-border-focus focus:ring-[0.125rem] focus:ring-primary-500/25 data-[state=open]:border-select-border-focus data-[state=open]:ring-[0.125rem] data-[state=open]:ring-primary-500/25',
|
||||
/* Keep the red error border on focus; focus border color applies only when there's no error. */
|
||||
'focus:outline-none',
|
||||
'transition-[color,background-color,border-color,box-shadow] duration-150',
|
||||
|
||||
@@ -199,8 +199,8 @@
|
||||
--color-field-list-expansion-border: var(--color-border-default);
|
||||
|
||||
/* Stepper */
|
||||
--color-stepper-indicator-default: var(--color-border-strong);
|
||||
--color-stepper-indicator-default-text: var(--color-text-primary);
|
||||
--color-stepper-indicator-default: var(--color-surface-subtle);
|
||||
--color-stepper-indicator-default-text: var(--color-text-secondary);
|
||||
--color-stepper-indicator-active: var(--color-primary-600);
|
||||
--color-stepper-indicator-done: var(--color-primary-600);
|
||||
--color-stepper-indicator-error: var(--color-error-600);
|
||||
@@ -4210,9 +4210,15 @@ button.date-time-button {
|
||||
border-bottom: 0.03rem solid var(--o2-border-color);
|
||||
}
|
||||
|
||||
/* Single source of truth for the count/label shown in a listing-table footer
|
||||
(e.g. "20 Dashboards"). Deliberately sets NO color so it inherits the exact
|
||||
same ambient color as the "Showing X of Y / Records per page" pagination
|
||||
text sitting beside it — the two must always read identically. Only size and
|
||||
weight are fixed here; consumers apply ONLY this class plus layout utilities,
|
||||
never their own color/size/weight, so every listing footer reads the same. */
|
||||
.o2-table-footer-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- Footer: selected count -->
|
||||
<div
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title text-[var(--o2-text-4)]!"
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title"
|
||||
>
|
||||
{{ selectedMetricStreams.length }} of
|
||||
{{ uniqueMetricStreams.length }} selected
|
||||
@@ -785,7 +785,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- Footer: selected count -->
|
||||
<div
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title text-[var(--o2-text-4)]!"
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title"
|
||||
>
|
||||
{{ selectedMetricStreams.length }} of
|
||||
{{ uniqueMetricStreams.length }} selected
|
||||
|
||||
@@ -15,64 +15,53 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="rounded-md p-0 bg-(--q-background)">
|
||||
<div class="w-full flex flex-col h-full overflow-hidden">
|
||||
<!-- Header Card -->
|
||||
<div class="card-container mb-[0.625rem] mt-[0.325rem] mx-2.5 shrink-0">
|
||||
<div class="flex justify-between w-full py-3 px-4 items-center">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="text-xl tracking-[0.005em] font-[600]" data-test="inspector-title">
|
||||
Search Job Inspector
|
||||
</div>
|
||||
<div
|
||||
v-if="profileData && !hasNoData"
|
||||
<div class="p-0 bg-(--q-background) w-full h-full flex flex-col min-h-0">
|
||||
<AppPageHeader
|
||||
title="Search Job Inspector"
|
||||
:back="{ onClick: goBack, dataTest: 'inspector-close-button' }"
|
||||
class="shrink-0 px-4 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="inspector-title">Search Job Inspector</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<div
|
||||
v-if="profileData && !hasNoData"
|
||||
:class="[
|
||||
'flex items-center gap-1.5 px-2 py-1 rounded-md border',
|
||||
store.state.theme === 'dark'
|
||||
? 'bg-gray-800/50 border-gray-600'
|
||||
: 'bg-gray-50 border-gray-200'
|
||||
]"
|
||||
>
|
||||
<svg class="w-3.5 h-3.5 opacity-70" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="3" y="6" width="18" height="14" rx="2" :stroke="store.state.theme === 'dark' ? '#9CA3AF' : '#6B7280'" stroke-width="2"/>
|
||||
<path d="M3 10h18M8 3v4M16 3v4" :stroke="store.state.theme === 'dark' ? '#9CA3AF' : '#6B7280'" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span
|
||||
:class="[
|
||||
'flex items-center gap-1.5 px-2 py-1 rounded-md border',
|
||||
'text-[10px] font-small px-1.5 py-0.5 rounded',
|
||||
store.state.theme === 'dark'
|
||||
? 'bg-gray-800/50 border-gray-600'
|
||||
: 'bg-gray-50 border-gray-200'
|
||||
? 'text-gray-300 bg-gray-700/50'
|
||||
: 'text-gray-600 bg-gray-100'
|
||||
]"
|
||||
>
|
||||
<svg class="w-[14px] h-[14px] opacity-70" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="3" y="6" width="18" height="14" rx="2" :stroke="store.state.theme === 'dark' ? '#9CA3AF' : '#6B7280'" stroke-width="2"/>
|
||||
<path d="M3 10h18M8 3v4M16 3v4" :stroke="store.state.theme === 'dark' ? '#9CA3AF' : '#6B7280'" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span
|
||||
:class="[
|
||||
'text-[10px] font-small px-1.5 py-0.5 rounded',
|
||||
store.state.theme === 'dark'
|
||||
? 'text-gray-300 bg-gray-700/50'
|
||||
: 'text-gray-600 bg-gray-100'
|
||||
]"
|
||||
>
|
||||
{{ store.state.timezone || 'UTC' }}
|
||||
</span>
|
||||
<div
|
||||
:class="[
|
||||
'text-xs font-semibold',
|
||||
store.state.theme === 'dark' ? 'text-gray-200' : 'text-gray-800'
|
||||
]"
|
||||
>
|
||||
{{ formatTimeRange(profileData.start_time, profileData.end_time) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ store.state.timezone || 'UTC' }}
|
||||
</span>
|
||||
<div
|
||||
:class="[
|
||||
'text-xs font-semibold',
|
||||
store.state.theme === 'dark' ? 'text-gray-200' : 'text-gray-800'
|
||||
]"
|
||||
>
|
||||
{{ formatTimeRange(profileData.start_time, profileData.end_time) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<OButton
|
||||
icon-left="close"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
@click="goBack"
|
||||
data-test="inspector-close-button"
|
||||
>
|
||||
<OTooltip content="Close" />
|
||||
</OButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
<div class="w-full flex flex-col flex-1 min-h-0 overflow-hidden pt-2.5">
|
||||
<!-- Summary Stats Card -->
|
||||
<div v-if="!loading" class="mb-[0.625rem] mx-2.5 shrink-0">
|
||||
<div class="grid gap-3" style="grid-template-columns: 1fr 1fr 1fr 1.6fr 0.9fr;">
|
||||
@@ -370,6 +359,7 @@ import { useRouter, useRoute } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
import searchService from "@/services/search";
|
||||
import NoData from "@/components/shared/grid/NoData.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import OButton from "@/lib/core/Button/OButton.vue";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import ODrawer from "@/lib/overlay/Drawer/ODrawer.vue";
|
||||
@@ -421,6 +411,7 @@ export default defineComponent({
|
||||
name: "SearchJobInspector",
|
||||
components: {
|
||||
NoData,
|
||||
AppPageHeader,
|
||||
OButton,
|
||||
ODrawer,
|
||||
ODialog,
|
||||
|
||||
@@ -29,8 +29,164 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"
|
||||
>
|
||||
<div v-if="showHeader" class="trace-combined-header-wrapper card-container border-b border-border-default">
|
||||
<!-- New Modern Header -->
|
||||
<!-- Standalone (routed) header: shared AppPageHeader -->
|
||||
<AppPageHeader
|
||||
v-if="mode === 'standalone'"
|
||||
:title="traceTree[0]?.operationName || t('traces.loadingTrace')"
|
||||
:back="
|
||||
showBackButton
|
||||
? {
|
||||
onClick: handleBackOrClose,
|
||||
dataTest: 'trace-details-back-btn',
|
||||
}
|
||||
: undefined
|
||||
"
|
||||
>
|
||||
<template #title>
|
||||
<span
|
||||
data-test="trace-details-operation-name"
|
||||
:title="traceTree[0]?.operationName"
|
||||
>
|
||||
{{ traceTree[0]?.operationName || t("traces.loadingTrace") }}
|
||||
<OTooltip :content="traceTree[0]?.operationName" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #subtitle>
|
||||
<div
|
||||
class="flex items-center space-x-2 text-[0.6875rem] text-(--o2-text-secondary) whitespace-nowrap"
|
||||
>
|
||||
<span>{{
|
||||
formatTimestamp(traceStartTime, store.state.timezone)
|
||||
}}</span>
|
||||
<div class="bg-(--o2-text-3) py-0 w-px h-4" />
|
||||
<span class="mr-1">
|
||||
{{ t("traces.traceId") }}:
|
||||
<span
|
||||
data-test="trace-details-trace-id"
|
||||
class="text-(--o2-text-primary) font-mono"
|
||||
:title="effectiveTraceId"
|
||||
>
|
||||
{{ effectiveTraceId }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Copy Trace ID Button -->
|
||||
<OIcon
|
||||
data-test="trace-details-copy-trace-id-btn"
|
||||
name="content-copy"
|
||||
size="xs"
|
||||
class="cursor-pointer hover:text-(--o2-text-primary)"
|
||||
:title="t('traces.copyTraceId')"
|
||||
@click="copyTraceId"
|
||||
/>
|
||||
|
||||
<!-- Session ID (LLM traces) -->
|
||||
<template v-if="sessionId">
|
||||
<div class="bg-(--o2-text-3) py-0 w-px h-4" />
|
||||
<span class="mr-1">
|
||||
Session ID:
|
||||
<span
|
||||
data-test="trace-details-session-id"
|
||||
class="text-(--o2-text-primary) font-mono"
|
||||
:title="sessionId"
|
||||
>
|
||||
{{ sessionId }}
|
||||
</span>
|
||||
</span>
|
||||
<OIcon
|
||||
data-test="trace-details-copy-session-id-btn"
|
||||
name="content-copy"
|
||||
size="xs"
|
||||
class="cursor-pointer hover:text-(--o2-text-primary)"
|
||||
title="Copy Session ID"
|
||||
@click="copySessionId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="bg-(--o2-text-3) py-0 w-px h-4" />
|
||||
<!-- Span Count Badge -->
|
||||
<span class="inline-flex">
|
||||
<OTag
|
||||
type="logsResultChip"
|
||||
value="neutral"
|
||||
data-test="trace-details-spans-count"
|
||||
>
|
||||
<span data-test="span-count-text">
|
||||
{{ formatLargeNumber(effectiveSpanList.length) }}
|
||||
{{ t("traces.spansLabel") }}
|
||||
</span>
|
||||
</OTag>
|
||||
<OTooltip
|
||||
:content="
|
||||
effectiveSpanList.length + ' ' + t('traces.spansLabel')
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div class="bg-(--o2-text-3) py-0 w-px h-4" />
|
||||
|
||||
<!-- Error Count Badge -->
|
||||
<span class="inline-flex">
|
||||
<OTag
|
||||
type="logsResultChip"
|
||||
value="error"
|
||||
data-test="trace-details-error-spans-count"
|
||||
>
|
||||
<span
|
||||
>{{ formatLargeNumber(errorSpansCount) }}
|
||||
{{ t("traces.errorsLabel") }}</span
|
||||
>
|
||||
</OTag>
|
||||
<OTooltip
|
||||
:content="errorSpansCount + ' ' + t('traces.errorsLabel')"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<!-- Apply filters button -->
|
||||
<OButton
|
||||
v-if="areFiltersAdded"
|
||||
data-test="trace-details-apply-filters-btn-right"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
@click="openFilterPopover"
|
||||
>
|
||||
<template #icon-left
|
||||
><OIcon name="filter-alt" size="xs"
|
||||
/></template>
|
||||
<span class="text-[0.75rem]">{{ t("traces.viewFilters") }}</span>
|
||||
<OTooltip :content="t('traces.reviewAndApplyFilters')" />
|
||||
</OButton>
|
||||
|
||||
<!-- Share button -->
|
||||
<share-button
|
||||
v-if="showShareButton"
|
||||
data-test="trace-details-share-link-btn"
|
||||
:url="traceDetailsShareURL"
|
||||
variant="outline"
|
||||
size="icon-xs"
|
||||
/>
|
||||
|
||||
<!-- Close button -->
|
||||
<OButton
|
||||
v-if="showCloseButton"
|
||||
data-test="trace-details-close-btn"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
@click="handleBackOrClose"
|
||||
>
|
||||
<OIcon name="close" size="sm" />
|
||||
<OTooltip :content="t('common.cancel')" />
|
||||
</OButton>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<!-- Embedded (logs) header: existing inline header, kept as-is -->
|
||||
<header
|
||||
v-else
|
||||
class="h-auto py-[0.125rem] flex! items-center justify-between bg-[var(--o2-surface)] pl-1"
|
||||
>
|
||||
<div class="flex items-center space-x-4 w-fit!">
|
||||
@@ -803,6 +959,7 @@ import {
|
||||
} from "vue";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import ShareButton from "@/components/common/ShareButton.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import OTag from "@/lib/core/Badge/OTag.vue";
|
||||
import useTraces from "@/composables/useTraces";
|
||||
import TraceDetailsSidebar from "./TraceDetailsSidebar.vue";
|
||||
@@ -967,6 +1124,7 @@ export default defineComponent({
|
||||
},
|
||||
components: {
|
||||
ShareButton,
|
||||
AppPageHeader,
|
||||
OTag,
|
||||
TraceDetailsSidebar,
|
||||
TraceTree,
|
||||
|
||||
@@ -218,7 +218,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- Selected Count Footer -->
|
||||
<div
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title text-[var(--o2-text-4)]!"
|
||||
class="p-3 border-t border-solid border-[var(--o2-border-color)] o2-table-footer-title"
|
||||
>
|
||||
{{ selectedDimensions.length }}
|
||||
{{ t("latencyInsights.dimensionsSelected") }}
|
||||
|
||||
@@ -340,14 +340,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="flex w-full justify-between items-center py-1"
|
||||
>
|
||||
<div
|
||||
class="o2-table-footer-title flex items-center gap-2 shrink-0"
|
||||
class="o2-table-footer-title flex items-center shrink-0"
|
||||
>
|
||||
<span class="text-text-primary">{{
|
||||
resultTotal || 0
|
||||
}}</span>
|
||||
<span class="text-text-secondary">{{
|
||||
t("dashboard.header")
|
||||
}}</span>
|
||||
{{ resultTotal || 0 }} {{ t("dashboard.header") }}
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedIds.length > 0"
|
||||
|
||||
@@ -14,7 +14,7 @@ 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="mx-2">
|
||||
<div class="mx-2 flex flex-col h-[calc(100vh-var(--navbar-height))] min-h-0 overflow-hidden">
|
||||
<AppPageHeader
|
||||
:title="t('dashboard.importDashboard')"
|
||||
:back="{ label: t('dashboard.header'), onClick: goBack }"
|
||||
@@ -47,17 +47,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
<div class="flex w-full">
|
||||
<div class="flex w-full min-w-0">
|
||||
<div class="flex w-full flex-1 min-h-0">
|
||||
<div class="flex w-full min-w-0 min-h-0">
|
||||
<OSplitter
|
||||
v-model="splitterModel"
|
||||
class="w-full min-w-0"
|
||||
class="w-full min-w-0 h-full min-h-0"
|
||||
>
|
||||
<template #before>
|
||||
<OForm id="import-dashboard-form" :form="form">
|
||||
<div class="w-full h-full">
|
||||
<OForm id="import-dashboard-form" :form="form" class="h-full flex flex-col min-h-0">
|
||||
<div class="w-full h-full flex flex-col min-h-0">
|
||||
<div
|
||||
class="card-container py-[0.625rem] pl-[0.625rem] mb-[0.625rem]"
|
||||
class="card-container py-[0.625rem] pl-[0.625rem] mb-1 shrink-0"
|
||||
>
|
||||
<div class="app-tabs-container h-[36px] w-fit">
|
||||
<app-tabs
|
||||
@@ -71,10 +71,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
<div
|
||||
v-if="activeTab == 'import_json_url'"
|
||||
class="editor-container-url card-container py-1"
|
||||
class="editor-container-url card-container py-1 flex-1 min-h-0 flex flex-col"
|
||||
>
|
||||
<div class="mx-2 my-2">
|
||||
<div style="width: calc(100% - 10px)" class="flex gap-2 mt-2 w-full items-center">
|
||||
<div class="mx-2 mt-1 mb-1 flex flex-col flex-1 min-h-0">
|
||||
<div style="width: calc(100% - 10px)" class="flex gap-2 w-full items-center shrink-0">
|
||||
<div
|
||||
data-test="dashboard-import-url-input"
|
||||
style="width: 69%"
|
||||
@@ -102,7 +102,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
data-test="dashboard-import-url-editor"
|
||||
ref="queryEditorFileRef"
|
||||
editor-id="dashboards-query-editor-file"
|
||||
class="my-4 h-[calc(100vh-285px)]! overflow-hidden resize-none border border-(--o2-border-color) rounded-md"
|
||||
class="mt-2 flex-1 min-h-0 overflow-hidden resize-none border border-(--o2-border-color) rounded-md"
|
||||
:debounceTime="300"
|
||||
v-model:query="jsonStr"
|
||||
language="json"
|
||||
@@ -111,10 +111,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
</div>
|
||||
<div
|
||||
v-if="activeTab == 'import_json_file'"
|
||||
class="dashboard-import-json-container card-container py-1"
|
||||
class="dashboard-import-json-container card-container py-1 flex-1 min-h-0 flex flex-col"
|
||||
>
|
||||
<div class="mx-2 my-2">
|
||||
<div style="width: calc(100% - 10px)" class="flex gap-2 w-full items-center">
|
||||
<div class="mx-2 mt-1 mb-1 flex flex-col flex-1 min-h-0">
|
||||
<div style="width: calc(100% - 10px)" class="flex gap-2 w-full items-center shrink-0">
|
||||
<div
|
||||
data-test="dashboard-import-file-input"
|
||||
style="width: 69%"
|
||||
@@ -159,13 +159,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
data-test="dashboard-import-json-file-editor"
|
||||
ref="queryEditorJsonRef"
|
||||
editor-id="dashboards-query-editor-json"
|
||||
class="my-4 h-[calc(100vh-282px)]! overflow-hidden resize-none border border-(--o2-border-color) rounded-md"
|
||||
class="mt-2 flex-1 min-h-0 overflow-hidden resize-none border border-(--o2-border-color) rounded-md"
|
||||
:debounceTime="300"
|
||||
v-model:query="jsonStr"
|
||||
language="json"
|
||||
/>
|
||||
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,12 +172,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<template #after>
|
||||
<div
|
||||
data-test="dashboard-import-error-container"
|
||||
class="card-container h-[calc(100vh-110px)] border-l border-border-default"
|
||||
class="card-container h-full flex flex-col min-h-0 border-l border-border-default"
|
||||
>
|
||||
<div class="text-center text-[1.0625rem] font-semibold leading-[1.45] tracking-[-0.02em] text-text-primary py-2">Error Validations</div>
|
||||
<OSeparator class="mt-4" />
|
||||
<div class="text-center text-[0.9375rem] font-semibold text-text-primary py-3 shrink-0">Error Validations</div>
|
||||
<OSeparator class="mt-1 shrink-0" />
|
||||
<div
|
||||
class="error-section p-[10px] mb-[10px]"
|
||||
class="error-section p-[10px] mb-[10px] flex-1 min-h-0 overflow-auto"
|
||||
v-if="dashboardErrorsToDisplay.length > 0"
|
||||
>
|
||||
<div class="error-reporter-container">
|
||||
|
||||
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<!-- eslint-disable vue/x-invalid-end-tag -->
|
||||
<template>
|
||||
<div class="rounded-md ingestionPage h-full flex flex-col" data-test="ingestion-page">
|
||||
<div class="ingestionPage h-full flex flex-col" data-test="ingestion-page">
|
||||
<!-- Standard page header: title + icon. Search / token controls live in the
|
||||
toolbar row below. -->
|
||||
<!-- Standard page header: title + icon, with search / token controls and the
|
||||
|
||||
@@ -215,7 +215,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
class="flex items-center justify-between w-full py-2"
|
||||
>
|
||||
<div
|
||||
class="flex items-center w-full font-bold text-[14px]"
|
||||
class="flex items-center w-full o2-table-footer-title"
|
||||
>
|
||||
{{ scope.totalRows }} Stream(s)
|
||||
<OButton
|
||||
|
||||
@@ -17,55 +17,61 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<!-- eslint-disable vue/v-on-event-hyphenation -->
|
||||
<!-- eslint-disable vue/attribute-hyphenation -->
|
||||
<template>
|
||||
<div :key="store.state.selectedOrganization.identifier">
|
||||
<div class="pb-2.5">
|
||||
<div class="card-container">
|
||||
<div class="flex justify-between items-center py-2 px-3">
|
||||
<div data-test="rum-performance-title" class="text-2xl">
|
||||
{{ t("rum.performanceSummaryLabel") }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<DateTimePickerDashboard
|
||||
class="rum-date-time-picker"
|
||||
ref="dateTimePicker"
|
||||
v-model="selectedDate"
|
||||
menu-align="end"
|
||||
/>
|
||||
<AutoRefreshInterval
|
||||
v-model="refreshInterval"
|
||||
:min-refresh-interval="
|
||||
store.state?.zoConfig?.min_auto_refresh_interval || 5
|
||||
"
|
||||
trigger
|
||||
class="app-performance-auto-refresh-interval"
|
||||
@trigger="refreshData"
|
||||
/>
|
||||
<OButton
|
||||
icon-left="refresh"
|
||||
:variant="isVariablesChanged ? 'ghost-warning' : 'outline'"
|
||||
size="icon-toolbar"
|
||||
data-test="rum-performance-refresh"
|
||||
@click="refreshData"
|
||||
>
|
||||
<OTooltip :content="isVariablesChanged ? t('dashboard.refreshToApplyVariableChanges') : t('dashboard.refresh')" />
|
||||
</OButton>
|
||||
</div>
|
||||
</div>
|
||||
<OTabs
|
||||
class="px-3"
|
||||
v-model="activePerformanceTab"
|
||||
align="left"
|
||||
dense
|
||||
<div
|
||||
:key="store.state.selectedOrganization.identifier"
|
||||
data-test="rum-performance-page"
|
||||
class="w-full h-full flex flex-col min-h-0"
|
||||
>
|
||||
<AppPageHeader
|
||||
:title="t('rum.performanceSummaryLabel')"
|
||||
class="shrink-0 px-4 border-b border-border-default"
|
||||
>
|
||||
<template #title>
|
||||
<span data-test="rum-performance-title">
|
||||
{{ t("rum.performanceSummaryLabel") }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<DateTimePickerDashboard
|
||||
class="rum-date-time-picker"
|
||||
ref="dateTimePicker"
|
||||
v-model="selectedDate"
|
||||
menu-align="end"
|
||||
/>
|
||||
<AutoRefreshInterval
|
||||
v-model="refreshInterval"
|
||||
:min-refresh-interval="
|
||||
store.state?.zoConfig?.min_auto_refresh_interval || 5
|
||||
"
|
||||
trigger
|
||||
class="app-performance-auto-refresh-interval"
|
||||
@trigger="refreshData"
|
||||
/>
|
||||
<OButton
|
||||
icon-left="refresh"
|
||||
:variant="isVariablesChanged ? 'ghost-warning' : 'outline'"
|
||||
size="icon-toolbar"
|
||||
data-test="rum-performance-refresh"
|
||||
@click="refreshData"
|
||||
>
|
||||
<OTab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
:name="tab.value"
|
||||
:label="tab.label"
|
||||
/>
|
||||
</OTabs>
|
||||
</div>
|
||||
</div>
|
||||
<OTooltip :content="isVariablesChanged ? t('dashboard.refreshToApplyVariableChanges') : t('dashboard.refresh')" />
|
||||
</OButton>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
|
||||
<OTabs
|
||||
class="shrink-0 px-3 border-b border-border-default"
|
||||
v-model="activePerformanceTab"
|
||||
align="left"
|
||||
dense
|
||||
>
|
||||
<OTab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
:name="tab.value"
|
||||
:label="tab.label"
|
||||
/>
|
||||
</OTabs>
|
||||
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive>
|
||||
@@ -115,6 +121,7 @@ import usePerformance from "@/composables/rum/usePerformance";
|
||||
import useRum from "@/composables/rum/useRum";
|
||||
import OButton from "@/lib/core/Button/OButton.vue";
|
||||
import OTooltip from "@/lib/overlay/Tooltip/OTooltip.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AppPerformance",
|
||||
@@ -125,6 +132,7 @@ export default defineComponent({
|
||||
DateTimePickerDashboard,
|
||||
OButton,
|
||||
OTooltip,
|
||||
AppPageHeader,
|
||||
},
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -181,7 +181,7 @@ describe("SessionViewer.vue", () => {
|
||||
});
|
||||
|
||||
it('should render the "Go Back" navigation button', () => {
|
||||
const backBtn = wrapper.find('[title="Go Back"]');
|
||||
const backBtn = wrapper.find('[data-test="session-viewer-back-btn"]');
|
||||
expect(backBtn.exists()).toBe(true);
|
||||
});
|
||||
|
||||
@@ -222,7 +222,7 @@ describe("SessionViewer.vue", () => {
|
||||
describe("navigation", () => {
|
||||
it("should call router.back() when Go Back button is clicked", async () => {
|
||||
const backSpy = vi.spyOn(router, "back");
|
||||
const backBtn = wrapper.find('[title="Go Back"]');
|
||||
const backBtn = wrapper.find('[data-test="session-viewer-back-btn"]');
|
||||
expect(backBtn.exists()).toBe(true);
|
||||
await backBtn.trigger("click");
|
||||
expect(backSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -16,59 +16,58 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col qp-2 h-full">
|
||||
<div class="w-full flex items-end border-b border-border-default">
|
||||
<div class="w-full flex card-container px-[0.625rem] py-[0.625rem]">
|
||||
<div
|
||||
class="flex justify-center items-center mr-3 cursor-pointer hover:text-[var(--o2-primary-btn-bg)] border-[1.5px] border-solid rounded-full w-[1.375rem] h-[1.375rem]"
|
||||
title="Go Back"
|
||||
@click="router.back()"
|
||||
>
|
||||
<OIcon name="arrow-back-ios-new" size="xs" />
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5 mr-3">
|
||||
<OIcon name="language" size="sm" />
|
||||
{{ sessionDetails.ip }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5 mr-3">
|
||||
<OIcon name="calendar-month" size="sm" />
|
||||
{{ sessionDetails.date }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5 mr-3">
|
||||
<OIcon name="person" size="sm" />
|
||||
{{ sessionDetails.user_email || "Unknown User" }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5 mr-3">
|
||||
<OIcon name="location-on" size="sm" />
|
||||
{{ sessionDetails.city }}, {{ sessionDetails.country }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5 mr-3">
|
||||
<OIcon name="settings" size="sm" />
|
||||
{{ sessionDetails.browser }}, {{ sessionDetails.os }}
|
||||
</div>
|
||||
<div
|
||||
v-if="frustrationCount > 0"
|
||||
class="text-xs truncate flex items-center"
|
||||
:title="`${frustrationCount} frustration signal${frustrationCount > 1 ? 's' : ''} detected`"
|
||||
data-test="session-viewer-frustration-summary"
|
||||
>
|
||||
<OIcon
|
||||
name="sentiment-very-dissatisfied"
|
||||
size="sm"
|
||||
class="pr-1"
|
||||
style="color: #fb923c"
|
||||
data-test="frustration-summary-icon"
|
||||
/>
|
||||
<span
|
||||
class="font-semibold"
|
||||
style="color: #fb923c"
|
||||
data-test="frustration-summary-text"
|
||||
>{{ frustrationCount }} Frustration{{
|
||||
frustrationCount > 1 ? "s" : ""
|
||||
}}</span
|
||||
<AppPageHeader
|
||||
:title="sessionDetails.id || t('rum.sessionReplay')"
|
||||
:back="{ onClick: () => router.back(), dataTest: 'session-viewer-back-btn' }"
|
||||
class="shrink-0 border-b border-border-default"
|
||||
>
|
||||
<template #subtitle>
|
||||
<div class="flex items-center flex-wrap gap-x-3 gap-y-1 min-w-0">
|
||||
<div class="text-xs truncate flex items-center gap-1.5">
|
||||
<OIcon name="language" size="sm" />
|
||||
{{ sessionDetails.ip }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5">
|
||||
<OIcon name="calendar-month" size="sm" />
|
||||
{{ sessionDetails.date }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5">
|
||||
<OIcon name="person" size="sm" />
|
||||
{{ sessionDetails.user_email || "Unknown User" }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5">
|
||||
<OIcon name="location-on" size="sm" />
|
||||
{{ sessionDetails.city }}, {{ sessionDetails.country }}
|
||||
</div>
|
||||
<div class="text-xs truncate flex items-center gap-1.5">
|
||||
<OIcon name="settings" size="sm" />
|
||||
{{ sessionDetails.browser }}, {{ sessionDetails.os }}
|
||||
</div>
|
||||
<div
|
||||
v-if="frustrationCount > 0"
|
||||
class="text-xs truncate flex items-center"
|
||||
:title="`${frustrationCount} frustration signal${frustrationCount > 1 ? 's' : ''} detected`"
|
||||
data-test="session-viewer-frustration-summary"
|
||||
>
|
||||
<OIcon
|
||||
name="sentiment-very-dissatisfied"
|
||||
size="sm"
|
||||
class="pr-1"
|
||||
style="color: #fb923c"
|
||||
data-test="frustration-summary-icon"
|
||||
/>
|
||||
<span
|
||||
class="font-semibold"
|
||||
style="color: #fb923c"
|
||||
data-test="frustration-summary-text"
|
||||
>{{ frustrationCount }} Frustration{{
|
||||
frustrationCount > 1 ? "s" : ""
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AppPageHeader>
|
||||
<div
|
||||
class="w-full flex card-container overflow-hidden h-[calc(100%-3.125)]! flex-1 min-h-0"
|
||||
>
|
||||
@@ -122,12 +121,14 @@ import { cloneDeep } from "lodash-es";
|
||||
import { computed, onBeforeMount, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import searchService from "@/services/search";
|
||||
import useQuery from "@/composables/useQuery";
|
||||
import useSessionsReplay from "@/composables/useSessionReplay";
|
||||
import usePerformance from "@/composables/rum/usePerformance";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import OSplitter from "@/lib/core/Splitter/OSplitter.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
|
||||
import { formatDate } from "@/utils/date";
|
||||
import { getUUID } from "@/utils/zincutils";
|
||||
@@ -151,6 +152,7 @@ const sessionId = ref("1");
|
||||
const currentTime = ref(0);
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
const isLoading = ref<boolean[]>([]);
|
||||
const { buildQueryPayload, getTimeInterval, parseQuery } = useQuery();
|
||||
const segments = ref<any[]>([]);
|
||||
|
||||
@@ -15,29 +15,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col w-full h-full px-2.5 bg-(--q-background)">
|
||||
<div class="w-full h-full flex flex-col min-h-0 px-2.5 bg-(--q-background)">
|
||||
<!-- Top Header Bar -->
|
||||
<div class="header-bar shrink-0 card-container flex items-center justify-between py-[0.675rem] h-16 px-[0.675rem] mb-[0.675rem] border-b border-[var(--o2-border-color)]">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
data-test="add-alert-back-btn"
|
||||
class="flex justify-center items-center mr-3 cursor-pointer"
|
||||
style="
|
||||
border: 1.5px solid;
|
||||
border-radius: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
"
|
||||
title="Go Back"
|
||||
@click="navigateBack()"
|
||||
>
|
||||
<OIcon name="arrow-back-ios-new" size="xs" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-xl font-semibold text-weight-medium">Upload Source Maps</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AppPageHeader
|
||||
title="Upload Source Maps"
|
||||
:back="{ onClick: navigateBack, dataTest: 'add-alert-back-btn' }"
|
||||
class="px-4 border-b border-border-default"
|
||||
/>
|
||||
|
||||
<!-- Form Content Area -->
|
||||
<div class="flex-1 overflow-y-auto card-container mb-[0.675rem] p-6" style="height: calc(100vh - 172px); overflow: auto">
|
||||
@@ -164,6 +148,7 @@ import sourcemapsService from "@/services/sourcemaps";
|
||||
import OButton from "@/lib/core/Button/OButton.vue";
|
||||
import OInput from "@/lib/forms/Input/OInput.vue";
|
||||
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
||||
import AppPageHeader from "@/components/common/AppPageHeader.vue";
|
||||
import { toast } from "@/lib/feedback/Toast/useToast";
|
||||
|
||||
const store = useStore();
|
||||
|
||||
Reference in New Issue
Block a user